[telepathy-gabble/master] Generate and implement Connection.FUTURE.EnsureSidecar()

Will Thompson will.thompson at collabora.co.uk
Mon Nov 16 04:47:27 PST 2009


---
 extensions/Connection_Future.xml |  110 +++++++++++++
 extensions/all.xml               |    4 +
 src/Makefile.am                  |    2 +
 src/conn-sidecars.c              |  316 ++++++++++++++++++++++++++++++++++++++
 src/conn-sidecars.h              |   35 ++++
 src/connection.c                 |    6 +
 src/connection.h                 |    7 +
 7 files changed, 480 insertions(+), 0 deletions(-)
 create mode 100644 extensions/Connection_Future.xml
 create mode 100644 src/conn-sidecars.c
 create mode 100644 src/conn-sidecars.h

diff --git a/extensions/Connection_Future.xml b/extensions/Connection_Future.xml
new file mode 100644
index 0000000..1104798
--- /dev/null
+++ b/extensions/Connection_Future.xml
@@ -0,0 +1,110 @@
+<?xml version="1.0" ?>
+<node name="/Connection_FUTURE"
+  xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
+  >
+  <tp:copyright>Copyright © 2009 Collabora Limited</tp:copyright>
+  <tp:copyright>Copyright © 2009 Nokia Corporation</tp:copyright>
+  <tp:license xmlns="http://www.w3.org/1999/xhtml">
+<p>This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.</p>
+
+<p>This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.</p>
+
+<p>You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+USA.</p>
+  </tp:license>
+  <interface name="org.freedesktop.Telepathy.Connection.FUTURE"
+             tp:causes-havoc='experimental'>
+    <tp:requires interface="org.freedesktop.Telepathy.Connection"/>
+
+    <method name="EnsureSidecar" tp:name-for-bindings="Ensure_Sidecar">
+      <tp:added version="0.19.UNRELEASED"/>
+
+      <arg direction="in" name="Main_Interface" type="s"
+           tp:type="DBus_Interface">
+        <tp:docstring>
+          The "primary" interface implemented by an object attached
+          to a connection. For example, a Gabble plugin implementing
+          fine-grained control of XEP-0016 privacy lists might expose an object
+          implementing <tt>com.example.PrivacyLists</tt>.
+        </tp:docstring>
+      </arg>
+
+      <arg direction="out" name="Path" type="o">
+        <tp:docstring>The object path of the sidecar, exported by the same bus
+          name as the Connection to which it is attached.</tp:docstring>
+      </arg>
+      <arg direction="out" name="Properties" type="a{sv}"
+           tp:type="Qualified_Property_Value_Map">
+        <tp:docstring>Immutable properties of the sidecar.</tp:docstring>
+      </arg>
+
+      <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+        <p>Request an object with a particular interface providing additional
+          connection-specific functionality, together with its immutable
+          properties. These will often be implemented by plug-ins to the
+          connection managers; for example, support for an XMPP XEP for which
+          no generic Telepathy interface exists might be implemented by a
+          Gabble plugin exposing a sidecar with a particular interface.</p>
+
+        <p>This method may be called at any point during the lifetime of a
+          connection, even before its <tp:type>Connection_Status</tp:type>
+          changes to Connected. It MAY take a long time to
+          return—perhaps it needs to wait for a connection to be established
+          and for all the services supported by the server to be discovered
+          before determining whether necessary server-side support is
+          available—so callers SHOULD override the default method timeout (25
+          seconds) with a much higher value (perhaps even MAX_INT32, meaning
+          “no timeout” in recent versions of libdbus).</p>
+
+        <tp:rationale>
+          <p>There is an implicit assumption that any connection
+            manager plugin will only want to export one “primary” object per
+            feature it implements, since there is a one-to-one mapping between
+            interface and object. This is reasonable since Sidecars are
+            (intended to be) analogous to extra interfaces on the connection,
+            providing once-per-connection shared functionality; it also makes
+            client code straightforward (look up the interface you care about
+            in a dictionary, build a proxy object from the value). More
+            “plural” plugins are likely to want to implement new types of
+            <tp:dbus-ref
+              namespace="org.freedesktop.Telepathy">Channel</tp:dbus-ref>
+            instead.</p>
+        </tp:rationale>
+      </tp:docstring>
+
+      <tp:error name="org.freedesktop.Telepathy.Error.NotImplemented">
+        <tp:docstring>
+          The requested sidecar is not implemented by this connection manager,
+          or a necessary server-side component does not exist. (FIXME: split
+          these two errors out? Then again, once we list the guaranteed and
+          possible sidecars on a Protocol object, clients can tell the
+          difference themselves, because they shouldn't be calling this in the
+          first case.)
+        </tp:docstring>
+      </tp:error>
+
+      <tp:error name="org.freedesktop.Telepathy.Error.ServiceBusy">
+        <tp:docstring>
+          A server-side component needed by the requested sidecar reported it
+          is currently too busy, or did not respond for some
+          implementation-defined time. The caller may wish to try again later.
+        </tp:docstring>
+      </tp:error>
+
+      <tp:error name="org.freedesktop.Telepathy.Error.Cancelled">
+        <tp:docstring>
+          The connection was disconnected while the sidecar was being set up.
+        </tp:docstring>
+      </tp:error>
+    </method>
+
+  </interface>
+</node>
diff --git a/extensions/all.xml b/extensions/all.xml
index a2055df..9f6c76b 100644
--- a/extensions/all.xml
+++ b/extensions/all.xml
@@ -44,6 +44,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA</p>
 
 <xi:include href="Channel_Type_Contact_Search.xml"/>
 <xi:include href="Connection_Interface_Contact_Info.xml"/>
+<xi:include href="Connection_Future.xml"/>
 
 <tp:generic-types>
   <tp:external-type name="Contact_Handle" type="u"
@@ -94,6 +95,9 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA</p>
   <tp:external-type name="Requestable_Channel_Class" type="(a{sv}as)"
     from="Telepathy specification"/>
 
+  <tp:external-type name="Connection_Status" type="u"
+    from="Telepathy specification"/>
+
 </tp:generic-types>
 
 </tp:spec>
diff --git a/src/Makefile.am b/src/Makefile.am
index 4178c72..9034676 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -41,6 +41,8 @@ libgabble_convenience_la_SOURCES = \
     conn-olpc.c \
     conn-presence.h \
     conn-presence.c \
+    conn-sidecars.h \
+    conn-sidecars.c \
     connection.h \
     connection.c \
     connection-manager.h \
diff --git a/src/conn-sidecars.c b/src/conn-sidecars.c
new file mode 100644
index 0000000..e21766e
--- /dev/null
+++ b/src/conn-sidecars.c
@@ -0,0 +1,316 @@
+/*
+ * conn-sidecars.h - Gabble connection implementation of sidecars
+ * Copyright © 2009 Collabora Ltd.
+ * Copyright © 2009 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "conn-sidecars.h"
+
+#include <telepathy-glib/dbus.h>
+
+#include "extensions/extensions.h"
+
+#define DEBUG_FLAG GABBLE_DEBUG_CONNECTION
+#include "debug.h"
+#include "plugin-loader.h"
+#include "sidecar.h"
+
+static void
+sidecars_conn_status_changed_cb (
+    GabbleConnection *conn,
+    guint status,
+    guint reason,
+    gpointer unused);
+
+void
+conn_sidecars_init (GabbleConnection *conn)
+{
+  conn->sidecars = g_hash_table_new_full (g_str_hash, g_str_equal,
+      g_free, g_object_unref);
+  conn->pending_sidecars = g_hash_table_new_full (g_str_hash, g_str_equal,
+      g_free, (GDestroyNotify) g_list_free);
+
+  g_signal_connect (conn, "status-changed",
+      (GCallback) sidecars_conn_status_changed_cb, NULL);
+}
+
+void
+conn_sidecars_dispose (GabbleConnection *conn)
+{
+  g_warn_if_fail (g_hash_table_size (conn->sidecars) == 0);
+  g_hash_table_unref (conn->sidecars);
+  conn->sidecars = NULL;
+
+  g_warn_if_fail (g_hash_table_size (conn->pending_sidecars) == 0);
+  g_hash_table_unref (conn->pending_sidecars);
+  conn->pending_sidecars = NULL;
+}
+
+static gchar *
+make_sidecar_path (
+    GabbleConnection *conn,
+    const gchar *sidecar_iface)
+{
+  TpBaseConnection *base_conn = TP_BASE_CONNECTION (conn);
+
+  return g_strdelimit (
+      g_strdup_printf ("%s/Sidecar/%s", base_conn->object_path, sidecar_iface),
+      ".", '/');
+}
+
+/**
+ * connection_install_sidecar:
+ *
+ * Registers @sidecar on the bus, and returns its object path.
+ */
+static gchar *
+connection_install_sidecar (
+    GabbleConnection *conn,
+    GabbleSidecar *sidecar,
+    const gchar *sidecar_iface)
+{
+  gchar *path = make_sidecar_path (conn, sidecar_iface);
+
+  dbus_g_connection_register_g_object (tp_get_bus (), path, G_OBJECT (sidecar));
+  g_hash_table_insert (conn->sidecars, g_strdup (sidecar_iface),
+      g_object_ref (sidecar));
+
+  return path;
+}
+
+typedef struct {
+    GabbleConnection *conn;
+    gchar *sidecar_iface;
+} Grr;
+
+static Grr *
+grr_new (
+    GabbleConnection *conn,
+    const gchar *sidecar_iface)
+{
+  Grr *grr = g_slice_new (Grr);
+
+  grr->conn = g_object_ref (conn);
+  grr->sidecar_iface = g_strdup (sidecar_iface);
+
+  return grr;
+}
+
+static void
+grr_free (Grr *grr)
+{
+  g_object_unref (grr->conn);
+  g_free (grr->sidecar_iface);
+
+  g_slice_free (Grr, grr);
+}
+
+static void
+create_sidecar_cb (
+    GObject *loader_obj,
+    GAsyncResult *result,
+    gpointer user_data)
+{
+  GabblePluginLoader *loader = GABBLE_PLUGIN_LOADER (loader_obj);
+  Grr *ctx = user_data;
+  GabbleConnection *conn = ctx->conn;
+  const gchar *sidecar_iface = ctx->sidecar_iface;
+  GabbleSidecar *sidecar;
+  GList *contexts;
+  GError *error = NULL;
+
+  sidecar = gabble_plugin_loader_create_sidecar_finish (loader, result, &error);
+  contexts = g_hash_table_lookup (conn->pending_sidecars, sidecar_iface);
+
+  if (contexts == NULL)
+    {
+      /* We never use the empty list as a value in pending_sidecars, so this
+       * must mean we've disconnected and already returned. Jettison the
+       * sidecar!
+       */
+      DEBUG ("creating sidecar %s %s after connection closed; jettisoning!",
+          sidecar_iface, (sidecar != NULL ? "succeeded" : "failed"));
+      goto out;
+    }
+
+  if (sidecar != NULL)
+    {
+      const gchar *actual_iface = gabble_sidecar_get_interface (sidecar);
+
+      if (tp_strdiff (ctx->sidecar_iface, actual_iface))
+        {
+          /* TODO: maybe this lives in the loader? It knows what the plugin is
+           * called. */
+          g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED,
+              "A buggy plugin created a %s sidecar when asked to create %s",
+              actual_iface, ctx->sidecar_iface);
+        }
+    }
+
+  if (error == NULL)
+    {
+      gchar *path = connection_install_sidecar (ctx->conn, sidecar,
+          ctx->sidecar_iface);
+      GHashTable *props = gabble_sidecar_get_immutable_properties (sidecar);
+      GList *l;
+
+      for (l = contexts; l != NULL; l = l->next)
+        gabble_svc_connection_future_return_from_ensure_sidecar (l->data,
+            path, props);
+
+      g_hash_table_unref (props);
+    }
+  else
+    {
+      g_list_foreach (contexts, (GFunc) dbus_g_method_return_error, error);
+    }
+
+  g_hash_table_remove (ctx->conn->pending_sidecars, ctx->sidecar_iface);
+
+out:
+  if (sidecar != NULL)
+    g_object_unref (sidecar);
+
+  g_clear_error (&error);
+  grr_free (ctx);
+}
+
+static void
+gabble_connection_ensure_sidecar (
+    GabbleSvcConnectionFUTURE *iface,
+    const gchar *sidecar_iface,
+    DBusGMethodInvocation *context)
+{
+  GabbleConnection *conn = GABBLE_CONNECTION (iface);
+  TpBaseConnection *base_conn = TP_BASE_CONNECTION (conn);
+  GabbleSidecar *sidecar;
+  gpointer key, value;
+  GabblePluginLoader *loader;
+  GError *error = NULL;
+
+  if (base_conn->status == TP_CONNECTION_STATUS_DISCONNECTED)
+    {
+      GError e = { TP_ERRORS, TP_ERROR_DISCONNECTED,
+          "This connection has already disconnected" };
+
+      DEBUG ("already disconnected, declining request for %s", sidecar_iface);
+      dbus_g_method_return_error (context, &e);
+      return;
+    }
+
+  if (!tp_dbus_check_valid_interface_name (sidecar_iface, &error))
+    {
+      error->domain = TP_ERRORS;
+      error->code = TP_ERROR_INVALID_ARGUMENT;
+      DEBUG ("%s is malformed: %s", sidecar_iface, error->message);
+      dbus_g_method_return_error (context, error);
+      g_clear_error (&error);
+      return;
+    }
+
+  sidecar = g_hash_table_lookup (conn->sidecars, sidecar_iface);
+
+  if (sidecar != NULL)
+    {
+      gchar *path = make_sidecar_path (conn, sidecar_iface);
+      GHashTable *props = gabble_sidecar_get_immutable_properties (sidecar);
+
+      DEBUG ("sidecar %s already exists at %s", sidecar_iface, path);
+      gabble_svc_connection_future_return_from_ensure_sidecar (context, path,
+          props);
+
+      g_free (path);
+      g_hash_table_unref (props);
+      return;
+    }
+
+  if (g_hash_table_lookup_extended (conn->pending_sidecars, sidecar_iface,
+          &key, &value))
+    {
+      GList *contexts = value;
+
+      DEBUG ("already awaiting %s, joining a queue of %u", sidecar_iface,
+          g_list_length (contexts));
+
+      contexts = g_list_prepend (contexts, context);
+      g_hash_table_steal (conn->pending_sidecars, key);
+      g_hash_table_insert (conn->pending_sidecars, key, contexts);
+      return;
+    }
+
+  DEBUG ("requesting %s from the plugin loader", sidecar_iface);
+  loader = gabble_plugin_loader_dup ();
+  g_hash_table_insert (conn->pending_sidecars, g_strdup (sidecar_iface),
+      g_list_prepend (NULL, context));
+  gabble_plugin_loader_create_sidecar (loader, sidecar_iface,
+      create_sidecar_cb, grr_new (conn, sidecar_iface));
+  g_object_unref (loader);
+}
+
+static void
+sidecars_conn_status_changed_cb (
+    GabbleConnection *conn,
+    guint status,
+    guint reason,
+    gpointer unused)
+{
+  DBusGConnection *bus = tp_get_bus ();
+  GHashTableIter iter;
+  gpointer key, value;
+
+  if (status != TP_CONNECTION_STATUS_DISCONNECTED)
+    return;
+
+  g_hash_table_iter_init (&iter, conn->sidecars);
+
+  while (g_hash_table_iter_next (&iter, NULL, &value))
+    {
+      DEBUG ("removing %s from the bus", gabble_sidecar_get_interface (value));
+      dbus_g_connection_unregister_g_object (bus, G_OBJECT (value));
+    }
+
+  g_hash_table_iter_init (&iter, conn->pending_sidecars);
+
+  while (g_hash_table_iter_next (&iter, &key, &value))
+    {
+      const gchar *sidecar_iface = key;
+      GList *contexts = value;
+
+      GError *error = g_error_new (TP_ERRORS, TP_ERROR_CANCELLED,
+          "Disconnected before %s could be created", sidecar_iface);
+
+      g_list_foreach (contexts, (GFunc) dbus_g_method_return_error, error);
+      g_error_free (error);
+    }
+
+  g_hash_table_remove_all (conn->sidecars);
+  g_hash_table_remove_all (conn->pending_sidecars);
+}
+
+void
+conn_future_iface_init (
+    gpointer g_iface,
+    gpointer iface_data)
+{
+  GabbleSvcConnectionFUTUREClass *klass = g_iface;
+
+#define IMPLEMENT(x) \
+    gabble_svc_connection_future_implement_##x (\
+    klass, gabble_connection_##x)
+  IMPLEMENT (ensure_sidecar);
+#undef IMPLEMENT
+}
diff --git a/src/conn-sidecars.h b/src/conn-sidecars.h
new file mode 100644
index 0000000..46e076c
--- /dev/null
+++ b/src/conn-sidecars.h
@@ -0,0 +1,35 @@
+/*
+ * conn-sidecars.h - Header for Gabble connection implementation of sidecars
+ * Copyright © 2009 Collabora Ltd.
+ * Copyright © 2009 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __CONN_SIDECARS_H__
+#define __CONN_SIDECARS_H__
+
+#include "connection.h"
+
+G_BEGIN_DECLS
+
+void conn_sidecars_init (GabbleConnection *conn);
+void conn_sidecars_dispose (GabbleConnection *conn);
+void conn_future_iface_init (gpointer g_iface, gpointer iface_data);
+
+G_END_DECLS
+
+#endif /* __CONN_SIDECARS_H__ */
+
diff --git a/src/connection.c b/src/connection.c
index 9dba372..d301d3d 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -55,6 +55,7 @@
 #include "conn-avatars.h"
 #include "conn-location.h"
 #include "conn-presence.h"
+#include "conn-sidecars.h"
 #include "conn-olpc.h"
 #include "debug.h"
 #include "disco.h"
@@ -118,6 +119,8 @@ G_DEFINE_TYPE_WITH_CODE(GabbleConnection,
       gabble_conn_contact_caps_iface_init);
     G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SVC_OLPC_GADGET,
       olpc_gadget_iface_init);
+    G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SVC_CONNECTION_FUTURE,
+      conn_future_iface_init);
     )
 
 /* properties */
@@ -329,6 +332,7 @@ gabble_connection_constructor (GType type,
   conn_presence_init (self);
   conn_olpc_activity_properties_init (self);
   conn_location_init (self);
+  conn_sidecars_init (self);
 
   tp_contacts_mixin_add_contact_attributes_iface (G_OBJECT (self),
       TP_IFACE_CONNECTION_INTERFACE_CAPABILITIES,
@@ -1043,6 +1047,8 @@ gabble_connection_dispose (GObject *object)
       self->pep_olpc_act_props = NULL;
     }
 
+  conn_sidecars_dispose (self);
+
   if (G_OBJECT_CLASS (gabble_connection_parent_class)->dispose)
     G_OBJECT_CLASS (gabble_connection_parent_class)->dispose (object);
 }
diff --git a/src/connection.h b/src/connection.h
index 73006d4..e15c25d 100644
--- a/src/connection.h
+++ b/src/connection.h
@@ -200,6 +200,13 @@ struct _GabbleConnection {
     WockyPepService *pep_olpc_current_act;
     WockyPepService *pep_olpc_act_props;
 
+    /* Sidecars */
+    /* gchar *interface → GabbleSidecar */
+    GHashTable *sidecars;
+
+    /* gchar *interface → GList<DBusGMethodInvocation> */
+    GHashTable *pending_sidecars;
+
     GabbleConnectionPrivate *priv;
 };
 
-- 
1.5.6.5




More information about the telepathy-commits mailing list