[folks] Make BackendStore.load_backends() unload disabled backends.
- From: Travis Reitter <treitter src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [folks] Make BackendStore.load_backends() unload disabled backends.
- Date: Thu, 4 Nov 2010 00:41:14 +0000 (UTC)
commit 037df05b1a069d484ff84de2aa5f86b3da259f1f
Author: Travis Reitter <travis reitter collabora co uk>
Date: Tue Nov 2 09:53:29 2010 -0700
Make BackendStore.load_backends() unload disabled backends.
This also allows this function to be called multiple times (previously,
subsequent calls were ignored).
Fixes bgo#629081.
NEWS | 8 ++
backends/key-file/kf-backend.vala | 16 +++
backends/telepathy/tp-backend.vala | 27 ++++++
folks/backend-store.vala | 183 +++++++++++++++++++++++-------------
folks/backend.vala | 16 +++
tests/folks/backend-loading.vala | 131 ++++++++++++++++++++++++++
6 files changed, 316 insertions(+), 65 deletions(-)
---
diff --git a/NEWS b/NEWS
index faa4af4..6d558be 100644
--- a/NEWS
+++ b/NEWS
@@ -1,10 +1,18 @@
Overview of changes from libfolks 0.3.1 to libfolks 0.3.2
==========================================================
+Major changes:
+* BackendStore.load_backends() now (un)loads backends which have been
+ (dis|en)abled since the last call
+
API changes:
* Added BackendStore.prepare() and BackendStore::is-prepared
* Add BackendStore.enable_backend().
* Add BackendStore.disable_backend().
+* Add BackendStore.unprepare().
+
+Bugs fixed:
+* Bug 629081 â?? Add API to allow specific backends to be disabled
Overview of changes from libfolks 0.3.0 to libfolks 0.3.1
==========================================================
diff --git a/backends/key-file/kf-backend.vala b/backends/key-file/kf-backend.vala
index b36c4f4..32ca991 100644
--- a/backends/key-file/kf-backend.vala
+++ b/backends/key-file/kf-backend.vala
@@ -110,6 +110,22 @@ public class Folks.Backends.Kf.Backend : Folks.Backend
}
}
+ /**
+ * { inheritDoc}
+ */
+ public override async void unprepare () throws GLib.Error
+ {
+ this.persona_stores.foreach ((k, v) =>
+ {
+ this.persona_store_removed ((PersonaStore) v);
+ });
+
+ this.persona_stores.remove_all ();
+
+ this._is_prepared = false;
+ this.notify_property ("is-prepared");
+ }
+
private void store_removed_cb (Folks.PersonaStore store)
{
this.persona_store_removed (store);
diff --git a/backends/telepathy/tp-backend.vala b/backends/telepathy/tp-backend.vala
index edc8df8..81ef4ab 100644
--- a/backends/telepathy/tp-backend.vala
+++ b/backends/telepathy/tp-backend.vala
@@ -98,6 +98,33 @@ public class Folks.Backends.Tp.Backend : Folks.Backend
}
}
+ /**
+ * { inheritDoc}
+ */
+ public override async void unprepare () throws GLib.Error
+ {
+ this.account_manager.account_enabled.disconnect (this.account_enabled_cb);
+ this.account_manager.account_validity_changed.disconnect (
+ this.account_validity_changed_cb);
+ this.account_manager = null;
+
+ this.persona_stores.foreach ((k, v) =>
+ {
+ this.persona_store_removed ((PersonaStore) v);
+ });
+
+ this.persona_stores.remove_all ();
+
+ this._is_prepared = false;
+ this.notify_property ("is-prepared");
+ }
+
+ private void account_validity_changed_cb (Account account, bool valid)
+ {
+ if (valid)
+ this.account_enabled_cb (account);
+ }
+
private void account_enabled_cb (Account account)
{
PersonaStore store = this.persona_stores.lookup (
diff --git a/folks/backend-store.vala b/folks/backend-store.vala
index 3649c9a..9e87510 100644
--- a/folks/backend-store.vala
+++ b/folks/backend-store.vala
@@ -39,13 +39,13 @@ public class Folks.BackendStore : Object {
[CCode (has_target = false)]
private delegate void ModuleFinalizeFunc (BackendStore store);
+ /* this contains all backends, regardless of enabled or prepared state */
private HashMap<string,Backend> backend_hash;
private HashMap<string,Backend> _prepared_backends;
private File config_file;
private GLib.KeyFile backends_key_file;
- private GLib.List<ModuleFinalizeFunc> finalize_funcs = null;
+ private HashMap<string,Module> modules;
private static weak BackendStore instance;
- private static bool _backends_loaded = false;
private bool _is_prepared = false;
/**
@@ -121,6 +121,7 @@ public class Folks.BackendStore : Object {
/* Treat this as a library init function */
Debug.set_flags (Environment.get_variable ("FOLKS_DEBUG"));
+ this.modules = new HashMap<string,unowned Module> (str_hash, str_equal);
this.backend_hash = new HashMap<string,Backend> (str_hash, str_equal);
this._prepared_backends = new HashMap<string,Backend> (str_hash,
str_equal);
@@ -128,13 +129,17 @@ public class Folks.BackendStore : Object {
~BackendStore ()
{
- /* Finalize all the loaded modules */
- foreach (ModuleFinalizeFunc func in this.finalize_funcs)
- func (this);
-
- /* reset status of backends */
- lock (this._backends_loaded)
- this._backends_loaded = false;
+ /* Finalize all the loaded modules that have finalize functions */
+ foreach (var entry in this.modules)
+ {
+ unowned Module module = entry.value;
+ void* func;
+ if (module.symbol ("module_finalize", out func))
+ {
+ ModuleFinalizeFunc module_finalize = (ModuleFinalizeFunc) func;
+ module_finalize (this);
+ }
+ }
/* manually clear the singleton instance */
instance = null;
@@ -151,13 +156,13 @@ public class Folks.BackendStore : Object {
*/
public async void prepare ()
{
+ /* (re-)load the list of disabled backends */
+ yield this.load_disabled_backend_names ();
+
if (this._is_prepared == true)
return;
this._is_prepared = true;
- /* Load the list of disabled backends */
- yield this.load_disabled_backend_names ();
-
this.notify_property ("is-prepared");
}
@@ -170,61 +175,100 @@ public class Folks.BackendStore : Object {
*/
public async void load_backends () throws GLib.Error
{
- lock (this._backends_loaded)
+ assert (Module.supported());
+
+ yield this.prepare ();
+
+ /* unload backends that have been disabled since they were loaded */
+ foreach (var backend_existing in this.backend_hash.values)
{
- if (!this._backends_loaded)
- {
- assert (Module.supported());
+ yield this.backend_unload_if_needed (backend_existing);
+ }
+
+ var path = Environment.get_variable ("FOLKS_BACKEND_DIR");
+ if (path == null)
+ {
+ path = BuildConf.BACKEND_DIR;
- if (this._is_prepared == false)
- yield this.prepare ();
+ debug ("Using built-in backend dir '%s' (override with " +
+ "environment variable FOLKS_BACKEND_DIR)", path);
+ }
+ else
+ {
+ debug ("Using environment variable FOLKS_BACKEND_DIR = " +
+ "'%s' to look for backends", path);
+ }
- var path = Environment.get_variable ("FOLKS_BACKEND_DIR");
- if (path == null)
- {
- path = BuildConf.BACKEND_DIR;
+ File dir = File.new_for_path (path);
+ assert (dir != null && yield is_dir (dir));
- debug ("Using built-in backend dir '%s' (override with " +
- "environment variable FOLKS_BACKEND_DIR)", path);
- }
- else
+ var modules = yield this.get_modules_from_dir (dir);
+
+ /* this will load any new modules found in the backends dir and will
+ * prepare and unprepare backends such that they match the state in the
+ * backend store key file */
+ foreach (var mod_entry in modules)
+ {
+ var module = (File) mod_entry.value;
+ this.load_module_from_file (module);
+ }
+
+ /* this is populated indirectly from load_module_from_file(), above */
+ foreach (var backend in this.backend_hash.values)
+ {
+ yield this.backend_load_if_needed (backend);
+ }
+ }
+
+ private async void backend_load_if_needed (Backend backend)
+ {
+ if (this.backend_is_enabled (backend.name))
+ {
+ try
+ {
+ if (!this._prepared_backends.has_key (backend.name))
{
- debug ("Using environment variable FOLKS_BACKEND_DIR = " +
- "'%s' to look for backends", path);
+ yield backend.prepare ();
+
+ debug ("New backend '%s' prepared", backend.name);
+ this._prepared_backends.set (backend.name, backend);
+ this.backend_available (backend);
}
+ }
+ catch (GLib.Error e)
+ {
+ warning ("Error preparing Backend '%s': %s", backend.name,
+ e.message);
+ }
+ }
+ }
- File dir = File.new_for_path (path);
- assert (dir != null && yield is_dir (dir));
+ private async bool backend_unload_if_needed (Backend backend)
+ {
+ bool unloaded = false;
- var modules = yield this.get_modules_from_dir (dir);
- foreach (var entry in modules)
+ if (!this.backend_is_enabled (backend.name))
+ {
+ var backend_existing = this.backend_hash.get (backend.name);
+ if (backend_existing != null)
+ {
+ try
{
- var module = (File) entry.value;
- this.load_module_from_file (module);
+ yield backend_existing.unprepare ();
}
-
- /* this is populated indirectly from load_module_from_file(),
- * above */
- foreach (var backend in this.backend_hash.values)
+ catch (GLib.Error e)
{
- try
- {
- yield backend.prepare ();
-
- debug ("New backend '%s' prepared", backend.name);
- this._prepared_backends.set (backend.name, backend);
- this.backend_available (backend);
- }
- catch (GLib.Error e)
- {
- warning ("Error preparing Backend '%s': %s", backend.name,
- e.message);
- }
+ warning ("Error unpreparing Backend '%s': %s", backend.name,
+ e.message);
}
- this._backends_loaded = true;
+ this._prepared_backends.unset (backend_existing.name);
+
+ unloaded = true;
}
}
+
+ return unloaded;
}
/**
@@ -234,12 +278,23 @@ public class Folks.BackendStore : Object {
*/
public void add_backend (Backend backend)
{
- /* Check the backend isn't disabled */
+ /* Purge any other backend with the same name; re-add if enabled */
+ var backend_existing = this.backend_hash.get (backend.name);
+ if (backend_existing != null && backend_existing != backend)
+ {
+ backend_existing.unprepare ();
+ this._prepared_backends.unset (backend_existing.name);
+ }
+
+ this.backend_hash.set (backend.name, backend);
+ }
+
+ private bool backend_is_enabled (string name)
+ {
bool enabled = true;
try
{
- enabled = this.backends_key_file.get_boolean (backend.name,
- "enabled");
+ enabled = this.backends_key_file.get_boolean (name, "enabled");
}
catch (KeyFileError e)
{
@@ -248,13 +303,12 @@ public class Folks.BackendStore : Object {
{
warning ("Couldn't check enabled state of backend '%s': %s\n" +
"Disabling backend.",
- backend.name, e.message);
+ name, e.message);
enabled = false;
}
}
- if (enabled == true)
- this.backend_hash.set (backend.name, backend);
+ return enabled;
}
/**
@@ -378,6 +432,9 @@ public class Folks.BackendStore : Object {
{
string file_path = file.get_path ();
+ if (this.modules.has_key (file_path))
+ return;
+
Module module = Module.open (file_path, ModuleFlags.BIND_LOCAL);
if (module == null)
{
@@ -390,7 +447,8 @@ public class Folks.BackendStore : Object {
void* function;
/* this causes the module to call add_backend() for its backends (adding
- * them to the backend hash) */
+ * them to the backend hash); any backends that already existed will be
+ * removed if they've since been disabled */
if (!module.symbol("module_init", out function))
{
warning ("Failed to find entry point function '%s' in '%s': %s",
@@ -404,12 +462,7 @@ public class Folks.BackendStore : Object {
ModuleInitFunc module_init = (ModuleInitFunc) function;
assert (module_init != null);
- /* It's optional for modules to have a finalize function */
- if (module.symbol ("module_finalize", out function))
- {
- ModuleFinalizeFunc module_finalize = (ModuleFinalizeFunc) function;
- this.finalize_funcs.prepend (module_finalize);
- }
+ this.modules.set (file_path, module);
/* We don't want our modules to ever unload */
module.make_resident ();
diff --git a/folks/backend.vala b/folks/backend.vala
index 696a00c..5b73602 100644
--- a/folks/backend.vala
+++ b/folks/backend.vala
@@ -105,6 +105,22 @@ public abstract class Folks.Backend : Object
*/
public abstract async void prepare () throws GLib.Error;
+ /**
+ * Revert the Backend to its pre-prepared state.
+ *
+ * This will disconnect this Backend and its dependencies from their
+ * respective services and the Backend will issue
+ * { link Backend.persona_store_removed} for each of its
+ * { link PersonaStore}s.
+ *
+ * Most users won't need to use this function.
+ *
+ * If this function throws an error, the Backend will not be functional.
+ *
+ * @since 0.3.2
+ */
+ public abstract async void unprepare () throws GLib.Error;
+
construct
{
this.persona_stores = new HashTable<string, PersonaStore> (str_hash,
diff --git a/tests/folks/backend-loading.vala b/tests/folks/backend-loading.vala
index bcea573..e666753 100644
--- a/tests/folks/backend-loading.vala
+++ b/tests/folks/backend-loading.vala
@@ -20,6 +20,7 @@ public class BackendLoadingTests : Folks.TestCase
this.add_test ("load and prep", this.test_load_and_prep);
this.add_test ("disabling", this.test_disabling);
+ this.add_test ("reloading", this.test_reloading);
}
public override void set_up ()
@@ -119,6 +120,136 @@ public class BackendLoadingTests : Folks.TestCase
GLib.error ("Failed to load backends: %s", e.message);
}
}
+
+ public void test_reloading ()
+ {
+ this.main_loop = new GLib.MainLoop (null, false);
+
+ var store = BackendStore.dup ();
+ this.test_reloading_async (store, (o, r) =>
+ {
+ this.test_reloading_async.end (r);
+ });
+
+ main_loop.run ();
+ }
+
+ private async void test_reloading_async (BackendStore store)
+ {
+ HashSet<string> backends_expected;
+
+ /*
+ * First loading
+ */
+ backends_expected = new HashSet<string> (str_hash, str_equal);
+ backends_expected.add ("key-file");
+ backends_expected.add ("telepathy");
+
+ try
+ {
+ yield store.load_backends ();
+
+ store.enabled_backends.foreach ((i) =>
+ {
+ var backend = (Backend) i;
+ assert (backends_expected.contains (backend.name));
+ backends_expected.remove (backend.name);
+ });
+
+ assert (backends_expected.size == 0);
+ }
+ catch (GLib.Error e1)
+ {
+ GLib.error ("Failed to load backends: %s", e1.message);
+ }
+
+ /*
+ * Second loading: late disabling
+ */
+ backends_expected = new HashSet<string> (str_hash, str_equal);
+ backends_expected.add ("telepathy");
+
+ /* Disable some backends */
+ yield store.disable_backend ("key-file");
+
+ /* this time we should get (all - key-file) */
+ try
+ {
+ yield store.load_backends ();
+
+ store.enabled_backends.foreach ((i) =>
+ {
+ var backend = (Backend) i;
+ assert (backends_expected.contains (backend.name));
+ backends_expected.remove (backend.name);
+ });
+
+ assert (backends_expected.size == 0);
+ }
+ catch (GLib.Error e2)
+ {
+ GLib.error ("Failed to load backends: %s", e2.message);
+ }
+
+ /*
+ * Third loading: late enabling
+ */
+ backends_expected = new HashSet<string> (str_hash, str_equal);
+ backends_expected.add ("key-file");
+ backends_expected.add ("telepathy");
+
+ /* Re-enable some backends */
+ yield store.enable_backend ("key-file");
+
+ /* this time we should get them all */
+ try
+ {
+ yield store.load_backends ();
+
+ store.enabled_backends.foreach ((i) =>
+ {
+ var backend = (Backend) i;
+ assert (backends_expected.contains (backend.name));
+ backends_expected.remove (backend.name);
+ });
+
+ assert (backends_expected.size == 0);
+ }
+ catch (GLib.Error e3)
+ {
+ GLib.error ("Failed to load backends: %s", e3.message);
+ }
+
+ /*
+ * Fourth loading: idempotency
+ */
+
+ backends_expected = new HashSet<string> (str_hash, str_equal);
+ backends_expected.add ("key-file");
+ backends_expected.add ("telepathy");
+
+ /* this time we should get them all */
+ try
+ {
+ yield store.load_backends ();
+
+ store.enabled_backends.foreach ((i) =>
+ {
+ var backend = (Backend) i;
+
+ assert (backends_expected.contains (backend.name));
+ backends_expected.remove (backend.name);
+ });
+
+ assert (backends_expected.size == 0);
+ }
+ catch (GLib.Error e4)
+ {
+ GLib.error ("Failed to load backends: %s", e4.message);
+ }
+
+ this.main_loop.quit ();
+ }
}
public int main (string[] args)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]