dee

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

Proposed by Michal Hruby
Status: Merged
Approved by: Mikkel Kamstrup Erlandsen
Approved revision: 338
Merged at revision: 323
Proposed branch: lp:~mhr3/dee/private-connections
Merge into: lp:dee
Diff against target: 3551 lines (+2453/-255)
30 files modified
.bzrignore (+41/-46)
dee/Makefile.am (+5/-0)
dee/dee-client.c (+403/-0)
dee/dee-client.h (+81/-0)
dee/dee-filter-model.c (+11/-11)
dee/dee-peer.c (+107/-15)
dee/dee-peer.h (+12/-5)
dee/dee-sequence-model.c (+1/-1)
dee/dee-server.c (+492/-0)
dee/dee-server.h (+83/-0)
dee/dee-shared-model.c (+307/-145)
dee/dee-shared-model.h (+2/-0)
dee/dee.h (+2/-0)
doc/reference/dee-1.0/dee-1.0-docs.sgml (+2/-0)
doc/reference/dee-1.0/dee-1.0.types (+14/-11)
tests/Makefile.am (+9/-0)
tests/model-helper-add3rows.c (+5/-1)
tests/model-helper-change3rows.c (+5/-1)
tests/model-helper-clear3add5.c (+5/-1)
tests/model-helper-clear3rows.c (+5/-1)
tests/model-helper-clone3rows.c (+6/-1)
tests/model-helper-insert1row.c (+5/-1)
tests/model-helper-remove3rows.c (+5/-1)
tests/model-helper-schemaless.c (+4/-1)
tests/server-helper-client.c (+108/-0)
tests/test-client-server.c (+644/-0)
tests/test-dee.c (+2/-0)
tests/test-filter-model.c (+6/-2)
tools/dee-tool.c (+53/-7)
vapi/dee-1.0.vapi (+28/-4)
To merge this branch: bzr merge lp:~mhr3/dee/private-connections
Reviewer Review Type Date Requested Status
Mikkel Kamstrup Erlandsen (community) Approve
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.
Revision history for this message
Mikkel Kamstrup Erlandsen (kamstrup) wrote :

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

Revision history for this message
Mikkel Kamstrup Erlandsen (kamstrup) wrote :

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

Revision history for this message
Mikkel Kamstrup Erlandsen (kamstrup) wrote :

DeePeer and DeeServer does not seem to be included in the gtk-docs. Can you please add them under the Peer Discovery section?

Wrt to socket addresses.. Are you sure that abstract filenames are per-user? Otherwise we'd end up with collisions. And can you also make triple sure that users can not connect to each other? So on this line:

1115 + // FIXME: check same user_id?

The answer is "yes". This needs to be secure by default. If someone really wants to do some cross user talking, they're gonna have to go through the hazzle themselves I think.

Overall the code looks good, but I want to take it through a more thorough inspection before I approve this.

review: Needs Fixing
Revision history for this message
Mikkel Kamstrup Erlandsen (kamstrup) wrote :

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

lp:~mhr3/dee/private-connections updated
325. By Michal Hruby

Enable also the schemaless test

326. By Michal Hruby

Add test for multiple connections

327. By Michal Hruby

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

328. By Michal Hruby

Allow only same user to connect to the abstract socket

329. By Michal Hruby

Merge trunk

330. By Michal Hruby

Set schema on server model

331. By Michal Hruby

Regenerate doc types, add client/server to Peer discovery section

Revision history for this message
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
332. By Michal Hruby

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

333. By Michal Hruby

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

334. By Michal Hruby

Add dee_{client,server}_new_for_address and documentation

335. By Michal Hruby

A couple of docs fixes

336. By Michal Hruby

Fix warnings

337. By Michal Hruby

Few more docs fixes

Revision history for this message
Michal Hruby (mhr3) wrote :

Should be mostly ready to go...

Revision history for this message
Mikkel Kamstrup Erlandsen (kamstrup) wrote :

Awesome work dude! Code looks good and test tests are passing. Just three nitpicks and we can merge:

410 + * created using dee_server_new. The #DeePeer: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
338. By Michal Hruby

Fix issues brought up during review

Revision history for this message
Michal Hruby (mhr3) wrote :

Done, done, done...

Revision history for this message
Mikkel Kamstrup Erlandsen (kamstrup) wrote :

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: