[evolution-data-server] Bug #727431 - Merge common factories' code into a superclass



commit d915f5c6fb19b0c6c388a4c0c85199bd6b5818c8
Author: Fabiano Fidêncio <fidencio redhat com>
Date:   Mon Mar 17 02:40:13 2014 +0100

    Bug #727431 - Merge common factories' code into a superclass

 addressbook/libedata-book/e-book-backend-factory.c |   19 +-
 addressbook/libedata-book/e-data-book-factory.c    |  532 ++---------------
 addressbook/libedata-book/e-data-book-factory.h    |    3 -
 addressbook/libedata-book/e-data-book.c            |    8 +
 calendar/libedata-cal/e-cal-backend-factory.c      |    8 +-
 calendar/libedata-cal/e-data-cal-factory.c         |  647 +++-----------------
 calendar/libedata-cal/e-data-cal-factory.h         |    2 -
 calendar/libedata-cal/e-data-cal.c                 |    8 +
 configure.ac                                       |    6 +-
 docs/reference/eds/eds-sections.txt                |    6 +-
 libebackend/e-data-factory.c                       |  628 +++++++++++++++++++-
 libebackend/e-data-factory.h                       |   26 +
 libebackend/e-source-registry-server.c             |   38 +-
 13 files changed, 822 insertions(+), 1109 deletions(-)
---
diff --git a/addressbook/libedata-book/e-book-backend-factory.c 
b/addressbook/libedata-book/e-book-backend-factory.c
index 296c13c..702dfbe 100644
--- a/addressbook/libedata-book/e-book-backend-factory.c
+++ b/addressbook/libedata-book/e-book-backend-factory.c
@@ -17,6 +17,7 @@
  **/
 
 #include <config.h>
+#include <string.h>
 
 #include "e-book-backend.h"
 #include "e-book-backend-factory.h"
@@ -41,15 +42,23 @@ static const gchar *
 book_backend_factory_get_hash_key (EBackendFactory *factory)
 {
        EBookBackendFactoryClass *class;
+       const gchar *component_name;
+       gchar *hash_key;
+       gsize length;
 
        class = E_BOOK_BACKEND_FACTORY_GET_CLASS (factory);
        g_return_val_if_fail (class->factory_name != NULL, NULL);
 
-       /* For address book backends the factory hash key is simply
-        * the factory name.  See ECalBackendFactory for a slightly
-        * more complex scheme. */
+       component_name = E_SOURCE_EXTENSION_ADDRESS_BOOK;
 
-       return class->factory_name;
+       /* Hash Key: FACTORY_NAME ':' COMPONENT_NAME */
+       length = strlen (class->factory_name) + strlen (component_name) + 2;
+       hash_key = g_alloca (length);
+       g_snprintf (
+               hash_key, length, "%s:%s",
+               class->factory_name, component_name);
+
+       return g_intern_string (hash_key);
 }
 
 static EBackend *
@@ -65,7 +74,7 @@ book_backend_factory_new_backend (EBackendFactory *factory,
                class->backend_type, E_TYPE_BOOK_BACKEND), NULL);
 
        data_factory = book_backend_factory_get_data_factory (factory);
-       registry = e_data_book_factory_get_registry (data_factory);
+       registry = e_data_factory_get_registry (E_DATA_FACTORY (data_factory));
 
        return g_object_new (
                class->backend_type,
diff --git a/addressbook/libedata-book/e-data-book-factory.c b/addressbook/libedata-book/e-data-book-factory.c
index 96fb1d4..0d6bb95 100644
--- a/addressbook/libedata-book/e-data-book-factory.c
+++ b/addressbook/libedata-book/e-data-book-factory.c
@@ -51,19 +51,8 @@
        ((obj), E_TYPE_DATA_BOOK_FACTORY, EDataBookFactoryPrivate))
 
 struct _EDataBookFactoryPrivate {
-       ESourceRegistry *registry;
        EDBusAddressBookFactory *dbus_factory;
 
-       /* This is a hash table of client bus names to an array of
-        * EBookBackend references; one for every connection opened. */
-       GHashTable *connections;
-       GMutex connections_lock;
-
-       /* This is a hash table of client bus names being watched.
-        * The value is the watcher ID for g_bus_unwatch_name(). */
-       GHashTable *watched_names;
-       GMutex watched_names_lock;
-
        /* Watching "org.freedesktop.locale1" for locale changes */
        guint localed_watch_id;
        EDBusLocale1 *localed_proxy;
@@ -71,10 +60,7 @@ struct _EDataBookFactoryPrivate {
        gchar *locale;
 };
 
-enum {
-       PROP_0,
-       PROP_REGISTRY
-};
+static GInitableIface *initable_parent_interface;
 
 /* Forward Declarations */
 static void    e_data_book_factory_initable_init
@@ -88,236 +74,14 @@ G_DEFINE_TYPE_WITH_CODE (
                G_TYPE_INITABLE,
                e_data_book_factory_initable_init))
 
-static void
-watched_names_value_free (gpointer value)
+static GDBusInterfaceSkeleton *
+data_book_factory_get_dbus_interface_skeleton (EDBusServer *server)
 {
-       g_bus_unwatch_name (GPOINTER_TO_UINT (value));
-}
-
-static void
-data_book_factory_toggle_notify_cb (gpointer data,
-                                    GObject *backend,
-                                    gboolean is_last_ref)
-{
-       if (is_last_ref) {
-               /* Take a strong reference before removing the
-                * toggle reference, to keep the backend alive. */
-               g_object_ref (backend);
-
-               g_object_remove_toggle_ref (
-                       backend, data_book_factory_toggle_notify_cb, data);
-
-               g_signal_emit_by_name (backend, "shutdown");
-
-               g_object_unref (backend);
-       }
-}
-
-static void
-data_book_factory_connections_add (EDataBookFactory *factory,
-                                   const gchar *name,
-                                   EBookBackend *backend)
-{
-       GHashTable *connections;
-       GPtrArray *array;
-
-       g_return_if_fail (name != NULL);
-       g_return_if_fail (backend != NULL);
-
-       g_mutex_lock (&factory->priv->connections_lock);
-
-       connections = factory->priv->connections;
-
-       if (g_hash_table_size (connections) == 0)
-               e_dbus_server_hold (E_DBUS_SERVER (factory));
-
-       array = g_hash_table_lookup (connections, name);
-
-       if (array == NULL) {
-               array = g_ptr_array_new_with_free_func (
-                       (GDestroyNotify) g_object_unref);
-               g_hash_table_insert (
-                       connections, g_strdup (name), array);
-       }
-
-       g_ptr_array_add (array, g_object_ref (backend));
-
-       g_mutex_unlock (&factory->priv->connections_lock);
-}
-
-static gboolean
-data_book_factory_connections_remove (EDataBookFactory *factory,
-                                      const gchar *name,
-                                      EBookBackend *backend)
-{
-       GHashTable *connections;
-       GPtrArray *array;
-       gboolean removed = FALSE;
-
-       /* If backend is NULL, we remove all backends for name. */
-       g_return_val_if_fail (name != NULL, FALSE);
-
-       g_mutex_lock (&factory->priv->connections_lock);
-
-       connections = factory->priv->connections;
-       array = g_hash_table_lookup (connections, name);
-
-       if (array != NULL) {
-               if (backend != NULL) {
-                       removed = g_ptr_array_remove_fast (array, backend);
-               } else if (array->len > 0) {
-                       g_ptr_array_set_size (array, 0);
-                       removed = TRUE;
-               }
-
-               if (array->len == 0)
-                       g_hash_table_remove (connections, name);
-
-               if (g_hash_table_size (connections) == 0)
-                       e_dbus_server_release (E_DBUS_SERVER (factory));
-       }
-
-       g_mutex_unlock (&factory->priv->connections_lock);
-
-       return removed;
-}
-
-static void
-data_book_factory_connections_remove_all (EDataBookFactory *factory)
-{
-       GHashTable *connections;
-
-       g_mutex_lock (&factory->priv->connections_lock);
-
-       connections = factory->priv->connections;
-
-       if (g_hash_table_size (connections) > 0) {
-               g_hash_table_remove_all (connections);
-               e_dbus_server_release (E_DBUS_SERVER (factory));
-       }
-
-       g_mutex_unlock (&factory->priv->connections_lock);
-}
-
-static void
-data_book_factory_name_vanished_cb (GDBusConnection *connection,
-                                    const gchar *name,
-                                    gpointer user_data)
-{
-       GWeakRef *weak_ref = user_data;
        EDataBookFactory *factory;
 
-       factory = g_weak_ref_get (weak_ref);
-
-       if (factory != NULL) {
-               data_book_factory_connections_remove (factory, name, NULL);
-
-               /* Unwatching the bus name from here will corrupt the
-                * 'name' argument, and possibly also the 'user_data'.
-                *
-                * This is a GDBus bug.  Work around it by unwatching
-                * the bus name last.
-                *
-                * See: https://bugzilla.gnome.org/706088
-                */
-               g_mutex_lock (&factory->priv->watched_names_lock);
-               g_hash_table_remove (factory->priv->watched_names, name);
-               g_mutex_unlock (&factory->priv->watched_names_lock);
-
-               g_object_unref (factory);
-       }
-}
-
-static void
-data_book_factory_watched_names_add (EDataBookFactory *factory,
-                                     GDBusConnection *connection,
-                                     const gchar *name)
-{
-       GHashTable *watched_names;
-
-       g_return_if_fail (name != NULL);
-
-       g_mutex_lock (&factory->priv->watched_names_lock);
-
-       watched_names = factory->priv->watched_names;
-
-       if (!g_hash_table_contains (watched_names, name)) {
-               guint watcher_id;
-
-               /* The g_bus_watch_name() documentation says one of the two
-                * callbacks are guaranteed to be invoked after calling the
-                * function.  But which one is determined asynchronously so
-                * there should be no chance of the name vanished callback
-                * deadlocking with us when it tries to acquire the lock. */
-               watcher_id = g_bus_watch_name_on_connection (
-                       connection, name,
-                       G_BUS_NAME_WATCHER_FLAGS_NONE,
-                       (GBusNameAppearedCallback) NULL,
-                       data_book_factory_name_vanished_cb,
-                       e_weak_ref_new (factory),
-                       (GDestroyNotify) e_weak_ref_free);
-
-               g_hash_table_insert (
-                       watched_names, g_strdup (name),
-                       GUINT_TO_POINTER (watcher_id));
-       }
-
-       g_mutex_unlock (&factory->priv->watched_names_lock);
-}
-
-static EBackend *
-data_book_factory_ref_backend (EDataFactory *factory,
-                               ESource *source,
-                               GError **error)
-{
-       EBackend *backend;
-       ESourceBackend *extension;
-       const gchar *extension_name;
-       gchar *backend_name;
-
-       /* For address books the hash key is simply the backend name.
-        * (cf. calendar hash keys, which are slightly more complex) */
-
-       extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK;
-       extension = e_source_get_extension (source, extension_name);
-       backend_name = e_source_backend_dup_backend_name (extension);
-
-       if (backend_name == NULL || *backend_name == '\0') {
-               g_set_error (
-                       error, E_DATA_BOOK_ERROR,
-                       E_DATA_BOOK_STATUS_NO_SUCH_BOOK,
-                       _("No backend name in source '%s'"),
-                       e_source_get_display_name (source));
-               g_free (backend_name);
-               return NULL;
-       }
-
-       backend = e_data_factory_ref_initable_backend (
-               factory, backend_name, source, NULL, error);
-
-       g_free (backend_name);
-
-       return backend;
-}
-
-static gchar *
-construct_book_factory_path (void)
-{
-       static volatile gint counter = 1;
-
-       g_atomic_int_inc (&counter);
-
-       return g_strdup_printf (
-               "/org/gnome/evolution/dataserver/AddressBook/%d/%u",
-               getpid (), counter);
-}
+       factory = E_DATA_BOOK_FACTORY (server);
 
-static void
-data_book_factory_closed_cb (EBookBackend *backend,
-                             const gchar *sender,
-                             EDataBookFactory *factory)
-{
-       data_book_factory_connections_remove (factory, sender, backend);
+       return G_DBUS_INTERFACE_SKELETON (factory->priv->dbus_factory);
 }
 
 /* This is totally backwards, for some insane reason we holding
@@ -329,74 +93,33 @@ static GList *
 data_book_factory_list_books (EDataBookFactory *factory)
 {
        GList *books = NULL;
-       GHashTable *connections;
-       GHashTableIter iter;
-       gpointer key, value;
+       GSList *backends, *l;
 
-       g_mutex_lock (&factory->priv->connections_lock);
+       backends = e_data_factory_list_backends (E_DATA_FACTORY (factory));
 
-       connections = factory->priv->connections;
+       for (l = backends; l != NULL; l = g_slist_next (l)); {
+               EBookBackend *backend = l->data;
+               EDataBook *book = e_book_backend_ref_data_book (backend);
 
-       g_hash_table_iter_init (&iter, connections);
-       while (g_hash_table_iter_next (&iter, &key, &value)) {
-               GPtrArray *array = (GPtrArray *) value;
-               gint i;
-
-               for (i = 0; i < array->len; i++) {
-                       EBookBackend *backend = g_ptr_array_index (array, i);
-                       EDataBook *book = e_book_backend_ref_data_book (backend);
-
-                       if (g_list_find (books, book) == NULL)
-                               books = g_list_prepend (books, book);
-                       else
-                               g_object_unref (book);
-               }
+               if (g_list_find (books, book) == NULL)
+                       books = g_list_prepend (books, book);
+               else
+                       g_object_unref (book);
        }
-       g_mutex_unlock (&factory->priv->connections_lock);
 
        return books;
 }
 
 static gchar *
-data_book_factory_open (EDataBookFactory *factory,
-                        GDBusConnection *connection,
-                        const gchar *sender,
-                        const gchar *uid,
-                        GError **error)
+data_book_factory_open (EDataFactory *data_factory,
+                       EBackend *backend,
+                       GDBusConnection *connection,
+                       GError **error)
 {
+       EDataBookFactory *factory = E_DATA_BOOK_FACTORY (data_factory);
        EDataBook *data_book;
-       EBackend *backend;
-       ESourceRegistry *registry;
-       ESource *source;
        gchar *object_path;
 
-       if (uid == NULL || *uid == '\0') {
-               g_set_error (
-                       error, E_DATA_BOOK_ERROR,
-                       E_DATA_BOOK_STATUS_NO_SUCH_BOOK,
-                       _("Missing source UID"));
-               return NULL;
-       }
-
-       registry = e_data_book_factory_get_registry (factory);
-       source = e_source_registry_ref_source (registry, uid);
-
-       if (source == NULL) {
-               g_set_error (
-                       error, E_DATA_BOOK_ERROR,
-                       E_DATA_BOOK_STATUS_NO_SUCH_BOOK,
-                       _("No such source for UID '%s'"), uid);
-               return NULL;
-       }
-
-       backend = data_book_factory_ref_backend (
-               E_DATA_FACTORY (factory), source, error);
-
-       g_object_unref (source);
-
-       if (backend == NULL)
-               return NULL;
-
        /* If the backend already has an EDataBook installed, return its
         * object path.  Otherwise we need to install a new EDataBook. */
 
@@ -406,7 +129,7 @@ data_book_factory_open (EDataBookFactory *factory,
                object_path = g_strdup (
                        e_data_book_get_object_path (data_book));
        } else {
-               object_path = construct_book_factory_path ();
+               object_path = e_data_factory_construct_path (data_factory);
 
                /* The EDataBook will attach itself to EBookBackend,
                 * so no need to call e_book_backend_set_data_book(). */
@@ -415,18 +138,8 @@ data_book_factory_open (EDataBookFactory *factory,
                        connection, object_path, error);
 
                if (data_book != NULL) {
-                       /* Install a toggle reference on the backend
-                        * so we can signal it to shut down once all
-                        * client connections are closed. */
-                       g_object_add_toggle_ref (
-                               G_OBJECT (backend),
-                               data_book_factory_toggle_notify_cb,
-                               NULL);
-
-                       g_signal_connect_object (
-                               backend, "closed",
-                               G_CALLBACK (data_book_factory_closed_cb),
-                               factory, 0);
+                       e_data_factory_set_backend_callbacks (
+                               data_factory, backend);
 
                        /* Don't set the locale on a new book if we have not
                         * yet received a notification of a locale change
@@ -442,21 +155,7 @@ data_book_factory_open (EDataBookFactory *factory,
                }
        }
 
-       if (data_book != NULL) {
-               /* Watch the sender's bus name so we can clean
-                * up its connections if the bus name vanishes. */
-               data_book_factory_watched_names_add (
-                       factory, connection, sender);
-
-               /* A client may create multiple EClient instances for the
-                * same ESource, each of which calls close() individually.
-                * So we must track each and every connection made. */
-               data_book_factory_connections_add (
-                       factory, sender, E_BOOK_BACKEND (backend));
-       }
-
        g_clear_object (&data_book);
-       g_clear_object (&backend);
 
        return object_path;
 }
@@ -475,8 +174,9 @@ data_book_factory_handle_open_address_book_cb (EDBusAddressBookFactory *iface,
        connection = g_dbus_method_invocation_get_connection (invocation);
        sender = g_dbus_method_invocation_get_sender (invocation);
 
-       object_path = data_book_factory_open (
-               factory, connection, sender, uid, &error);
+       object_path = e_data_factory_open_backend (
+               E_DATA_FACTORY (factory), connection, sender,
+               uid, E_SOURCE_EXTENSION_ADDRESS_BOOK, &error);
 
        if (object_path != NULL) {
                e_dbus_address_book_factory_complete_open_address_book (
@@ -491,57 +191,25 @@ data_book_factory_handle_open_address_book_cb (EDBusAddressBookFactory *iface,
 }
 
 static void
-data_book_factory_get_property (GObject *object,
-                                guint property_id,
-                                GValue *value,
-                                GParamSpec *pspec)
-{
-       switch (property_id) {
-               case PROP_REGISTRY:
-                       g_value_set_object (
-                               value,
-                               e_data_book_factory_get_registry (
-                               E_DATA_BOOK_FACTORY (object)));
-                       return;
-       }
-
-       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
-}
-
-static void
 data_book_factory_dispose (GObject *object)
 {
+       EDataBookFactory *factory;
        EDataBookFactoryPrivate *priv;
 
-       priv = E_DATA_BOOK_FACTORY_GET_PRIVATE (object);
+       factory = E_DATA_BOOK_FACTORY (object);
+       priv = factory->priv;
 
-       if (priv->registry != NULL) {
-               g_object_unref (priv->registry);
-               priv->registry = NULL;
-       }
+       g_clear_object (&priv->dbus_factory);
 
-       if (priv->dbus_factory != NULL) {
-               g_object_unref (priv->dbus_factory);
-               priv->dbus_factory = NULL;
-       }
-
-       if (priv->localed_cancel) {
+       if (priv->localed_cancel)
                g_cancellable_cancel (priv->localed_cancel);
-               g_object_unref (priv->localed_cancel);
-               priv->localed_cancel = NULL;
-       }
 
-       if (priv->localed_proxy) {
-               g_object_unref (priv->localed_proxy);
-               priv->localed_proxy = NULL;
-       }
+       g_clear_object (&priv->localed_cancel);
+       g_clear_object (&priv->localed_proxy);
 
        if (priv->localed_watch_id > 0)
                g_bus_unwatch_name (priv->localed_watch_id);
 
-       g_hash_table_remove_all (priv->connections);
-       g_hash_table_remove_all (priv->watched_names);
-
        /* Chain up to parent's dispose() method. */
        G_OBJECT_CLASS (e_data_book_factory_parent_class)->dispose (object);
 }
@@ -549,80 +217,16 @@ data_book_factory_dispose (GObject *object)
 static void
 data_book_factory_finalize (GObject *object)
 {
-       EDataBookFactoryPrivate *priv;
-
-       priv = E_DATA_BOOK_FACTORY_GET_PRIVATE (object);
-
-       g_hash_table_destroy (priv->connections);
-       g_mutex_clear (&priv->connections_lock);
+       EDataBookFactory *factory;
 
-       g_hash_table_destroy (priv->watched_names);
-       g_mutex_clear (&priv->watched_names_lock);
+       factory = E_DATA_BOOK_FACTORY (object);
 
-       g_free (priv->locale);
+       g_free (factory->priv->locale);
 
        /* Chain up to parent's finalize() method. */
        G_OBJECT_CLASS (e_data_book_factory_parent_class)->finalize (object);
 }
 
-static void
-data_book_factory_bus_acquired (EDBusServer *server,
-                                GDBusConnection *connection)
-{
-       EDataBookFactoryPrivate *priv;
-       GError *error = NULL;
-
-       priv = E_DATA_BOOK_FACTORY_GET_PRIVATE (server);
-
-       g_dbus_interface_skeleton_export (
-               G_DBUS_INTERFACE_SKELETON (priv->dbus_factory),
-               connection,
-               "/org/gnome/evolution/dataserver/AddressBookFactory",
-               &error);
-
-       if (error != NULL) {
-               g_error (
-                       "Failed to export AddressBookFactory interface: %s",
-                       error->message);
-               g_assert_not_reached ();
-       }
-
-       /* Chain up to parent's bus_acquired() method. */
-       E_DBUS_SERVER_CLASS (e_data_book_factory_parent_class)->
-               bus_acquired (server, connection);
-}
-
-static void
-data_book_factory_bus_name_lost (EDBusServer *server,
-                                 GDBusConnection *connection)
-{
-       EDataBookFactory *factory;
-
-       factory = E_DATA_BOOK_FACTORY (server);
-
-       data_book_factory_connections_remove_all (factory);
-
-       /* Chain up to parent's bus_name_lost() method. */
-       E_DBUS_SERVER_CLASS (e_data_book_factory_parent_class)->
-               bus_name_lost (server, connection);
-}
-
-static void
-data_book_factory_quit_server (EDBusServer *server,
-                               EDBusServerExitCode exit_code)
-{
-       /* This factory does not support reloading, so stop the signal
-        * emission and return without chaining up to prevent quitting. */
-       if (exit_code == E_DBUS_SERVER_EXIT_RELOAD) {
-               g_signal_stop_emission_by_name (server, "quit-server");
-               return;
-       }
-
-       /* Chain up to parent's quit_server() method. */
-       E_DBUS_SERVER_CLASS (e_data_book_factory_parent_class)->
-               quit_server (server, exit_code);
-}
-
 static gchar *
 data_book_factory_interpret_locale_value (const gchar *value)
 {
@@ -797,12 +401,10 @@ data_book_factory_initable_init (GInitable *initable,
                                  GCancellable *cancellable,
                                  GError **error)
 {
-       EDataBookFactoryPrivate *priv;
+       EDataBookFactory *factory;
        GBusType bus_type = G_BUS_TYPE_SYSTEM;
 
-       priv = E_DATA_BOOK_FACTORY_GET_PRIVATE (initable);
-
-       priv->registry = e_source_registry_new_sync (cancellable, error);
+       factory = E_DATA_BOOK_FACTORY (initable);
 
        /* When running tests, we pretend to be the "org.freedesktop.locale1" service
         * on the session bus instead of the real location on the system bus.
@@ -811,7 +413,7 @@ data_book_factory_initable_init (GInitable *initable,
                bus_type = G_BUS_TYPE_SESSION;
 
        /* Watch system bus for locale change notifications */
-       priv->localed_watch_id =
+       factory->priv->localed_watch_id =
                g_bus_watch_name (
                        bus_type,
                        "org.freedesktop.locale1",
@@ -821,7 +423,8 @@ data_book_factory_initable_init (GInitable *initable,
                        initable,
                        NULL);
 
-       return (priv->registry != NULL);
+       /* Chain up to parent interface's init() method. */
+       return initable_parent_interface->init (initable, cancellable, error);
 }
 
 static void
@@ -841,35 +444,26 @@ e_data_book_factory_class_init (EDataBookFactoryClass *class)
        g_type_class_add_private (class, sizeof (EDataBookFactoryPrivate));
 
        object_class = G_OBJECT_CLASS (class);
-       object_class->get_property = data_book_factory_get_property;
        object_class->dispose = data_book_factory_dispose;
        object_class->finalize = data_book_factory_finalize;
 
        dbus_server_class = E_DBUS_SERVER_CLASS (class);
        dbus_server_class->bus_name = ADDRESS_BOOK_DBUS_SERVICE_NAME;
        dbus_server_class->module_directory = modules_directory;
-       dbus_server_class->bus_acquired = data_book_factory_bus_acquired;
-       dbus_server_class->bus_name_lost = data_book_factory_bus_name_lost;
-       dbus_server_class->quit_server = data_book_factory_quit_server;
 
        data_factory_class = E_DATA_FACTORY_CLASS (class);
        data_factory_class->backend_factory_type = E_TYPE_BOOK_BACKEND_FACTORY;
-
-       g_object_class_install_property (
-               object_class,
-               PROP_REGISTRY,
-               g_param_spec_object (
-                       "registry",
-                       "Registry",
-                       "Data source registry",
-                       E_TYPE_SOURCE_REGISTRY,
-                       G_PARAM_READABLE |
-                       G_PARAM_STATIC_STRINGS));
+       data_factory_class->factory_object_path = "/org/gnome/evolution/dataserver/AddressBookFactory";
+       data_factory_class->data_object_path_prefix = "/org/gnome/evolution/dataserver/Addressbook";
+       data_factory_class->get_dbus_interface_skeleton = data_book_factory_get_dbus_interface_skeleton;
+       data_factory_class->data_open = data_book_factory_open;
 }
 
 static void
 e_data_book_factory_initable_init (GInitableIface *iface)
 {
+       initable_parent_interface = g_type_interface_peek_parent (iface);
+
        iface->init = data_book_factory_initable_init;
 }
 
@@ -885,21 +479,6 @@ e_data_book_factory_init (EDataBookFactory *factory)
                factory->priv->dbus_factory, "handle-open-address-book",
                G_CALLBACK (data_book_factory_handle_open_address_book_cb),
                factory);
-
-       factory->priv->connections = g_hash_table_new_full (
-               (GHashFunc) g_str_hash,
-               (GEqualFunc) g_str_equal,
-               (GDestroyNotify) g_free,
-               (GDestroyNotify) g_ptr_array_unref);
-
-       g_mutex_init (&factory->priv->connections_lock);
-       g_mutex_init (&factory->priv->watched_names_lock);
-
-       factory->priv->watched_names = g_hash_table_new_full (
-               (GHashFunc) g_str_hash,
-               (GEqualFunc) g_str_equal,
-               (GDestroyNotify) g_free,
-               (GDestroyNotify) watched_names_value_free);
 }
 
 EDBusServer *
@@ -910,22 +489,3 @@ e_data_book_factory_new (GCancellable *cancellable,
                E_TYPE_DATA_BOOK_FACTORY,
                cancellable, error, NULL);
 }
-
-/**
- * e_data_book_factory_get_registry:
- * @factory: an #EDataBookFactory
- *
- * Returns the #ESourceRegistry owned by @factory.
- *
- * Returns: the #ESourceRegistry
- *
- * Since: 3.6
- **/
-ESourceRegistry *
-e_data_book_factory_get_registry (EDataBookFactory *factory)
-{
-       g_return_val_if_fail (E_IS_DATA_BOOK_FACTORY (factory), NULL);
-
-       return factory->priv->registry;
-}
-
diff --git a/addressbook/libedata-book/e-data-book-factory.h b/addressbook/libedata-book/e-data-book-factory.h
index 80990a9..9c515c3 100644
--- a/addressbook/libedata-book/e-data-book-factory.h
+++ b/addressbook/libedata-book/e-data-book-factory.h
@@ -71,9 +71,6 @@ struct _EDataBookFactoryClass {
 GType          e_data_book_factory_get_type    (void) G_GNUC_CONST;
 EDBusServer *  e_data_book_factory_new         (GCancellable *cancellable,
                                                 GError **error);
-ESourceRegistry *
-               e_data_book_factory_get_registry
-                                               (EDataBookFactory *factory);
 
 G_END_DECLS
 
diff --git a/addressbook/libedata-book/e-data-book.c b/addressbook/libedata-book/e-data-book.c
index 26986dc..f197916 100644
--- a/addressbook/libedata-book/e-data-book.c
+++ b/addressbook/libedata-book/e-data-book.c
@@ -235,6 +235,14 @@ data_book_convert_to_client_error (GError *error)
 {
        g_return_if_fail (error != NULL);
 
+       /* Data-Factory returns common error for unknown/broken ESource-s */
+       if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
+               error->domain = E_BOOK_CLIENT_ERROR;
+               error->code = E_BOOK_CLIENT_ERROR_NO_SUCH_BOOK;
+
+               return;
+       }
+
        if (error->domain != E_DATA_BOOK_ERROR)
                return;
 
diff --git a/calendar/libedata-cal/e-cal-backend-factory.c b/calendar/libedata-cal/e-cal-backend-factory.c
index 30d1a8e..48b1d8d 100644
--- a/calendar/libedata-cal/e-cal-backend-factory.c
+++ b/calendar/libedata-cal/e-cal-backend-factory.c
@@ -51,13 +51,13 @@ cal_backend_factory_get_hash_key (EBackendFactory *factory)
 
        switch (class->component_kind) {
                case ICAL_VEVENT_COMPONENT:
-                       component_name = "VEVENT";
+                       component_name = E_SOURCE_EXTENSION_CALENDAR;
                        break;
                case ICAL_VTODO_COMPONENT:
-                       component_name = "VTODO";
+                       component_name = E_SOURCE_EXTENSION_TASK_LIST;
                        break;
                case ICAL_VJOURNAL_COMPONENT:
-                       component_name = "VJOURNAL";
+                       component_name = E_SOURCE_EXTENSION_MEMO_LIST;
                        break;
                default:
                        g_return_val_if_reached (NULL);
@@ -86,7 +86,7 @@ cal_backend_factory_new_backend (EBackendFactory *factory,
                class->backend_type, E_TYPE_CAL_BACKEND), NULL);
 
        data_factory = cal_backend_factory_get_data_factory (factory);
-       registry = e_data_cal_factory_get_registry (data_factory);
+       registry = e_data_factory_get_registry (E_DATA_FACTORY (data_factory));
 
        return g_object_new (
                class->backend_type,
diff --git a/calendar/libedata-cal/e-data-cal-factory.c b/calendar/libedata-cal/e-data-cal-factory.c
index eb34085..a3d57f1 100644
--- a/calendar/libedata-cal/e-data-cal-factory.c
+++ b/calendar/libedata-cal/e-data-cal-factory.c
@@ -56,24 +56,10 @@
        ((obj), E_TYPE_DATA_CAL_FACTORY, EDataCalFactoryPrivate))
 
 struct _EDataCalFactoryPrivate {
-       ESourceRegistry *registry;
        EDBusCalendarFactory *dbus_factory;
-
-       /* This is a hash table of client bus names to an array of
-        * ECalBackend references; one for every connection opened. */
-       GHashTable *connections;
-       GMutex connections_lock;
-
-       /* This is a hash table of client bus names being watched.
-        * The value is the watcher ID for g_bus_unwatch_name(). */
-       GHashTable *watched_names;
-       GMutex watched_names_lock;
 };
 
-enum {
-       PROP_0,
-       PROP_REGISTRY
-};
+static GInitableIface *initable_parent_interface;
 
 /* Forward Declarations */
 static void    e_data_cal_factory_initable_init
@@ -87,278 +73,25 @@ G_DEFINE_TYPE_WITH_CODE (
                G_TYPE_INITABLE,
                e_data_cal_factory_initable_init))
 
-static void
-watched_names_value_free (gpointer value)
-{
-       g_bus_unwatch_name (GPOINTER_TO_UINT (value));
-}
-
-static void
-data_cal_factory_toggle_notify_cb (gpointer data,
-                                   GObject *backend,
-                                   gboolean is_last_ref)
-{
-       if (is_last_ref) {
-               /* Take a strong reference before removing the
-                * toggle reference, to keep the backend alive. */
-               g_object_ref (backend);
-
-               g_object_remove_toggle_ref (
-                       backend, data_cal_factory_toggle_notify_cb, data);
-
-               g_signal_emit_by_name (backend, "shutdown");
-
-               g_object_unref (backend);
-       }
-}
-
-static void
-data_cal_factory_connections_add (EDataCalFactory *factory,
-                                  const gchar *name,
-                                  ECalBackend *backend)
-{
-       GHashTable *connections;
-       GPtrArray *array;
-
-       g_return_if_fail (name != NULL);
-       g_return_if_fail (backend != NULL);
-
-       g_mutex_lock (&factory->priv->connections_lock);
-
-       connections = factory->priv->connections;
-
-       if (g_hash_table_size (connections) == 0)
-               e_dbus_server_hold (E_DBUS_SERVER (factory));
-
-       array = g_hash_table_lookup (connections, name);
-
-       if (array == NULL) {
-               array = g_ptr_array_new_with_free_func (
-                       (GDestroyNotify) g_object_unref);
-               g_hash_table_insert (
-                       connections, g_strdup (name), array);
-       }
-
-       g_ptr_array_add (array, g_object_ref (backend));
-
-       g_mutex_unlock (&factory->priv->connections_lock);
-}
-
-static gboolean
-data_cal_factory_connections_remove (EDataCalFactory *factory,
-                                     const gchar *name,
-                                     ECalBackend *backend)
+static GDBusInterfaceSkeleton *
+data_cal_factory_get_dbus_interface_skeleton (EDBusServer *server)
 {
-       GHashTable *connections;
-       GPtrArray *array;
-       gboolean removed = FALSE;
-
-       /* If backend is NULL, we remove all backends for name. */
-       g_return_val_if_fail (name != NULL, FALSE);
-
-       g_mutex_lock (&factory->priv->connections_lock);
-
-       connections = factory->priv->connections;
-       array = g_hash_table_lookup (connections, name);
-
-       if (array != NULL) {
-               if (backend != NULL) {
-                       removed = g_ptr_array_remove_fast (array, backend);
-               } else if (array->len > 0) {
-                       g_ptr_array_set_size (array, 0);
-                       removed = TRUE;
-               }
-
-               if (array->len == 0)
-                       g_hash_table_remove (connections, name);
-
-               if (g_hash_table_size (connections) == 0)
-                       e_dbus_server_release (E_DBUS_SERVER (factory));
-       }
-
-       g_mutex_unlock (&factory->priv->connections_lock);
-
-       return removed;
-}
-
-static void
-data_cal_factory_connections_remove_all (EDataCalFactory *factory)
-{
-       GHashTable *connections;
-
-       g_mutex_lock (&factory->priv->connections_lock);
-
-       connections = factory->priv->connections;
-
-       if (g_hash_table_size (connections) > 0) {
-               g_hash_table_remove_all (connections);
-               e_dbus_server_release (E_DBUS_SERVER (factory));
-       }
-
-       g_mutex_unlock (&factory->priv->connections_lock);
-}
-
-static void
-data_cal_factory_name_vanished_cb (GDBusConnection *connection,
-                                   const gchar *name,
-                                   gpointer user_data)
-{
-       GWeakRef *weak_ref = user_data;
        EDataCalFactory *factory;
 
-       factory = g_weak_ref_get (weak_ref);
-
-       if (factory != NULL) {
-               data_cal_factory_connections_remove (factory, name, NULL);
-
-               /* Unwatching the bus name from here will corrupt the
-                * 'name' argument, and possibly also the 'user_data'.
-                *
-                * This is a GDBus bug.  Work around it by unwatching
-                * the bus name last.
-                *
-                * See: https://bugzilla.gnome.org/706088
-                */
-               g_mutex_lock (&factory->priv->watched_names_lock);
-               g_hash_table_remove (factory->priv->watched_names, name);
-               g_mutex_unlock (&factory->priv->watched_names_lock);
-
-               g_object_unref (factory);
-       }
-}
-
-static void
-data_cal_factory_watched_names_add (EDataCalFactory *factory,
-                                    GDBusConnection *connection,
-                                    const gchar *name)
-{
-       GHashTable *watched_names;
-
-       g_return_if_fail (name != NULL);
-
-       g_mutex_lock (&factory->priv->watched_names_lock);
-
-       watched_names = factory->priv->watched_names;
-
-       if (!g_hash_table_contains (watched_names, name)) {
-               guint watcher_id;
-
-               /* The g_bus_watch_name() documentation says one of the two
-                * callbacks are guaranteed to be invoked after calling the
-                * function.  But which one is determined asynchronously so
-                * there should be no chance of the name vanished callback
-                * deadlocking with us when it tries to acquire the lock. */
-               watcher_id = g_bus_watch_name_on_connection (
-                       connection, name,
-                       G_BUS_NAME_WATCHER_FLAGS_NONE,
-                       (GBusNameAppearedCallback) NULL,
-                       data_cal_factory_name_vanished_cb,
-                       e_weak_ref_new (factory),
-                       (GDestroyNotify) e_weak_ref_free);
-
-               g_hash_table_insert (
-                       watched_names, g_strdup (name),
-                       GUINT_TO_POINTER (watcher_id));
-       }
-
-       g_mutex_unlock (&factory->priv->watched_names_lock);
-}
-
-static EBackend *
-data_cal_factory_ref_backend (EDataFactory *factory,
-                              ESource *source,
-                              const gchar *extension_name,
-                              const gchar *type_string,
-                              GError **error)
-{
-       EBackend *backend;
-       ESourceBackend *extension;
-       gchar *backend_name;
-       gchar *hash_key;
-
-       extension = e_source_get_extension (source, extension_name);
-       backend_name = e_source_backend_dup_backend_name (extension);
-
-       if (backend_name == NULL || *backend_name == '\0') {
-               g_set_error (
-                       error, E_DATA_CAL_ERROR, NoSuchCal,
-                       _("No backend name in source '%s'"),
-                       e_source_get_display_name (source));
-               g_free (backend_name);
-               return NULL;
-       }
-
-       hash_key = g_strdup_printf ("%s:%s", backend_name, type_string);
-       backend = e_data_factory_ref_initable_backend (
-               factory, hash_key, source, NULL, error);
-       g_free (hash_key);
-
-       g_free (backend_name);
-
-       return backend;
-}
-
-static gchar *
-construct_cal_factory_path (void)
-{
-       static volatile gint counter = 1;
-
-       g_atomic_int_inc (&counter);
-
-       return g_strdup_printf (
-               "/org/gnome/evolution/dataserver/Calendar/%d/%u",
-               getpid (), counter);
-}
+       factory = E_DATA_CAL_FACTORY (server);
 
-static void
-data_cal_factory_closed_cb (ECalBackend *backend,
-                            const gchar *sender,
-                            EDataCalFactory *factory)
-{
-       data_cal_factory_connections_remove (factory, sender, backend);
+       return G_DBUS_INTERFACE_SKELETON (factory->priv->dbus_factory);
 }
 
 static gchar *
-data_cal_factory_open (EDataCalFactory *factory,
-                       GDBusConnection *connection,
-                       const gchar *sender,
-                       const gchar *uid,
-                       const gchar *extension_name,
-                       const gchar *type_string,
-                       GError **error)
+data_cal_factory_open (EDataFactory *data_factory,
+                      EBackend *backend,
+                      GDBusConnection *connection,
+                      GError **error)
 {
        EDataCal *data_cal;
-       EBackend *backend;
-       ESourceRegistry *registry;
-       ESource *source;
        gchar *object_path;
 
-       if (uid == NULL || *uid == '\0') {
-               g_set_error (
-                       error, E_DATA_CAL_ERROR, NoSuchCal,
-                       _("Missing source UID"));
-               return NULL;
-       }
-
-       registry = e_data_cal_factory_get_registry (factory);
-       source = e_source_registry_ref_source (registry, uid);
-
-       if (source == NULL) {
-               g_set_error (
-                       error, E_DATA_CAL_ERROR, NoSuchCal,
-                       _("No such source for UID '%s'"), uid);
-               return NULL;
-       }
-
-       backend = data_cal_factory_ref_backend (
-               E_DATA_FACTORY (factory), source,
-               extension_name, type_string, error);
-
-       g_object_unref (source);
-
-       if (backend == NULL)
-               return NULL;
-
        /* If the backend already has an EDataCal installed, return its
         * object path.  Otherwise we need to install a new EDataCal. */
 
@@ -368,7 +101,7 @@ data_cal_factory_open (EDataCalFactory *factory,
                object_path = g_strdup (
                        e_data_cal_get_object_path (data_cal));
        } else {
-               object_path = construct_cal_factory_path ();
+               object_path = e_data_factory_construct_path (data_factory);
 
                /* The EDataCal will attach itself to ECalBackend,
                 * so no need to call e_cal_backend_set_data_cal(). */
@@ -377,270 +110,39 @@ data_cal_factory_open (EDataCalFactory *factory,
                        connection, object_path, error);
 
                if (data_cal != NULL) {
-                       /* Install a toggle reference on the backend
-                        * so we can signal it to shut down once all
-                        * client connections are closed. */
-                       g_object_add_toggle_ref (
-                               G_OBJECT (backend),
-                               data_cal_factory_toggle_notify_cb,
-                               NULL);
-
-                       g_signal_connect_object (
-                               backend, "closed",
-                               G_CALLBACK (data_cal_factory_closed_cb),
-                               factory, 0);
-
+                       e_data_factory_set_backend_callbacks (
+                               data_factory, backend);
                } else {
                        g_free (object_path);
                        object_path = NULL;
                }
        }
 
-       if (data_cal != NULL) {
-               /* Watch the sender's bus name so we can clean
-                * up its connections if the bus name vanishes. */
-               data_cal_factory_watched_names_add (
-                       factory, connection, sender);
-
-               /* A client may create multiple EClient instances for the
-                * same ESource, each of which calls close() individually.
-                * So we must track each and every connection made. */
-               data_cal_factory_connections_add (
-                       factory, sender, E_CAL_BACKEND (backend));
-
-               g_object_unref (data_cal);
-       }
-
-       g_object_unref (backend);
+       g_clear_object (&data_cal);
 
        return object_path;
 }
 
 static gboolean
-data_cal_factory_handle_open_calendar_cb (EDBusCalendarFactory *dbus_interface,
-                                          GDBusMethodInvocation *invocation,
-                                          const gchar *uid,
-                                          EDataCalFactory *factory)
-{
-       GDBusConnection *connection;
-       const gchar *sender;
-       gchar *object_path;
-       GError *error = NULL;
-
-       connection = g_dbus_method_invocation_get_connection (invocation);
-       sender = g_dbus_method_invocation_get_sender (invocation);
-
-       object_path = data_cal_factory_open (
-               factory, connection, sender, uid,
-               E_SOURCE_EXTENSION_CALENDAR, "VEVENT", &error);
-
-       if (object_path != NULL) {
-               e_dbus_calendar_factory_complete_open_calendar (
-                       dbus_interface, invocation, object_path);
-               g_free (object_path);
-       } else {
-               g_return_val_if_fail (error != NULL, FALSE);
-               g_dbus_method_invocation_take_error (invocation, error);
-       }
-
-       return TRUE;
-}
-
-static gboolean
-data_cal_factory_handle_open_task_list_cb (EDBusCalendarFactory *dbus_interface,
-                                           GDBusMethodInvocation *invocation,
-                                           const gchar *uid,
-                                           EDataCalFactory *factory)
-{
-       GDBusConnection *connection;
-       const gchar *sender;
-       gchar *object_path;
-       GError *error = NULL;
-
-       connection = g_dbus_method_invocation_get_connection (invocation);
-       sender = g_dbus_method_invocation_get_sender (invocation);
-
-       object_path = data_cal_factory_open (
-               factory, connection, sender, uid,
-               E_SOURCE_EXTENSION_TASK_LIST, "VTODO", &error);
-
-       if (object_path != NULL) {
-               e_dbus_calendar_factory_complete_open_task_list (
-                       dbus_interface, invocation, object_path);
-               g_free (object_path);
-       } else {
-               g_return_val_if_fail (error != NULL, FALSE);
-               g_dbus_method_invocation_take_error (invocation, error);
-       }
-
-       return TRUE;
-}
-
-static gboolean
-data_cal_factory_handle_open_memo_list_cb (EDBusCalendarFactory *dbus_interface,
-                                           GDBusMethodInvocation *invocation,
-                                           const gchar *uid,
-                                           EDataCalFactory *factory)
-{
-       GDBusConnection *connection;
-       const gchar *sender;
-       gchar *object_path;
-       GError *error = NULL;
-
-       connection = g_dbus_method_invocation_get_connection (invocation);
-       sender = g_dbus_method_invocation_get_sender (invocation);
-
-       object_path = data_cal_factory_open (
-               factory, connection, sender, uid,
-               E_SOURCE_EXTENSION_MEMO_LIST, "VJOURNAL", &error);
-
-       if (object_path != NULL) {
-               e_dbus_calendar_factory_complete_open_memo_list (
-                       dbus_interface, invocation, object_path);
-               g_free (object_path);
-       } else {
-               g_return_val_if_fail (error != NULL, FALSE);
-               g_dbus_method_invocation_take_error (invocation, error);
-       }
-
-       return TRUE;
-}
-
-static void
-data_cal_factory_get_property (GObject *object,
-                               guint property_id,
-                               GValue *value,
-                               GParamSpec *pspec)
-{
-       switch (property_id) {
-               case PROP_REGISTRY:
-                       g_value_set_object (
-                               value,
-                               e_data_cal_factory_get_registry (
-                               E_DATA_CAL_FACTORY (object)));
-                       return;
-       }
-
-       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
-}
-
-static void
-data_cal_factory_dispose (GObject *object)
-{
-       EDataCalFactoryPrivate *priv;
-
-       priv = E_DATA_CAL_FACTORY_GET_PRIVATE (object);
-
-       if (priv->registry != NULL) {
-               g_object_unref (priv->registry);
-               priv->registry = NULL;
-       }
-
-       if (priv->dbus_factory != NULL) {
-               g_object_unref (priv->dbus_factory);
-               priv->dbus_factory = NULL;
-       }
-
-       g_hash_table_remove_all (priv->connections);
-       g_hash_table_remove_all (priv->watched_names);
-
-       /* Chain up to parent's dispose() method. */
-       G_OBJECT_CLASS (e_data_cal_factory_parent_class)->dispose (object);
-}
-
-static void
-data_cal_factory_finalize (GObject *object)
-{
-       EDataCalFactoryPrivate *priv;
-
-       priv = E_DATA_CAL_FACTORY_GET_PRIVATE (object);
-
-       g_hash_table_destroy (priv->connections);
-       g_mutex_clear (&priv->connections_lock);
-
-       g_hash_table_destroy (priv->watched_names);
-       g_mutex_clear (&priv->watched_names_lock);
-
-       /* Chain up to parent's finalize() method. */
-       G_OBJECT_CLASS (e_data_cal_factory_parent_class)->finalize (object);
-}
-
-static void
-data_cal_factory_bus_acquired (EDBusServer *server,
-                               GDBusConnection *connection)
-{
-       EDataCalFactoryPrivate *priv;
-       GError *error = NULL;
-
-       priv = E_DATA_CAL_FACTORY_GET_PRIVATE (server);
-
-       g_dbus_interface_skeleton_export (
-               G_DBUS_INTERFACE_SKELETON (priv->dbus_factory),
-               connection,
-               "/org/gnome/evolution/dataserver/CalendarFactory",
-               &error);
-
-       if (error != NULL) {
-               g_error (
-                       "Failed to export CalendarFactory interface: %s",
-                       error->message);
-               g_assert_not_reached ();
-       }
-
-       /* Chain up to parent's bus_acquired() method. */
-       E_DBUS_SERVER_CLASS (e_data_cal_factory_parent_class)->
-               bus_acquired (server, connection);
-}
-
-static void
-data_cal_factory_bus_name_lost (EDBusServer *server,
-                                GDBusConnection *connection)
+data_cal_factory_initable_init (GInitable *initable,
+                               GCancellable *cancellable,
+                               GError **error)
 {
-       EDataCalFactory *factory;
-
-       factory = E_DATA_CAL_FACTORY (server);
-
-       data_cal_factory_connections_remove_all (factory);
-
-       /* Chain up to parent's bus_name_lost() method. */
-       E_DBUS_SERVER_CLASS (e_data_cal_factory_parent_class)->
-               bus_name_lost (server, connection);
+       /* Chain up to parent interface's init() method. */
+       return initable_parent_interface->init (initable, cancellable, error);
 }
 
 static void
-data_cal_factory_quit_server (EDBusServer *server,
-                              EDBusServerExitCode exit_code)
-{
-       /* This factory does not support reloading, so stop the signal
-        * emission and return without chaining up to prevent quitting. */
-       if (exit_code == E_DBUS_SERVER_EXIT_RELOAD) {
-               g_signal_stop_emission_by_name (server, "quit-server");
-               return;
-       }
-
-       /* Chain up to parent's quit_server() method. */
-       E_DBUS_SERVER_CLASS (e_data_cal_factory_parent_class)->
-               quit_server (server, exit_code);
-}
-
-static gboolean
-data_cal_factory_initable_init (GInitable *initable,
-                                GCancellable *cancellable,
-                                GError **error)
+e_data_cal_factory_initable_init (GInitableIface *iface)
 {
-       EDataCalFactoryPrivate *priv;
-
-       priv = E_DATA_CAL_FACTORY_GET_PRIVATE (initable);
-
-       priv->registry = e_source_registry_new_sync (cancellable, error);
+       initable_parent_interface = g_type_interface_peek_parent (iface);
 
-       return (priv->registry != NULL);
+       iface->init = data_cal_factory_initable_init;
 }
 
 static void
 e_data_cal_factory_class_init (EDataCalFactoryClass *class)
 {
-       GObjectClass *object_class;
        EDBusServerClass *dbus_server_class;
        EDataFactoryClass *data_factory_class;
        const gchar *modules_directory = BACKENDDIR;
@@ -653,39 +155,71 @@ e_data_cal_factory_class_init (EDataCalFactoryClass *class)
 
        g_type_class_add_private (class, sizeof (EDataCalFactoryPrivate));
 
-       object_class = G_OBJECT_CLASS (class);
-       object_class->get_property = data_cal_factory_get_property;
-       object_class->dispose = data_cal_factory_dispose;
-       object_class->finalize = data_cal_factory_finalize;
-
        dbus_server_class = E_DBUS_SERVER_CLASS (class);
        dbus_server_class->bus_name = CALENDAR_DBUS_SERVICE_NAME;
        dbus_server_class->module_directory = modules_directory;
-       dbus_server_class->bus_acquired = data_cal_factory_bus_acquired;
-       dbus_server_class->bus_name_lost = data_cal_factory_bus_name_lost;
-       dbus_server_class->quit_server = data_cal_factory_quit_server;
 
        data_factory_class = E_DATA_FACTORY_CLASS (class);
        data_factory_class->backend_factory_type = E_TYPE_CAL_BACKEND_FACTORY;
+       data_factory_class->factory_object_path = "/org/gnome/evolution/dataserver/CalendarFactory";
+       data_factory_class->data_object_path_prefix = "/org/gnome/evolution/dataserver/Calendar";
+       data_factory_class->get_dbus_interface_skeleton = data_cal_factory_get_dbus_interface_skeleton;
+       data_factory_class->data_open = data_cal_factory_open;
+}
 
-       g_object_class_install_property (
-               object_class,
-               PROP_REGISTRY,
-               g_param_spec_object (
-                       "registry",
-                       "Registry",
-                       "Data source registry",
-                       E_TYPE_SOURCE_REGISTRY,
-                       G_PARAM_READABLE |
-                       G_PARAM_STATIC_STRINGS));
+#define HANDLE_OPEN_CB(extension_name, minus)                                  \
+       GDBusConnection *connection;                                            \
+       const gchar *sender;                                                    \
+       gchar *object_path;                                                     \
+       GError *error = NULL;                                                   \
+                                                                               \
+       connection = g_dbus_method_invocation_get_connection (invocation);      \
+       sender = g_dbus_method_invocation_get_sender (invocation);              \
+                                                                               \
+       object_path = e_data_factory_open_backend (                             \
+               E_DATA_FACTORY (factory), connection, sender,                   \
+               uid, extension_name, &error);                                   \
+                                                                               \
+       if (object_path != NULL) {                                              \
+               e_dbus_calendar_factory_complete_open_##minus (                 \
+                       dbus_interface, invocation, object_path);               \
+               g_free (object_path);                                           \
+       } else {                                                                \
+               g_return_val_if_fail (error != NULL, FALSE);                    \
+               g_dbus_method_invocation_take_error (invocation, error);        \
+       }                                                                       \
+                                                                               \
+       return TRUE;
+
+static gboolean
+data_cal_factory_handle_open_calendar_cb (EDBusCalendarFactory *dbus_interface,
+                                         GDBusMethodInvocation *invocation,
+                                         const gchar *uid,
+                                         EDataCalFactory *factory)
+{
+       HANDLE_OPEN_CB (E_SOURCE_EXTENSION_CALENDAR, calendar);
 }
 
-static void
-e_data_cal_factory_initable_init (GInitableIface *iface)
+static gboolean
+data_cal_factory_handle_open_task_list_cb (EDBusCalendarFactory *dbus_interface,
+                                          GDBusMethodInvocation *invocation,
+                                          const gchar *uid,
+                                          EDataCalFactory *factory)
 {
-       iface->init = data_cal_factory_initable_init;
+       HANDLE_OPEN_CB (E_SOURCE_EXTENSION_TASK_LIST, task_list);
 }
 
+static gboolean
+data_cal_factory_handle_open_memo_list_cb (EDBusCalendarFactory *dbus_interface,
+                                          GDBusMethodInvocation *invocation,
+                                          const gchar *uid,
+                                          EDataCalFactory *factory)
+{
+       HANDLE_OPEN_CB (E_SOURCE_EXTENSION_MEMO_LIST, memo_list);
+}
+
+
+
 static void
 e_data_cal_factory_init (EDataCalFactory *factory)
 {
@@ -708,21 +242,6 @@ e_data_cal_factory_init (EDataCalFactory *factory)
                factory->priv->dbus_factory, "handle-open-memo-list",
                G_CALLBACK (data_cal_factory_handle_open_memo_list_cb),
                factory);
-
-       factory->priv->connections = g_hash_table_new_full (
-               (GHashFunc) g_str_hash,
-               (GEqualFunc) g_str_equal,
-               (GDestroyNotify) g_free,
-               (GDestroyNotify) g_ptr_array_unref);
-
-       g_mutex_init (&factory->priv->connections_lock);
-       g_mutex_init (&factory->priv->watched_names_lock);
-
-       factory->priv->watched_names = g_hash_table_new_full (
-               (GHashFunc) g_str_hash,
-               (GEqualFunc) g_str_equal,
-               (GDestroyNotify) g_free,
-               (GDestroyNotify) watched_names_value_free);
 }
 
 EDBusServer *
@@ -763,21 +282,3 @@ e_data_cal_factory_new (GCancellable *cancellable,
                E_TYPE_DATA_CAL_FACTORY,
                cancellable, error, NULL);
 }
-
-/**
- * e_data_cal_factory_get_registry:
- * @factory: an #EDataCalFactory
- *
- * Returns the #ESourceRegistry owned by @factory.
- *
- * Returns: the #ESourceRegistry
- *
- * Since: 3.6
- **/
-ESourceRegistry *
-e_data_cal_factory_get_registry (EDataCalFactory *factory)
-{
-       g_return_val_if_fail (E_IS_DATA_CAL_FACTORY (factory), NULL);
-
-       return factory->priv->registry;
-}
diff --git a/calendar/libedata-cal/e-data-cal-factory.h b/calendar/libedata-cal/e-data-cal-factory.h
index e8fec09..18d16d4 100644
--- a/calendar/libedata-cal/e-data-cal-factory.h
+++ b/calendar/libedata-cal/e-data-cal-factory.h
@@ -72,8 +72,6 @@ struct _EDataCalFactoryClass {
 GType          e_data_cal_factory_get_type     (void);
 EDBusServer *  e_data_cal_factory_new          (GCancellable *cancellable,
                                                 GError **error);
-ESourceRegistry *
-               e_data_cal_factory_get_registry (EDataCalFactory *factory);
 
 G_END_DECLS
 
diff --git a/calendar/libedata-cal/e-data-cal.c b/calendar/libedata-cal/e-data-cal.c
index d42516f..22753ff 100644
--- a/calendar/libedata-cal/e-data-cal.c
+++ b/calendar/libedata-cal/e-data-cal.c
@@ -218,6 +218,14 @@ data_cal_convert_to_client_error (GError *error)
 {
        g_return_if_fail (error != NULL);
 
+       /* Data-Factory returns common error for unknown/broken ESource-s */
+       if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
+               error->domain = E_CAL_CLIENT_ERROR;
+               error->code = E_CAL_CLIENT_ERROR_NO_SUCH_CALENDAR;
+
+               return;
+       }
+
        if (error->domain != E_DATA_CAL_ERROR)
                return;
 
diff --git a/configure.ac b/configure.ac
index 293fb94..9c029b2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -114,11 +114,11 @@ LIBECAL_CURRENT=16
 LIBECAL_REVISION=0
 LIBECAL_AGE=0
 
-LIBEDATACAL_CURRENT=23
+LIBEDATACAL_CURRENT=24
 LIBEDATACAL_REVISION=0
 LIBEDATACAL_AGE=0
 
-LIBEDATABOOK_CURRENT=20
+LIBEDATABOOK_CURRENT=21
 LIBEDATABOOK_REVISION=0
 LIBEDATABOOK_AGE=0
 
@@ -134,7 +134,7 @@ LIBCAMEL_CURRENT=49
 LIBCAMEL_REVISION=0
 LIBCAMEL_AGE=0
 
-LIBEBACKEND_CURRENT=7
+LIBEBACKEND_CURRENT=8
 LIBEBACKEND_REVISION=0
 LIBEBACKEND_AGE=0
 
diff --git a/docs/reference/eds/eds-sections.txt b/docs/reference/eds/eds-sections.txt
index 30684c9..cd8bd29 100644
--- a/docs/reference/eds/eds-sections.txt
+++ b/docs/reference/eds/eds-sections.txt
@@ -1941,7 +1941,6 @@ EDS_ADDRESS_BOOK_MODULES
 EDataBookFactory
 EDataBookFactoryClass
 e_data_book_factory_new
-e_data_book_factory_get_registry
 <SUBSECTION Standard>
 EDataBookFactoryPrivate
 E_DATA_BOOK_FACTORY
@@ -2032,7 +2031,6 @@ EDS_CALENDAR_MODULES
 EDataCalFactory
 EDataCalFactoryClass
 e_data_cal_factory_new
-e_data_cal_factory_get_registry
 <SUBSECTION Standard>
 EDataCalFactoryPrivate
 E_DATA_CAL_FACTORY
@@ -2089,6 +2087,10 @@ EDataFactoryClass
 e_data_factory_ref_backend
 e_data_factory_ref_initable_backend
 e_data_factory_ref_backend_factory
+e_data_factory_get_registry
+e_data_factory_open
+e_data_factory_construct_path
+e_data_factory_set_backend_callbacks
 <SUBSECTION Standard>
 EDataFactoryPrivate
 E_DATA_FACTORY
diff --git a/libebackend/e-data-factory.c b/libebackend/e-data-factory.c
index 338f9be..a881a35 100644
--- a/libebackend/e-data-factory.c
+++ b/libebackend/e-data-factory.c
@@ -28,12 +28,15 @@
 
 #include <libebackend/e-extensible.h>
 #include <libebackend/e-backend-factory.h>
+#include <libebackend/e-dbus-server.h>
 
 #define E_DATA_FACTORY_GET_PRIVATE(obj) \
        (G_TYPE_INSTANCE_GET_PRIVATE \
        ((obj), E_TYPE_DATA_FACTORY, EDataFactoryPrivate))
 
 struct _EDataFactoryPrivate {
+       ESourceRegistry *registry;
+
        /* The mutex guards the 'backends' hash table.  The
         * 'backend_factories' hash table doesn't really need
         * guarding since it gets populated during construction
@@ -45,6 +48,16 @@ struct _EDataFactoryPrivate {
 
        /* Hash Key -> EBackendFactory */
        GHashTable *backend_factories;
+
+       /* This is a hash table of client bus names to an array of
+        * EBackend references; one for every connection opened. */
+       GHashTable *connections;
+       GMutex connections_lock;
+
+       /* This is a hash table of client bus names being watched.
+        * The value is the watcher ID for g_bus_unwatch_name(). */
+       GHashTable *watched_names;
+       GMutex watched_names_lock;
 };
 
 enum {
@@ -52,10 +65,23 @@ enum {
        LAST_SIGNAL
 };
 
+enum {
+       PROP_0,
+       PROP_REGISTRY
+};
+
 static guint signals[LAST_SIGNAL];
 
-G_DEFINE_ABSTRACT_TYPE (
-       EDataFactory, e_data_factory, E_TYPE_DBUS_SERVER)
+/* Forward Declarations */
+static void    e_data_factory_initable_init    (GInitableIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (
+       EDataFactory,
+       e_data_factory,
+       E_TYPE_DBUS_SERVER,
+       G_IMPLEMENT_INTERFACE (
+               G_TYPE_INITABLE,
+               e_data_factory_initable_init))
 
 static GWeakRef *
 data_factory_backends_lookup (EDataFactory *data_factory,
@@ -76,15 +102,276 @@ data_factory_backends_lookup (EDataFactory *data_factory,
 }
 
 static void
+watched_names_value_free (gpointer value)
+{
+       g_bus_unwatch_name (GPOINTER_TO_UINT (value));
+}
+
+static void
+data_factory_bus_acquired (EDBusServer *server,
+                          GDBusConnection *connection)
+{
+       GDBusInterfaceSkeleton *skeleton_interface;
+       EDataFactoryClass *class;
+       GError *error = NULL;
+
+       class = E_DATA_FACTORY_GET_CLASS (E_DATA_FACTORY (server));
+
+       skeleton_interface = class->get_dbus_interface_skeleton (server);
+
+       g_dbus_interface_skeleton_export (
+               skeleton_interface,
+               connection,
+               class->factory_object_path,
+               &error);
+
+       if (error != NULL) {
+               g_warning ("%s: %s", G_STRFUNC, error->message);
+               e_dbus_server_quit (server, E_DBUS_SERVER_EXIT_NORMAL);
+               g_error_free (error);
+
+               return;
+       }
+
+       /* Chain up to parent's bus_acquired() method. */
+       E_DBUS_SERVER_CLASS (e_data_factory_parent_class)->
+               bus_acquired (server, connection);
+}
+
+static void
+data_factory_connections_add (EDataFactory *data_factory,
+                             const gchar *name,
+                             EBackend *backend)
+{
+       GHashTable *connections;
+       GPtrArray *array;
+
+       g_return_if_fail (name != NULL);
+       g_return_if_fail (backend != NULL);
+
+       g_mutex_lock (&data_factory->priv->connections_lock);
+
+       connections = data_factory->priv->connections;
+
+       if (g_hash_table_size (connections) == 0)
+               e_dbus_server_hold (E_DBUS_SERVER (data_factory));
+
+       array = g_hash_table_lookup (connections, name);
+
+       if (array == NULL) {
+               array = g_ptr_array_new_with_free_func (
+                       (GDestroyNotify) g_object_unref);
+               g_hash_table_insert (
+                       connections, g_strdup (name), array);
+       }
+
+       g_ptr_array_add (array, g_object_ref (backend));
+
+       g_mutex_unlock (&data_factory->priv->connections_lock);
+}
+
+static gboolean
+data_factory_connections_remove (EDataFactory *data_factory,
+                                const gchar *name,
+                                EBackend *backend)
+{
+       GHashTable *connections;
+       GPtrArray *array;
+       gboolean removed = FALSE;
+
+       /* If backend is NULL, we remove all backends for name. */
+       g_return_val_if_fail (name != NULL, FALSE);
+
+       g_mutex_lock (&data_factory->priv->connections_lock);
+
+       connections = data_factory->priv->connections;
+       array = g_hash_table_lookup (connections, name);
+
+       if (array != NULL) {
+               if (backend != NULL) {
+                       removed = g_ptr_array_remove_fast (array, backend);
+               } else if (array->len > 0) {
+                       g_ptr_array_set_size (array, 0);
+                       removed = TRUE;
+               }
+
+               if (array->len == 0)
+                       g_hash_table_remove (connections, name);
+
+               if (g_hash_table_size (connections) == 0)
+                       e_dbus_server_release (E_DBUS_SERVER (data_factory));
+       }
+
+       g_mutex_unlock (&data_factory->priv->connections_lock);
+
+       return removed;
+}
+
+static void
+data_factory_connections_remove_all (EDataFactory *data_factory)
+{
+       GHashTable *connections;
+
+       g_mutex_lock (&data_factory->priv->connections_lock);
+
+       connections = data_factory->priv->connections;
+
+       if (g_hash_table_size (connections) > 0) {
+               g_hash_table_remove_all (connections);
+               e_dbus_server_release (E_DBUS_SERVER (data_factory));
+       }
+
+       g_mutex_unlock (&data_factory->priv->connections_lock);
+}
+
+static void
+data_factory_closed_cb (EBackend *backend,
+                        const gchar *sender,
+                        EDataFactory *data_factory)
+{
+       data_factory_connections_remove (data_factory, sender, backend);
+}
+
+static void
+data_factory_name_vanished_cb (GDBusConnection *connection,
+                              const gchar *name,
+                              gpointer user_data)
+{
+       GWeakRef *weak_ref = user_data;
+       EDataFactory *data_factory;
+
+       data_factory = g_weak_ref_get (weak_ref);
+
+       if (data_factory != NULL) {
+               data_factory_connections_remove (data_factory, name, NULL);
+
+               /* Unwatching the bus name from here will corrupt the
+                * 'name' argument, and possibly also the 'user_data'.
+                *
+                * This is a GDBus bug.  Work around it by unwatching
+                * the bus name last.
+                *
+                * See: https://bugzilla.gnome.org/706088
+                */
+               g_mutex_lock (&data_factory->priv->watched_names_lock);
+               g_hash_table_remove (data_factory->priv->watched_names, name);
+               g_mutex_unlock (&data_factory->priv->watched_names_lock);
+
+               g_object_unref (data_factory);
+       }
+}
+
+static void
+data_factory_watched_names_add (EDataFactory *data_factory,
+                               GDBusConnection *connection,
+                               const gchar *name)
+{
+       GHashTable *watched_names;
+
+       g_return_if_fail (name != NULL);
+
+       g_mutex_lock (&data_factory->priv->watched_names_lock);
+
+       watched_names = data_factory->priv->watched_names;
+
+       if (!g_hash_table_contains (watched_names, name)) {
+               guint watcher_id;
+
+               /* The g_bus_watch_name() documentation says one of the two
+                * callbacks are guaranteed to be invoked after calling the
+                * function.  But which one is determined asynchronously so
+                * there should be no chance of the name vanished callback
+                * deadlocking with us when it tries to acquire the lock. */
+               watcher_id = g_bus_watch_name_on_connection (
+                       connection, name,
+                       G_BUS_NAME_WATCHER_FLAGS_NONE,
+                       (GBusNameAppearedCallback) NULL,
+                       data_factory_name_vanished_cb,
+                       e_weak_ref_new (data_factory),
+                       (GDestroyNotify) e_weak_ref_free);
+
+               g_hash_table_insert (
+                       watched_names, g_strdup (name),
+                       GUINT_TO_POINTER (watcher_id));
+       }
+
+       g_mutex_unlock (&data_factory->priv->watched_names_lock);
+}
+
+static void
+data_factory_bus_name_lost (EDBusServer *server,
+                           GDBusConnection *connection)
+{
+       EDataFactory *data_factory;
+
+       data_factory = E_DATA_FACTORY (server);
+
+       data_factory_connections_remove_all (data_factory);
+
+       /* Chain up to parent's bus_name_lost() method. */
+       E_DBUS_SERVER_CLASS (e_data_factory_parent_class)->
+               bus_name_lost (server, connection);
+}
+
+static void
+data_factory_quit_server (EDBusServer *server,
+                         EDBusServerExitCode exit_code)
+{
+       GDBusInterfaceSkeleton *skeleton_interface;
+       EDataFactoryClass *class;
+
+       class = E_DATA_FACTORY_GET_CLASS (E_DATA_FACTORY (server));
+
+       skeleton_interface = class->get_dbus_interface_skeleton (server);
+       g_dbus_interface_skeleton_unexport (skeleton_interface);
+
+       /* This factory does not support reloading, so stop the signal
+        * emission and return without chaining up to prevent quitting. */
+       if (exit_code == E_DBUS_SERVER_EXIT_RELOAD) {
+               g_signal_stop_emission_by_name (server, "quit-server");
+               return;
+       }
+
+       /* Chain up to parent's quit_server() method. */
+       E_DBUS_SERVER_CLASS (e_data_factory_parent_class)->
+               quit_server (server, exit_code);
+}
+
+static void
+e_data_factory_get_property (GObject *object,
+                            guint property_id,
+                            GValue *value,
+                            GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_REGISTRY:
+                       g_value_set_object (
+                               value,
+                               e_data_factory_get_registry (
+                               E_DATA_FACTORY (object)));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
 data_factory_dispose (GObject *object)
 {
+       EDataFactory *data_factory;
        EDataFactoryPrivate *priv;
 
-       priv = E_DATA_FACTORY_GET_PRIVATE (object);
+       data_factory = E_DATA_FACTORY (object);
+       priv = data_factory->priv;
 
        g_hash_table_remove_all (priv->backends);
        g_hash_table_remove_all (priv->backend_factories);
 
+       g_clear_object (&priv->registry);
+
+       g_hash_table_remove_all (priv->connections);
+       g_hash_table_remove_all (priv->watched_names);
+
        /* Chain up to parent's dispose() method. */
        G_OBJECT_CLASS (e_data_factory_parent_class)->dispose (object);
 }
@@ -92,15 +379,23 @@ data_factory_dispose (GObject *object)
 static void
 data_factory_finalize (GObject *object)
 {
+       EDataFactory *data_factory;
        EDataFactoryPrivate *priv;
 
-       priv = E_DATA_FACTORY_GET_PRIVATE (object);
+       data_factory = E_DATA_FACTORY (object);
+       priv = data_factory->priv;
 
        g_mutex_clear (&priv->mutex);
 
        g_hash_table_destroy (priv->backends);
        g_hash_table_destroy (priv->backend_factories);
 
+       g_hash_table_destroy (priv->connections);
+       g_mutex_clear (&priv->connections_lock);
+
+       g_hash_table_destroy (priv->watched_names);
+       g_mutex_clear (&priv->watched_names_lock);
+
        /* Chain up to parent's finalize() method. */
        G_OBJECT_CLASS (e_data_factory_parent_class)->finalize (object);
 }
@@ -109,11 +404,11 @@ static void
 data_factory_constructed (GObject *object)
 {
        EDataFactoryClass *class;
-       EDataFactoryPrivate *priv;
+       EDataFactory *data_factory;
        GList *list, *link;
 
-       class = E_DATA_FACTORY_GET_CLASS (object);
-       priv = E_DATA_FACTORY_GET_PRIVATE (object);
+       data_factory = E_DATA_FACTORY (object);
+       class = E_DATA_FACTORY_GET_CLASS (data_factory);
 
        /* Chain up to parent's constructed() method. */
        G_OBJECT_CLASS (e_data_factory_parent_class)->constructed (object);
@@ -132,7 +427,7 @@ data_factory_constructed (GObject *object)
 
                if (hash_key != NULL) {
                        g_hash_table_insert (
-                               priv->backend_factories,
+                               data_factory->priv->backend_factories,
                                g_strdup (hash_key),
                                g_object_ref (backend_factory));
                        g_debug (
@@ -145,20 +440,59 @@ data_factory_constructed (GObject *object)
        g_list_free (list);
 }
 
+static gboolean
+data_factory_initable_init (GInitable *initable,
+                           GCancellable *cancellable,
+                           GError **error)
+{
+       EDataFactory *data_factory;
+
+       data_factory = E_DATA_FACTORY (initable);
+
+       data_factory->priv->registry = e_source_registry_new_sync (
+               cancellable, error);
+
+       return (data_factory->priv->registry != NULL);
+}
+
+static void
+e_data_factory_initable_init (GInitableIface *iface)
+{
+       iface->init = data_factory_initable_init;
+}
+
 static void
 e_data_factory_class_init (EDataFactoryClass *class)
 {
        GObjectClass *object_class;
+       EDBusServerClass *dbus_server_class;
 
        g_type_class_add_private (class, sizeof (EDataFactoryPrivate));
 
        object_class = G_OBJECT_CLASS (class);
+       object_class->get_property = e_data_factory_get_property;
        object_class->dispose = data_factory_dispose;
        object_class->finalize = data_factory_finalize;
        object_class->constructed = data_factory_constructed;
 
        class->backend_factory_type = E_TYPE_BACKEND_FACTORY;
 
+       dbus_server_class = E_DBUS_SERVER_CLASS (class);
+       dbus_server_class->bus_acquired = data_factory_bus_acquired;
+       dbus_server_class->bus_name_lost = data_factory_bus_name_lost;
+       dbus_server_class->quit_server = data_factory_quit_server;
+
+       g_object_class_install_property (
+               object_class,
+               PROP_REGISTRY,
+               g_param_spec_object (
+                       "registry",
+                       "Registry",
+                       "Data source registry",
+                       E_TYPE_SOURCE_REGISTRY,
+                       G_PARAM_READABLE |
+                       G_PARAM_STATIC_STRINGS));
+
        /**
         * EDataFactory::backend-created:
         * @data_factory: the #EDataFactory which emitted the signal
@@ -186,6 +520,8 @@ e_data_factory_init (EDataFactory *data_factory)
        data_factory->priv = E_DATA_FACTORY_GET_PRIVATE (data_factory);
 
        g_mutex_init (&data_factory->priv->mutex);
+       g_mutex_init (&data_factory->priv->connections_lock);
+       g_mutex_init (&data_factory->priv->watched_names_lock);
 
        data_factory->priv->backends = g_hash_table_new_full (
                (GHashFunc) g_str_hash,
@@ -198,6 +534,18 @@ e_data_factory_init (EDataFactory *data_factory)
                (GEqualFunc) g_str_equal,
                (GDestroyNotify) g_free,
                (GDestroyNotify) g_object_unref);
+
+       data_factory->priv->connections = g_hash_table_new_full (
+               (GHashFunc) g_str_hash,
+               (GEqualFunc) g_str_equal,
+               (GDestroyNotify) g_free,
+               (GDestroyNotify) g_ptr_array_unref);
+
+       data_factory->priv->watched_names = g_hash_table_new_full (
+               (GHashFunc) g_str_hash,
+               (GEqualFunc) g_str_equal,
+               (GDestroyNotify) g_free,
+               (GDestroyNotify) watched_names_value_free);
 }
 
 /**
@@ -205,6 +553,7 @@ e_data_factory_init (EDataFactory *data_factory)
  * @data_factory: an #EDataFactory
  * @hash_key: hash key for an #EBackendFactory
  * @source: an #ESource
+ * @error: return location for a #GError, or %NULL
  *
  * Returns either a newly-created or existing #EBackend for #ESource.
  * The returned #EBackend is referenced for thread-safety and must be
@@ -374,3 +723,266 @@ e_data_factory_ref_backend_factory (EDataFactory *data_factory,
        return backend_factory;
 }
 
+static void
+data_factory_toggle_notify_cb (gpointer data,
+                              GObject *backend,
+                              gboolean is_last_ref)
+{
+       if (is_last_ref) {
+               /* Take a strong reference before removing the
+                * toggle reference, to keep the backend alive. */
+               g_object_ref (backend);
+
+               g_object_remove_toggle_ref (
+                       backend, data_factory_toggle_notify_cb, data);
+
+               g_signal_emit_by_name (backend, "shutdown");
+
+               g_object_unref (backend);
+       }
+}
+
+/**
+ * e_data_factory_get_registry:
+ * @data_factory: an #EDataFactory
+ * @backend: an #EBackend
+ *
+ * Install a toggle reference on the backend, that can receive a signal to
+ * shutdown once all client connections are closed.
+ *
+ * Since: 3.14
+ **/
+void
+e_data_factory_set_backend_callbacks (EDataFactory *data_factory,
+                                     EBackend *backend)
+{
+       g_return_if_fail (E_IS_DATA_FACTORY (data_factory));
+       g_return_if_fail (data_factory != NULL);
+       g_return_if_fail (backend != NULL);
+
+       /* Install a toggle reference on the backend
+        * so we can signal it to shut down once all
+        * client connections are closed.
+        */
+       g_object_add_toggle_ref (
+               G_OBJECT (backend),
+               data_factory_toggle_notify_cb,
+               NULL);
+
+       g_signal_connect_object (
+               backend, "closed",
+               G_CALLBACK (data_factory_closed_cb),
+               data_factory, 0);
+}
+
+/**
+ * e_data_factory_get_registry:
+ * @data_factory: an #EDataFactory
+ *
+ * Returns the #ESourceRegistry owned by @data_factory.
+ *
+ * Returns: the #ESourceRegistry
+ *
+ * Since: 3.14
+ **/
+ESourceRegistry *
+e_data_factory_get_registry (EDataFactory *data_factory)
+{
+       g_return_val_if_fail (E_IS_DATA_FACTORY (data_factory), NULL);
+
+       return data_factory->priv->registry;
+}
+
+/**
+ * e_data_factory_list_backends:
+ * @data_factory: an #EDataFactory
+ *
+ * Returns a list of backends connected to the @data_factory
+ *
+ * Returns: a #GSList of backends connected to the @data_factory.
+ *          The list should be freed using
+ *          g_slist_free_full (backends, g_object_unref)
+ *
+ * Since: 3.14
+ **/
+GSList *
+e_data_factory_list_backends (EDataFactory *data_factory)
+{
+       GSList *backends = NULL;
+       GHashTable *connections;
+       GHashTableIter iter;
+       gpointer key, value;
+
+       g_return_val_if_fail (E_IS_DATA_FACTORY (data_factory), NULL);
+
+       g_mutex_lock (&data_factory->priv->connections_lock);
+
+       connections = data_factory->priv->connections;
+
+       g_hash_table_iter_init (&iter, connections);
+       while (g_hash_table_iter_next (&iter, &key, &value)) {
+               GPtrArray *array = value;
+               gint ii;
+
+               for (ii = 0; ii < array->len; ii++) {
+                       EBackend *backend = g_ptr_array_index (array, ii);
+                       backends = g_slist_prepend (backends, g_object_ref (backend));
+               }
+       }
+
+       backends = g_slist_reverse (backends);
+       g_mutex_unlock (&data_factory->priv->connections_lock);
+
+       return backends;
+}
+
+static EBackend *
+data_factory_ref_backend (EDataFactory *data_factory,
+                         ESource *source,
+                         const gchar *extension_name,
+                         GError **error)
+{
+       EBackend *backend;
+       ESourceBackend *extension;
+       gchar *backend_name;
+       gchar *hash_key = NULL;
+
+       g_return_val_if_fail (E_IS_DATA_FACTORY (data_factory), NULL);
+
+       extension = e_source_get_extension (source, extension_name);
+       backend_name = e_source_backend_dup_backend_name (extension);
+
+       if (backend_name == NULL || *backend_name == '\0') {
+               g_set_error (
+                       error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+                       _("No backend name in source '%s'"),
+                       e_source_get_display_name (source));
+               g_free (backend_name);
+               return NULL;
+       }
+
+       hash_key = g_strdup_printf ("%s:%s", backend_name, extension_name);
+
+       backend = e_data_factory_ref_initable_backend (
+               data_factory,
+               hash_key,
+               source,
+               NULL,
+               error);
+
+       g_free (hash_key);
+       g_free (backend_name);
+
+       return backend;
+}
+
+/**
+ * e_data_factory_construct_path:
+ * @data_factory: an #EDataFactory
+ *
+ * Returns a new and unique object path for a D-Bus interface based
+ * in the data object path prefix of the @data_factory
+ *
+ * Returns: a newly allocated string, representing the object path for
+ *          the D-Bus interface.
+ *
+ * Since: 3.14
+ **/
+gchar *
+e_data_factory_construct_path (EDataFactory *data_factory)
+{
+       EDataFactoryClass *class;
+       static volatile gint counter = 1;
+
+       g_return_val_if_fail (E_IS_DATA_FACTORY (data_factory), NULL);
+
+       g_atomic_int_inc (&counter);
+
+       class = E_DATA_FACTORY_GET_CLASS (data_factory);
+       g_return_val_if_fail (class->data_object_path_prefix != NULL, NULL);
+
+       return g_strdup_printf (
+               "%s/%d/%u",
+               class->data_object_path_prefix, getpid (), counter);
+}
+
+/**
+ * e_data_factory_open_backend:
+ * @data_factory: an #EDataFactory
+ * @connection: a #GDBusConnection
+ * @sender: a string
+ * @uid: UID of an #ESource to open
+ * @extension_name: an extension name
+ * @error: return location for a #GError, or %NULL
+ *
+ * Returns the #EBackend data D-Bus object path
+ *
+ * Returns: a newly allocated string that represents the #EBackend
+ *          data D-Bus object path.
+ *
+ * Since: 3.14
+ **/
+gchar *
+e_data_factory_open_backend (EDataFactory *data_factory,
+                            GDBusConnection *connection,
+                            const gchar *sender,
+                            const gchar *uid,
+                            const gchar *extension_name,
+                            GError **error)
+{
+       EDataFactoryClass *class;
+       EBackend *backend;
+       ESourceRegistry *registry;
+       ESource *source;
+       gchar *object_path;
+
+       g_return_val_if_fail (E_IS_DATA_FACTORY (data_factory), NULL);
+
+       if (uid == NULL || *uid == '\0') {
+               g_set_error (
+                       error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+                       _("Missing source UID"));
+               return NULL;
+       }
+
+       registry = e_data_factory_get_registry (data_factory);
+       source = e_source_registry_ref_source (registry, uid);
+
+       if (source == NULL) {
+               g_set_error (
+                       error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+                       _("No such source for UID '%s'"), uid);
+               return NULL;
+       }
+
+       backend = data_factory_ref_backend (
+               data_factory, source, extension_name, error);
+
+       g_object_unref (source);
+
+       if (backend == NULL)
+               return NULL;
+
+       class = E_DATA_FACTORY_GET_CLASS (data_factory);
+       object_path = class->data_open (
+               data_factory, backend, connection, error);
+
+       if (object_path != NULL) {
+               /* Watch the sender's bus name so we can clean
+                * up its connections if the bus name vanishes.
+                */
+               data_factory_watched_names_add (
+                       data_factory, connection, sender);
+
+               /* A client my create multiple Eclient instances for the
+                * same ESource, each of which calls close() individually.
+                * So we must track each and every connection made.
+                */
+               data_factory_connections_add (
+                       data_factory, sender, backend);
+       }
+
+       g_clear_object (&backend);
+
+       return object_path;
+}
diff --git a/libebackend/e-data-factory.h b/libebackend/e-data-factory.h
index 88d0213..676be07 100644
--- a/libebackend/e-data-factory.h
+++ b/libebackend/e-data-factory.h
@@ -68,10 +68,23 @@ struct _EDataFactoryClass {
 
        GType backend_factory_type;
 
+       const gchar *factory_object_path;
+       const gchar *data_object_path_prefix;
+
        /* Signals */
        void            (*backend_created)      (EDataFactory *data_factory,
                                                 EBackend *backend);
 
+       /* Virtual methods */
+       GDBusInterfaceSkeleton *
+                       (*get_dbus_interface_skeleton)
+                                               (EDBusServer *server);
+
+       gchar *         (*data_open)            (EDataFactory *data_factory,
+                                                EBackend *backend,
+                                                GDBusConnection *connection,
+                                                GError **error);
+
        gpointer reserved[15];
 };
 
@@ -89,6 +102,19 @@ EBackendFactory *
                e_data_factory_ref_backend_factory
                                                (EDataFactory *data_factory,
                                                 const gchar *hash_key);
+ESourceRegistry *
+               e_data_factory_get_registry     (EDataFactory *data_factory);
+GSList *       e_data_factory_list_backends    (EDataFactory *data_factory);
+gchar *                e_data_factory_open_backend     (EDataFactory *data_factory,
+                                                GDBusConnection *connection,
+                                                const gchar *sender,
+                                                const gchar *uid,
+                                                const gchar *extension_name,
+                                                GError **error);
+gchar *                e_data_factory_construct_path   (EDataFactory *data_factory);
+void           e_data_factory_set_backend_callbacks
+                                               (EDataFactory *data_factory,
+                                                EBackend *backend);
 
 G_END_DECLS
 
diff --git a/libebackend/e-source-registry-server.c b/libebackend/e-source-registry-server.c
index 7525b8b..6ab0c9d 100644
--- a/libebackend/e-source-registry-server.c
+++ b/libebackend/e-source-registry-server.c
@@ -85,8 +85,6 @@ struct _ESourceRegistryServerPrivate {
        GMutex auth_lock;
        GHashTable *running_auths;
        GHashTable *waiting_auths;
-
-       guint authentication_count;
 };
 
 struct _AuthRequest {
@@ -684,7 +682,6 @@ source_registry_server_authenticate_cb (EDBusSourceManager *dbus_interface,
        GDBusConnection *connection;
        EAuthenticationSession *session;
        ESourceAuthenticator *authenticator;
-       const gchar *base_object_path;
        const gchar *sender;
        gchar *auth_object_path;
        GError *error = NULL;
@@ -693,13 +690,11 @@ source_registry_server_authenticate_cb (EDBusSourceManager *dbus_interface,
         * effectively starts a new authentication session with the
         * method caller. */
 
-       base_object_path = E_SOURCE_REGISTRY_SERVER_OBJECT_PATH;
        connection = g_dbus_method_invocation_get_connection (invocation);
        sender = g_dbus_method_invocation_get_sender (invocation);
 
-       auth_object_path = g_strdup_printf (
-               "%s/auth_%u", base_object_path,
-               server->priv->authentication_count++);
+       auth_object_path = e_data_factory_construct_path (
+               E_DATA_FACTORY (server));
 
        authenticator = e_authentication_mediator_new (
                connection, auth_object_path, sender, &error);
@@ -1102,24 +1097,12 @@ source_registry_server_bus_acquired (EDBusServer *server,
                                      GDBusConnection *connection)
 {
        ESourceRegistryServerPrivate *priv;
-       GError *error = NULL;
 
        priv = E_SOURCE_REGISTRY_SERVER_GET_PRIVATE (server);
 
        g_dbus_object_manager_server_set_connection (
                priv->object_manager, connection);
 
-       g_dbus_interface_skeleton_export (
-               G_DBUS_INTERFACE_SKELETON (priv->source_manager),
-               connection, E_SOURCE_REGISTRY_SERVER_OBJECT_PATH, &error);
-
-       /* Terminate the server if we can't export the interface. */
-       if (error != NULL) {
-               g_warning ("%s: %s", G_STRFUNC, error->message);
-               e_dbus_server_quit (server, E_DBUS_SERVER_EXIT_NORMAL);
-               g_error_free (error);
-       }
-
        /* Chain up to parent's bus_acquired() method. */
        E_DBUS_SERVER_CLASS (e_source_registry_server_parent_class)->
                bus_acquired (server, connection);
@@ -1140,9 +1123,6 @@ source_registry_server_quit_server (EDBusServer *server,
        g_dbus_object_manager_server_set_connection (
                priv->object_manager, NULL);
 
-       g_dbus_interface_skeleton_unexport (
-               G_DBUS_INTERFACE_SKELETON (priv->source_manager));
-
        /* Chain up to parent's quit_server() method. */
        E_DBUS_SERVER_CLASS (e_source_registry_server_parent_class)->
                quit_server (server, code);
@@ -1250,6 +1230,16 @@ source_registry_server_any_true (GSignalInvocationHint *ihint,
        return TRUE;
 }
 
+static GDBusInterfaceSkeleton *
+source_registry_server_get_dbus_interface_skeleton (EDBusServer *server)
+{
+       ESourceRegistryServerPrivate *priv;
+
+       priv = E_SOURCE_REGISTRY_SERVER_GET_PRIVATE (server);
+
+       return G_DBUS_INTERFACE_SKELETON (priv->source_manager);
+}
+
 static void
 e_source_registry_server_class_init (ESourceRegistryServerClass *class)
 {
@@ -1276,10 +1266,12 @@ e_source_registry_server_class_init (ESourceRegistryServerClass *class)
        dbus_server_class->module_directory = modules_directory;
        dbus_server_class->bus_acquired = source_registry_server_bus_acquired;
        dbus_server_class->quit_server = source_registry_server_quit_server;
-
        data_factory_class = E_DATA_FACTORY_CLASS (class);
        backend_factory_type = E_TYPE_COLLECTION_BACKEND_FACTORY;
        data_factory_class->backend_factory_type = backend_factory_type;
+       data_factory_class->factory_object_path = E_SOURCE_REGISTRY_SERVER_OBJECT_PATH;
+       data_factory_class->data_object_path_prefix = E_SOURCE_REGISTRY_SERVER_OBJECT_PATH;
+       data_factory_class->get_dbus_interface_skeleton = source_registry_server_get_dbus_interface_skeleton;
 
        class->source_added = source_registry_server_source_added;
        class->source_removed = source_registry_server_source_removed;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]