1
Description: Make plugins be able to stall dbus service from signaling that
2
couchdb is ready to use.
3
Bug-Ubuntu: https://bugs.edge.launchpad.net/ubuntu/+source/desktopcouch/+bug/760236
4
Applied-upstream: bzr id chad.miller@canonical.com-20110412173802-flmya9dma0et83qr
5
Author: Chad Miller <chad.miller@canonical.com>
8
=== modified file 'desktopcouch/application/plugins/__init__.py'
9
--- desktopcouch/application/plugins/__init__.py 2011-02-02 18:15:53 +0000
10
+++ desktopcouch/application/plugins/__init__.py 2011-04-15 23:40:28 +0000
12
DESKTOPCOUCH_PLUGIN_PATHS = [os.path.join(os.path.dirname(__file__))]
15
-def load_plugins(couchdb_port):
16
- """Load the desktopcouch application plug-ins."""
17
+def load_plugins(couchdb_port, blocking_semaphores, gobject):
18
+ """Load the desktopcouch application plug-ins.
20
+ The blocking_semaphores set is OPTIONALLY mutated by any plugin to signal
21
+ that the service is not ready until a plugin has finished its asynchronous
22
+ operations. Plugins may add a distinguishing object to the set, and it
23
+ must remove what it adds when it is finished.
25
+ couchdb -- the integer of the port number of the running couchdb
26
+ blocking_semaphores -- the set() of semaphores, which we will mutate
27
+ gobject -- the mainloop module. always 'gobject' except when testing.
30
for path in DESKTOPCOUCH_PLUGIN_PATHS:
33
modpath = name.replace(os.path.sep, '.')[:-3]
35
plugin = __import__(modpath, None, None, [''])
36
- plugin.plugin_init(couchdb_port)
37
+ plugin.plugin_init(couchdb_port, blocking_semaphores, gobject)
38
except (ImportError, AttributeError):
39
logging.warning('Failed to load plug-in: %s', modpath)
41
=== modified file 'desktopcouch/application/plugins/ubuntuone_pairing.py'
42
--- desktopcouch/application/plugins/ubuntuone_pairing.py 2011-02-02 18:15:53 +0000
43
+++ desktopcouch/application/plugins/ubuntuone_pairing.py 2011-04-15 23:41:29 +0000
45
from desktopcouch.application.server import DesktopDatabase
46
from ubuntuone.clientdefs import APP_NAME
48
+PLUGIN_NAME = __name__
49
U1_PAIR_RECORD = "ubuntu_one_pair_record"
50
MAP_JS = """function(doc) {
51
if (doc.service_name == "ubuntuone") {
56
-def pair_with_ubuntuone(couchdb_port, management_db=None,
57
+def pair_with_ubuntuone(couchdb_port, blocking_semaphores,
59
db_class=DesktopDatabase,
60
put_service_fn=put_static_paired_service):
61
"""Adds a pairing record with ubuntu one when needed."""
62
- # Use explicit uri so that we do not access dbus service.
63
- uri = "http://localhost:%s" % (couchdb_port,)
64
- if not management_db:
65
- management_db = db_class("management", uri=uri, create=True, ctx=None)
66
- # we indeed have credentials to add to the pairing records
67
- # but first we ensure that the required view is present
68
- if not management_db.view_exists(U1_PAIR_RECORD, U1_PAIR_RECORD):
69
- management_db.add_view(
70
- U1_PAIR_RECORD, MAP_JS, design_doc=U1_PAIR_RECORD)
71
- view_results = management_db.execute_view(U1_PAIR_RECORD, U1_PAIR_RECORD)
72
- pairing_found = False
73
- # Results should contain either one row or no rows
74
- # If there is one row, its value will be 0, meaning that there is
75
- # already an Ubuntu One pairing record, or 1, meaning that there
76
- # was an Ubuntu One pairing record but it has since been unpaired
77
- # Only create a new record if there is not one already. Specifically,
78
- # do not add the record if there is a deleted one, as this means
79
- # that the user explicitly unpaired it!
80
- for row in view_results:
81
- pairing_found = True
83
- logging.debug("Not adding desktopcouch pairing since the user "
84
- "has explicitly unpaired with Ubuntu One")
86
- logging.debug("Not adding desktopcouch pairing since we are "
88
- if not pairing_found:
89
- put_service_fn(None, "ubuntuone", uri=uri, ctx=None)
90
- logging.debug("Pairing desktopcouch with Ubuntu One")
93
-def got_new_credentials(couchdb_port, app_name, credentials):
95
+ # Use explicit uri so that we do not access dbus service.
96
+ uri = "http://localhost:%s" % (couchdb_port,)
97
+ if not management_db:
98
+ management_db = db_class("management", uri=uri, create=True,
100
+ # we indeed have credentials to add to the pairing records
101
+ # but first we ensure that the required view is present
102
+ if not management_db.view_exists(U1_PAIR_RECORD, U1_PAIR_RECORD):
103
+ management_db.add_view(
104
+ U1_PAIR_RECORD, MAP_JS, design_doc=U1_PAIR_RECORD)
105
+ view_results = management_db.execute_view(U1_PAIR_RECORD,
107
+ pairing_found = False
108
+ # Results should contain either one row or no rows
109
+ # If there is one row, its value will be 0, meaning that there is
110
+ # already an Ubuntu One pairing record, or 1, meaning that there
111
+ # was an Ubuntu One pairing record but it has since been unpaired
112
+ # Only create a new record if there is not one already. Specifically,
113
+ # do not add the record if there is a deleted one, as this means
114
+ # that the user explicitly unpaired it!
115
+ for row in view_results:
116
+ pairing_found = True
118
+ logging.debug("Not adding desktopcouch pairing since the user "
119
+ "has explicitly unpaired with Ubuntu One")
121
+ logging.debug("Not adding desktopcouch pairing since we are "
123
+ if not pairing_found:
124
+ put_service_fn(None, "ubuntuone", uri=uri, ctx=None)
125
+ logging.debug("Pairing desktopcouch with Ubuntu One")
128
+ logging.info("removing semaphore for %s", PLUGIN_NAME)
129
+ blocking_semaphores.discard(PLUGIN_NAME)
132
+def got_new_credentials(couchdb_port, blocking_semaphores,
133
+ app_name, credentials):
134
"""Pair with Ubuntu One when we get the new credentials."""
135
if app_name == APP_NAME:
136
- pair_with_ubuntuone(couchdb_port)
139
-def listen_to_dbus(couchdb_port):
140
+ pair_with_ubuntuone(couchdb_port, blocking_semaphores)
143
+def listen_to_dbus(couchdb_port, blocking_semaphores):
144
"""Set up the signal handler on D-Bus for Ubuntu One pairing."""
146
bus = dbus.SessionBus()
151
- receiver = lambda *args: got_new_credentials(couchdb_port, *args)
152
+ receiver = lambda *args: \
153
+ got_new_credentials(couchdb_port, blocking_semaphores,
156
iface = ubuntu_sso.DBUS_CREDENTIALS_IFACE
157
bus.add_signal_receiver(handler_function=receiver,
159
sso_backend.find_credentials(APP_NAME, {})
161
logging.info('Ubuntu SSO is not available.')
164
-def plugin_init(couchdb_port):
165
+ blocking_semaphores.discard(PLUGIN_NAME)
168
+def plugin_init(couchdb_port, blocking_semaphores, gobject):
169
"""Set up the signal handler for pairing with Ubuntu One."""
170
logging.info('Loaded Ubuntu One extension for desktopcouch.')
171
if sys.platform == 'win32':
172
logging.warning('Windows support for Ubuntu One is not yet ready.')
175
- gobject.idle_add(listen_to_dbus, couchdb_port)
177
+ # Signal that we are critical for desktopcouch usage, and the server
178
+ # must not begin until we are finished. We are responsible for
179
+ # removing this item from the list.
180
+ logging.info("adding %s to to blocking semaphore list", PLUGIN_NAME)
181
+ blocking_semaphores.add(PLUGIN_NAME)
183
+ gobject.idle_add(listen_to_dbus, couchdb_port, blocking_semaphores)
185
=== modified file 'desktopcouch/application/service.py'
186
--- desktopcouch/application/service.py 2011-02-02 18:15:53 +0000
187
+++ desktopcouch/application/service.py 2011-04-15 23:42:17 +0000
190
import logging.handlers
194
from desktopcouch.application import local_files
195
from desktopcouch.application import replication
197
replication_actions=replication,
198
advertiser_factory=PortAdvertiser, set_logging=set_up_logging,
199
fork=os.fork, nice=os.nice,
200
- kill=os.kill, sleep=time.sleep):
201
+ kill=os.kill, sleep=time.sleep, set_type=set,
202
+ gobject_module=gobject):
203
self._mainloop = main_loop
204
self._pid_finder = pid_finder
205
self._port_finder = port_finder
210
+ self._set = set_type
211
+ self._gobject = gobject_module
212
# pylint: enable=C0301
214
def _start_replicator_main(self, couchdb_port):
215
@@ -112,11 +116,30 @@
216
replication.tear_down(*replication_runtime)
218
def _start_server_main(self, couchdb_port):
219
- """Start server."""
220
- self._advertiser_factory(self._mainloop.stop, self._ctx)
221
+ """Start server answering DBus calls, and run plugins first."""
223
+ def if_all_semaphores_cleared(blocking_semaphores,
224
+ func, *args, **kwargs):
225
+ """Run a function if no semaphores exist, else try later."""
226
+ if blocking_semaphores:
227
+ return True # Make idle call try us again.
229
+ func(*args, **kwargs)
230
+ return False # Handled!
232
+ blocking_semaphores = self._set()
233
+ load_plugins(couchdb_port, blocking_semaphores, self._gobject)
235
+ # Answering queries on DBus signals that we are ready for users
236
+ # to connect. We mustn't begin that until every plugin has a chance
237
+ # to run to completion if it needs it.
238
+ self._gobject.idle_add(if_all_semaphores_cleared, blocking_semaphores,
239
+ self._advertiser_factory,
240
+ self._mainloop.stop,
243
logging.debug("starting dbus main loop")
245
- load_plugins(couchdb_port)
248
logging.debug("ending dbus main loop")