Merge lp:~mhr3/dee/private-connections into lp:dee
- private-connections
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Mikkel Kamstrup Erlandsen |
Approved revision: | 338 |
Merged at revision: | 323 |
Proposed branch: | lp:~mhr3/dee/private-connections |
Merge into: | lp:dee |
Diff against target: |
3551 lines (+2453/-255) 30 files modified
.bzrignore (+41/-46) dee/Makefile.am (+5/-0) dee/dee-client.c (+403/-0) dee/dee-client.h (+81/-0) dee/dee-filter-model.c (+11/-11) dee/dee-peer.c (+107/-15) dee/dee-peer.h (+12/-5) dee/dee-sequence-model.c (+1/-1) dee/dee-server.c (+492/-0) dee/dee-server.h (+83/-0) dee/dee-shared-model.c (+307/-145) dee/dee-shared-model.h (+2/-0) dee/dee.h (+2/-0) doc/reference/dee-1.0/dee-1.0-docs.sgml (+2/-0) doc/reference/dee-1.0/dee-1.0.types (+14/-11) tests/Makefile.am (+9/-0) tests/model-helper-add3rows.c (+5/-1) tests/model-helper-change3rows.c (+5/-1) tests/model-helper-clear3add5.c (+5/-1) tests/model-helper-clear3rows.c (+5/-1) tests/model-helper-clone3rows.c (+6/-1) tests/model-helper-insert1row.c (+5/-1) tests/model-helper-remove3rows.c (+5/-1) tests/model-helper-schemaless.c (+4/-1) tests/server-helper-client.c (+108/-0) tests/test-client-server.c (+644/-0) tests/test-dee.c (+2/-0) tests/test-filter-model.c (+6/-2) tools/dee-tool.c (+53/-7) vapi/dee-1.0.vapi (+28/-4) |
To merge this branch: | bzr merge lp:~mhr3/dee/private-connections |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Mikkel Kamstrup Erlandsen (community) | Approve | ||
Review via email: mp+85680@code.launchpad.net |
Commit message
Description of the change
This branch implements possibility to create private connections which can be used to synchronize the shared models.
Mikkel Kamstrup Erlandsen (kamstrup) wrote : | # |
Mikkel Kamstrup Erlandsen (kamstrup) wrote : | # |
Sorry - never mind. Why didn't I just do it myself once I had the thought? ;-) Bug #904299 filed.
Mikkel Kamstrup Erlandsen (kamstrup) wrote : | # |
DeePeer and DeeServer does not seem to be included in the gtk-docs. Can you please add them under the Peer Discovery section?
Wrt to socket addresses.. Are you sure that abstract filenames are per-user? Otherwise we'd end up with collisions. And can you also make triple sure that users can not connect to each other? So on this line:
1115 + // FIXME: check same user_id?
The answer is "yes". This needs to be secure by default. If someone really wants to do some cross user talking, they're gonna have to go through the hazzle themselves I think.
Overall the code looks good, but I want to take it through a more thorough inspection before I approve this.
Mikkel Kamstrup Erlandsen (kamstrup) wrote : | # |
(btw ran the extended tests suite and all tests pass - and distcheck still passes as well - so far so good :-))
- 325. By Michal Hruby
-
Enable also the schemaless test
- 326. By Michal Hruby
-
Add test for multiple connections
- 327. By Michal Hruby
- 328. By Michal Hruby
-
Allow only same user to connect to the abstract socket
- 329. By Michal Hruby
-
Merge trunk
- 330. By Michal Hruby
-
Set schema on server model
- 331. By Michal Hruby
-
Regenerate doc types, add client/server to Peer discovery section
Michal Hruby (mhr3) wrote : | # |
A reminder: DeeClient needs to watch the connection and emit connection-closed when that happens.
- 332. By Michal Hruby
-
Make sure we emit peer-found & peer-lost signals on DeeServer and connection-closed on DeeClient
- 333. By Michal Hruby
-
Make sure we wait for the server to initialize when testing client connections
- 334. By Michal Hruby
-
Add dee_{client,
server} _new_for_ address and documentation - 335. By Michal Hruby
-
A couple of docs fixes
- 336. By Michal Hruby
-
Fix warnings
- 337. By Michal Hruby
-
Few more docs fixes
Michal Hruby (mhr3) wrote : | # |
Should be mostly ready to go...
Mikkel Kamstrup Erlandsen (kamstrup) wrote : | # |
Awesome work dude! Code looks good and test tests are passing. Just three nitpicks and we can merge:
410 + * created using dee_server_new. The #DeePeer:
Can you add braces, dee_server_new(), so we gtk-doc to make the function a link.
1404 + trace_object ("User id doesn't match, rejecting connection");
This call needs a 'self' argument
.... aaand! please update the VAPI! (yes yes! I finally nailed you on that one! :-D)
- 338. By Michal Hruby
-
Fix issues brought up during review
Michal Hruby (mhr3) wrote : | # |
Done, done, done...
Mikkel Kamstrup Erlandsen (kamstrup) wrote : | # |
Great work!
Preview Diff
1 | === modified file '.bzrignore' |
2 | --- .bzrignore 2010-04-15 13:29:59 +0000 |
3 | +++ .bzrignore 2011-12-19 08:59:26 +0000 |
4 | @@ -12,7 +12,7 @@ |
5 | config.status |
6 | config.sub |
7 | configure |
8 | -dbusmodel.pc |
9 | +dee-1.0.pc |
10 | depcomp |
11 | install-sh |
12 | libtool |
13 | @@ -29,72 +29,67 @@ |
14 | build/autotools/ltsugar.m4 |
15 | build/autotools/ltversion.m4 |
16 | build/autotools/lt~obsolete.m4 |
17 | -dbusmodel/DbusModel-0.1.gir |
18 | -dbusmodel/DbusModel-0.1.typelib |
19 | -dbusmodel/Makefile |
20 | -dbusmodel/Makefile.in |
21 | -dbusmodel/libdbusmodel.la |
22 | -dbusmodel/libdbusmodel_la-dbus-model.lo |
23 | doc/Makefile |
24 | doc/Makefile.in |
25 | doc/reference/Makefile |
26 | doc/reference/Makefile.in |
27 | -doc/reference/libdbusmodel/Makefile |
28 | -doc/reference/libdbusmodel/Makefile.in |
29 | examples/Makefile |
30 | examples/Makefile.in |
31 | examples/synced-lists |
32 | tests/Makefile |
33 | tests/Makefile.in |
34 | -tests/test-dbusmodel |
35 | -tests/test-dbusmodel-results.xml |
36 | -tests/test-dbusmodel-results.html |
37 | -doc/reference/libdbusmodel/html/ |
38 | -doc/reference/libdbusmodel/html-build.stamp |
39 | -doc/reference/libdbusmodel/html.stamp |
40 | -doc/reference/libdbusmodel/libdbusmodel-decl-list.txt |
41 | -doc/reference/libdbusmodel/libdbusmodel-decl.txt |
42 | -doc/reference/libdbusmodel/libdbusmodel-decl.txt.bak |
43 | -doc/reference/libdbusmodel/libdbusmodel-overrides.txt |
44 | -doc/reference/libdbusmodel/libdbusmodel-sections.txt |
45 | -doc/reference/libdbusmodel/libdbusmodel-undeclared.txt |
46 | -doc/reference/libdbusmodel/libdbusmodel-undocumented.txt |
47 | -doc/reference/libdbusmodel/libdbusmodel-unused.txt |
48 | -doc/reference/libdbusmodel/libdbusmodel.args |
49 | -doc/reference/libdbusmodel/libdbusmodel.hierarchy |
50 | -doc/reference/libdbusmodel/libdbusmodel.interfaces |
51 | -doc/reference/libdbusmodel/libdbusmodel.prerequisites |
52 | -doc/reference/libdbusmodel/libdbusmodel.signals |
53 | -doc/reference/libdbusmodel/libdbusmodel.types |
54 | -doc/reference/libdbusmodel/scan-build.stamp |
55 | -doc/reference/libdbusmodel/sgml-build.stamp |
56 | -doc/reference/libdbusmodel/sgml.stamp |
57 | -doc/reference/libdbusmodel/tmpl/ |
58 | -doc/reference/libdbusmodel/tmpl-build.stamp |
59 | -doc/reference/libdbusmodel/tmpl.stamp |
60 | -doc/reference/libdbusmodel/xml/ |
61 | +dee/Dee-1.0.gir |
62 | +dee/Dee-1.0.typelib |
63 | +dee/com.canonical.Dee.Model-xml.h |
64 | +dee/com.canonical.Dee.Peer-xml.h |
65 | +dee/dee-marshal.c |
66 | +dee/dee-marshal.h |
67 | +dee/libdee-1.0.la |
68 | +doc/reference/dee-1.0/dee-1.0-decl-list.txt |
69 | +doc/reference/dee-1.0/dee-1.0-decl.txt |
70 | +doc/reference/dee-1.0/dee-1.0-overrides.txt |
71 | +doc/reference/dee-1.0/dee-1.0-sections.txt |
72 | +doc/reference/dee-1.0/dee-1.0-undeclared.txt |
73 | +doc/reference/dee-1.0/dee-1.0-undocumented.txt |
74 | +doc/reference/dee-1.0/dee-1.0-unused.txt |
75 | +doc/reference/dee-1.0/dee-1.0.args |
76 | +doc/reference/dee-1.0/dee-1.0.hierarchy |
77 | +doc/reference/dee-1.0/dee-1.0.interfaces |
78 | +doc/reference/dee-1.0/dee-1.0.prerequisites |
79 | +doc/reference/dee-1.0/dee-1.0.signals |
80 | +doc/reference/dee-1.0/html/ |
81 | +doc/reference/dee-1.0/html-build.stamp |
82 | +doc/reference/dee-1.0/html.stamp |
83 | +doc/reference/dee-1.0/scan-build.stamp |
84 | +doc/reference/dee-1.0/setup-build.stamp |
85 | +doc/reference/dee-1.0/sgml-build.stamp |
86 | +doc/reference/dee-1.0/sgml.stamp |
87 | +doc/reference/dee-1.0/tmpl/ |
88 | +doc/reference/dee-1.0/tmpl-build.stamp |
89 | +doc/reference/dee-1.0/tmpl.stamp |
90 | +doc/reference/dee-1.0/xml/ |
91 | .lo |
92 | *.lo |
93 | .lo |
94 | -dbusmodel/dbus-model-client.h |
95 | -dbusmodel/dbus-model-server.h |
96 | -doc/reference/libdbusmodel/libdbusmodel-decl |
97 | -doc/reference/libdbusmodel/libdbusmodel-decl-list.txt.bak |
98 | examples/peers |
99 | -dbusmodel/dbus-model-marshal.list |
100 | examples/master-model.c |
101 | examples/slave-model.c |
102 | -dbusmodel/dbus-model-marshal.h |
103 | -dbusmodel/dbus-model-marshal.c |
104 | examples/master-model |
105 | examples/slave-model |
106 | examples/test |
107 | examples/master-model-vala |
108 | -dbusmodel-0.1.0.tar.gz |
109 | -dbusmodel/dbus-swarm-client.h |
110 | -dbusmodel/dbus-swarm-server.h |
111 | +tools/dee-tool |
112 | tests/model-helper-add3rows |
113 | tests/model-helper-change3rows |
114 | tests/model-helper-clear3rows |
115 | tests/model-helper-clone3rows |
116 | tests/model-helper-insert1row |
117 | +tests/model-helper-clear3add5 |
118 | +tests/model-helper-introspect |
119 | +tests/model-helper-remove3rows |
120 | +tests/model-helper-schemaless |
121 | +tests/peer-helper-1peer |
122 | +tests/server-helper-client |
123 | +tests/test-dee |
124 | +tests/test-benchmark |
125 | +tests/test-dee-results.xml |
126 | |
127 | === modified file 'dee/Makefile.am' |
128 | --- dee/Makefile.am 2011-12-15 12:28:43 +0000 |
129 | +++ dee/Makefile.am 2011-12-19 08:59:26 +0000 |
130 | @@ -38,6 +38,8 @@ |
131 | dee-model.h \ |
132 | dee-model-reader.h \ |
133 | dee-peer.h \ |
134 | + dee-server.h \ |
135 | + dee-client.h \ |
136 | dee-proxy-model.h \ |
137 | dee-resource-manager.h \ |
138 | dee-result-set.h \ |
139 | @@ -77,6 +79,8 @@ |
140 | dee-marshal.c \ |
141 | dee-marshal.h \ |
142 | dee-peer.c \ |
143 | + dee-server.c \ |
144 | + dee-client.c \ |
145 | dee-proxy-model.c \ |
146 | dee-resource-manager.c \ |
147 | dee-result-set.c \ |
148 | @@ -167,6 +171,7 @@ |
149 | -I$(top_builddir) \ |
150 | -DDEE_COMPILATION \ |
151 | --include=GObject-2.0 \ |
152 | + --include=Gio-2.0 \ |
153 | --include=GLib-2.0 \ |
154 | --library=dee-1.0 \ |
155 | --output $@ $(irscanner_sources) |
156 | |
157 | === added file 'dee/dee-client.c' |
158 | --- dee/dee-client.c 1970-01-01 00:00:00 +0000 |
159 | +++ dee/dee-client.c 2011-12-19 08:59:26 +0000 |
160 | @@ -0,0 +1,403 @@ |
161 | +/* |
162 | + * Copyright (C) 2011 Canonical, Ltd. |
163 | + * |
164 | + * This library is free software; you can redistribute it and/or modify |
165 | + * it under the terms of the GNU Lesser General Public License |
166 | + * version 3.0 as published by the Free Software Foundation. |
167 | + * |
168 | + * This library is distributed in the hope that it will be useful, |
169 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
170 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
171 | + * GNU Lesser General Public License version 3.0 for more details. |
172 | + * |
173 | + * You should have received a copy of the GNU Lesser General Public |
174 | + * License along with this library. If not, see |
175 | + * <http://www.gnu.org/licenses/>. |
176 | + * |
177 | + * Authored by: Michal Hruby <michal.hruby@canonical.com> |
178 | + * |
179 | + */ |
180 | +/** |
181 | + * SECTION:dee-client |
182 | + * @short_description: Creates a client object you can use to connect |
183 | + * to a #DeeServer. |
184 | + * @include: dee.h |
185 | + * |
186 | + * #DeeClient is the endpoint for connecting to #DeeServer. |
187 | + */ |
188 | + |
189 | +#ifdef HAVE_CONFIG_H |
190 | +#include <config.h> |
191 | +#endif |
192 | + |
193 | +#include <gio/gio.h> |
194 | + |
195 | +#include "dee-client.h" |
196 | +#include "dee-marshal.h" |
197 | +#include "trace-log.h" |
198 | + |
199 | +G_DEFINE_TYPE (DeeClient, dee_client, DEE_TYPE_PEER) |
200 | + |
201 | +#define GET_PRIVATE(o) \ |
202 | + (G_TYPE_INSTANCE_GET_PRIVATE ((o), DEE_TYPE_CLIENT, DeeClientPrivate)) |
203 | + |
204 | +/** |
205 | + * DeeClientPrivate: |
206 | + * |
207 | + * Ignore this structure. |
208 | + **/ |
209 | +struct _DeeClientPrivate |
210 | +{ |
211 | + GDBusConnection *connection; |
212 | + GCancellable *cancellable; |
213 | + gchar *bus_address; |
214 | + |
215 | + guint peer_found_timer_id; |
216 | + gulong closed_signal_handler_id; |
217 | +}; |
218 | + |
219 | +/* Globals */ |
220 | +enum |
221 | +{ |
222 | + PROP_0, |
223 | + PROP_BUS_ADDRESS |
224 | +}; |
225 | + |
226 | +enum |
227 | +{ |
228 | + LAST_SIGNAL |
229 | +}; |
230 | + |
231 | +//static guint32 _server_signals[LAST_SIGNAL] = { 0 }; |
232 | + |
233 | +/* Forwards */ |
234 | +static gboolean dee_client_is_swarm_leader (DeePeer *peer); |
235 | + |
236 | +static const gchar* dee_client_get_swarm_leader (DeePeer *peer); |
237 | + |
238 | +static GSList* dee_client_get_connections (DeePeer *peer); |
239 | + |
240 | +static void connecting_finished (GObject *object, |
241 | + GAsyncResult *res, |
242 | + gpointer user_data); |
243 | + |
244 | +static void connection_closed (GDBusConnection *connection, |
245 | + gboolean remote_peer_vanished, |
246 | + GError *error, |
247 | + DeeClient *client); |
248 | + |
249 | +/* GObject methods */ |
250 | +static void |
251 | +dee_client_get_property (GObject *object, guint property_id, |
252 | + GValue *value, GParamSpec *pspec) |
253 | +{ |
254 | + DeeClientPrivate *priv; |
255 | + |
256 | + priv = DEE_CLIENT (object)->priv; |
257 | + |
258 | + switch (property_id) |
259 | + { |
260 | + case PROP_BUS_ADDRESS: |
261 | + g_value_set_string (value, priv->bus_address); |
262 | + break; |
263 | + default: |
264 | + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); |
265 | + } |
266 | +} |
267 | + |
268 | +static void |
269 | +dee_client_set_property (GObject *object, guint property_id, |
270 | + const GValue *value, GParamSpec *pspec) |
271 | +{ |
272 | + DeeClientPrivate *priv; |
273 | + |
274 | + priv = DEE_CLIENT (object)->priv; |
275 | + |
276 | + switch (property_id) |
277 | + { |
278 | + case PROP_BUS_ADDRESS: |
279 | + if (priv->bus_address) g_free (priv->bus_address); |
280 | + priv->bus_address = g_value_dup_string (value); |
281 | + break; |
282 | + default: |
283 | + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); |
284 | + } |
285 | +} |
286 | + |
287 | +static void |
288 | +dee_client_constructed (GObject *self) |
289 | +{ |
290 | + DeeClientPrivate *priv; |
291 | + const gchar *swarm_name; |
292 | + GDBusConnectionFlags flags; |
293 | + |
294 | + priv = DEE_CLIENT (self)->priv; |
295 | + |
296 | + /* we should chain up the constructed method here, but peer does things we |
297 | + * don't want to, so not chaining up... */ |
298 | + |
299 | + swarm_name = dee_peer_get_swarm_name (DEE_PEER (self)); |
300 | + if (swarm_name == NULL) |
301 | + { |
302 | + g_critical ("DeeClient created without a swarm name. You must specify " |
303 | + "a non-NULL swarm name"); |
304 | + return; |
305 | + } |
306 | + |
307 | + if (!priv->bus_address) |
308 | + { |
309 | + const gchar *username = g_get_user_name (); |
310 | + priv->bus_address = g_strdup_printf ("unix:abstract=%s-%s", |
311 | + username, swarm_name); |
312 | + } |
313 | + |
314 | + flags = G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT; |
315 | + priv->cancellable = g_cancellable_new (); |
316 | + g_dbus_connection_new_for_address (priv->bus_address, |
317 | + flags, |
318 | + NULL, // AuthObserver |
319 | + priv->cancellable, |
320 | + connecting_finished, |
321 | + self); |
322 | +} |
323 | + |
324 | +static void |
325 | +dee_client_finalize (GObject *object) |
326 | +{ |
327 | + DeeClientPrivate *priv; |
328 | + |
329 | + priv = DEE_CLIENT (object)->priv; |
330 | + |
331 | + if (priv->cancellable) |
332 | + { |
333 | + g_cancellable_cancel (priv->cancellable); |
334 | + g_object_unref (priv->cancellable); |
335 | + } |
336 | + |
337 | + if (priv->closed_signal_handler_id) |
338 | + { |
339 | + g_signal_handler_disconnect (priv->connection, |
340 | + priv->closed_signal_handler_id); |
341 | + priv->closed_signal_handler_id = 0; |
342 | + } |
343 | + |
344 | + if (priv->connection) |
345 | + { |
346 | + // FIXME: close the connection? |
347 | + g_object_unref (priv->connection); |
348 | + } |
349 | + |
350 | + if (priv->peer_found_timer_id) |
351 | + { |
352 | + g_source_remove (priv->peer_found_timer_id); |
353 | + priv->peer_found_timer_id = 0; |
354 | + } |
355 | + |
356 | + if (priv->bus_address) |
357 | + { |
358 | + g_free (priv->bus_address); |
359 | + } |
360 | + |
361 | + G_OBJECT_CLASS (dee_client_parent_class)->finalize (object); |
362 | +} |
363 | + |
364 | +static void |
365 | +dee_client_class_init (DeeClientClass *klass) |
366 | +{ |
367 | + GParamSpec *pspec; |
368 | + GObjectClass *object_class = G_OBJECT_CLASS (klass); |
369 | + DeePeerClass *peer_class = DEE_PEER_CLASS (klass); |
370 | + |
371 | + g_type_class_add_private (klass, sizeof (DeeClientPrivate)); |
372 | + |
373 | + object_class->constructed = dee_client_constructed; |
374 | + object_class->get_property = dee_client_get_property; |
375 | + object_class->set_property = dee_client_set_property; |
376 | + object_class->finalize = dee_client_finalize; |
377 | + |
378 | + peer_class->is_swarm_leader = dee_client_is_swarm_leader; |
379 | + peer_class->get_swarm_leader = dee_client_get_swarm_leader; |
380 | + peer_class->get_connections = dee_client_get_connections; |
381 | + |
382 | + /** |
383 | + * DeeClient::bus-address: |
384 | + * |
385 | + * D-Bus address the client will connect to. If you do not specify this |
386 | + * property DeeClient will try to use abstract unix domain socket where |
387 | + * its name is a concatenation of user name running current process and |
388 | + * the swarm name. |
389 | + */ |
390 | + pspec = g_param_spec_string ("bus-address", "Bus address", |
391 | + "Bus address to use for the connection", |
392 | + NULL, |
393 | + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
394 | + | G_PARAM_STATIC_STRINGS); |
395 | + g_object_class_install_property (object_class, PROP_BUS_ADDRESS, pspec); |
396 | +} |
397 | + |
398 | +static void |
399 | +dee_client_init (DeeClient *self) |
400 | +{ |
401 | + self->priv = GET_PRIVATE (self); |
402 | +} |
403 | + |
404 | +/** |
405 | + * dee_client_new: |
406 | + * |
407 | + * @swarm_name: Name of swarm to join. |
408 | + * |
409 | + * Creates a new instance of #DeeClient and tries to connect to #DeeServer |
410 | + * created using dee_server_new(). The #DeePeer:swarm-leader property will |
411 | + * be set once the client connects. |
412 | + * |
413 | + * Return value: (transfer full): A newly constructed #DeeClient. |
414 | + */ |
415 | +DeeClient* |
416 | +dee_client_new (const gchar* swarm_name) |
417 | +{ |
418 | + g_return_val_if_fail (swarm_name != NULL, NULL); |
419 | + |
420 | + return DEE_CLIENT (g_object_new (DEE_TYPE_CLIENT, |
421 | + "swarm-name", swarm_name, NULL)); |
422 | +} |
423 | + |
424 | +/** |
425 | + * dee_client_new_for_address: |
426 | + * |
427 | + * @swarm_name: Name of swarm to join. |
428 | + * @bus_address: D-Bus address to use when connecting to the server. |
429 | + * |
430 | + * Creates a new instance of #DeeClient and tries to connect to @bus_address. |
431 | + * The #DeePeer:swarm-leader property will be set once the client connects. |
432 | + * |
433 | + * Return value: (transfer full): A newly constructed #DeeClient. |
434 | + */ |
435 | +DeeClient* |
436 | +dee_client_new_for_address (const gchar* swarm_name, |
437 | + const gchar* bus_address) |
438 | +{ |
439 | + g_return_val_if_fail (swarm_name != NULL, NULL); |
440 | + |
441 | + return DEE_CLIENT (g_object_new (DEE_TYPE_CLIENT, |
442 | + "swarm-name", swarm_name, |
443 | + "bus-address", bus_address, NULL)); |
444 | +} |
445 | + |
446 | +/* Private Methods */ |
447 | + |
448 | +static gboolean |
449 | +dee_client_is_swarm_leader (DeePeer *peer) |
450 | +{ |
451 | + return FALSE; |
452 | +} |
453 | + |
454 | +static const gchar* |
455 | +dee_client_get_swarm_leader (DeePeer *peer) |
456 | +{ |
457 | + DeeClientPrivate *priv; |
458 | + |
459 | + priv = DEE_CLIENT (peer)->priv; |
460 | + return priv->connection ? g_dbus_connection_get_guid (priv->connection) : NULL; |
461 | +} |
462 | + |
463 | +static GSList* |
464 | +dee_client_get_connections (DeePeer *peer) |
465 | +{ |
466 | + DeeClientPrivate *priv; |
467 | + GSList *list = NULL; |
468 | + |
469 | + priv = DEE_CLIENT (peer)->priv; |
470 | + |
471 | + if (priv->connection) |
472 | + { |
473 | + list = g_slist_append (list, priv->connection); |
474 | + } |
475 | + |
476 | + return list; |
477 | +} |
478 | + |
479 | +static gboolean |
480 | +emit_peer_found (gpointer user_data) |
481 | +{ |
482 | + g_return_val_if_fail (DEE_IS_CLIENT (user_data), FALSE); |
483 | + |
484 | + DeeClientPrivate *priv = DEE_CLIENT (user_data)->priv; |
485 | + |
486 | + g_signal_emit_by_name (user_data, "peer-found", |
487 | + g_dbus_connection_get_guid (priv->connection)); |
488 | + |
489 | + priv->peer_found_timer_id = 0; |
490 | + |
491 | + return FALSE; |
492 | +} |
493 | + |
494 | +static void |
495 | +connecting_finished (GObject *object, GAsyncResult *res, gpointer user_data) |
496 | +{ |
497 | + GDBusConnection *connection; |
498 | + DeeClient *self; |
499 | + DeeClientPrivate *priv; |
500 | + GError *error = NULL; |
501 | + |
502 | + connection = g_dbus_connection_new_for_address_finish (res, &error); |
503 | + |
504 | + if (error) |
505 | + { |
506 | + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) |
507 | + { |
508 | + g_warning ("Unable to connect to server: %s", error->message); |
509 | + // swarm-leader will be set to NULL for unsuccessful connections |
510 | + g_object_notify (G_OBJECT (user_data), "swarm-leader"); |
511 | + } |
512 | + /* Don't touch the object in case we were cancelled, it's most likely |
513 | + * unreffed by now */ |
514 | + |
515 | + g_error_free (error); |
516 | + return; |
517 | + } |
518 | + |
519 | + self = DEE_CLIENT (user_data); |
520 | + priv = self->priv; |
521 | + priv->connection = connection; |
522 | + |
523 | + g_object_unref (priv->cancellable); |
524 | + priv->cancellable = NULL; |
525 | + |
526 | + priv->closed_signal_handler_id = g_signal_connect (connection, "closed", |
527 | + G_CALLBACK (connection_closed), self); |
528 | + |
529 | + g_object_notify (G_OBJECT (user_data), "swarm-leader"); |
530 | + |
531 | + g_signal_emit_by_name (user_data, "connection-acquired", connection); |
532 | + |
533 | + // FIXME: we might want to call some List method (same as DeePeer), so far |
534 | + // we'll just simulate an async method (tests expect this anyway) |
535 | + priv->peer_found_timer_id = g_idle_add_full (G_PRIORITY_DEFAULT, |
536 | + emit_peer_found, user_data, |
537 | + NULL); |
538 | +} |
539 | + |
540 | +static void |
541 | +connection_closed (GDBusConnection *connection, gboolean remote_peer_vanished, |
542 | + GError *error, DeeClient *client) |
543 | +{ |
544 | + DeeClientPrivate *priv; |
545 | + |
546 | + g_return_if_fail (DEE_IS_CLIENT (client)); |
547 | + |
548 | + priv = client->priv; |
549 | + priv->connection = NULL; |
550 | + |
551 | + g_signal_handler_disconnect (connection, priv->closed_signal_handler_id); |
552 | + priv->closed_signal_handler_id = 0; |
553 | + |
554 | + /* Let's do reverse order of connecting_finished */ |
555 | + g_signal_emit_by_name (client, "peer-lost", |
556 | + g_dbus_connection_get_guid (priv->connection)); |
557 | + g_signal_emit_by_name (client, "connection-closed", connection); |
558 | + |
559 | + g_object_notify (G_OBJECT (client), "swarm-leader"); |
560 | + |
561 | + g_object_unref (connection); |
562 | +} |
563 | + |
564 | |
565 | === added file 'dee/dee-client.h' |
566 | --- dee/dee-client.h 1970-01-01 00:00:00 +0000 |
567 | +++ dee/dee-client.h 2011-12-19 08:59:26 +0000 |
568 | @@ -0,0 +1,81 @@ |
569 | +/* |
570 | + * Copyright (C) 2011 Canonical, Ltd. |
571 | + * |
572 | + * This library is free software; you can redistribute it and/or modify |
573 | + * it under the terms of the GNU Lesser General Public License |
574 | + * version 3.0 as published by the Free Software Foundation. |
575 | + * |
576 | + * This library is distributed in the hope that it will be useful, |
577 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
578 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
579 | + * GNU Lesser General Public License version 3.0 for more details. |
580 | + * |
581 | + * You should have received a copy of the GNU Lesser General Public |
582 | + * License along with this library. If not, see |
583 | + * <http://www.gnu.org/licenses/>. |
584 | + * |
585 | + * Authored by Michal Hruby <michal.hruby@canonical.com> |
586 | + */ |
587 | + |
588 | +#if !defined (_DEE_H_INSIDE) && !defined (DEE_COMPILATION) |
589 | +#error "Only <dee.h> can be included directly." |
590 | +#endif |
591 | + |
592 | +#ifndef _HAVE_DEE_CLIENT_H |
593 | +#define _HAVE_DEE_CLIENT_H |
594 | + |
595 | +#include <glib.h> |
596 | +#include <glib-object.h> |
597 | +#include "dee-peer.h" |
598 | + |
599 | +G_BEGIN_DECLS |
600 | + |
601 | +#define DEE_TYPE_CLIENT dee_client_get_type() |
602 | + |
603 | +#define DEE_CLIENT(obj) \ |
604 | + (G_TYPE_CHECK_INSTANCE_CAST ((obj), DEE_TYPE_CLIENT, DeeClient)) |
605 | + |
606 | +#define DEE_CLIENT_CLASS(klass) \ |
607 | + (G_TYPE_CHECK_CLASS_CAST ((klass), DEE_TYPE_CLIENT, DeeClientClass)) |
608 | + |
609 | +#define DEE_IS_CLIENT(obj) \ |
610 | + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DEE_TYPE_CLIENT)) |
611 | + |
612 | +#define DEE_IS_CLIENT_CLASS(klass) \ |
613 | + (G_TYPE_CHECK_CLASS_TYPE ((klass), DEE_TYPE_CLIENT)) |
614 | + |
615 | +#define DEE_CLIENT_GET_CLASS(obj) \ |
616 | + (G_TYPE_INSTANCE_GET_CLASS ((obj), DEE_TYPE_CLIENT, DeeClientClass)) |
617 | + |
618 | +typedef struct _DeeClientPrivate DeeClientPrivate; |
619 | + |
620 | +typedef struct { |
621 | + /*< private >*/ |
622 | + DeePeer parent; |
623 | + |
624 | + DeeClientPrivate *priv; |
625 | +} DeeClient; |
626 | + |
627 | +typedef struct { |
628 | + /*< private >*/ |
629 | + DeePeerClass parent_class; |
630 | +} DeeClientClass; |
631 | + |
632 | +/** |
633 | + * dee_client_get_type: |
634 | + * |
635 | + * The GType of #DeeClient. |
636 | + * |
637 | + * Return value: the #GType of #DeeClient. |
638 | + **/ |
639 | +GType dee_client_get_type (void); |
640 | + |
641 | +DeeClient* dee_client_new (const gchar *swarm_name); |
642 | + |
643 | +DeeClient* dee_client_new_for_address (const gchar* swarm_name, |
644 | + const gchar* bus_address); |
645 | + |
646 | +G_END_DECLS |
647 | + |
648 | +#endif /* _HAVE_DEE_CLIENT_H */ |
649 | + |
650 | |
651 | === modified file 'dee/dee-filter-model.c' |
652 | --- dee/dee-filter-model.c 2011-12-14 14:44:58 +0000 |
653 | +++ dee/dee-filter-model.c 2011-12-19 08:59:26 +0000 |
654 | @@ -35,8 +35,8 @@ |
655 | * a "transfomation map" to the filtered data). |
656 | * |
657 | * The reuse of row iters also minimizes the amount of memory shuffling needed |
658 | - * to set up a filter model. The filtering functions, #DeeModelMapFunc and |
659 | - * #DeeModelMapNotify, has also been designed to minimize the amount of work |
660 | + * to set up a filter model. The filtering functions, #DeeFilterMapFunc and |
661 | + * #DeeFilterMapNotify, has also been designed to minimize the amount of work |
662 | * done to create a filter model. So if the filter functions are written |
663 | * optimally the resulting filter models should be cheap to construct. |
664 | * |
665 | @@ -396,8 +396,8 @@ |
666 | * Includes @iter from the back end model in the filtered model, appending |
667 | * it to the end of the filtered rows. |
668 | * |
669 | - * This method is usually called when implementing #DeeModelMapFunc or |
670 | - * #DeeModelMapNotify methods. |
671 | + * This method is usually called when implementing #DeeFilterMapFunc or |
672 | + * #DeeFilterMapNotify methods. |
673 | * |
674 | * Return value: (transfer none): Always returns @iter |
675 | */ |
676 | @@ -436,8 +436,8 @@ |
677 | * Includes @iter from the back end model in the filtered model, prepending |
678 | * it to the beginning of the filtered rows. |
679 | * |
680 | - * This method is usually called when implementing #DeeModelMapFunc or |
681 | - * #DeeModelMapNotify methods. |
682 | + * This method is usually called when implementing #DeeFilterMapFunc or |
683 | + * #DeeFilterMapNotify methods. |
684 | * |
685 | * Return value: (transfer none): Always returns @iter |
686 | */ |
687 | @@ -476,8 +476,8 @@ |
688 | * Includes @iter from the back end model in the filtered model, inserting it at |
689 | * @pos pushing other rows down. |
690 | * |
691 | - * This method is usually called when implementing #DeeModelMapFunc or |
692 | - * #DeeModelMapNotify methods. |
693 | + * This method is usually called when implementing #DeeFilterMapFunc or |
694 | + * #DeeFilterMapNotify methods. |
695 | * |
696 | * Return value: (transfer none): Always returns @iter |
697 | */ |
698 | @@ -504,8 +504,8 @@ |
699 | * Includes @iter from the back end model in the filtered model, inserting it at |
700 | * the position before @pos pushing other rows down. |
701 | * |
702 | - * This method is usually called when implementing #DeeModelMapFunc or |
703 | - * #DeeModelMapNotify methods. |
704 | + * This method is usually called when implementing #DeeFilterMapFunc or |
705 | + * #DeeFilterMapNotify methods. |
706 | * |
707 | * Return value: (transfer none): Always returns @iter |
708 | */ |
709 | @@ -555,7 +555,7 @@ |
710 | * is already ordered this way. If that's not the case then this method has |
711 | * undefined behaviour. |
712 | * |
713 | - * This method is mainly intended as a helper for #DeeModelMapNotify functions |
714 | + * This method is mainly intended as a helper for #DeeFilterMapNotify functions |
715 | * of #DeeFilter implementations that creates filter models sorted in |
716 | * accordance with the original models. |
717 | * |
718 | |
719 | === modified file 'dee/dee-peer.c' |
720 | --- dee/dee-peer.c 2011-12-01 16:00:03 +0000 |
721 | +++ dee/dee-peer.c 2011-12-19 08:59:26 +0000 |
722 | @@ -129,9 +129,10 @@ |
723 | |
724 | enum |
725 | { |
726 | - CONNECTED, |
727 | PEER_FOUND, |
728 | PEER_LOST, |
729 | + CONNECTION_ACQUIRED, |
730 | + CONNECTION_CLOSED, |
731 | |
732 | LAST_SIGNAL |
733 | }; |
734 | @@ -198,7 +199,13 @@ |
735 | gboolean incoming, |
736 | gpointer user_data); |
737 | |
738 | -/* GObject methods */ |
739 | +static const gchar* dee_peer_real_get_swarm_leader (DeePeer *self); |
740 | + |
741 | +static gboolean dee_peer_real_is_swarm_leader (DeePeer *self); |
742 | + |
743 | +static GSList* dee_peer_real_get_connections (DeePeer *self); |
744 | + |
745 | + /* GObject methods */ |
746 | static void |
747 | dee_peer_finalize (GObject *object) |
748 | { |
749 | @@ -382,7 +389,7 @@ |
750 | g_value_set_string (value, DEE_PEER (object)->priv->swarm_name); |
751 | break; |
752 | case PROP_SWARM_LEADER: |
753 | - g_value_set_string (value, DEE_PEER (object)->priv->swarm_leader); |
754 | + g_value_set_string (value, dee_peer_get_swarm_leader (DEE_PEER (object))); |
755 | break; |
756 | default: |
757 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, id, pspec); |
758 | @@ -401,6 +408,10 @@ |
759 | obj_class->get_property = dee_peer_get_property; |
760 | obj_class->constructed = dee_peer_constructed; |
761 | |
762 | + /* Virtual methods */ |
763 | + klass->get_swarm_leader = dee_peer_real_get_swarm_leader; |
764 | + klass->is_swarm_leader = dee_peer_real_is_swarm_leader; |
765 | + klass->get_connections = dee_peer_real_get_connections; |
766 | |
767 | /* Add Signals */ |
768 | |
769 | @@ -439,6 +450,42 @@ |
770 | G_TYPE_NONE, 1, |
771 | G_TYPE_STRING); |
772 | |
773 | + /** |
774 | + * DeePeer::new-connection: |
775 | + * @self: the #DeePeer on which the signal is emitted |
776 | + * @connection: the new #GDBusConnection |
777 | + * |
778 | + * Connect to this signal to be notified when peers connect via |
779 | + * new #GDBusConnection. |
780 | + **/ |
781 | + _peer_signals[CONNECTION_ACQUIRED] = |
782 | + g_signal_new ("connection-acquired", |
783 | + G_TYPE_FROM_CLASS (klass), |
784 | + G_SIGNAL_RUN_LAST, |
785 | + G_STRUCT_OFFSET (DeePeerClass, connection_acquired), |
786 | + NULL, NULL, |
787 | + g_cclosure_marshal_VOID__OBJECT, |
788 | + G_TYPE_NONE, 1, |
789 | + G_TYPE_DBUS_CONNECTION); |
790 | + |
791 | + /** |
792 | + * DeePeer::connection-closed: |
793 | + * @self: the #DeePeer on which the signal is emitted |
794 | + * @connection: the closed #GDBusConnection |
795 | + * |
796 | + * Connect to this signal to be notified when peers close |
797 | + * their #GDBusConnection. |
798 | + **/ |
799 | + _peer_signals[CONNECTION_CLOSED] = |
800 | + g_signal_new ("connection-closed", |
801 | + G_TYPE_FROM_CLASS (klass), |
802 | + G_SIGNAL_RUN_LAST, |
803 | + G_STRUCT_OFFSET (DeePeerClass, connection_closed), |
804 | + NULL, NULL, |
805 | + g_cclosure_marshal_VOID__OBJECT, |
806 | + G_TYPE_NONE, 1, |
807 | + G_TYPE_DBUS_CONNECTION); |
808 | + |
809 | /* Add properties */ |
810 | /** |
811 | * DeePeer::swarm-name: |
812 | @@ -631,6 +678,31 @@ |
813 | NULL); /* user_data */ |
814 | } |
815 | |
816 | +static const gchar* |
817 | +dee_peer_real_get_swarm_leader (DeePeer *self) |
818 | +{ |
819 | + return self->priv->swarm_leader; |
820 | +} |
821 | + |
822 | +static gboolean |
823 | +dee_peer_real_is_swarm_leader (DeePeer *self) |
824 | +{ |
825 | + return self->priv->is_swarm_leader; |
826 | +} |
827 | + |
828 | +static GSList* |
829 | +dee_peer_real_get_connections (DeePeer *self) |
830 | +{ |
831 | + GSList *list = NULL; |
832 | + |
833 | + if (self->priv->connection) |
834 | + { |
835 | + list = g_slist_append (list, self->priv->connection); |
836 | + } |
837 | + |
838 | + return list; |
839 | +} |
840 | + |
841 | /* Public Methods */ |
842 | |
843 | /** |
844 | @@ -653,37 +725,38 @@ |
845 | } |
846 | |
847 | /** |
848 | - * dee_peer_is_swarm_leader |
849 | + * dee_peer_is_swarm_leader: |
850 | * @self: a #DeePeer |
851 | * |
852 | * Return value: %TRUE if and only if this peer owns the swarm name on |
853 | * the session bus |
854 | */ |
855 | -const gboolean |
856 | -dee_peer_is_swarm_leader (DeePeer *self) |
857 | +gboolean |
858 | +dee_peer_is_swarm_leader (DeePeer *self) |
859 | { |
860 | g_return_val_if_fail (DEE_PEER (self), FALSE); |
861 | |
862 | - return self->priv->is_swarm_leader; |
863 | + DeePeerClass *klass = DEE_PEER_GET_CLASS (self); |
864 | + return klass->is_swarm_leader (self); |
865 | } |
866 | |
867 | /** |
868 | - * dee_peer_get_swarm_leader |
869 | + * dee_peer_get_swarm_leader: |
870 | * @self: a #DeePeer |
871 | * |
872 | - * Gets the unique DBus address of the current swarm leader. |
873 | - * |
874 | - * This function can only be used after dee_peer_connect() has been called. |
875 | - * |
876 | - * Return value: Unique DBus address of the current swarm leader, possibly %NULL |
877 | - * if the leader has not been detected yet |
878 | + * In case this peer is connected to a message bus, gets the unique DBus |
879 | + * address of the current swarm leader, otherwise returns id of the leader. |
880 | + * |
881 | + * Return value: Unique DBus address of the current swarm leader, |
882 | + * possibly %NULL if the leader has not been detected yet |
883 | */ |
884 | const gchar* |
885 | dee_peer_get_swarm_leader (DeePeer *self) |
886 | { |
887 | g_return_val_if_fail (DEE_PEER (self), NULL); |
888 | |
889 | - return self->priv->swarm_leader; |
890 | + DeePeerClass *klass = DEE_PEER_GET_CLASS (self); |
891 | + return klass->get_swarm_leader (self); |
892 | } |
893 | |
894 | /** |
895 | @@ -703,6 +776,23 @@ |
896 | return self->priv->swarm_name; |
897 | } |
898 | |
899 | +/** |
900 | + * dee_peer_get_connections: |
901 | + * |
902 | + * Gets list of #GDBusConnection instances used by this #DeePeer instance. |
903 | + * |
904 | + * Return value: (transfer container) (element-type Gio.DBusConnection): |
905 | + * List of connections. |
906 | + */ |
907 | +GSList* |
908 | +dee_peer_get_connections (DeePeer *self) |
909 | +{ |
910 | + g_return_val_if_fail (DEE_PEER (self), NULL); |
911 | + |
912 | + DeePeerClass *klass = DEE_PEER_GET_CLASS (self); |
913 | + return klass->get_connections (self); |
914 | +} |
915 | + |
916 | static void |
917 | emit_peer_found (DeePeer *self, |
918 | const gchar *name) |
919 | @@ -778,6 +868,8 @@ |
920 | priv->connection = g_object_ref (connection); |
921 | priv->own_name = g_strdup (g_dbus_connection_get_unique_name (connection)); |
922 | |
923 | + g_signal_emit (self, _peer_signals[CONNECTION_ACQUIRED], 0, priv->connection); |
924 | + |
925 | priv->filter_id = g_dbus_connection_add_filter (priv->connection, |
926 | gdbus_message_filter, |
927 | self, /* self */ |
928 | |
929 | === modified file 'dee/dee-peer.h' |
930 | --- dee/dee-peer.h 2011-12-14 10:59:40 +0000 |
931 | +++ dee/dee-peer.h 2011-12-19 08:59:26 +0000 |
932 | @@ -26,6 +26,7 @@ |
933 | |
934 | #include <glib.h> |
935 | #include <glib-object.h> |
936 | +#include <gio/gio.h> |
937 | |
938 | G_BEGIN_DECLS |
939 | |
940 | @@ -74,11 +75,15 @@ |
941 | /*< public >*/ |
942 | |
943 | /*< signals >*/ |
944 | - void (*connected) (DeePeer *peer, const gchar *peer_name); |
945 | - void (*peer_found) (DeePeer *peer, const gchar *name); |
946 | - void (*peer_lost) (DeePeer *peer, const gchar *name); |
947 | + void (*peer_found) (DeePeer *peer, const gchar *name); |
948 | + void (*peer_lost) (DeePeer *peer, const gchar *name); |
949 | + void (*connection_acquired) (DeePeer *peer, GDBusConnection *connection); |
950 | + void (*connection_closed) (DeePeer *peer, GDBusConnection *connection); |
951 | |
952 | /*< vtable >*/ |
953 | + const gchar* (*get_swarm_leader) (DeePeer *peer); |
954 | + gboolean (*is_swarm_leader) (DeePeer *peer); |
955 | + GSList* (*get_connections) (DeePeer *peer); |
956 | |
957 | /*< private >*/ |
958 | void (*_dee_peer_1) (void); |
959 | @@ -98,11 +103,13 @@ |
960 | |
961 | DeePeer* dee_peer_new (const gchar* swarm_name); |
962 | |
963 | -const gboolean dee_peer_is_swarm_leader (DeePeer *self); |
964 | +gboolean dee_peer_is_swarm_leader (DeePeer *self); |
965 | |
966 | const gchar* dee_peer_get_swarm_leader (DeePeer *self); |
967 | |
968 | -const gchar * dee_peer_get_swarm_name (DeePeer *self); |
969 | +const gchar* dee_peer_get_swarm_name (DeePeer *self); |
970 | + |
971 | +GSList* dee_peer_get_connections (DeePeer *self); |
972 | |
973 | G_END_DECLS |
974 | |
975 | |
976 | === modified file 'dee/dee-sequence-model.c' |
977 | --- dee/dee-sequence-model.c 2011-12-09 14:14:11 +0000 |
978 | +++ dee/dee-sequence-model.c 2011-12-19 08:59:26 +0000 |
979 | @@ -25,7 +25,7 @@ |
980 | * |
981 | * #DeeSequenceModel is an implementation of the #DeeModel<!-- --> interface |
982 | * backed by a #GSequence. It extends #DeeSerializableModel so that you may use |
983 | - * it as back end model for a #DeeShareedModel. |
984 | + * it as back end model for a #DeeSharedModel. |
985 | * |
986 | * The implementation is backed by a #GSequence giving a good tradeoff between |
987 | * random access time versus random- insertion and deletion times. |
988 | |
989 | === added file 'dee/dee-server.c' |
990 | --- dee/dee-server.c 1970-01-01 00:00:00 +0000 |
991 | +++ dee/dee-server.c 2011-12-19 08:59:26 +0000 |
992 | @@ -0,0 +1,492 @@ |
993 | +/* |
994 | + * Copyright (C) 2011 Canonical, Ltd. |
995 | + * |
996 | + * This library is free software; you can redistribute it and/or modify |
997 | + * it under the terms of the GNU Lesser General Public License |
998 | + * version 3.0 as published by the Free Software Foundation. |
999 | + * |
1000 | + * This library is distributed in the hope that it will be useful, |
1001 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1002 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1003 | + * GNU Lesser General Public License version 3.0 for more details. |
1004 | + * |
1005 | + * You should have received a copy of the GNU Lesser General Public |
1006 | + * License along with this library. If not, see |
1007 | + * <http://www.gnu.org/licenses/>. |
1008 | + * |
1009 | + * Authored by: Michal Hruby <michal.hruby@canonical.com> |
1010 | + * |
1011 | + */ |
1012 | +/** |
1013 | + * SECTION:dee-server |
1014 | + * @short_description: Creates a server object you can connect to. |
1015 | + * @include: dee.h |
1016 | + * |
1017 | + * #DeeServer allows you to create private connections (connections which |
1018 | + * are not routed via dbus-daemon). Note that, unlike #DeePeer, #DeeServer |
1019 | + * will always be swarm leader, and clients connected to it cannot overtake |
1020 | + * swarm leadership once the server connection is closed. |
1021 | + */ |
1022 | + |
1023 | +#ifdef HAVE_CONFIG_H |
1024 | +#include <config.h> |
1025 | +#endif |
1026 | + |
1027 | +#include <gio/gio.h> |
1028 | + |
1029 | +#include "dee-server.h" |
1030 | +#include "dee-marshal.h" |
1031 | +#include "trace-log.h" |
1032 | + |
1033 | +G_DEFINE_TYPE (DeeServer, dee_server, DEE_TYPE_PEER) |
1034 | + |
1035 | +#define GET_PRIVATE(o) \ |
1036 | + (G_TYPE_INSTANCE_GET_PRIVATE ((o), DEE_TYPE_SERVER, DeeServerPrivate)) |
1037 | + |
1038 | +/** |
1039 | + * DeeServerPrivate: |
1040 | + * |
1041 | + * Ignore this structure. |
1042 | + **/ |
1043 | +struct _DeeServerPrivate |
1044 | +{ |
1045 | + GCredentials *our_creds; |
1046 | + GDBusServer *server; |
1047 | + gchar *bus_address; |
1048 | + gboolean same_user_only; |
1049 | + guint initialize_server_timer_id; |
1050 | + |
1051 | + GSList *active_connections; |
1052 | + guint connection_id; |
1053 | + GHashTable *connection_id_map; |
1054 | +}; |
1055 | + |
1056 | +/* Globals */ |
1057 | +enum |
1058 | +{ |
1059 | + PROP_0, |
1060 | + PROP_BUS_ADDRESS, |
1061 | + PROP_SAME_USER_ONLY |
1062 | +}; |
1063 | + |
1064 | +enum |
1065 | +{ |
1066 | + LAST_SIGNAL |
1067 | +}; |
1068 | + |
1069 | +//static guint32 _server_signals[LAST_SIGNAL] = { 0 }; |
1070 | + |
1071 | +/* Forwards */ |
1072 | +static gboolean on_new_connection (GDBusServer *server, |
1073 | + GDBusConnection *connection, |
1074 | + gpointer user_data); |
1075 | + |
1076 | +static void on_connection_closed (GDBusConnection *connection, |
1077 | + gboolean remote_peer_vanished, |
1078 | + GError *error, |
1079 | + gpointer user_data); |
1080 | + |
1081 | +static gboolean dee_server_is_swarm_leader (DeePeer *peer); |
1082 | + |
1083 | +static const gchar* dee_server_get_swarm_leader (DeePeer *peer); |
1084 | + |
1085 | +static GSList* dee_server_get_connections (DeePeer *peer); |
1086 | + |
1087 | +/* GObject methods */ |
1088 | +static void |
1089 | +dee_server_get_property (GObject *object, guint property_id, |
1090 | + GValue *value, GParamSpec *pspec) |
1091 | +{ |
1092 | + DeeServerPrivate *priv = DEE_SERVER (object)->priv; |
1093 | + |
1094 | + switch (property_id) |
1095 | + { |
1096 | + case PROP_BUS_ADDRESS: |
1097 | + g_value_set_string (value, priv->bus_address); |
1098 | + break; |
1099 | + case PROP_SAME_USER_ONLY: |
1100 | + g_value_set_boolean (value, priv->same_user_only); |
1101 | + break; |
1102 | + default: |
1103 | + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); |
1104 | + } |
1105 | +} |
1106 | + |
1107 | +static void |
1108 | +dee_server_set_property (GObject *object, guint property_id, |
1109 | + const GValue *value, GParamSpec *pspec) |
1110 | +{ |
1111 | + DeeServerPrivate *priv = DEE_SERVER (object)->priv; |
1112 | + |
1113 | + switch (property_id) |
1114 | + { |
1115 | + case PROP_BUS_ADDRESS: |
1116 | + if (priv->bus_address) g_free (priv->bus_address); |
1117 | + priv->bus_address = g_value_dup_string (value); |
1118 | + break; |
1119 | + case PROP_SAME_USER_ONLY: |
1120 | + priv->same_user_only = g_value_get_boolean (value); |
1121 | + break; |
1122 | + default: |
1123 | + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); |
1124 | + } |
1125 | +} |
1126 | + |
1127 | +static gboolean |
1128 | +initialize_server (DeeServer *self) |
1129 | +{ |
1130 | + DeeServerPrivate *priv; |
1131 | + gchar *guid; |
1132 | + GDBusServerFlags server_flags; |
1133 | + GError *error = NULL; |
1134 | + |
1135 | + priv = self->priv; |
1136 | + |
1137 | + priv->initialize_server_timer_id = 0; |
1138 | + |
1139 | + guid = g_dbus_generate_guid (); |
1140 | + server_flags = G_DBUS_SERVER_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS; |
1141 | + |
1142 | + priv->server = g_dbus_server_new_sync (priv->bus_address, |
1143 | + server_flags, |
1144 | + guid, |
1145 | + NULL, |
1146 | + NULL, |
1147 | + &error); |
1148 | + |
1149 | + if (error) |
1150 | + { |
1151 | + g_critical ("Unable to set up DBusServer: %s", error->message); |
1152 | + |
1153 | + g_error_free (error); |
1154 | + g_free (guid); |
1155 | + |
1156 | + g_object_notify (G_OBJECT (self), "swarm-leader"); |
1157 | + return FALSE; |
1158 | + } |
1159 | + |
1160 | + g_signal_connect (priv->server, "new-connection", |
1161 | + G_CALLBACK (on_new_connection), self); |
1162 | + |
1163 | + g_dbus_server_start (priv->server); |
1164 | + |
1165 | + g_object_notify (G_OBJECT (self), "swarm-leader"); |
1166 | + |
1167 | + g_free (guid); |
1168 | + |
1169 | + return FALSE; |
1170 | +} |
1171 | + |
1172 | +static void |
1173 | +dee_server_constructed (GObject *self) |
1174 | +{ |
1175 | + DeeServerPrivate *priv; |
1176 | + const gchar *swarm_name; |
1177 | + |
1178 | + priv = DEE_SERVER (self)->priv; |
1179 | + |
1180 | + /* we should chain up the constructed method here, but peer does things we |
1181 | + * don't want to, so not chaining up... */ |
1182 | + |
1183 | + swarm_name = dee_peer_get_swarm_name (DEE_PEER (self)); |
1184 | + if (swarm_name == NULL) |
1185 | + { |
1186 | + g_critical ("DeeServer created without a swarm name. You must specify " |
1187 | + "a non-NULL swarm name"); |
1188 | + return; |
1189 | + } |
1190 | + |
1191 | + priv->our_creds = g_credentials_new (); |
1192 | + |
1193 | + if (!priv->bus_address) |
1194 | + { |
1195 | + const gchar *username = g_get_user_name (); |
1196 | + priv->bus_address = g_strdup_printf ("unix:abstract=%s-%s", |
1197 | + username, swarm_name); |
1198 | + } |
1199 | + |
1200 | + /* Ideally we'd call the async variant of g_dbus_server_new (which doesn't |
1201 | + * exist atm) */ |
1202 | + priv->initialize_server_timer_id = g_idle_add_full (G_PRIORITY_DEFAULT, |
1203 | + (GSourceFunc)initialize_server, self, NULL); |
1204 | +} |
1205 | + |
1206 | +static void |
1207 | +close_connection (gpointer data, gpointer user_data) |
1208 | +{ |
1209 | + GDBusConnection *connection = G_DBUS_CONNECTION (data); |
1210 | + |
1211 | + g_signal_handlers_disconnect_by_func (connection, on_connection_closed, |
1212 | + user_data); |
1213 | + |
1214 | + // FIXME: should we use the sync variant? and flush first? |
1215 | + g_dbus_connection_close (connection, NULL, NULL, NULL); |
1216 | +} |
1217 | + |
1218 | +static void |
1219 | +dee_server_finalize (GObject *object) |
1220 | +{ |
1221 | + DeeServerPrivate *priv; |
1222 | + |
1223 | + priv = DEE_SERVER (object)->priv; |
1224 | + |
1225 | + if (priv->initialize_server_timer_id) |
1226 | + { |
1227 | + g_source_remove (priv->initialize_server_timer_id); |
1228 | + priv->initialize_server_timer_id = 0; |
1229 | + } |
1230 | + |
1231 | + if (priv->active_connections) |
1232 | + { |
1233 | + g_slist_foreach (priv->active_connections, close_connection, object); |
1234 | + g_slist_free_full (priv->active_connections, g_object_unref); |
1235 | + priv->active_connections = NULL; |
1236 | + } |
1237 | + |
1238 | + if (priv->server) |
1239 | + { |
1240 | + /* this should be done automatically on unref, but it doesn't seem so */ |
1241 | + g_dbus_server_stop (priv->server); |
1242 | + |
1243 | + g_object_unref (priv->server); |
1244 | + } |
1245 | + |
1246 | + if (priv->connection_id_map) |
1247 | + { |
1248 | + g_hash_table_unref (priv->connection_id_map); |
1249 | + priv->connection_id_map = NULL; |
1250 | + } |
1251 | + |
1252 | + if (priv->bus_address) |
1253 | + { |
1254 | + g_free (priv->bus_address); |
1255 | + } |
1256 | + |
1257 | + if (priv->our_creds) |
1258 | + { |
1259 | + g_object_unref (priv->our_creds); |
1260 | + priv->our_creds = NULL; |
1261 | + } |
1262 | + |
1263 | + G_OBJECT_CLASS (dee_server_parent_class)->finalize (object); |
1264 | +} |
1265 | + |
1266 | +static void |
1267 | +dee_server_class_init (DeeServerClass *klass) |
1268 | +{ |
1269 | + GParamSpec *pspec; |
1270 | + GObjectClass *object_class = G_OBJECT_CLASS (klass); |
1271 | + DeePeerClass *peer_class = DEE_PEER_CLASS (klass); |
1272 | + |
1273 | + g_type_class_add_private (klass, sizeof (DeeServerPrivate)); |
1274 | + |
1275 | + object_class->constructed = dee_server_constructed; |
1276 | + object_class->get_property = dee_server_get_property; |
1277 | + object_class->set_property = dee_server_set_property; |
1278 | + object_class->finalize = dee_server_finalize; |
1279 | + |
1280 | + peer_class->is_swarm_leader = dee_server_is_swarm_leader; |
1281 | + peer_class->get_swarm_leader = dee_server_get_swarm_leader; |
1282 | + peer_class->get_connections = dee_server_get_connections; |
1283 | + |
1284 | + /** |
1285 | + * DeeServer::bus-address: |
1286 | + * |
1287 | + * D-Bus address the server is bound to. If you do not specify this property |
1288 | + * DeeServer will try to use abstract unix domain socket where its name is |
1289 | + * a concatenation of user name running current process and the swarm name. |
1290 | + * You can use dee_server_get_client_address to get address string |
1291 | + * that can be used by clients to connect to. |
1292 | + */ |
1293 | + pspec = g_param_spec_string ("bus-address", "Bus address", |
1294 | + "Bus address to use for the connection", |
1295 | + NULL, |
1296 | + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
1297 | + | G_PARAM_STATIC_STRINGS); |
1298 | + g_object_class_install_property (object_class, PROP_BUS_ADDRESS, pspec); |
1299 | + |
1300 | + /** |
1301 | + * DeeServer::same-user-only: |
1302 | + * |
1303 | + * A boolean specifying whether the server should only accept connections |
1304 | + * from same user as the current process owner. |
1305 | + */ |
1306 | + pspec = g_param_spec_boolean ("same-user-only", "Same user only", |
1307 | + "Accept connections from current user only", |
1308 | + TRUE, |
1309 | + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
1310 | + | G_PARAM_STATIC_STRINGS); |
1311 | + g_object_class_install_property (object_class, PROP_SAME_USER_ONLY, pspec); |
1312 | +} |
1313 | + |
1314 | +static void |
1315 | +dee_server_init (DeeServer *self) |
1316 | +{ |
1317 | + self->priv = GET_PRIVATE (self); |
1318 | + |
1319 | + self->priv->connection_id_map = g_hash_table_new_full (g_direct_hash, |
1320 | + g_direct_equal, |
1321 | + NULL, |
1322 | + g_free); |
1323 | +} |
1324 | + |
1325 | +/** |
1326 | + * dee_server_new: |
1327 | + * |
1328 | + * @swarm_name: Name of swarm to join. |
1329 | + * |
1330 | + * Creates a new instance of #DeeServer and tries to bind |
1331 | + * to #DeeServer:bus-address. The #DeePeer:swarm-leader property will be set |
1332 | + * when the binding succeeds. |
1333 | + * |
1334 | + * Return value: (transfer full): A newly constructed #DeeServer. |
1335 | + */ |
1336 | +DeeServer* |
1337 | +dee_server_new (const gchar* swarm_name) |
1338 | +{ |
1339 | + g_return_val_if_fail (swarm_name != NULL, NULL); |
1340 | + |
1341 | + return DEE_SERVER (g_object_new (DEE_TYPE_SERVER, |
1342 | + "swarm-name", swarm_name, NULL)); |
1343 | +} |
1344 | + |
1345 | +/** |
1346 | + * dee_server_new_for_address: |
1347 | + * |
1348 | + * @swarm_name: Name of swarm to join. |
1349 | + * @bus_address: D-Bus address to use for the connection. |
1350 | + * |
1351 | + * Creates a new instance of #DeeServer and tries to bind to @bus_address. |
1352 | + * The #DeePeer:swarm-leader property will be set when the binding succeeds. |
1353 | + * |
1354 | + * Return value: (transfer full): A newly constructed #DeeServer. |
1355 | + */ |
1356 | +DeeServer* |
1357 | +dee_server_new_for_address (const gchar* swarm_name, const gchar* bus_address) |
1358 | +{ |
1359 | + g_return_val_if_fail (swarm_name != NULL, NULL); |
1360 | + |
1361 | + return DEE_SERVER (g_object_new (DEE_TYPE_SERVER, |
1362 | + "swarm-name", swarm_name, |
1363 | + "bus-address", bus_address, NULL)); |
1364 | +} |
1365 | + |
1366 | +/** |
1367 | + * dee_server_get_client_address: |
1368 | + * |
1369 | + * @self: A #DeeServer. |
1370 | + * |
1371 | + * Gets a D-Bus address string that can be used by clients to connect to server. |
1372 | + * |
1373 | + * Return value: A D-Bus address string. Do not free. |
1374 | + */ |
1375 | +const gchar* |
1376 | +dee_server_get_client_address (DeeServer *server) |
1377 | +{ |
1378 | + DeeServerPrivate *priv; |
1379 | + g_return_val_if_fail (DEE_IS_SERVER (server), NULL); |
1380 | + |
1381 | + priv = server->priv; |
1382 | + |
1383 | + return priv->server != NULL ? |
1384 | + g_dbus_server_get_client_address (priv->server) : NULL; |
1385 | +} |
1386 | + |
1387 | +/* Private Methods */ |
1388 | +static gboolean |
1389 | +on_new_connection (GDBusServer *server, |
1390 | + GDBusConnection *connection, |
1391 | + gpointer user_data) |
1392 | +{ |
1393 | + gchar *connection_name; |
1394 | + GCredentials *creds; |
1395 | + DeeServer *self = DEE_SERVER (user_data); |
1396 | + DeeServerPrivate *priv = self->priv; |
1397 | + |
1398 | + trace_object (self, "New connection: [%p]", connection); |
1399 | + |
1400 | + creds = g_dbus_connection_get_peer_credentials (connection); |
1401 | + if (!g_credentials_is_same_user (creds, priv->our_creds, NULL) |
1402 | + && priv->same_user_only) |
1403 | + { |
1404 | + trace_object (self, "User id doesn't match, rejecting connection"); |
1405 | + return FALSE; |
1406 | + } |
1407 | + |
1408 | + priv->active_connections = g_slist_prepend (priv->active_connections, |
1409 | + g_object_ref (connection)); |
1410 | + |
1411 | + g_signal_connect (connection, "closed", |
1412 | + G_CALLBACK (on_connection_closed), self); |
1413 | + |
1414 | + /* time to register dbus objects on this connection */ |
1415 | + g_signal_emit_by_name (self, "connection-acquired", connection); |
1416 | + |
1417 | + connection_name = g_strdup_printf ("%s:%u", |
1418 | + g_dbus_server_get_guid (priv->server), |
1419 | + ++priv->connection_id); |
1420 | + /* hash table assumes ownership of connection_name */ |
1421 | + g_hash_table_insert (priv->connection_id_map, connection, connection_name); |
1422 | + |
1423 | + g_signal_emit_by_name (self, "peer-found", connection_name); |
1424 | + |
1425 | + return TRUE; |
1426 | +} |
1427 | + |
1428 | +static void |
1429 | +on_connection_closed (GDBusConnection *connection, |
1430 | + gboolean remote_peer_vanished, |
1431 | + GError *error, |
1432 | + gpointer user_data) |
1433 | +{ |
1434 | + GSList *element; |
1435 | + DeeServer *self = DEE_SERVER (user_data); |
1436 | + DeeServerPrivate *priv = self->priv; |
1437 | + |
1438 | + trace_object (self, "Connection [%p] was closed", connection); |
1439 | + |
1440 | + element = g_slist_find (priv->active_connections, connection); |
1441 | + if (element == NULL) |
1442 | + { |
1443 | + g_warning ("Connection closed for element which isn't " |
1444 | + "in active_connections"); |
1445 | + return; |
1446 | + } |
1447 | + |
1448 | + priv->active_connections = g_slist_delete_link (priv->active_connections, |
1449 | + element); |
1450 | + |
1451 | + /* reverse order of signals than in new-connection handler */ |
1452 | + g_signal_emit_by_name (self, "peer-lost", |
1453 | + g_hash_table_lookup (priv->connection_id_map, |
1454 | + connection)); |
1455 | + g_hash_table_remove (priv->connection_id_map, connection); |
1456 | + |
1457 | + g_signal_emit_by_name (self, "connection-closed", connection); |
1458 | + |
1459 | + g_object_unref (connection); |
1460 | +} |
1461 | + |
1462 | +static gboolean |
1463 | +dee_server_is_swarm_leader (DeePeer *peer) |
1464 | +{ |
1465 | + DeeServerPrivate *priv = DEE_SERVER (peer)->priv; |
1466 | + return priv->server != NULL; |
1467 | +} |
1468 | + |
1469 | +static const gchar* |
1470 | +dee_server_get_swarm_leader (DeePeer *peer) |
1471 | +{ |
1472 | + DeeServerPrivate *priv = DEE_SERVER (peer)->priv; |
1473 | + |
1474 | + return priv->server ? g_dbus_server_get_guid (priv->server) : NULL; |
1475 | +} |
1476 | + |
1477 | +static GSList* |
1478 | +dee_server_get_connections (DeePeer *peer) |
1479 | +{ |
1480 | + DeeServerPrivate *priv = DEE_SERVER (peer)->priv; |
1481 | + |
1482 | + return g_slist_copy (priv->active_connections); |
1483 | +} |
1484 | + |
1485 | |
1486 | === added file 'dee/dee-server.h' |
1487 | --- dee/dee-server.h 1970-01-01 00:00:00 +0000 |
1488 | +++ dee/dee-server.h 2011-12-19 08:59:26 +0000 |
1489 | @@ -0,0 +1,83 @@ |
1490 | +/* |
1491 | + * Copyright (C) 2011 Canonical, Ltd. |
1492 | + * |
1493 | + * This library is free software; you can redistribute it and/or modify |
1494 | + * it under the terms of the GNU Lesser General Public License |
1495 | + * version 3.0 as published by the Free Software Foundation. |
1496 | + * |
1497 | + * This library is distributed in the hope that it will be useful, |
1498 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1499 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1500 | + * GNU Lesser General Public License version 3.0 for more details. |
1501 | + * |
1502 | + * You should have received a copy of the GNU Lesser General Public |
1503 | + * License along with this library. If not, see |
1504 | + * <http://www.gnu.org/licenses/>. |
1505 | + * |
1506 | + * Authored by Michal Hruby <michal.hruby@canonical.com> |
1507 | + */ |
1508 | + |
1509 | +#if !defined (_DEE_H_INSIDE) && !defined (DEE_COMPILATION) |
1510 | +#error "Only <dee.h> can be included directly." |
1511 | +#endif |
1512 | + |
1513 | +#ifndef _HAVE_DEE_SERVER_H |
1514 | +#define _HAVE_DEE_SERVER_H |
1515 | + |
1516 | +#include <glib.h> |
1517 | +#include <glib-object.h> |
1518 | +#include "dee-peer.h" |
1519 | + |
1520 | +G_BEGIN_DECLS |
1521 | + |
1522 | +#define DEE_TYPE_SERVER dee_server_get_type() |
1523 | + |
1524 | +#define DEE_SERVER(obj) \ |
1525 | + (G_TYPE_CHECK_INSTANCE_CAST ((obj), DEE_TYPE_SERVER, DeeServer)) |
1526 | + |
1527 | +#define DEE_SERVER_CLASS(klass) \ |
1528 | + (G_TYPE_CHECK_CLASS_CAST ((klass), DEE_TYPE_SERVER, DeeServerClass)) |
1529 | + |
1530 | +#define DEE_IS_SERVER(obj) \ |
1531 | + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DEE_TYPE_SERVER)) |
1532 | + |
1533 | +#define DEE_IS_SERVER_CLASS(klass) \ |
1534 | + (G_TYPE_CHECK_CLASS_TYPE ((klass), DEE_TYPE_SERVER)) |
1535 | + |
1536 | +#define DEE_SERVER_GET_CLASS(obj) \ |
1537 | + (G_TYPE_INSTANCE_GET_CLASS ((obj), DEE_TYPE_SERVER, DeeServerClass)) |
1538 | + |
1539 | +typedef struct _DeeServerPrivate DeeServerPrivate; |
1540 | + |
1541 | +typedef struct { |
1542 | + /*< private >*/ |
1543 | + DeePeer parent; |
1544 | + |
1545 | + DeeServerPrivate *priv; |
1546 | +} DeeServer; |
1547 | + |
1548 | +typedef struct { |
1549 | + /*< private >*/ |
1550 | + DeePeerClass parent_class; |
1551 | +} DeeServerClass; |
1552 | + |
1553 | +/** |
1554 | + * dee_server_get_type: |
1555 | + * |
1556 | + * The GType of #DeeServer |
1557 | + * |
1558 | + * Return value: the #GType of #DeeServer |
1559 | + **/ |
1560 | +GType dee_server_get_type (void); |
1561 | + |
1562 | +DeeServer* dee_server_new (const gchar *swarm_name); |
1563 | + |
1564 | +DeeServer* dee_server_new_for_address (const gchar* swarm_name, |
1565 | + const gchar* bus_address); |
1566 | + |
1567 | +const gchar* dee_server_get_client_address (DeeServer *server); |
1568 | + |
1569 | +G_END_DECLS |
1570 | + |
1571 | +#endif /* _HAVE_DEE_SERVER_H */ |
1572 | + |
1573 | |
1574 | === modified file 'dee/dee-shared-model.c' |
1575 | --- dee/dee-shared-model.c 2011-11-22 15:27:36 +0000 |
1576 | +++ dee/dee-shared-model.c 2011-12-19 08:59:26 +0000 |
1577 | @@ -17,6 +17,7 @@ |
1578 | * Authored by: |
1579 | * Mikkel Kamstrup Erlandsen <mikkel.kamstrup@canonical.com> |
1580 | * Neil Jagdish Patel <neil.patel@canonical.com> |
1581 | + * Michal Hruby <michal.hruby@canonical.com> |
1582 | */ |
1583 | |
1584 | /** |
1585 | @@ -80,19 +81,21 @@ |
1586 | **/ |
1587 | struct _DeeSharedModelPrivate |
1588 | { |
1589 | - DeePeer *swarm; |
1590 | - GDBusConnection *connection; |
1591 | - gchar *model_path; |
1592 | + DeePeer *swarm; |
1593 | + GSList *connections; |
1594 | + gchar *model_path; |
1595 | |
1596 | /* Buffer of DeeSharedModelRevisions that we keep in order to batch |
1597 | * our DBus signals. The invariant is that all buffered revisions |
1598 | * are of the same type */ |
1599 | GSList *revision_queue; |
1600 | guint revision_queue_timeout_id; |
1601 | - guint dbus_signal_handler; |
1602 | - guint model_registration_id; |
1603 | + guint acquisition_timer_id; |
1604 | gulong swarm_leader_handler; |
1605 | - |
1606 | + gulong connection_acquired_handler; |
1607 | + gulong connection_closed_handler; |
1608 | + GArray *connection_infos; |
1609 | + |
1610 | gboolean synchronized; |
1611 | gboolean found_first_peer; |
1612 | gboolean suppress_remote_signals; |
1613 | @@ -108,6 +111,12 @@ |
1614 | DeeModel *model; |
1615 | } DeeSharedModelRevision; |
1616 | |
1617 | +typedef struct |
1618 | +{ |
1619 | + GDBusConnection *connection; |
1620 | + guint signal_subscription_id; |
1621 | + guint registration_id; |
1622 | +} DeeConnectionInfo; |
1623 | /* Globals */ |
1624 | static GQuark dee_shared_model_error_quark = 0; |
1625 | |
1626 | @@ -138,9 +147,13 @@ |
1627 | //static guint32 _signals[LAST_SIGNAL] = { 0 }; |
1628 | |
1629 | /* Forwards */ |
1630 | -static void on_bus_connection_acquired (GObject *source_object, |
1631 | - GAsyncResult *res, |
1632 | - gpointer user_data); |
1633 | +static void on_connection_acquired (DeeSharedModel *self, |
1634 | + GDBusConnection *connection, |
1635 | + DeePeer *peer); |
1636 | + |
1637 | +static void on_connection_closed (DeeSharedModel *self, |
1638 | + GDBusConnection *connection, |
1639 | + DeePeer *peer); |
1640 | |
1641 | static void commit_transaction (DeeSharedModel *self, |
1642 | const gchar *sender_name, |
1643 | @@ -192,7 +205,8 @@ |
1644 | static void reset_model (DeeModel *self); |
1645 | |
1646 | static void invalidate_peer (DeeSharedModel *self, |
1647 | - const gchar *sender_name); |
1648 | + const gchar *sender_name, |
1649 | + GDBusConnection *except); |
1650 | |
1651 | static gboolean on_invalidate (DeeSharedModel *self); |
1652 | |
1653 | @@ -263,7 +277,9 @@ |
1654 | DeeSharedModelRevision *rev; |
1655 | GError *error; |
1656 | GSList *iter; |
1657 | + GSList *connection_iter; |
1658 | GVariant *schema; |
1659 | + GVariant *transaction_variant; |
1660 | GVariantBuilder aav, au, ay, transaction; |
1661 | guint64 seqnum_begin = 0, seqnum_end = 0; |
1662 | guint n_cols, i; |
1663 | @@ -278,7 +294,7 @@ |
1664 | * we'll assume the programmer knows this |
1665 | * 2) We are resetting the model - no problem |
1666 | */ |
1667 | - if (priv->connection == NULL) |
1668 | + if (priv->connections == NULL) |
1669 | { |
1670 | trace_object (self, "Flushing revision queue, without a connection. " |
1671 | "This will blow up unless you are the leader model"); |
1672 | @@ -368,21 +384,27 @@ |
1673 | g_variant_builder_add_value (&transaction, |
1674 | g_variant_new ("(tt)", seqnum_begin, seqnum_end)); |
1675 | |
1676 | - /* Throw a Commit signal on the bus */ |
1677 | - error = NULL; |
1678 | - g_dbus_connection_emit_signal(priv->connection, |
1679 | - NULL, |
1680 | - priv->model_path, |
1681 | - "com.canonical.Dee.Model", |
1682 | - "Commit", |
1683 | - g_variant_builder_end (&transaction), |
1684 | - &error); |
1685 | + transaction_variant = g_variant_builder_end (&transaction); |
1686 | |
1687 | - if (error != NULL) |
1688 | + /* Throw a Commit signal */ |
1689 | + for (connection_iter = priv->connections; connection_iter != NULL; |
1690 | + connection_iter = connection_iter->next) |
1691 | { |
1692 | - g_critical ("Failed to emit DBus signal " |
1693 | - "com.canonical.Dee.Model.Commit: %s", error->message); |
1694 | - g_error_free (error); |
1695 | + error = NULL; |
1696 | + g_dbus_connection_emit_signal((GDBusConnection*) connection_iter->data, |
1697 | + NULL, |
1698 | + priv->model_path, |
1699 | + "com.canonical.Dee.Model", |
1700 | + "Commit", |
1701 | + transaction_variant, |
1702 | + &error); |
1703 | + |
1704 | + if (error != NULL) |
1705 | + { |
1706 | + g_critical ("Failed to emit DBus signal " |
1707 | + "com.canonical.Dee.Model.Commit: %s", error->message); |
1708 | + g_error_free (error); |
1709 | + } |
1710 | } |
1711 | |
1712 | trace_object (self, "Flushed %"G_GUINT64_FORMAT" revisions. " |
1713 | @@ -429,6 +451,7 @@ |
1714 | static void |
1715 | dee_shared_model_finalize (GObject *object) |
1716 | { |
1717 | + guint i; |
1718 | DeeSharedModelPrivate *priv = DEE_SHARED_MODEL (object)->priv; |
1719 | |
1720 | /* Flush any pending revisions */ |
1721 | @@ -437,18 +460,40 @@ |
1722 | flush_revision_queue (DEE_MODEL(object)); |
1723 | priv->revision_queue = NULL; |
1724 | } |
1725 | - |
1726 | - if (priv->model_registration_id != 0 && priv->connection != NULL) |
1727 | - { |
1728 | - g_dbus_connection_unregister_object (priv->connection, |
1729 | - priv->model_registration_id); |
1730 | - priv->model_registration_id = 0; |
1731 | - } |
1732 | - if (priv->dbus_signal_handler != 0 && priv->connection != NULL) |
1733 | - { |
1734 | - g_dbus_connection_signal_unsubscribe(priv->connection, |
1735 | - priv->dbus_signal_handler); |
1736 | - priv->dbus_signal_handler = 0; |
1737 | + |
1738 | + if (priv->acquisition_timer_id != 0) |
1739 | + { |
1740 | + g_source_remove (priv->acquisition_timer_id); |
1741 | + priv->acquisition_timer_id = 0; |
1742 | + } |
1743 | + |
1744 | + if (priv->connection_acquired_handler) |
1745 | + { |
1746 | + g_signal_handler_disconnect (priv->swarm, |
1747 | + priv->connection_acquired_handler); |
1748 | + priv->connection_acquired_handler = 0; |
1749 | + } |
1750 | + |
1751 | + if (priv->connection_closed_handler) |
1752 | + { |
1753 | + g_signal_handler_disconnect (priv->swarm, priv->connection_closed_handler); |
1754 | + priv->connection_closed_handler = 0; |
1755 | + } |
1756 | + |
1757 | + if (priv->connection_infos != NULL) |
1758 | + { |
1759 | + for (i = 0; i < priv->connection_infos->len; i++) |
1760 | + { |
1761 | + DeeConnectionInfo *info; |
1762 | + info = &g_array_index (priv->connection_infos, DeeConnectionInfo, i); |
1763 | + g_dbus_connection_unregister_object (info->connection, |
1764 | + info->registration_id); |
1765 | + g_dbus_connection_signal_unsubscribe (info->connection, |
1766 | + info->signal_subscription_id); |
1767 | + } |
1768 | + |
1769 | + g_array_unref (priv->connection_infos); |
1770 | + priv->connection_infos = NULL; |
1771 | } |
1772 | if (priv->swarm_leader_handler != 0) |
1773 | { |
1774 | @@ -459,10 +504,10 @@ |
1775 | { |
1776 | g_free (priv->model_path); |
1777 | } |
1778 | - if (priv->connection) |
1779 | + if (priv->connections) |
1780 | { |
1781 | - g_object_unref (priv->connection); |
1782 | - priv->connection = NULL; |
1783 | + g_slist_free (priv->connections); |
1784 | + priv->connections = NULL; |
1785 | } |
1786 | if (priv->swarm) |
1787 | { |
1788 | @@ -515,23 +560,49 @@ |
1789 | g_value_set_object (value, priv->swarm); |
1790 | break; |
1791 | case PROP_SYNCHRONIZED: |
1792 | - g_value_set_boolean (value, priv->synchronized); |
1793 | - break; |
1794 | + g_value_set_boolean (value, priv->synchronized); |
1795 | + break; |
1796 | default: |
1797 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, id, pspec); |
1798 | break; |
1799 | } |
1800 | } |
1801 | |
1802 | +static gboolean |
1803 | +iterate_connections (DeeSharedModel *self) |
1804 | +{ |
1805 | + DeeSharedModelPrivate *priv; |
1806 | + GSList *iter; |
1807 | + |
1808 | + g_return_val_if_fail (DEE_IS_SHARED_MODEL (self), FALSE); |
1809 | + priv = self->priv; |
1810 | + |
1811 | + priv->connections = dee_peer_get_connections (priv->swarm); |
1812 | + |
1813 | + for (iter = priv->connections; iter != NULL; iter = iter->next) |
1814 | + { |
1815 | + on_connection_acquired (self, (GDBusConnection*) iter->data, priv->swarm); |
1816 | + } |
1817 | + |
1818 | + priv->acquisition_timer_id = 0; |
1819 | + |
1820 | + return FALSE; |
1821 | +} |
1822 | + |
1823 | static void |
1824 | -dee_shared_model_constructed (GObject *self) |
1825 | +dee_shared_model_constructed (GObject *object) |
1826 | { |
1827 | - DeeSharedModel *_self; |
1828 | + DeeSharedModel *self; |
1829 | DeeSharedModelPrivate *priv; |
1830 | gchar *dummy; |
1831 | |
1832 | - _self = DEE_SHARED_MODEL (self); |
1833 | - priv = _self->priv; |
1834 | + /* GObjectClass has NULL 'constructed' member, but we add this check for |
1835 | + * future robustness if we ever move to another base class */ |
1836 | + if (G_OBJECT_CLASS (dee_shared_model_parent_class)->constructed != NULL) |
1837 | + G_OBJECT_CLASS (dee_shared_model_parent_class)->constructed (object); |
1838 | + |
1839 | + self = DEE_SHARED_MODEL (object); |
1840 | + priv = self->priv; |
1841 | |
1842 | if (priv->swarm == NULL) |
1843 | { |
1844 | @@ -546,21 +617,24 @@ |
1845 | NULL); |
1846 | g_free (dummy); |
1847 | |
1848 | - priv->swarm_leader_handler = g_signal_connect_swapped(priv->swarm, |
1849 | - "notify::swarm-leader", |
1850 | - G_CALLBACK (on_leader_changed), |
1851 | - self); |
1852 | - |
1853 | - /* Connect asynchronously to the bus */ |
1854 | - g_bus_get (G_BUS_TYPE_SESSION, |
1855 | - NULL, |
1856 | - on_bus_connection_acquired, |
1857 | - g_object_ref (self)); // ref to stay alive during async call |
1858 | - |
1859 | - /* GObjectClass has NULL 'constructed' member, but we add this check for |
1860 | - * future robustness if we ever move to another base class */ |
1861 | - if (G_OBJECT_CLASS (dee_shared_model_parent_class)->constructed != NULL) |
1862 | - G_OBJECT_CLASS (dee_shared_model_parent_class)->constructed (self); |
1863 | + priv->swarm_leader_handler = |
1864 | + g_signal_connect_swapped (priv->swarm, "notify::swarm-leader", |
1865 | + G_CALLBACK (on_leader_changed), self); |
1866 | + |
1867 | + priv->connection_acquired_handler = |
1868 | + g_signal_connect_swapped (priv->swarm, "connection-acquired", |
1869 | + G_CALLBACK (on_connection_acquired), self); |
1870 | + |
1871 | + priv->connection_closed_handler = |
1872 | + g_signal_connect_swapped (priv->swarm, "connection-closed", |
1873 | + G_CALLBACK (on_connection_closed), self); |
1874 | + |
1875 | + /* we don't want to invoke on_connection_acquired from here, it would mean |
1876 | + * emitting important signal when inside g_object_new */ |
1877 | + /* using G_PRIORITY_DEFAULT will ensure that this will be dispatched before |
1878 | + * we'll have a chance to acquire new connections... FIXME: right? */ |
1879 | + priv->acquisition_timer_id = g_idle_add_full (G_PRIORITY_DEFAULT, |
1880 | + (GSourceFunc) iterate_connections, self, NULL); |
1881 | } |
1882 | |
1883 | static void |
1884 | @@ -612,14 +686,11 @@ |
1885 | |
1886 | priv = self->priv = DEE_SHARED_MODEL_GET_PRIVATE (self); |
1887 | |
1888 | - priv->synchronized = FALSE; |
1889 | priv->swarm = NULL; |
1890 | priv->model_path = NULL; |
1891 | |
1892 | priv->revision_queue = NULL; |
1893 | priv->revision_queue_timeout_id = 0; |
1894 | - priv->dbus_signal_handler = 0; |
1895 | - priv->model_registration_id = 0; |
1896 | priv->swarm_leader_handler = 0; |
1897 | |
1898 | priv->synchronized = FALSE; |
1899 | @@ -629,7 +700,8 @@ |
1900 | if (!dee_shared_model_error_quark) |
1901 | dee_shared_model_error_quark = g_quark_from_string ("dbus-model-error"); |
1902 | |
1903 | - priv->connection = NULL; |
1904 | + priv->connections = NULL; |
1905 | + priv->connection_infos = g_array_new (FALSE, TRUE, sizeof (DeeConnectionInfo)); |
1906 | |
1907 | /* Connect to our own signals so we can queue up revisions to be emitted |
1908 | * on the bus */ |
1909 | @@ -689,51 +761,38 @@ |
1910 | }; |
1911 | |
1912 | static void |
1913 | -on_bus_connection_acquired (GObject *source_object, |
1914 | - GAsyncResult *res, |
1915 | - gpointer user_data) |
1916 | +on_connection_acquired (DeeSharedModel *self, |
1917 | + GDBusConnection *connection, |
1918 | + DeePeer *peer) |
1919 | { |
1920 | - DeeSharedModel *self; |
1921 | DeeSharedModelPrivate *priv; |
1922 | - GError *error; |
1923 | + DeeConnectionInfo connection_info; |
1924 | GDBusNodeInfo *model_introspection_data; |
1925 | + guint dbus_signal_handler; |
1926 | + guint model_registration_id; |
1927 | |
1928 | /* Keep the parsed introspection data of the Model interface around */ |
1929 | static GDBusInterfaceInfo *model_interface_info = NULL; |
1930 | |
1931 | - g_return_if_fail (DEE_IS_SHARED_MODEL (user_data)); |
1932 | + g_return_if_fail (DEE_IS_SHARED_MODEL (self)); |
1933 | |
1934 | - self = DEE_SHARED_MODEL (user_data); |
1935 | priv = self->priv; |
1936 | |
1937 | - if (priv->connection) |
1938 | - { |
1939 | - g_critical ("Internal error in DeeSharedModel. " |
1940 | - "DBus connection acquired twice."); |
1941 | - |
1942 | - /* We don't know whether or not to g_object_unref(self) at this point |
1943 | - * since the internal state is messed up. We don't do the unref to try |
1944 | - * and avoid double freeing self */ |
1945 | - |
1946 | - return; |
1947 | - } |
1948 | - |
1949 | - error = NULL; |
1950 | - priv->connection = g_bus_get_finish (res, &error); |
1951 | - |
1952 | - if (error != NULL) |
1953 | - { |
1954 | - g_critical ("DeeSharedModel@%p failed to connect to session bus: %s", |
1955 | - self, error->message); |
1956 | - g_error_free (error); |
1957 | - g_object_unref (self); // held during async call |
1958 | - return; |
1959 | - } |
1960 | + if (connection == NULL) |
1961 | + { |
1962 | + g_warning ("Internal error in DeeSharedModel. %s called with NULL " |
1963 | + "connection", __func__); |
1964 | + return; |
1965 | + } |
1966 | + |
1967 | + /* Update our list of connections */ |
1968 | + if (priv->connections) g_slist_free (priv->connections); |
1969 | + priv->connections = dee_peer_get_connections (priv->swarm); |
1970 | |
1971 | /* Listen for changes from the peers in the same swarm. |
1972 | * We do this by matching arg0 with the swarm name */ |
1973 | - priv->dbus_signal_handler = g_dbus_connection_signal_subscribe ( |
1974 | - priv->connection, |
1975 | + dbus_signal_handler = g_dbus_connection_signal_subscribe ( |
1976 | + connection, |
1977 | NULL, // sender |
1978 | "com.canonical.Dee.Model", // iface |
1979 | NULL, // member |
1980 | @@ -758,8 +817,8 @@ |
1981 | } |
1982 | |
1983 | /* Export the model on the bus */ |
1984 | - priv->model_registration_id = |
1985 | - g_dbus_connection_register_object (priv->connection, |
1986 | + model_registration_id = |
1987 | + g_dbus_connection_register_object (connection, |
1988 | priv->model_path, /* object path */ |
1989 | model_interface_info, |
1990 | &model_interface_vtable, |
1991 | @@ -767,12 +826,17 @@ |
1992 | NULL, /* user_data_free_func */ |
1993 | NULL); /* GError** */ |
1994 | |
1995 | + connection_info.connection = connection; |
1996 | + connection_info.signal_subscription_id = dbus_signal_handler; |
1997 | + connection_info.registration_id = model_registration_id; |
1998 | + g_array_append_val (priv->connection_infos, connection_info); |
1999 | + |
2000 | /* If we are swarm leaders and we have column type info we are ready by now. |
2001 | * Otherwise we will be ready when we receive the model clone from the leader |
2002 | */ |
2003 | if (dee_peer_is_swarm_leader (priv->swarm)) |
2004 | { |
2005 | - if (dee_model_get_n_columns (DEE_MODEL (self)) > 0) |
2006 | + if (dee_model_get_n_columns (DEE_MODEL (self)) > 0 && !priv->synchronized) |
2007 | { |
2008 | priv->synchronized = TRUE; |
2009 | g_object_notify (G_OBJECT (self), "synchronized"); |
2010 | @@ -789,8 +853,40 @@ |
2011 | { |
2012 | // FIXME: There's no known leader |
2013 | } |
2014 | - |
2015 | - g_object_unref (self); // held self-ref during async call |
2016 | +} |
2017 | + |
2018 | +static void |
2019 | +on_connection_closed (DeeSharedModel *self, |
2020 | + GDBusConnection *connection, |
2021 | + DeePeer *peer) |
2022 | +{ |
2023 | + DeeSharedModelPrivate *priv; |
2024 | + guint i; |
2025 | + |
2026 | + g_return_if_fail (DEE_IS_SHARED_MODEL (self)); |
2027 | + |
2028 | + priv = self->priv; |
2029 | + |
2030 | + /* Update our list of connections */ |
2031 | + if (priv->connections) g_slist_free (priv->connections); |
2032 | + priv->connections = dee_peer_get_connections (priv->swarm); |
2033 | + |
2034 | + /* Disconnect signals etc */ |
2035 | + for (i = 0; i < priv->connection_infos->len; i++) |
2036 | + { |
2037 | + DeeConnectionInfo *info; |
2038 | + info = &g_array_index (priv->connection_infos, DeeConnectionInfo, i); |
2039 | + if (info->connection == connection) |
2040 | + { |
2041 | + g_dbus_connection_unregister_object (info->connection, |
2042 | + info->registration_id); |
2043 | + g_dbus_connection_signal_unsubscribe (info->connection, |
2044 | + info->signal_subscription_id); |
2045 | + /* remove the item */ |
2046 | + g_array_remove_index (priv->connection_infos, i); |
2047 | + break; |
2048 | + } |
2049 | + } |
2050 | } |
2051 | |
2052 | /* Callback for clone_leader() */ |
2053 | @@ -811,7 +907,7 @@ |
2054 | priv = self->priv; |
2055 | |
2056 | error = NULL; |
2057 | - transaction = g_dbus_connection_call_finish (G_DBUS_CONNECTION(source_object), |
2058 | + transaction = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), |
2059 | res, &error); |
2060 | |
2061 | if (error != NULL) |
2062 | @@ -859,10 +955,10 @@ |
2063 | clone_leader (DeeSharedModel *self) |
2064 | { |
2065 | DeeSharedModelPrivate *priv; |
2066 | + GSList *iter; |
2067 | |
2068 | g_return_if_fail (DEE_IS_SHARED_MODEL (self)); |
2069 | g_return_if_fail (dee_peer_get_swarm_leader (self->priv->swarm) != NULL); |
2070 | - g_return_if_fail (self->priv->connection != NULL); |
2071 | g_return_if_fail (self->priv->revision_queue == NULL); |
2072 | g_return_if_fail (dee_model_get_n_rows (DEE_MODEL (self)) == 0); |
2073 | |
2074 | @@ -871,18 +967,23 @@ |
2075 | trace_object (self, "Cloning leader '%s'", |
2076 | dee_shared_model_get_swarm_name (self)); |
2077 | |
2078 | - g_dbus_connection_call(priv->connection, |
2079 | - dee_shared_model_get_swarm_name (self), // name |
2080 | - priv->model_path, // obj path |
2081 | - "com.canonical.Dee.Model", // iface |
2082 | - "Clone", // member |
2083 | - NULL, // args |
2084 | - G_VARIANT_TYPE ("(sasaavauay(tt))"), // ret type |
2085 | - G_DBUS_CALL_FLAGS_NONE, |
2086 | - -1, // timeout |
2087 | - NULL, // cancel |
2088 | - on_clone_received, // cb |
2089 | - g_object_ref (self)); // userdata |
2090 | + /* This shouldn't really happen when we have multiple connections, but let's |
2091 | + * have it here for consistency */ |
2092 | + for (iter = priv->connections; iter != NULL; iter = iter->next) |
2093 | + { |
2094 | + g_dbus_connection_call((GDBusConnection*) iter->data, |
2095 | + dee_shared_model_get_swarm_name (self), // name |
2096 | + priv->model_path, // obj path |
2097 | + "com.canonical.Dee.Model", // iface |
2098 | + "Clone", // member |
2099 | + NULL, // args |
2100 | + G_VARIANT_TYPE ("(sasaavauay(tt))"), // ret type |
2101 | + G_DBUS_CALL_FLAGS_NONE, |
2102 | + -1, // timeout |
2103 | + NULL, // cancel |
2104 | + on_clone_received, // cb |
2105 | + g_object_ref (self)); // userdata |
2106 | + } |
2107 | } |
2108 | |
2109 | static void |
2110 | @@ -894,20 +995,33 @@ |
2111 | GVariant *parameters, |
2112 | gpointer user_data) |
2113 | { |
2114 | + DeeSharedModel *model; |
2115 | + const gchar *unique_name; |
2116 | + |
2117 | g_return_if_fail (DEE_IS_SHARED_MODEL (user_data)); |
2118 | |
2119 | + unique_name = g_dbus_connection_get_unique_name (connection); |
2120 | + |
2121 | + trace_object (user_data, "%s: sender: %s, our unique_name: %s", |
2122 | + __func__, sender_name, unique_name); |
2123 | + |
2124 | /* Ignore signals from our selves. We may get those because of the way |
2125 | * we set up the match rules */ |
2126 | - if (g_strcmp0 (sender_name, |
2127 | + if (unique_name != NULL && g_strcmp0 (sender_name, |
2128 | g_dbus_connection_get_unique_name (connection)) == 0) |
2129 | return; |
2130 | |
2131 | - |
2132 | if (g_strcmp0 (signal_name, "Commit") == 0) |
2133 | { |
2134 | - commit_transaction (DEE_SHARED_MODEL (user_data), |
2135 | - sender_name, |
2136 | - parameters); |
2137 | + model = DEE_SHARED_MODEL (user_data); |
2138 | + commit_transaction (model, sender_name, parameters); |
2139 | + |
2140 | + if (g_slist_length (model->priv->connections) > 1) |
2141 | + { |
2142 | + /* this is a server and a client (non-leader) just committed a change |
2143 | + * to the model, let's invalidate all other clients */ |
2144 | + invalidate_peer (model, sender_name, connection); |
2145 | + } |
2146 | } |
2147 | else |
2148 | g_warning ("Unexpected signal %s.%s from %s", |
2149 | @@ -959,7 +1073,6 @@ |
2150 | gint i, j; |
2151 | |
2152 | g_return_if_fail (DEE_IS_SHARED_MODEL (self)); |
2153 | - g_return_if_fail (sender_name != NULL); |
2154 | g_return_if_fail (transaction != NULL); |
2155 | |
2156 | g_variant_ref_sink (transaction); |
2157 | @@ -1036,7 +1149,7 @@ |
2158 | if (dee_shared_model_is_leader (self)) |
2159 | { |
2160 | g_warning ("Invalidating %s", sender_name); |
2161 | - invalidate_peer (self, sender_name); |
2162 | + invalidate_peer (self, sender_name, NULL); |
2163 | } |
2164 | |
2165 | g_variant_unref (transaction); |
2166 | @@ -1245,12 +1358,13 @@ |
2167 | /* Call DBus method com.canonical.Dee.Model.Invalidate() on @sender_name */ |
2168 | static void |
2169 | invalidate_peer (DeeSharedModel *self, |
2170 | - const gchar *sender_name) |
2171 | + const gchar *sender_name, |
2172 | + GDBusConnection *except) |
2173 | { |
2174 | DeeSharedModelPrivate *priv; |
2175 | + GSList *iter; |
2176 | |
2177 | g_return_if_fail (DEE_IS_SHARED_MODEL (self)); |
2178 | - g_return_if_fail (sender_name != NULL); |
2179 | |
2180 | if (!dee_shared_model_is_leader (self)) |
2181 | { |
2182 | @@ -1261,18 +1375,23 @@ |
2183 | |
2184 | priv = self->priv; |
2185 | |
2186 | - g_dbus_connection_call (priv->connection, |
2187 | - sender_name, |
2188 | - priv->model_path, |
2189 | - "com.canonical.Dee.Model", |
2190 | - "Invalidate", |
2191 | - NULL, /* params */ |
2192 | - NULL, /* reply type */ |
2193 | - G_DBUS_CALL_FLAGS_NONE, |
2194 | - -1, /* timeout */ |
2195 | - NULL, /* cancel */ |
2196 | - NULL, /* cb */ |
2197 | - NULL); /* user data */ |
2198 | + // invalidate peers on all connections |
2199 | + for (iter = priv->connections; iter != NULL; iter = iter->next) |
2200 | + { |
2201 | + if (iter->data == except) continue; |
2202 | + g_dbus_connection_call ((GDBusConnection*) iter->data, |
2203 | + sender_name, |
2204 | + priv->model_path, |
2205 | + "com.canonical.Dee.Model", |
2206 | + "Invalidate", |
2207 | + NULL, /* params */ |
2208 | + NULL, /* reply type */ |
2209 | + G_DBUS_CALL_FLAGS_NONE, |
2210 | + -1, /* timeout */ |
2211 | + NULL, /* cancel */ |
2212 | + NULL, /* cb */ |
2213 | + NULL); /* user data */ |
2214 | + } |
2215 | } |
2216 | |
2217 | /* Public Methods */ |
2218 | @@ -1309,6 +1428,44 @@ |
2219 | } |
2220 | |
2221 | /** |
2222 | + * dee_shared_model_new_for_peer: |
2223 | + * @peer: (transfer full): A #DeePeer instance. |
2224 | + * |
2225 | + * Create a new empty shared model without any column schema associated. |
2226 | + * The column schema will be set in one of two ways: firstly you may set it |
2227 | + * manually with dee_model_set_schema() or secondly it will be set once |
2228 | + * the first rows are exchanged with a peer model. |
2229 | + * |
2230 | + * A #DeeSharedModel with a schema manually set has to be created before |
2231 | + * creating more #DeeSharedModel with the same @name. |
2232 | + * |
2233 | + * A shared model created with this constructor will store row data in a |
2234 | + * suitably picked memory backed model. |
2235 | + * |
2236 | + * Return value: (transfer full) (type DeeSharedModel): a new #DeeSharedModel |
2237 | + */ |
2238 | +DeeModel* |
2239 | +dee_shared_model_new_for_peer (DeePeer *peer) |
2240 | +{ |
2241 | + DeeModel *self; |
2242 | + DeeModel *back_end; |
2243 | + |
2244 | + g_return_val_if_fail (peer != NULL, NULL); |
2245 | + |
2246 | + back_end = (DeeModel*) dee_sequence_model_new (); |
2247 | + |
2248 | + self = g_object_new (DEE_TYPE_SHARED_MODEL, |
2249 | + "back-end", back_end, |
2250 | + "peer", peer, |
2251 | + NULL); |
2252 | + |
2253 | + g_object_unref (back_end); |
2254 | + g_object_unref (peer); |
2255 | + |
2256 | + return self; |
2257 | +} |
2258 | + |
2259 | +/** |
2260 | * dee_shared_model_new_with_back_end: |
2261 | * @name: (transfer none): A well known name to publish this model under. |
2262 | * Models sharing this name will synchronize with each other |
2263 | @@ -1318,8 +1475,8 @@ |
2264 | * |
2265 | * Create a new shared model storing all data in @back_end. |
2266 | * |
2267 | - * In order to start synchronizing the new model with peer models you must call |
2268 | - * dee_shared_model_connect() on it. |
2269 | + * The model will start synchronizing with peer models as soon as possible and |
2270 | + * the #DeeSharedModel:synchronized property will be set once finished. |
2271 | * |
2272 | * Return value: (transfer full) (type DeeSharedModel): a new #DeeSharedModel |
2273 | */ |
2274 | @@ -1460,6 +1617,7 @@ |
2275 | { |
2276 | DeeSharedModelPrivate *priv; |
2277 | GError *error; |
2278 | + GSList *iter; |
2279 | guint n_revisions; |
2280 | |
2281 | g_return_val_if_fail (DEE_IS_SHARED_MODEL (self), 0); |
2282 | @@ -1467,14 +1625,18 @@ |
2283 | priv = self->priv; |
2284 | n_revisions = flush_revision_queue (DEE_MODEL (self)); |
2285 | |
2286 | - error = NULL; |
2287 | - g_dbus_connection_flush_sync (priv->connection, NULL, &error); |
2288 | - if (error) |
2289 | + for (iter = priv->connections; iter != NULL; iter = iter->next) |
2290 | { |
2291 | - g_critical ("Error when flushing %u revisions of %s@%p: %s", |
2292 | - n_revisions, G_OBJECT_TYPE_NAME (self), self, error->message); |
2293 | - g_error_free (error); |
2294 | - return 0; |
2295 | + error = NULL; |
2296 | + g_dbus_connection_flush_sync ((GDBusConnection*) iter->data, NULL, &error); |
2297 | + if (error) |
2298 | + { |
2299 | + g_critical ("Error when flushing %u revisions of %s@%p: %s", |
2300 | + n_revisions, G_OBJECT_TYPE_NAME (self), self, |
2301 | + error->message); |
2302 | + g_error_free (error); |
2303 | + return 0; |
2304 | + } |
2305 | } |
2306 | |
2307 | return n_revisions; |
2308 | |
2309 | === modified file 'dee/dee-shared-model.h' |
2310 | --- dee/dee-shared-model.h 2011-03-06 09:42:20 +0000 |
2311 | +++ dee/dee-shared-model.h 2011-12-19 08:59:26 +0000 |
2312 | @@ -104,6 +104,8 @@ |
2313 | |
2314 | DeeModel* dee_shared_model_new (const gchar *name); |
2315 | |
2316 | +DeeModel* dee_shared_model_new_for_peer (DeePeer *peer); |
2317 | + |
2318 | DeeModel* dee_shared_model_new_with_back_end (const gchar *name, |
2319 | DeeModel *back_end); |
2320 | |
2321 | |
2322 | === modified file 'dee/dee.h' |
2323 | --- dee/dee.h 2011-12-15 12:28:43 +0000 |
2324 | +++ dee/dee.h 2011-12-19 08:59:26 +0000 |
2325 | @@ -30,6 +30,8 @@ |
2326 | #define _DEE_H_INSIDE |
2327 | |
2328 | #include <dee-peer.h> |
2329 | +#include <dee-server.h> |
2330 | +#include <dee-client.h> |
2331 | #include <dee-model.h> |
2332 | #include <dee-model-reader.h> |
2333 | #include <dee-serializable-model.h> |
2334 | |
2335 | === modified file 'doc/reference/dee-1.0/dee-1.0-docs.sgml' |
2336 | --- doc/reference/dee-1.0/dee-1.0-docs.sgml 2011-12-14 14:44:58 +0000 |
2337 | +++ doc/reference/dee-1.0/dee-1.0-docs.sgml 2011-12-19 08:59:26 +0000 |
2338 | @@ -12,6 +12,8 @@ |
2339 | <chapter> |
2340 | <title>Peer Discovery</title> |
2341 | <xi:include href="xml/dee-peer.xml"/> |
2342 | + <xi:include href="xml/dee-server.xml"/> |
2343 | + <xi:include href="xml/dee-client.xml"/> |
2344 | </chapter> |
2345 | |
2346 | <chapter> |
2347 | |
2348 | === modified file 'doc/reference/dee-1.0/dee-1.0.types' |
2349 | --- doc/reference/dee-1.0/dee-1.0.types 2011-11-02 14:05:47 +0000 |
2350 | +++ doc/reference/dee-1.0/dee-1.0.types 2011-12-19 08:59:26 +0000 |
2351 | @@ -1,18 +1,21 @@ |
2352 | +dee_analyzer_get_type |
2353 | +dee_client_get_type |
2354 | +dee_file_resource_manager_get_type |
2355 | +dee_filter_model_get_type |
2356 | +dee_glist_result_set_get_type |
2357 | +dee_hash_index_get_type |
2358 | +dee_index_get_type |
2359 | +dee_model_get_type |
2360 | dee_peer_get_type |
2361 | -dee_model_get_type |
2362 | -dee_serializable_model_get_type |
2363 | dee_proxy_model_get_type |
2364 | +dee_resource_manager_get_type |
2365 | +dee_result_set_get_type |
2366 | dee_sequence_model_get_type |
2367 | +dee_serializable_get_type |
2368 | +dee_serializable_model_get_type |
2369 | +dee_server_get_type |
2370 | dee_shared_model_get_type |
2371 | dee_term_list_get_type |
2372 | -dee_result_set_get_type |
2373 | -dee_analyzer_get_type |
2374 | dee_text_analyzer_get_type |
2375 | -dee_index_get_type |
2376 | -dee_hash_index_get_type |
2377 | +dee_transaction_get_type |
2378 | dee_tree_index_get_type |
2379 | -dee_filter_model_get_type |
2380 | -dee_serializable_get_type |
2381 | -dee_resource_manager_get_type |
2382 | -dee_file_resource_manager_get_type |
2383 | - |
2384 | |
2385 | === modified file 'tests/Makefile.am' |
2386 | --- tests/Makefile.am 2011-12-13 13:49:08 +0000 |
2387 | +++ tests/Makefile.am 2011-12-19 08:59:26 +0000 |
2388 | @@ -48,6 +48,8 @@ |
2389 | if HAVE_GTX |
2390 | test_dee_SOURCES += test-model-interactions.c |
2391 | test_dee_SOURCES += test-peer-interactions.c |
2392 | +test_dee_SOURCES += test-client-server.c |
2393 | + |
2394 | AM_CPPFLAGS += $(GTX_CFLAGS) |
2395 | test_dee_LDADD += $(GTX_LIBS) |
2396 | |
2397 | @@ -67,9 +69,13 @@ |
2398 | peer-helper-1peer.c \ |
2399 | $(NULL) |
2400 | |
2401 | +server_helpers = \ |
2402 | + server-helper-client.c |
2403 | + |
2404 | noinst_PROGRAMS += \ |
2405 | $(model_helpers:.c=) \ |
2406 | $(peer_helpers:.c=) \ |
2407 | + $(server_helpers:.c=) \ |
2408 | $(NULL) |
2409 | |
2410 | model_helper_clone3rows_SOURCES = model-helper-clone3rows.c |
2411 | @@ -102,6 +108,9 @@ |
2412 | peer_helper_1peer_SOURCES = peer-helper-1peer.c |
2413 | peer_helper_1peer_LDADD = $(test_dee_LDADD) |
2414 | |
2415 | +server_helper_client_SOURCES = server-helper-client.c |
2416 | +server_helper_client_LDADD = $(test_dee_LDADD) |
2417 | + |
2418 | endif # HAVE_GTX |
2419 | |
2420 | # |
2421 | |
2422 | === modified file 'tests/model-helper-add3rows.c' |
2423 | --- tests/model-helper-add3rows.c 2011-11-30 10:19:23 +0000 |
2424 | +++ tests/model-helper-add3rows.c 2011-12-19 08:59:26 +0000 |
2425 | @@ -43,7 +43,11 @@ |
2426 | g_type_init (); |
2427 | g_thread_init (NULL); |
2428 | |
2429 | - model = dee_shared_model_new (argv[1]); |
2430 | + if (argc == 2) |
2431 | + model = dee_shared_model_new (argv[1]); |
2432 | + else |
2433 | + model = dee_shared_model_new_for_peer ((DeePeer*) dee_client_new (argv[1])); |
2434 | + |
2435 | |
2436 | /* Wait until we find the leader */ |
2437 | if (gtx_wait_for_signal (G_OBJECT (model), 1000, "notify::synchronized", NULL)) |
2438 | |
2439 | === modified file 'tests/model-helper-change3rows.c' |
2440 | --- tests/model-helper-change3rows.c 2011-11-30 10:19:23 +0000 |
2441 | +++ tests/model-helper-change3rows.c 2011-12-19 08:59:26 +0000 |
2442 | @@ -42,7 +42,11 @@ |
2443 | |
2444 | g_type_init (); |
2445 | |
2446 | - model = dee_shared_model_new (argv[1]); |
2447 | + if (argc == 2) |
2448 | + model = dee_shared_model_new (argv[1]); |
2449 | + else |
2450 | + model = dee_shared_model_new_for_peer ((DeePeer*) dee_client_new (argv[1])); |
2451 | + |
2452 | |
2453 | if (gtx_wait_for_signal (G_OBJECT (model), 1000, "notify::synchronized", NULL)) |
2454 | g_error ("Helper model timed out waiting for 'ready' signal"); |
2455 | |
2456 | === modified file 'tests/model-helper-clear3add5.c' |
2457 | --- tests/model-helper-clear3add5.c 2011-11-30 10:19:23 +0000 |
2458 | +++ tests/model-helper-clear3add5.c 2011-12-19 08:59:26 +0000 |
2459 | @@ -42,7 +42,11 @@ |
2460 | g_type_init (); |
2461 | g_thread_init (NULL); |
2462 | |
2463 | - model = dee_shared_model_new (argv[1]); |
2464 | + if (argc == 2) |
2465 | + model = dee_shared_model_new (argv[1]); |
2466 | + else |
2467 | + model = dee_shared_model_new_for_peer ((DeePeer*) dee_client_new (argv[1])); |
2468 | + |
2469 | |
2470 | if (gtx_wait_for_signal (G_OBJECT (model), 100000, "notify::synchronized", NULL)) |
2471 | g_error ("Helper model timed out waiting for 'ready' signal"); |
2472 | |
2473 | === modified file 'tests/model-helper-clear3rows.c' |
2474 | --- tests/model-helper-clear3rows.c 2011-11-30 10:19:23 +0000 |
2475 | +++ tests/model-helper-clear3rows.c 2011-12-19 08:59:26 +0000 |
2476 | @@ -42,7 +42,11 @@ |
2477 | g_type_init (); |
2478 | g_thread_init (NULL); |
2479 | |
2480 | - model = dee_shared_model_new (argv[1]); |
2481 | + if (argc == 2) |
2482 | + model = dee_shared_model_new (argv[1]); |
2483 | + else |
2484 | + model = dee_shared_model_new_for_peer ((DeePeer*) dee_client_new (argv[1])); |
2485 | + |
2486 | |
2487 | if (gtx_wait_for_signal (G_OBJECT (model), 100000, "notify::synchronized", NULL)) |
2488 | g_error ("Helper model timed out waiting for 'ready' signal"); |
2489 | |
2490 | === modified file 'tests/model-helper-clone3rows.c' |
2491 | --- tests/model-helper-clone3rows.c 2011-11-30 10:19:23 +0000 |
2492 | +++ tests/model-helper-clone3rows.c 2011-12-19 08:59:26 +0000 |
2493 | @@ -35,7 +35,12 @@ |
2494 | g_type_init (); |
2495 | g_thread_init (NULL); |
2496 | |
2497 | - model = dee_shared_model_new (argv[1]); |
2498 | + g_set_prgname ("model-helper"); |
2499 | + |
2500 | + if (argc == 2) |
2501 | + model = dee_shared_model_new (argv[1]); |
2502 | + else |
2503 | + model = dee_shared_model_new_for_peer ((DeePeer*) dee_client_new (argv[1])); |
2504 | |
2505 | if (gtx_wait_for_signal (G_OBJECT (model), 100000, "notify::synchronized", NULL)) |
2506 | g_error ("Helper model timed out waiting for 'ready' signal"); |
2507 | |
2508 | === modified file 'tests/model-helper-insert1row.c' |
2509 | --- tests/model-helper-insert1row.c 2011-11-30 10:19:23 +0000 |
2510 | +++ tests/model-helper-insert1row.c 2011-12-19 08:59:26 +0000 |
2511 | @@ -43,7 +43,11 @@ |
2512 | g_type_init (); |
2513 | g_thread_init (NULL); |
2514 | |
2515 | - model = dee_shared_model_new (argv[1]); |
2516 | + if (argc == 2) |
2517 | + model = dee_shared_model_new (argv[1]); |
2518 | + else |
2519 | + model = dee_shared_model_new_for_peer ((DeePeer*) dee_client_new (argv[1])); |
2520 | + |
2521 | |
2522 | /* Wait until we find the leader */ |
2523 | if (gtx_wait_for_signal (G_OBJECT (model), 1000, "notify::synchronized", NULL)) |
2524 | |
2525 | === modified file 'tests/model-helper-remove3rows.c' |
2526 | --- tests/model-helper-remove3rows.c 2011-11-30 10:19:23 +0000 |
2527 | +++ tests/model-helper-remove3rows.c 2011-12-19 08:59:26 +0000 |
2528 | @@ -53,7 +53,11 @@ |
2529 | |
2530 | g_type_init (); |
2531 | |
2532 | - model = dee_shared_model_new (argv[1]); |
2533 | + if (argc == 2) |
2534 | + model = dee_shared_model_new (argv[1]); |
2535 | + else |
2536 | + model = dee_shared_model_new_for_peer ((DeePeer*) dee_client_new (argv[1])); |
2537 | + |
2538 | |
2539 | if (gtx_wait_for_signal (G_OBJECT (model), 1000, "notify::synchronized", NULL)) |
2540 | g_error ("Helper model timed out waiting for 'ready' signal"); |
2541 | |
2542 | === modified file 'tests/model-helper-schemaless.c' |
2543 | --- tests/model-helper-schemaless.c 2011-11-30 10:19:23 +0000 |
2544 | +++ tests/model-helper-schemaless.c 2011-12-19 08:59:26 +0000 |
2545 | @@ -35,7 +35,10 @@ |
2546 | g_type_init (); |
2547 | g_thread_init (NULL); |
2548 | |
2549 | - model = dee_shared_model_new (argv[1]); |
2550 | + if (argc == 2) |
2551 | + model = dee_shared_model_new (argv[1]); |
2552 | + else |
2553 | + model = dee_shared_model_new_for_peer ((DeePeer*) dee_client_new (argv[1])); |
2554 | |
2555 | /* Important: In this test it's the *slave* that sets the schema, |
2556 | * the leader is schemaless at this point in time, and will |
2557 | |
2558 | === added file 'tests/server-helper-client.c' |
2559 | --- tests/server-helper-client.c 1970-01-01 00:00:00 +0000 |
2560 | +++ tests/server-helper-client.c 2011-12-19 08:59:26 +0000 |
2561 | @@ -0,0 +1,108 @@ |
2562 | +/* |
2563 | + * Copyright (C) 2011 Canonical Ltd |
2564 | + * |
2565 | + * This program is free software: you can redistribute it and/or modify |
2566 | + * it under the terms of the GNU General Public License version 3 as |
2567 | + * published by the Free Software Foundation. |
2568 | + * |
2569 | + * This program is distributed in the hope that it will be useful, |
2570 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2571 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2572 | + * GNU General Public License for more details. |
2573 | + * |
2574 | + * You should have received a copy of the GNU General Public License |
2575 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2576 | + * |
2577 | + * Authored by |
2578 | + * Michal Hruby <michal.hruby@canonical.com> |
2579 | + * |
2580 | + */ |
2581 | + |
2582 | +#include "config.h" |
2583 | +#include <glib.h> |
2584 | +#include <glib-object.h> |
2585 | +#include <stdlib.h> |
2586 | + |
2587 | +#include <gtx.h> |
2588 | +#include <dee.h> |
2589 | + |
2590 | +static void |
2591 | +on_peer_found (DeePeer *peer, gchar *name, gint *n_peers) |
2592 | +{ |
2593 | + (*n_peers)++; |
2594 | +} |
2595 | + |
2596 | +static void |
2597 | +on_peer_lost (DeePeer *peer, gchar *name, gint *n_peers) |
2598 | +{ |
2599 | + (*n_peers)++; |
2600 | +} |
2601 | + |
2602 | +static int |
2603 | +peer_function (gchar *argv[]) |
2604 | +{ |
2605 | + DeePeer *peer; |
2606 | + gint n_peers = 0; |
2607 | + |
2608 | + peer = (DeePeer*) dee_client_new (argv[1]); |
2609 | + |
2610 | + g_signal_connect (peer, "peer-found", G_CALLBACK (on_peer_found), &n_peers); |
2611 | + g_signal_connect (peer, "peer-lost", G_CALLBACK (on_peer_lost), &n_peers); |
2612 | + |
2613 | + if (gtx_wait_for_signal (G_OBJECT (peer), 10000, "notify::swarm-leader", NULL)) |
2614 | + g_error ("Peer helper timed out waiting for swarm leader"); |
2615 | + |
2616 | + /* The main process should be swarm leaders. Not us */ |
2617 | + g_assert_cmpint (0, ==, dee_peer_is_swarm_leader (peer)); |
2618 | + |
2619 | + /* At this point we shouldn't have emitted 'peer-found' yet */ |
2620 | + g_assert_cmpint (0, ==, n_peers); |
2621 | + |
2622 | + if (gtx_wait_for_signal (G_OBJECT (peer), 10000, "peer-found", NULL)) |
2623 | + g_error ("Peer helper timed out waiting for 'peer-found' signal"); |
2624 | + |
2625 | + g_assert_cmpint (1, ==, n_peers); |
2626 | + |
2627 | + gtx_assert_last_unref (peer); |
2628 | + |
2629 | + return 0; |
2630 | +} |
2631 | + |
2632 | +static gint finished_children = 0; |
2633 | +static void |
2634 | +child_exited (GPid pid, gint status, gpointer user_data) |
2635 | +{ |
2636 | + finished_children++; |
2637 | +} |
2638 | + |
2639 | +gint |
2640 | +main (gint argc, gchar *argv[]) |
2641 | +{ |
2642 | + int num_clients, i; |
2643 | + |
2644 | + g_type_init (); |
2645 | + g_thread_init (NULL); |
2646 | + |
2647 | + if (argc < 3) g_error ("Invalid invocation"); |
2648 | + |
2649 | + num_clients = i = atoi (argv[2]); |
2650 | + while (i-- > 0) |
2651 | + { |
2652 | + GPid pid = (GPid) fork (); |
2653 | + if (pid != 0) g_child_watch_add (pid, child_exited, NULL); |
2654 | + else |
2655 | + { |
2656 | + return peer_function (argv); |
2657 | + } |
2658 | + } |
2659 | + |
2660 | + for (i = 0; i < 10; i++) |
2661 | + { |
2662 | + gtx_yield_main_loop (200); |
2663 | + if (finished_children == num_clients) break; |
2664 | + } |
2665 | + |
2666 | + g_assert_cmpint (num_clients, ==, finished_children); |
2667 | + |
2668 | + return 0; |
2669 | +} |
2670 | |
2671 | === added file 'tests/test-client-server.c' |
2672 | --- tests/test-client-server.c 1970-01-01 00:00:00 +0000 |
2673 | +++ tests/test-client-server.c 2011-12-19 08:59:26 +0000 |
2674 | @@ -0,0 +1,644 @@ |
2675 | +/* |
2676 | + * Copyright (C) 2011 Canonical Ltd |
2677 | + * |
2678 | + * This program is free software: you can redistribute it and/or modify |
2679 | + * it under the terms of the GNU General Public License version 3 as |
2680 | + * published by the Free Software Foundation. |
2681 | + * |
2682 | + * This program is distributed in the hope that it will be useful, |
2683 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2684 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2685 | + * GNU General Public License for more details. |
2686 | + * |
2687 | + * You should have received a copy of the GNU General Public License |
2688 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2689 | + * |
2690 | + * Authored by Michal Hruby <michal.hruby@canonical.com> |
2691 | + * |
2692 | + */ |
2693 | + |
2694 | +#include <glib.h> |
2695 | +#include <glib-object.h> |
2696 | +#include <dee.h> |
2697 | +#include <gtx.h> |
2698 | + |
2699 | +#define TIMEOUT 100 |
2700 | +#define PEER_NAME "com.canonical.Dee.Peer.Tests.Interactions" |
2701 | +#define MODEL_NAME "com.canonical.Dee.Peer.Tests.Interactions" |
2702 | + |
2703 | +/* A command line that launches the appropriate *-helper-* executable, |
2704 | + * giving $name as first argument */ |
2705 | +#define SERVER_HELPER(helper,name,count) \ |
2706 | + (gchar *[]) { "./server-helper-"#helper, name, #count, NULL } |
2707 | + |
2708 | +#define MODEL_HELPER(helper,name) \ |
2709 | + (gchar *[]) { "./model-helper-"#helper, name, "DeeClient", NULL } |
2710 | + |
2711 | +typedef struct |
2712 | +{ |
2713 | + DeeServer *server; |
2714 | + |
2715 | +} ServerFixture; |
2716 | + |
2717 | +typedef struct |
2718 | +{ |
2719 | + DeeModel *model; |
2720 | +} Fixture; |
2721 | + |
2722 | +static void server_setup (ServerFixture *fix, gconstpointer data); |
2723 | +static void server_teardown (ServerFixture *fix, gconstpointer data); |
2724 | +static void model_setup (Fixture *fix, gconstpointer data); |
2725 | +static void model_teardown (Fixture *fix, gconstpointer data); |
2726 | +static void model_setup_null (Fixture *fix, gconstpointer data); |
2727 | +static void model_teardown_null (Fixture *fix, gconstpointer data); |
2728 | + |
2729 | +static void test_allocation (ServerFixture *fix, gconstpointer data); |
2730 | +static void test_become_leader (ServerFixture *fix, gconstpointer data); |
2731 | +static void test_valid_client_address (ServerFixture *fix, gconstpointer data); |
2732 | +static void test_one_client (ServerFixture *fix, gconstpointer data); |
2733 | +static void test_multiple_clients (ServerFixture *fix, gconstpointer data); |
2734 | + |
2735 | +static void test_ready (Fixture *fix, gconstpointer data); |
2736 | +static void test_clone (Fixture *fix, gconstpointer data); |
2737 | +static void test_row_added (Fixture *fix, gconstpointer data); |
2738 | +static void test_row_changed (Fixture *fix, gconstpointer data); |
2739 | +static void test_row_removed (Fixture *fix, gconstpointer data); |
2740 | +static void test_model_clear (Fixture *fix, gconstpointer data); |
2741 | +static void test_clear_add (Fixture *fix, gconstpointer data); |
2742 | +static void test_clear_then_add (Fixture *fix, gconstpointer data); |
2743 | +static void test_row_inserted (Fixture *fix, gconstpointer data); |
2744 | +static void test_schemaless_leader (Fixture *fix, gconstpointer data); |
2745 | + |
2746 | +static void test_client_commit (Fixture *fix, gconstpointer data); |
2747 | + |
2748 | +void |
2749 | +test_client_server_interactions_create_suite (void) |
2750 | +{ |
2751 | +#define DOMAIN "/ClientServer/Interactions" |
2752 | + |
2753 | + g_test_add (DOMAIN"/Allocation", ServerFixture, 0, |
2754 | + server_setup, test_allocation, server_teardown); |
2755 | + g_test_add (DOMAIN"/BecomeLeader", ServerFixture, 0, |
2756 | + server_setup, test_become_leader, server_teardown); |
2757 | + g_test_add (DOMAIN"/ValidClientAddress", ServerFixture, 0, |
2758 | + server_setup, test_valid_client_address, server_teardown); |
2759 | + g_test_add (DOMAIN"/OneClient", ServerFixture, 0, |
2760 | + server_setup, test_one_client, server_teardown); |
2761 | + g_test_add (DOMAIN"/MultipleClients", ServerFixture, 0, |
2762 | + server_setup, test_multiple_clients, server_teardown); |
2763 | + |
2764 | +#undef DOMAIN |
2765 | +#define DOMAIN "/ClientServer/Model/Interactions" |
2766 | + |
2767 | + g_test_add (DOMAIN"/Ready", Fixture, 0, |
2768 | + model_setup, test_ready, model_teardown); |
2769 | + g_test_add (DOMAIN"/Clone", Fixture, 0, |
2770 | + model_setup, test_clone, model_teardown); |
2771 | + g_test_add (DOMAIN"/RowAdded", Fixture, 0, |
2772 | + model_setup, test_row_added, model_teardown); |
2773 | + g_test_add (DOMAIN"/RowChanged", Fixture, 0, |
2774 | + model_setup, test_row_changed, model_teardown); |
2775 | + g_test_add (DOMAIN"/RowRemoved", Fixture, 0, |
2776 | + model_setup, test_row_removed, model_teardown); |
2777 | + g_test_add (DOMAIN"/Clear", Fixture, 0, |
2778 | + model_setup, test_model_clear, model_teardown); |
2779 | + g_test_add (DOMAIN"/ClearAndAdd", Fixture, 0, |
2780 | + model_setup, test_clear_add, model_teardown); |
2781 | + g_test_add (DOMAIN"/ClearThenAdd", Fixture, 0, |
2782 | + model_setup, test_clear_then_add, model_teardown); |
2783 | + g_test_add (DOMAIN"/RowInserted", Fixture, 0, |
2784 | + model_setup, test_row_inserted, model_teardown); |
2785 | + g_test_add (DOMAIN"/SchemalessLeader", Fixture, 0, |
2786 | + model_setup_null, test_schemaless_leader, model_teardown_null); |
2787 | + g_test_add (DOMAIN"/ClientCommit", Fixture, 0, |
2788 | + model_setup, test_client_commit, model_teardown); |
2789 | +} |
2790 | + |
2791 | +static void |
2792 | +server_setup (ServerFixture *fix, gconstpointer data) |
2793 | +{ |
2794 | + fix->server = dee_server_new (PEER_NAME); |
2795 | + |
2796 | + g_assert_cmpint (0, ==, dee_peer_is_swarm_leader (DEE_PEER (fix->server))); |
2797 | + |
2798 | + g_assert (DEE_IS_SERVER (fix->server)); |
2799 | +} |
2800 | + |
2801 | +static void |
2802 | +server_teardown (ServerFixture *fix, gconstpointer data) |
2803 | +{ |
2804 | + gtx_assert_last_unref (fix->server); |
2805 | + |
2806 | + /* Spin the mainloop a bit to check if we have any post-test |
2807 | + * async effect crashing us */ |
2808 | + gtx_yield_main_loop (200); |
2809 | +} |
2810 | + |
2811 | +static void |
2812 | +model_setup (Fixture *fix, gconstpointer data) |
2813 | +{ |
2814 | + DeeServer *server = dee_server_new (MODEL_NAME); |
2815 | + |
2816 | + fix->model = (DeeModel*) dee_shared_model_new_for_peer (DEE_PEER (server)); |
2817 | + dee_model_set_schema (fix->model, "i", "s", NULL); |
2818 | + |
2819 | + g_assert (DEE_IS_MODEL (fix->model)); |
2820 | +} |
2821 | + |
2822 | +static void |
2823 | +model_teardown (Fixture *fix, gconstpointer data) |
2824 | +{ |
2825 | + gtx_assert_last_unref (fix->model); |
2826 | + |
2827 | + /* Spin the mainloop so the socket service gets into usable state again */ |
2828 | + gtx_yield_main_loop (200); |
2829 | +} |
2830 | + |
2831 | +static void |
2832 | +model_setup_null (Fixture *fix, gconstpointer data) |
2833 | +{ |
2834 | +} |
2835 | + |
2836 | +static void |
2837 | +model_teardown_null (Fixture *fix, gconstpointer data) |
2838 | +{ |
2839 | +} |
2840 | + |
2841 | +/******************** The actual test cases ********************/ |
2842 | + |
2843 | +static void |
2844 | +test_allocation (ServerFixture *fix, gconstpointer data) |
2845 | +{ |
2846 | + /* Do nothing, this test basically just asserts that |
2847 | + * the fix->server is cleaned up after immediate construction */ |
2848 | +} |
2849 | + |
2850 | +static void |
2851 | +test_become_leader (ServerFixture *fix, gconstpointer data) |
2852 | +{ |
2853 | + gtx_wait_for_signal (G_OBJECT (fix->server), TIMEOUT, |
2854 | + "notify::swarm-leader", NULL); |
2855 | + |
2856 | + /* Assert that we have become swarm leaders. |
2857 | + * No other peers should be running */ |
2858 | + g_assert_cmpint (0, !=, dee_peer_is_swarm_leader (DEE_PEER (fix->server))); |
2859 | +} |
2860 | + |
2861 | +static void |
2862 | +test_valid_client_address (ServerFixture *fix, gconstpointer data) |
2863 | +{ |
2864 | + gtx_wait_for_signal (G_OBJECT (fix->server), TIMEOUT, |
2865 | + "notify::swarm-leader", NULL); |
2866 | + |
2867 | + g_assert (dee_server_get_client_address (fix->server) != NULL); |
2868 | +} |
2869 | + |
2870 | +static void |
2871 | +on_connection_acquired (DeeServer *server, GDBusConnection *connection, |
2872 | + GSList **list) |
2873 | +{ |
2874 | + /* Yes, I _know_ that append() is slow, but this is a test! */ |
2875 | + *list = g_slist_append (*list, connection); |
2876 | +} |
2877 | + |
2878 | +static void |
2879 | +test_one_client (ServerFixture *fix, gconstpointer data) |
2880 | +{ |
2881 | + GSList *list = NULL; |
2882 | + |
2883 | + g_signal_connect (fix->server, "connection-acquired", |
2884 | + G_CALLBACK (on_connection_acquired), &list); |
2885 | + |
2886 | + gtx_wait_for_signal (G_OBJECT (fix->server), TIMEOUT, |
2887 | + "notify::swarm-leader", NULL); |
2888 | + |
2889 | + /* We are leaders - launch the helper */ |
2890 | + if (gtx_wait_for_command (TESTDIR, |
2891 | + SERVER_HELPER (client, PEER_NAME, 1), |
2892 | + 2000)) |
2893 | + g_critical ("Peer helper timed out"); |
2894 | + |
2895 | + gtx_assert_last_command_status (0); |
2896 | + |
2897 | + g_assert_cmpuint (g_slist_length (list), ==, 1); |
2898 | + |
2899 | + g_slist_free (list); |
2900 | +} |
2901 | + |
2902 | +static void |
2903 | +test_multiple_clients (ServerFixture *fix, gconstpointer data) |
2904 | +{ |
2905 | + GSList *list = NULL; |
2906 | + |
2907 | + g_signal_connect (fix->server, "connection-acquired", |
2908 | + G_CALLBACK (on_connection_acquired), &list); |
2909 | + |
2910 | + gtx_wait_for_signal (G_OBJECT (fix->server), TIMEOUT, |
2911 | + "notify::swarm-leader", NULL); |
2912 | + |
2913 | + /* We are leaders - launch the helper */ |
2914 | + if (gtx_wait_for_command (TESTDIR, |
2915 | + SERVER_HELPER (client, PEER_NAME, 4), |
2916 | + 2000)) |
2917 | + g_critical ("Peer helper timed out"); |
2918 | + |
2919 | + gtx_assert_last_command_status (0); |
2920 | + |
2921 | + g_assert_cmpuint (g_slist_length (list), ==, 4); |
2922 | + |
2923 | + g_slist_free (list); |
2924 | +} |
2925 | + |
2926 | +static void |
2927 | +test_ready (Fixture *fix, gconstpointer data) |
2928 | +{ |
2929 | + GParamSpec *pspec; |
2930 | + gboolean synchronized; |
2931 | + |
2932 | + /* Test the GObject property reports FALSE */ |
2933 | + g_object_get (fix->model, "synchronized", &synchronized, NULL); |
2934 | + g_assert_cmpint (0, == , synchronized); |
2935 | + |
2936 | + /* Check that convenience getter now reports FALSE */ |
2937 | + g_assert_cmpint (0, ==, dee_shared_model_is_synchronized (DEE_SHARED_MODEL (fix->model))); |
2938 | + |
2939 | + if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", &pspec)) |
2940 | + g_critical ("Model never synchronized"); |
2941 | + else |
2942 | + g_param_spec_unref (pspec); |
2943 | + |
2944 | + /* Test the GObject property reports TRUE */ |
2945 | + g_object_get (fix->model, "synchronized", &synchronized, NULL); |
2946 | + g_assert_cmpint (0, != , synchronized); |
2947 | + |
2948 | + /* Check that convenience getter now reports TRUE */ |
2949 | + g_assert_cmpint (0, !=, dee_shared_model_is_synchronized (DEE_SHARED_MODEL (fix->model))); |
2950 | + |
2951 | + if (!gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", &pspec)) |
2952 | + { |
2953 | + g_critical ("Model changed synchronization state twice"); |
2954 | + g_param_spec_unref (pspec); |
2955 | + } |
2956 | +} |
2957 | + |
2958 | +static gboolean |
2959 | +_add3rows (DeeModel *model) |
2960 | +{ |
2961 | + g_return_val_if_fail (DEE_IS_MODEL (model), FALSE); |
2962 | + |
2963 | + dee_model_append (model, 0, "zero"); |
2964 | + dee_model_append (model, 1, "one"); |
2965 | + dee_model_append (model, 2, "two"); |
2966 | + return FALSE; |
2967 | +} |
2968 | + |
2969 | +static gboolean |
2970 | +_add5rows (DeeModel *model) |
2971 | +{ |
2972 | + g_return_val_if_fail (DEE_IS_MODEL (model), FALSE); |
2973 | + |
2974 | + dee_model_append (model, 0, "zero"); |
2975 | + dee_model_append (model, 1, "one"); |
2976 | + dee_model_append (model, 2, "two"); |
2977 | + dee_model_append (model, 3, "three"); |
2978 | + dee_model_append (model, 4, "four"); |
2979 | + return FALSE; |
2980 | +} |
2981 | + |
2982 | +static gboolean |
2983 | +_change3rows (DeeModel *model) |
2984 | +{ |
2985 | + DeeModelIter *iter; |
2986 | + |
2987 | + g_return_val_if_fail (DEE_IS_MODEL (model), FALSE); |
2988 | + |
2989 | + iter = dee_model_get_iter_at_row (model, 0); |
2990 | + dee_model_set_value (model, iter, 1, g_variant_new_string ("changed_zero")); |
2991 | + |
2992 | + iter = dee_model_get_iter_at_row (model, 1); |
2993 | + dee_model_set_value (model, iter, 1, g_variant_new_string ("changed_one")); |
2994 | + |
2995 | + iter = dee_model_get_iter_at_row (model, 2); |
2996 | + dee_model_set_value (model, iter, 1, g_variant_new_string ("changed_two")); |
2997 | + |
2998 | + return FALSE; |
2999 | +} |
3000 | + |
3001 | +/* Assumes a model with 5 rows. Removes rows 0, 4, and 2 |
3002 | + * in that order. Accounting for rows shifts this becomes |
3003 | + * 0, 3, and 1. Leaving the original rows 1 and 3 now in |
3004 | + * positions 0 and 1 */ |
3005 | +static gboolean |
3006 | +_remove3rows (DeeModel *model) |
3007 | +{ |
3008 | + DeeModelIter *iter0, *iter4, *iter2; |
3009 | + DeeModelIter *orig_iter1, *orig_iter3; |
3010 | + |
3011 | + g_return_val_if_fail (DEE_IS_MODEL (model), FALSE); |
3012 | + g_return_val_if_fail (dee_model_get_n_rows (model) == 5, FALSE); |
3013 | + |
3014 | + iter0 = dee_model_get_iter_at_row (model, 0); |
3015 | + iter4 = dee_model_get_iter_at_row (model, 4); |
3016 | + iter2 = dee_model_get_iter_at_row (model, 2); |
3017 | + |
3018 | + orig_iter1 = dee_model_get_iter_at_row (model, 1); |
3019 | + orig_iter3 = dee_model_get_iter_at_row (model, 3); |
3020 | + |
3021 | + dee_model_remove (model, iter0); |
3022 | + dee_model_remove (model, iter4); |
3023 | + dee_model_remove (model, iter2); |
3024 | + |
3025 | + g_assert_cmpint (dee_model_get_n_rows (model), ==, 2); |
3026 | + g_assert (dee_model_get_iter_at_row (model, 0) == orig_iter1); |
3027 | + g_assert (dee_model_get_iter_at_row (model, 1) == orig_iter3); |
3028 | + |
3029 | + return FALSE; |
3030 | +} |
3031 | + |
3032 | +static gboolean |
3033 | +_insert1row (DeeModel *model) |
3034 | +{ |
3035 | + g_return_val_if_fail (DEE_IS_MODEL (model), FALSE); |
3036 | + |
3037 | + dee_model_insert (model, 1, 27, "twentyseven"); |
3038 | + return FALSE; |
3039 | +} |
3040 | + |
3041 | +static gboolean |
3042 | +_clear_model (DeeModel *model) |
3043 | +{ |
3044 | + g_return_val_if_fail (DEE_IS_MODEL (model), FALSE); |
3045 | + |
3046 | + dee_model_clear (model); |
3047 | + |
3048 | + return FALSE; |
3049 | +} |
3050 | + |
3051 | +static gboolean |
3052 | +_clear_and_add5rows (DeeModel *model) |
3053 | +{ |
3054 | + g_return_val_if_fail (DEE_IS_MODEL (model), FALSE); |
3055 | + |
3056 | + _clear_model (model); |
3057 | + _add5rows (model); |
3058 | + |
3059 | + return FALSE; |
3060 | +} |
3061 | + |
3062 | +static void |
3063 | +test_clone (Fixture *fix, gconstpointer data) |
3064 | +{ |
3065 | + if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL)) |
3066 | + g_critical ("Model never synchronized"); |
3067 | + |
3068 | + _add3rows (fix->model); |
3069 | + |
3070 | + if (gtx_wait_for_command (TESTDIR, |
3071 | + MODEL_HELPER (clone3rows, MODEL_NAME), |
3072 | + 1000)) |
3073 | + g_critical ("Model helper timed out"); |
3074 | + |
3075 | + gtx_assert_last_command_status (0); |
3076 | + |
3077 | + /* We test that we can do this two times */ |
3078 | + /*if (gtx_wait_for_command (TESTDIR, MODEL_HELPER (3rows, MODEL_NAME), 1000)) |
3079 | + g_critical ("Model helper timed out"); |
3080 | + |
3081 | + gtx_assert_last_command_status (0);*/ |
3082 | +} |
3083 | + |
3084 | +static void |
3085 | +test_row_added (Fixture *fix, gconstpointer data) |
3086 | +{ |
3087 | + if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL)) |
3088 | + g_critical ("Model never emitted 'ready' signal"); |
3089 | + |
3090 | + g_timeout_add (500, (GSourceFunc)_add3rows, fix->model); |
3091 | + |
3092 | + if (gtx_wait_for_command (TESTDIR, |
3093 | + MODEL_HELPER (add3rows, MODEL_NAME), |
3094 | + 2000)) |
3095 | + g_critical ("Model helper timed out"); |
3096 | + |
3097 | + gtx_assert_last_command_status (0); |
3098 | +} |
3099 | + |
3100 | +static void |
3101 | +test_row_changed (Fixture *fix, gconstpointer data) |
3102 | +{ |
3103 | + if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL)) |
3104 | + g_critical ("Model never emitted 'ready' signal"); |
3105 | + |
3106 | + _add3rows (fix->model); |
3107 | + g_timeout_add (500, (GSourceFunc)_change3rows, fix->model); |
3108 | + |
3109 | + //const gchar *cmd[] = {"dbus-monitor", NULL}; |
3110 | + //const gchar *cmd[] = {"sleep", "1",NULL}; |
3111 | + if (gtx_wait_for_command (TESTDIR, |
3112 | + MODEL_HELPER (change3rows, MODEL_NAME), |
3113 | + 2000)) |
3114 | + g_critical ("Model helper timed out"); |
3115 | + |
3116 | + gtx_assert_last_command_status (0); |
3117 | +} |
3118 | + |
3119 | +static void |
3120 | +test_row_removed (Fixture *fix, gconstpointer data) |
3121 | +{ |
3122 | + if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL)) |
3123 | + g_critical ("Model never emitted 'ready' signal"); |
3124 | + |
3125 | + _add5rows (fix->model); |
3126 | + g_timeout_add (500, (GSourceFunc)_remove3rows, fix->model); |
3127 | + |
3128 | + if (gtx_wait_for_command (TESTDIR, |
3129 | + MODEL_HELPER (remove3rows, MODEL_NAME), |
3130 | + 2000)) |
3131 | + g_critical ("Model helper timed out"); |
3132 | + |
3133 | + gtx_assert_last_command_status (0); |
3134 | +} |
3135 | + |
3136 | +static void |
3137 | +test_model_clear (Fixture *fix, gconstpointer data) |
3138 | +{ |
3139 | + if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL)) |
3140 | + g_critical ("Model never emitted 'ready' signal"); |
3141 | + |
3142 | + _add3rows (fix->model); |
3143 | + g_timeout_add (500, (GSourceFunc)_clear_model, fix->model); |
3144 | + |
3145 | + if (gtx_wait_for_command (TESTDIR, |
3146 | + MODEL_HELPER (clear3rows, MODEL_NAME), |
3147 | + 2000)) |
3148 | + g_critical ("Model helper timed out"); |
3149 | + |
3150 | + gtx_assert_last_command_status (0); |
3151 | +} |
3152 | + |
3153 | +static void |
3154 | +test_clear_add (Fixture *fix, gconstpointer data) |
3155 | +{ |
3156 | + if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL)) |
3157 | + g_critical ("Model never emitted 'ready' signal"); |
3158 | + |
3159 | + g_timeout_add (500, (GSourceFunc)_add3rows, fix->model); |
3160 | + if (gtx_wait_for_command (TESTDIR, |
3161 | + MODEL_HELPER (add3rows, MODEL_NAME), |
3162 | + 2000)) |
3163 | + g_critical ("Model helper timed out"); |
3164 | + |
3165 | + gtx_assert_last_command_status (0); |
3166 | + |
3167 | + g_timeout_add (500, (GSourceFunc)_clear_and_add5rows, fix->model); |
3168 | + if (gtx_wait_for_command (TESTDIR, |
3169 | + MODEL_HELPER (clear3add5, MODEL_NAME), |
3170 | + 2000)) |
3171 | + g_critical ("Model helper timed out"); |
3172 | + |
3173 | + gtx_assert_last_command_status (0); |
3174 | +} |
3175 | + |
3176 | +static void |
3177 | +test_clear_then_add (Fixture *fix, gconstpointer data) |
3178 | +{ |
3179 | + if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL)) |
3180 | + g_critical ("Model never emitted 'ready' signal"); |
3181 | + |
3182 | + _clear_model (fix->model); |
3183 | + g_timeout_add (500, (GSourceFunc)_add3rows, fix->model); |
3184 | + |
3185 | + if (gtx_wait_for_command (TESTDIR, |
3186 | + MODEL_HELPER (add3rows, MODEL_NAME), |
3187 | + 2000)) |
3188 | + g_critical ("Model helper timed out"); |
3189 | + |
3190 | + gtx_assert_last_command_status (0); |
3191 | + |
3192 | + g_timeout_add (500, (GSourceFunc)_clear_model, fix->model); |
3193 | + if (gtx_wait_for_command (TESTDIR, |
3194 | + MODEL_HELPER (clear3rows, MODEL_NAME), |
3195 | + 2000)) |
3196 | + g_critical ("Model helper timed out"); |
3197 | + |
3198 | + gtx_assert_last_command_status (0); |
3199 | +} |
3200 | + |
3201 | +static void |
3202 | +test_row_inserted (Fixture *fix, gconstpointer data) |
3203 | +{ |
3204 | + if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL)) |
3205 | + g_critical ("Model never emitted 'ready' signal"); |
3206 | + |
3207 | + _add3rows (fix->model); |
3208 | + g_timeout_add (500, (GSourceFunc)_insert1row, fix->model); |
3209 | + |
3210 | + if (gtx_wait_for_command (TESTDIR, |
3211 | + MODEL_HELPER (insert1row, MODEL_NAME), |
3212 | + 2000)) |
3213 | + g_critical ("Model helper timed out"); |
3214 | + |
3215 | + gtx_assert_last_command_status (0); |
3216 | +} |
3217 | + |
3218 | +static void |
3219 | +_ready (DeeModel *model, GParamSpec *pspec, GSList **rows_so_far) |
3220 | +{ |
3221 | + /* We must not have any rows when 'ready' is emitted */ |
3222 | + g_assert (*rows_so_far == NULL); |
3223 | +} |
3224 | + |
3225 | +static void |
3226 | +_collect_row (DeeModel *model, DeeModelIter *iter, GSList **rows_so_far) |
3227 | +{ |
3228 | + /* Yes, I _know_ that append() is slow, but this is a test! */ |
3229 | + *rows_so_far = g_slist_append (*rows_so_far, iter); |
3230 | +} |
3231 | + |
3232 | +/* This case must run without a Fixture */ |
3233 | +static void |
3234 | +test_schemaless_leader (Fixture *fix, gconstpointer data) |
3235 | +{ |
3236 | + DeeModel *model; |
3237 | + DeeModelIter *iter; |
3238 | + GSList *rows_added; |
3239 | + |
3240 | + g_assert (fix->model == NULL); |
3241 | + |
3242 | + /* Set up a clean model *without* a schema. We will pick up the schema |
3243 | + * with the first transaction from the slave. Or at least, that's what we |
3244 | + * want to assert ;-) */ |
3245 | + model = dee_shared_model_new_for_peer (DEE_PEER (dee_server_new (MODEL_NAME))); |
3246 | + // no set_schema() on purpose! |
3247 | + |
3248 | + /* Listen for changes */ |
3249 | + rows_added = NULL; |
3250 | + g_signal_connect (model, "row-added", G_CALLBACK (_collect_row), &rows_added); |
3251 | + g_signal_connect (model, "notify::synchronized", G_CALLBACK (_ready), &rows_added); |
3252 | + |
3253 | + /* Remote process defines column types and adds two rows */ |
3254 | + if (gtx_wait_for_command (TESTDIR, |
3255 | + MODEL_HELPER (schemaless, MODEL_NAME), |
3256 | + 2000)) |
3257 | + g_critical ("Model helper timed out"); |
3258 | + |
3259 | + /* Check that we got the right schema from the peer */ |
3260 | + g_assert_cmpint (dee_model_get_n_columns (model), ==, 2); |
3261 | + g_assert_cmpstr (dee_model_get_column_schema (model, 0), ==, "i"); |
3262 | + g_assert_cmpstr (dee_model_get_column_schema (model, 1), ==, "s"); |
3263 | + |
3264 | + /* Check that we got what we expected */ |
3265 | + g_assert_cmpint (g_slist_length (rows_added), == , 2); |
3266 | + g_assert_cmpint (dee_model_get_n_rows (model), ==, 2); |
3267 | + g_assert_cmpint (dee_model_get_n_columns (model), ==, 2); |
3268 | + |
3269 | + iter = (DeeModelIter*) g_slist_nth (rows_added, 0)->data; |
3270 | + g_assert_cmpint (dee_model_get_position (model, iter), == , 0); |
3271 | + g_assert_cmpint (dee_model_get_int32 (model, iter, 0), == , 27); |
3272 | + g_assert_cmpstr (dee_model_get_string (model, iter, 1), == , "skunkworks"); |
3273 | + |
3274 | + iter = (DeeModelIter*) g_slist_nth (rows_added, 1)->data; |
3275 | + g_assert_cmpint (dee_model_get_position (model, iter), == , 1); |
3276 | + g_assert_cmpint (dee_model_get_int32 (model, iter, 0), == , 68); |
3277 | + g_assert_cmpstr (dee_model_get_string (model, iter, 1), == , "wumbo"); |
3278 | + |
3279 | + gtx_assert_last_unref (model); |
3280 | + g_slist_free (rows_added); |
3281 | + |
3282 | + /* Spin the mainloop so the socket service gets into usable state again */ |
3283 | + gtx_yield_main_loop (200); |
3284 | +} |
3285 | + |
3286 | +static void |
3287 | +test_client_commit (Fixture *fix, gconstpointer data) |
3288 | +{ |
3289 | + gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, |
3290 | + "notify::synchronized", NULL); |
3291 | + |
3292 | + DeeModel *client_model1 = dee_shared_model_new_for_peer ( |
3293 | + DEE_PEER (dee_client_new (MODEL_NAME))); |
3294 | + DeeModel *client_model2 = dee_shared_model_new_for_peer ( |
3295 | + DEE_PEER (dee_client_new (MODEL_NAME))); |
3296 | + |
3297 | + gtx_wait_for_signal (G_OBJECT (client_model1), TIMEOUT, |
3298 | + "notify::synchronized", NULL); |
3299 | + |
3300 | + g_assert (dee_shared_model_is_synchronized (DEE_SHARED_MODEL (client_model1))); |
3301 | + |
3302 | + if (!dee_shared_model_is_synchronized (DEE_SHARED_MODEL (client_model2))) |
3303 | + { |
3304 | + gtx_wait_for_signal (G_OBJECT (client_model2), TIMEOUT, |
3305 | + "notify::synchronized", NULL); |
3306 | + } |
3307 | + |
3308 | + dee_model_append (client_model1, 38, "client_change"); |
3309 | + |
3310 | + gtx_yield_main_loop (500); |
3311 | + |
3312 | + g_assert_cmpuint (dee_model_get_n_rows (fix->model), >, 0); |
3313 | + g_assert_cmpuint (dee_model_get_n_rows (client_model1), >, 0); |
3314 | + g_assert_cmpuint (dee_model_get_n_rows (client_model2), >, 0); |
3315 | + |
3316 | + g_object_unref (client_model1); |
3317 | + g_object_unref (client_model2); |
3318 | +} |
3319 | |
3320 | === modified file 'tests/test-dee.c' |
3321 | --- tests/test-dee.c 2011-12-06 20:52:49 +0000 |
3322 | +++ tests/test-dee.c 2011-12-19 08:59:26 +0000 |
3323 | @@ -40,6 +40,7 @@ |
3324 | #ifdef HAVE_GTX |
3325 | void test_model_interactions_create_suite(void); |
3326 | void test_peer_interactions_create_suite(void); |
3327 | +void test_client_server_interactions_create_suite(void); |
3328 | #endif /* HAVE_GTX */ |
3329 | |
3330 | gint |
3331 | @@ -69,6 +70,7 @@ |
3332 | #ifdef HAVE_GTX |
3333 | test_model_interactions_create_suite(); |
3334 | test_peer_interactions_create_suite(); |
3335 | + test_client_server_interactions_create_suite(); |
3336 | #else |
3337 | g_message ("Interactions' test suite disabled. GTX not found. You can download GTX from https://launchpad.net/gtx"); |
3338 | #endif /* HAVE_GTX */ |
3339 | |
3340 | === modified file 'tests/test-filter-model.c' |
3341 | --- tests/test-filter-model.c 2011-12-14 14:44:58 +0000 |
3342 | +++ tests/test-filter-model.c 2011-12-19 08:59:26 +0000 |
3343 | @@ -120,16 +120,18 @@ |
3344 | } |
3345 | } |
3346 | |
3347 | -static void |
3348 | +static gboolean |
3349 | discard_all_model_notify (DeeModel *orig_model, |
3350 | DeeModelIter *orig_iter, |
3351 | DeeFilterModel *mapped_model, |
3352 | gpointer user_data) |
3353 | { |
3354 | /* Do nothing */ |
3355 | + |
3356 | + return FALSE; |
3357 | } |
3358 | |
3359 | -static void |
3360 | +static gboolean |
3361 | append_all_model_notify (DeeModel *orig_model, |
3362 | DeeModelIter *orig_iter, |
3363 | DeeFilterModel *mapped_model, |
3364 | @@ -138,6 +140,8 @@ |
3365 | /* Always say "Thank you, I am delighted", |
3366 | * and append the new row to @mapped_model */ |
3367 | dee_filter_model_append_iter (mapped_model, orig_iter); |
3368 | + |
3369 | + return TRUE; |
3370 | } |
3371 | |
3372 | static void |
3373 | |
3374 | === modified file 'tools/dee-tool.c' |
3375 | --- tools/dee-tool.c 2011-11-24 21:56:23 +0000 |
3376 | +++ tools/dee-tool.c 2011-12-19 08:59:26 +0000 |
3377 | @@ -22,9 +22,12 @@ |
3378 | #include <glib/gprintf.h> |
3379 | #include <dee.h> |
3380 | |
3381 | -static gchar *resource_name = NULL; |
3382 | -static gchar *model_name = NULL; |
3383 | -static gchar *peer_name = NULL; |
3384 | +static gchar *resource_name = NULL; |
3385 | +static gchar *model_name = NULL; |
3386 | +static gchar *peer_name = NULL; |
3387 | +static gboolean linger; |
3388 | +static gboolean private; |
3389 | +static gboolean server; |
3390 | |
3391 | static GOptionEntry option_entries[] = { |
3392 | { "resource", 'r', 0, G_OPTION_ARG_STRING, &resource_name, |
3393 | @@ -33,6 +36,12 @@ |
3394 | "Dump a model given by name" }, |
3395 | { "peer", 'p', 0, G_OPTION_ARG_STRING, &peer_name, |
3396 | "List peers and leader of a swarm" }, |
3397 | + { "linger", '\0', 0, G_OPTION_ARG_NONE, &linger, |
3398 | + "Don't exit, but keep the process running the mainloop" }, |
3399 | + { "private", '\0', 0, G_OPTION_ARG_NONE, &private, |
3400 | + "Use a private (aka peer-2-peer) DBus connection" }, |
3401 | + { "server", '\0', 0, G_OPTION_ARG_NONE, &server, |
3402 | + "Set up a private DBus server. Implies --private and --linger" }, |
3403 | { NULL } |
3404 | }; |
3405 | |
3406 | @@ -81,7 +90,20 @@ |
3407 | GVariant *v; |
3408 | gchar *dump; |
3409 | |
3410 | - m = DEE_SHARED_MODEL (dee_shared_model_new (name)); |
3411 | + if (server) |
3412 | + { |
3413 | + m = DEE_SHARED_MODEL (dee_shared_model_new_for_peer ( |
3414 | + DEE_PEER (dee_server_new (name)))); |
3415 | + dee_model_set_schema (m, "s", "i", NULL); |
3416 | + } |
3417 | + else if (private) |
3418 | + { |
3419 | + m = DEE_SHARED_MODEL (dee_shared_model_new_for_peer ( |
3420 | + DEE_PEER (dee_client_new (name)))); |
3421 | + } |
3422 | + else |
3423 | + m = DEE_SHARED_MODEL (dee_shared_model_new (name)); |
3424 | + |
3425 | ctx = g_main_context_default (); |
3426 | |
3427 | while (!dee_shared_model_is_synchronized (m)) |
3428 | @@ -94,6 +116,14 @@ |
3429 | |
3430 | g_printf ("%s\n", dump); |
3431 | |
3432 | + if (linger) |
3433 | + { |
3434 | + while (TRUE) |
3435 | + { |
3436 | + g_main_context_iteration (ctx, TRUE); |
3437 | + } |
3438 | + } |
3439 | + |
3440 | g_free (dump); |
3441 | g_variant_unref (v); |
3442 | g_object_unref (m); |
3443 | @@ -124,8 +154,14 @@ |
3444 | { |
3445 | DeePeer *p; |
3446 | GMainContext *ctx; |
3447 | - |
3448 | - p = dee_peer_new (name); |
3449 | + |
3450 | + if (server) |
3451 | + p = DEE_PEER (dee_server_new (name)); |
3452 | + else if (private) |
3453 | + p = DEE_PEER (dee_client_new (name)); |
3454 | + else |
3455 | + p = dee_peer_new (name); |
3456 | + |
3457 | ctx = g_main_context_default (); |
3458 | |
3459 | g_signal_connect (p, "peer-found", G_CALLBACK (_peer_found_cb), NULL); |
3460 | @@ -139,7 +175,11 @@ |
3461 | |
3462 | g_printf ("LEADER %s\n", dee_peer_get_swarm_leader (p)); |
3463 | |
3464 | - g_timeout_add_seconds (1, _timeout_cb, NULL); |
3465 | + /* If we're serving a private conn stick around indefinitely, |
3466 | + * otherwise quit after 1s */ |
3467 | + if (!linger) |
3468 | + g_timeout_add_seconds (1, _timeout_cb, NULL); |
3469 | + |
3470 | while (!timed_out) |
3471 | { |
3472 | g_main_context_iteration (ctx, TRUE); |
3473 | @@ -164,6 +204,12 @@ |
3474 | g_error_free (error); |
3475 | return 1; |
3476 | } |
3477 | + |
3478 | + if (server) |
3479 | + { |
3480 | + private = TRUE; |
3481 | + linger = TRUE; |
3482 | + } |
3483 | |
3484 | if (resource_name) |
3485 | { |
3486 | |
3487 | === modified file 'vapi/dee-1.0.vapi' |
3488 | --- vapi/dee-1.0.vapi 2011-12-15 12:28:43 +0000 |
3489 | +++ vapi/dee-1.0.vapi 2011-12-19 08:59:26 +0000 |
3490 | @@ -13,6 +13,15 @@ |
3491 | public virtual string collate_key (string data); |
3492 | public virtual void tokenize (string data, Dee.TermList terms_out); |
3493 | } |
3494 | + [CCode (cheader_filename = "dee.h", type_id = "dee_client_get_type ()")] |
3495 | + public class Client : Dee.Peer { |
3496 | + [CCode (has_construct_function = false)] |
3497 | + public Client (string swarm_name); |
3498 | + [CCode (has_construct_function = false)] |
3499 | + public Client.for_address (string swarm_name, string bus_address); |
3500 | + [NoAccessorMethod] |
3501 | + public string bus_address { owned get; construct; } |
3502 | + } |
3503 | [CCode (cheader_filename = "dee.h", type_id = "dee_file_resource_manager_get_type ()")] |
3504 | public class FileResourceManager : GLib.Object, Dee.ResourceManager { |
3505 | [CCode (has_construct_function = false, type = "DeeResourceManager*")] |
3506 | @@ -69,14 +78,15 @@ |
3507 | public class Peer : GLib.Object { |
3508 | [CCode (has_construct_function = false)] |
3509 | public Peer (string swarm_name); |
3510 | - [NoWrapper] |
3511 | - public virtual void connected (string peer_name); |
3512 | - public unowned string get_swarm_leader (); |
3513 | + public virtual GLib.SList<weak GLib.DBusConnection> get_connections (); |
3514 | + public virtual unowned string get_swarm_leader (); |
3515 | public unowned string get_swarm_name (); |
3516 | - public bool is_swarm_leader (); |
3517 | + public virtual bool is_swarm_leader (); |
3518 | public string swarm_leader { get; } |
3519 | [NoAccessorMethod] |
3520 | public string swarm_name { owned get; set construct; } |
3521 | + public virtual signal void connection_acquired (GLib.DBusConnection connection); |
3522 | + public virtual signal void connection_closed (GLib.DBusConnection connection); |
3523 | public virtual signal void peer_found (string name); |
3524 | public virtual signal void peer_lost (string name); |
3525 | } |
3526 | @@ -102,11 +112,25 @@ |
3527 | public virtual uint64 inc_seqnum (); |
3528 | public virtual void set_seqnum (uint64 seqnum); |
3529 | } |
3530 | + [CCode (cheader_filename = "dee.h", type_id = "dee_server_get_type ()")] |
3531 | + public class Server : Dee.Peer { |
3532 | + [CCode (has_construct_function = false)] |
3533 | + public Server (string swarm_name); |
3534 | + [CCode (has_construct_function = false)] |
3535 | + public Server.for_address (string swarm_name, string bus_address); |
3536 | + public unowned string get_client_address (); |
3537 | + [NoAccessorMethod] |
3538 | + public string bus_address { owned get; construct; } |
3539 | + [NoAccessorMethod] |
3540 | + public bool same_user_only { get; construct; } |
3541 | + } |
3542 | [CCode (cheader_filename = "dee.h", type_id = "dee_shared_model_get_type ()")] |
3543 | public class SharedModel : Dee.ProxyModel, Dee.Model, Dee.Serializable { |
3544 | [CCode (has_construct_function = false, type = "DeeModel*")] |
3545 | public SharedModel (string name); |
3546 | public uint flush_revision_queue (); |
3547 | + [CCode (has_construct_function = false, type = "DeeModel*")] |
3548 | + public SharedModel.for_peer (owned Dee.Peer peer); |
3549 | public unowned Dee.Peer get_peer (); |
3550 | public unowned string get_swarm_name (); |
3551 | public bool is_leader (); |
Can you file a bug on this and target it for the 1.0.0 milestone please