Commit f3f1e94f authored by Fabiano Fidêncio's avatar Fabiano Fidêncio

Bug #732948 - Add backend-per-process support

With this we can have all backends running on theirs own processes, in
this way, if a backend crashes we don't have the whole factory crashing
together. Also, each instance of the EBackendFactory can decide if they
want to have all instances of the same backend (like calendar, memo list,
and task list) running in only one process (useful for evolution-ews and
evolution-mapi where we can share the connections between calendar, memo
list and task list) or have each extension running on its own process.
Apart from that, a configure option was added and, in case the user wants
to keep the old behavior, it can be disabled by passing
"--disable-backend-per-process" to the configure.

As a side effect of these changes, we are enforcing that the hash-key
used to keep track of the backend-factories will be built internally and
that *always* will follow the "backend-name:extension-name" structure,
even for ECollectionBackends.
parent 3a4f47ec
......@@ -28,6 +28,8 @@
typedef EBookBackendFactory EBookBackendFileFactory;
typedef EBookBackendFactoryClass EBookBackendFileFactoryClass;
static EModule *e_module;
/* Module Entry Points */
void e_module_load (GTypeModule *type_module);
void e_module_unload (GTypeModule *type_module);
......@@ -43,6 +45,12 @@ G_DEFINE_DYNAMIC_TYPE (
static void
e_book_backend_file_factory_class_init (EBookBackendFactoryClass *class)
{
EBackendFactoryClass *backend_factory_class;
backend_factory_class = E_BACKEND_FACTORY_CLASS (class);
backend_factory_class->e_module = e_module;
backend_factory_class->share_subprocess = FALSE;
class->factory_name = FACTORY_NAME;
class->backend_type = E_TYPE_BOOK_BACKEND_FILE;
}
......@@ -60,11 +68,13 @@ e_book_backend_file_factory_init (EBookBackendFactory *factory)
G_MODULE_EXPORT void
e_module_load (GTypeModule *type_module)
{
e_module = E_MODULE (type_module);
e_book_backend_file_factory_register_type (type_module);
}
G_MODULE_EXPORT void
e_module_unload (GTypeModule *type_module)
{
e_module = NULL;
}
......@@ -26,6 +26,8 @@
typedef EBookBackendFactory EBookBackendGoogleFactory;
typedef EBookBackendFactoryClass EBookBackendGoogleFactoryClass;
static EModule *e_module;
/* Module Entry Points */
void e_module_load (GTypeModule *type_module);
void e_module_unload (GTypeModule *type_module);
......@@ -41,6 +43,12 @@ G_DEFINE_DYNAMIC_TYPE (
static void
e_book_backend_google_factory_class_init (EBookBackendFactoryClass *class)
{
EBackendFactoryClass *backend_factory_class;
backend_factory_class = E_BACKEND_FACTORY_CLASS (class);
backend_factory_class->e_module = e_module;
backend_factory_class->share_subprocess = FALSE;
class->factory_name = FACTORY_NAME;
class->backend_type = E_TYPE_BOOK_BACKEND_GOOGLE;
}
......@@ -58,10 +66,13 @@ e_book_backend_google_factory_init (EBookBackendFactory *factory)
G_MODULE_EXPORT void
e_module_load (GTypeModule *type_module)
{
e_module = E_MODULE (type_module);
e_book_backend_google_factory_register_type (type_module);
}
G_MODULE_EXPORT void
e_module_unload (GTypeModule *type_module)
{
e_module = NULL;
}
......@@ -31,6 +31,8 @@
typedef EBookBackendFactory EBookBackendLDAPFactory;
typedef EBookBackendFactoryClass EBookBackendLDAPFactoryClass;
static EModule *e_module;
/* Module Entry Points */
void e_module_load (GTypeModule *type_module);
void e_module_unload (GTypeModule *type_module);
......@@ -46,6 +48,12 @@ G_DEFINE_DYNAMIC_TYPE (
static void
e_book_backend_ldap_factory_class_init (EBookBackendFactoryClass *class)
{
EBackendFactoryClass *backend_factory_class;
backend_factory_class = E_BACKEND_FACTORY_CLASS (class);
backend_factory_class->e_module = e_module;
backend_factory_class->share_subprocess = FALSE;
class->factory_name = FACTORY_NAME;
class->backend_type = E_TYPE_BOOK_BACKEND_LDAP;
}
......@@ -63,6 +71,8 @@ e_book_backend_ldap_factory_init (EBookBackendFactory *factory)
G_MODULE_EXPORT void
e_module_load (GTypeModule *type_module)
{
e_module = E_MODULE (type_module);
e_source_ldap_type_register (type_module);
e_book_backend_ldap_factory_register_type (type_module);
}
......@@ -70,5 +80,5 @@ e_module_load (GTypeModule *type_module)
G_MODULE_EXPORT void
e_module_unload (GTypeModule *type_module)
{
e_module = NULL;
}
......@@ -26,6 +26,8 @@
typedef EBookBackendFactory EBookBackendWebdavFactory;
typedef EBookBackendFactoryClass EBookBackendWebdavFactoryClass;
static EModule *e_module;
/* Module Entry Points */
void e_module_load (GTypeModule *type_module);
void e_module_unload (GTypeModule *type_module);
......@@ -41,6 +43,12 @@ G_DEFINE_DYNAMIC_TYPE (
static void
e_book_backend_webdav_factory_class_init (EBookBackendFactoryClass *class)
{
EBackendFactoryClass *backend_factory_class;
backend_factory_class = E_BACKEND_FACTORY_CLASS (class);
backend_factory_class->e_module = e_module;
backend_factory_class->share_subprocess = FALSE;
class->factory_name = FACTORY_NAME;
class->backend_type = E_TYPE_BOOK_BACKEND_WEBDAV;
}
......@@ -58,11 +66,13 @@ e_book_backend_webdav_factory_init (EBookBackendFactory *factory)
G_MODULE_EXPORT void
e_module_load (GTypeModule *type_module)
{
e_module = E_MODULE (type_module);
e_book_backend_webdav_factory_register_type (type_module);
}
G_MODULE_EXPORT void
e_module_unload (GTypeModule *type_module)
{
e_module = NULL;
}
......@@ -1103,18 +1103,23 @@ book_client_cursor_initable_init (GInitable *initable,
EBookClientCursor *cursor = E_BOOK_CLIENT_CURSOR (initable);
EBookClientCursorPrivate *priv = cursor->priv;
EDBusAddressBookCursor *proxy;
gchar *bus_name;
/* We only need a proxy for regular access, no need in DRA mode */
if (priv->direct_cursor)
return TRUE;
bus_name = e_client_dup_bus_name (E_CLIENT (priv->client));
proxy = e_dbus_address_book_cursor_proxy_new_sync (
priv->connection,
G_DBUS_PROXY_FLAGS_NONE,
ADDRESS_BOOK_DBUS_SERVICE_NAME,
bus_name,
priv->object_path,
cancellable, error);
g_free (bus_name);
if (!proxy)
return FALSE;
......
......@@ -876,19 +876,36 @@ book_client_view_initable_init (GInitable *initable,
GCancellable *cancellable,
GError **error)
{
EBookClient *book_client;
EBookClientViewPrivate *priv;
EGdbusBookView *gdbus_bookview;
gulong handler_id;
gchar *bus_name;
priv = E_BOOK_CLIENT_VIEW_GET_PRIVATE (initable);
book_client = g_weak_ref_get (&priv->client);
if (book_client == NULL) {
g_set_error (
error, E_CLIENT_ERROR,
E_CLIENT_ERROR_OTHER_ERROR,
_("Client disappeared"));
return FALSE;
}
bus_name = e_client_dup_bus_name (E_CLIENT (book_client));
g_object_unref (book_client);
gdbus_bookview = e_gdbus_book_view_proxy_new_sync (
priv->connection,
G_DBUS_PROXY_FLAGS_NONE,
ADDRESS_BOOK_DBUS_SERVICE_NAME,
bus_name,
priv->object_path,
cancellable, error);
g_free (bus_name);
if (gdbus_bookview == NULL)
return FALSE;
......
......@@ -974,6 +974,7 @@ book_client_init_in_dbus_thread (GSimpleAsyncResult *simple,
ESource *source;
const gchar *uid;
gchar *object_path = NULL;
gchar *bus_name = NULL;
gulong handler_id;
GError *local_error = NULL;
......@@ -1017,14 +1018,14 @@ book_client_init_in_dbus_thread (GSimpleAsyncResult *simple,
}
e_dbus_address_book_factory_call_open_address_book_sync (
factory_proxy, uid, &object_path, cancellable, &local_error);
factory_proxy, uid, &object_path, &bus_name, cancellable, &local_error);
g_object_unref (factory_proxy);
/* Sanity check. */
g_return_if_fail (
((object_path != NULL) && (local_error == NULL)) ||
((object_path == NULL) && (local_error != NULL)));
(((object_path != NULL) || (bus_name != NULL)) && (local_error == NULL)) ||
(((object_path == NULL) || (bus_name == NULL)) && (local_error != NULL)));
if (local_error != NULL) {
g_dbus_error_strip_remote_error (local_error);
......@@ -1033,13 +1034,15 @@ book_client_init_in_dbus_thread (GSimpleAsyncResult *simple,
return;
}
e_client_set_bus_name (client, bus_name);
priv->dbus_proxy = e_dbus_address_book_proxy_new_sync (
connection,
G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
ADDRESS_BOOK_DBUS_SERVICE_NAME,
object_path, cancellable, &local_error);
bus_name, object_path, cancellable, &local_error);
g_free (object_path);
g_free (bus_name);
/* Sanity check. */
g_return_if_fail (
......
......@@ -12,6 +12,7 @@ libedata_book_1_2_la_CPPFLAGS = \
-DLIBEDATA_BOOK_COMPILATION \
-DG_LOG_DOMAIN=\"libedata-book\" \
-DBACKENDDIR=\"$(ebook_backenddir)\" \
-DSUBPROCESS_BOOK_BACKEND_PATH=\"$(libexecdir)/evolution-addressbook-factory-subprocess\" \
-I$(top_srcdir) \
-I$(top_srcdir)/addressbook \
-I$(top_srcdir)/addressbook/libegdbus \
......@@ -40,6 +41,7 @@ libedata_book_1_2_la_SOURCES = \
e-data-book-direct.c \
e-data-book-factory.c \
e-data-book-view.c \
e-subprocess-book-factory.c \
ximian-vcard.h \
$(LIBDB_C_FILES) \
$(NULL)
......@@ -77,6 +79,7 @@ libedata_bookinclude_HEADERS = \
e-book-backend-cache.h \
e-book-backend-sqlitedb.h \
e-book-sqlite.h \
e-subprocess-book-factory.h \
$(LIBDB_H_FILES) \
$(NULL)
......@@ -97,6 +100,47 @@ e_book_backend_sqlitedb_test_LDADD = \
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libedata-book-$(API_VERSION).pc
libexec_PROGRAMS = evolution-addressbook-factory-subprocess
evolution_addressbook_factory_subprocess_CPPFLAGS= \
$(AM_CPPFLAGS) \
-DLOCALEDIR=\"$(localedir)\" \
-I$(top_srcdir) \
-I$(top_srcdir)/private \
-I$(top_srcdir)/addressbook \
-I$(top_builddir) \
-I$(top_builddir)/private \
-I$(top_builddir)/addressbook \
$(EVOLUTION_ADDRESSBOOK_CFLAGS) \
$(LIBSECRET_CFLAGS) \
$(FACTORY_GTK_CFLAGS) \
$(CAMEL_CFLAGS) \
$(SOUP_CFLAGS) \
$(GOA_CFLAGS) \
$(CODE_COVERAGE_CFLAGS) \
$(NULL)
evolution_addressbook_factory_subprocess_SOURCES = \
evolution-addressbook-factory-subprocess.c \
$(NULL)
evolution_addressbook_factory_subprocess_LDADD = \
$(top_builddir)/libebackend/libebackend-1.2.la \
$(top_builddir)/libedataserver/libedataserver-1.2.la \
libedata-book-1.2.la \
$(EVOLUTION_ADDRESSBOOK_LIBS) \
$(LIBSECRET_LIBS) \
$(FACTORY_GTK_LIBS) \
$(CAMEL_LIBS) \
$(SOUP_LIBS) \
$(GOA_LIBS) \
$(NULL)
evolution_addressbook_factory_subprocess_LDFLAGS = \
$(AM_LDFLAGS) \
$(CODE_COVERAGE_LDFLAGS) \
$(NULL)
DISTCLEANFILES = $(pkgconfig_DATA)
EXTRA_DIST = \
......
......@@ -42,7 +42,6 @@
#include "e-book-backend-factory.h"
#include "e-data-book.h"
#include "e-data-book-factory.h"
#include "e-dbus-localed.h"
#define d(x)
......@@ -52,16 +51,8 @@
struct _EDataBookFactoryPrivate {
EDBusAddressBookFactory *dbus_factory;
/* Watching "org.freedesktop.locale1" for locale changes */
guint localed_watch_id;
EDBusLocale1 *localed_proxy;
GCancellable *localed_cancel;
gchar *locale;
};
static GInitableIface *initable_parent_interface;
/* Forward Declarations */
static void e_data_book_factory_initable_init
(GInitableIface *iface);
......@@ -84,82 +75,27 @@ data_book_factory_get_dbus_interface_skeleton (EDBusServer *server)
return G_DBUS_INTERFACE_SKELETON (factory->priv->dbus_factory);
}
/* This is totally backwards, for some insane reason we holding
* references to the EBookBackends and then fetching the EDataBook
* via e_book_backend_ref_data_book(), the whole scheme should
* be reversed and this function probably removed.
*/
static GList *
data_book_factory_list_books (EDataBookFactory *factory)
static const gchar *
data_book_get_factory_name (EBackendFactory *backend_factory)
{
GList *books = NULL;
GSList *backends, *l;
backends = e_data_factory_list_backends (E_DATA_FACTORY (factory));
for (l = backends; l != NULL; l = g_slist_next (l)) {
EBookBackend *backend = l->data;
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);
}
EBookBackendFactoryClass *class;
g_slist_free_full (backends, g_object_unref);
class = E_BOOK_BACKEND_FACTORY_GET_CLASS (E_BOOK_BACKEND_FACTORY (backend_factory));
return books;
return class->factory_name;
}
static gchar *
data_book_factory_open (EDataFactory *data_factory,
EBackend *backend,
GDBusConnection *connection,
GError **error)
static void
data_book_complete_open (EDataFactory *data_factory,
GDBusMethodInvocation *invocation,
const gchar *object_path,
const gchar *bus_name,
const gchar *extension_name)
{
EDataBookFactory *factory = E_DATA_BOOK_FACTORY (data_factory);
EDataBook *data_book;
gchar *object_path;
/* If the backend already has an EDataBook installed, return its
* object path. Otherwise we need to install a new EDataBook. */
data_book = e_book_backend_ref_data_book (E_BOOK_BACKEND (backend));
EDataBookFactory *data_book_factory = E_DATA_BOOK_FACTORY (data_factory);
if (data_book != NULL) {
object_path = g_strdup (
e_data_book_get_object_path (data_book));
} else {
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(). */
data_book = e_data_book_new (
E_BOOK_BACKEND (backend),
connection, object_path, error);
if (data_book != NULL) {
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
*/
if (factory->priv->locale)
e_data_book_set_locale (
data_book,
factory->priv->locale,
NULL, NULL);
} else {
g_free (object_path);
object_path = NULL;
}
}
g_clear_object (&data_book);
return object_path;
e_dbus_address_book_factory_complete_open_address_book (
data_book_factory->priv->dbus_factory, invocation, object_path, bus_name);
}
static gboolean
......@@ -168,26 +104,10 @@ data_book_factory_handle_open_address_book_cb (EDBusAddressBookFactory *iface,
const gchar *uid,
EDataBookFactory *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);
EDataFactory *data_factory = E_DATA_FACTORY (factory);
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 (
iface, invocation, object_path);
g_free (object_path);
} else {
g_return_val_if_fail (error != NULL, FALSE);
g_dbus_method_invocation_take_error (invocation, error);
}
e_data_factory_spawn_subprocess_backend (
data_factory, invocation, uid, E_SOURCE_EXTENSION_ADDRESS_BOOK, SUBPROCESS_BOOK_BACKEND_PATH);
return TRUE;
}
......@@ -203,232 +123,10 @@ data_book_factory_dispose (GObject *object)
g_clear_object (&priv->dbus_factory);
if (priv->localed_cancel)
g_cancellable_cancel (priv->localed_cancel);
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);
/* Chain up to parent's dispose() method. */
G_OBJECT_CLASS (e_data_book_factory_parent_class)->dispose (object);
}
static void
data_book_factory_finalize (GObject *object)
{
EDataBookFactory *factory;
factory = E_DATA_BOOK_FACTORY (object);
g_free (factory->priv->locale);
/* Chain up to parent's finalize() method. */
G_OBJECT_CLASS (e_data_book_factory_parent_class)->finalize (object);
}
static gchar *
data_book_factory_interpret_locale_value (const gchar *value)
{
gchar *interpreted_value = NULL;
gchar **split;
split = g_strsplit (value, "=", 2);
if (split && split[0] && split[1])
interpreted_value = g_strdup (split[1]);
g_strfreev (split);
if (!interpreted_value)
g_warning ("Failed to interpret locale value: %s", value);
return interpreted_value;
}
static gchar *
data_book_factory_interpret_locale (const gchar * const * locale)
{
gint i;
gchar *interpreted_locale = NULL;
/* Prioritize LC_COLLATE and then LANG values
* in the 'locale' specified by localed.
*
* If localed explicitly specifies no locale, then
* default to checking system locale.
*/
if (locale) {
for (i = 0; locale[i] != NULL && interpreted_locale == NULL; i++) {
if (strncmp (locale[i], "LC_COLLATE", 10) == 0)
interpreted_locale =
data_book_factory_interpret_locale_value (locale[i]);
}
for (i = 0; locale[i] != NULL && interpreted_locale == NULL; i++) {
if (strncmp (locale[i], "LANG", 4) == 0)
interpreted_locale =
data_book_factory_interpret_locale_value (locale[i]);
}
}
if (!interpreted_locale) {
const gchar *system_locale = setlocale (LC_COLLATE, NULL);
interpreted_locale = g_strdup (system_locale);
}
return interpreted_locale;
}
static void
data_book_factory_set_locale (EDataBookFactory *factory,
const gchar *locale)
{
EDataBookFactoryPrivate *priv = factory->priv;
GError *error = NULL;
GList *books, *l;
if (g_strcmp0 (priv->locale, locale) != 0) {
g_free (priv->locale);
priv->locale = g_strdup (locale);
books = data_book_factory_list_books (factory);
for (l = books; l; l = l->next) {
EDataBook *book = l->data;
if (!e_data_book_set_locale (book, locale, NULL, &error)) {
g_warning (
"Failed to set locale on addressbook: %s",
error->message);
g_clear_error (&error);
}
}
g_list_free_full (books, g_object_unref);
}
}
static void
data_book_factory_locale_changed (GObject *object,
GParamSpec *pspec,
gpointer user_data)
{
EDBusLocale1 *locale_proxy = E_DBUS_LOCALE1 (object);
EDataBookFactory *factory = (EDataBookFactory *) user_data;
const gchar * const *locale;
gchar *interpreted_locale;
locale = e_dbus_locale1_get_locale (locale_proxy);
interpreted_locale = data_book_factory_interpret_locale (locale);
data_book_factory_set_locale (factory, interpreted_locale);
g_free (interpreted_locale);
}
static void
data_book_factory_localed_ready (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
EDataBookFactory *factory = (EDataBookFactory *) user_data;
GError *error = NULL;
factory->priv->localed_proxy = e_dbus_locale1_proxy_new_finish (res, &error);
if (factory->priv->localed_proxy == NULL) {
g_warning ("Error fetching localed proxy: %s", error->message);
g_error_free (error);
}
if (factory->priv->localed_cancel) {
g_object_unref (factory->priv->localed_cancel);
factory->priv->localed_cancel = NULL;
}
if (factory->priv->localed_proxy) {
g_signal_connect (
factory->priv->localed_proxy, "notify::locale",
G_CALLBACK (data_book_factory_locale_changed), factory);
/* Initial refresh of the locale */
data_book_factory_locale_changed (G_OBJECT (factory->priv->localed_proxy), NULL, factory);
}
}
static void
data_book_factory_localed_appeared (GDBusConnection *connection,
const gchar *name,
const gchar *name_owner,
gpointer user_data)
{
EDataBookFactory *factory = (EDataBookFactory *) user_data;
factory->priv->localed_cancel = g_cancellable_new ();
e_dbus_locale1_proxy_new (
connection,
G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES,
"org.freedesktop.locale1",
"/org/freedesktop/locale1",
factory->priv->localed_cancel,
data_book_factory_localed_ready,
factory);
}
static void
data_book_factory_localed_vanished (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
EDataBookFactory *factory = (EDataBookFactory *) user_data;
if (factory->priv->localed_cancel) {
g_cancellable_cancel (factory->priv->localed_cancel);
g_object_unref (factory->priv->localed_cancel);
factory->priv->localed_cancel = NULL;
}
if (factory->priv->localed_proxy) {
g_object_unref (factory->priv->localed_proxy);
factory->priv->localed_proxy = NULL;
}
}
static gboolean
data_book_factory_initable_init (GInitable *initable,
GCancellable *cancellable,
GError **error)
{
EDataBookFactory *factory;
GBusType bus_type = G_BUS_TYPE_SYSTEM;
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.
*/
if (g_getenv ("EDS_TESTING") != NULL)
bus_type = G_BUS_TYPE_SESSION;
/* Watch system bus for locale change notifications */
factory->priv->localed_watch_id =
g_bus_watch_name (
bus_type,
"org.freedesktop.locale1",
G_BUS_NAME_WATCHER_FLAGS_NONE,
data_book_factory_localed_appeared,
data_book_factory_localed_vanished,
initable,
NULL);
/* Chain up to parent interface's init() method. */
return initable_parent_interface->init (initable, cancellable, error);
}