dee

Merge lp:~mhr3/dee/private-connections into lp:dee

Proposed by Michal Hruby on 2011-12-14
Status: Merged
Approved by: Mikkel Kamstrup Erlandsen on 2011-12-19
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
Reviewer Review Type Date Requested Status
Mikkel Kamstrup Erlandsen (community) 2011-12-14 Approve on 2011-12-19
Review via email: mp+85680@code.launchpad.net

Description of the change

This branch implements possibility to create private connections which can be used to synchronize the shared models.

To post a comment you must log in.

Can you file a bug on this and target it for the 1.0.0 milestone please

Sorry - never mind. Why didn't I just do it myself once I had the thought? ;-) Bug #904299 filed.

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.

review: Needs Fixing

(btw ran the extended tests suite and all tests pass - and distcheck still passes as well - so far so good :-))

lp:~mhr3/dee/private-connections updated on 2011-12-15
325. By Michal Hruby on 2011-12-14

Enable also the schemaless test

326. By Michal Hruby on 2011-12-14

Add test for multiple connections

327. By Michal Hruby on 2011-12-15

Merged lp:~kamstrup/dee/private-connections-tool

328. By Michal Hruby on 2011-12-15

Allow only same user to connect to the abstract socket

329. By Michal Hruby on 2011-12-15

Merge trunk

330. By Michal Hruby on 2011-12-15

Set schema on server model

331. By Michal Hruby on 2011-12-15

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.

lp:~mhr3/dee/private-connections updated on 2011-12-16
332. By Michal Hruby on 2011-12-15

Make sure we emit peer-found & peer-lost signals on DeeServer and connection-closed on DeeClient

333. By Michal Hruby on 2011-12-15

Make sure we wait for the server to initialize when testing client connections

334. By Michal Hruby on 2011-12-16

Add dee_{client,server}_new_for_address and documentation

335. By Michal Hruby on 2011-12-16

A couple of docs fixes

336. By Michal Hruby on 2011-12-16

Fix warnings

337. By Michal Hruby on 2011-12-16

Few more docs fixes

Michal Hruby (mhr3) wrote :

Should be mostly ready to go...

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:swarm-leader property will be set

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)

review: Needs Fixing
lp:~mhr3/dee/private-connections updated on 2011-12-19
338. By Michal Hruby on 2011-12-19

Fix issues brought up during review

Michal Hruby (mhr3) wrote :

Done, done, done...

Great work!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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 ();

Subscribers

People subscribed via source and target branches

to all changes: