[PATCH 2/3] core: Refactor the way of loading plugins



From: "Juan A. Suarez Romero" <jasuarez igalia com>

Plugins' XML information files are loaded before the plugins themselves, so the
list of plugins and their identifiers are available to the user, who can just
load a specific subset of them.

As those XML files are just loaded once, if user unloads and reload again some
or all plugins, there is no need to re-scan all the directories, as the plugins
locations are already loaded and known.

Signed-off-by: Juan A. Suarez Romero <jasuarez igalia com>
---
 src/grl-plugin-registry.c |  382 +++++++++++++++++++++++++--------------------
 src/grl-plugin-registry.h |    2 +-
 2 files changed, 215 insertions(+), 169 deletions(-)

diff --git a/src/grl-plugin-registry.c b/src/grl-plugin-registry.c
index 47ff561..cf8dfd1 100644
--- a/src/grl-plugin-registry.c
+++ b/src/grl-plugin-registry.c
@@ -77,16 +77,32 @@ struct _GrlPluginRegistryPrivate {
   GParamSpecPool *system_keys;
   GHashTable *ranks;
   GSList *plugins_dir;
+  gboolean all_plugin_info_loaded;
   struct KeyIDHandler key_id_handler;
 };
 
 static void grl_plugin_registry_setup_ranks (GrlPluginRegistry *registry);
-static void grl_plugin_registry_load_plugin_infos (GrlPluginRegistry *registry);
+
+static GList *grl_plugin_registry_load_plugin_info_directory (GrlPluginRegistry *registry,
+                                                              const gchar *path,
+                                                              GError **error);
+
+static GrlPluginInfo *grl_plugin_registry_load_plugin_info (GrlPluginRegistry *registry,
+                                                            const gchar *plugin_id,
+                                                            const gchar *file);
+
+static void grl_plugin_registry_load_plugin_info_all (GrlPluginRegistry *registry);
 
 static void key_id_handler_init (struct KeyIDHandler *handler);
-static GrlKeyID key_id_handler_get_key (struct KeyIDHandler *handler, const gchar *key_name);
-static const gchar *key_id_handler_get_name (struct KeyIDHandler *handler, GrlKeyID key);
-static GrlKeyID key_id_handler_add (struct KeyIDHandler *handler, GrlKeyID key, const gchar *key_name);
+
+static GrlKeyID key_id_handler_get_key (struct KeyIDHandler *handler,
+                                        const gchar *key_name);
+
+static const gchar *key_id_handler_get_name (struct KeyIDHandler *handler,
+                                             GrlKeyID key);
+
+static GrlKeyID key_id_handler_add (struct KeyIDHandler *handler,
+                                    GrlKeyID key, const gchar *key_name);
 
 /* ================ GrlPluginRegistry GObject ================ */
 
@@ -160,7 +176,6 @@ grl_plugin_registry_init (GrlPluginRegistry *registry)
   key_id_handler_init (&registry->priv->key_id_handler);
 
   grl_plugin_registry_setup_ranks (registry);
-  grl_plugin_registry_load_plugin_infos (registry);
 }
 
 /* ================ Utitilies ================ */
@@ -291,43 +306,133 @@ get_info_from_plugin_xml (const gchar *xml_path)
   return hash_table;
 }
 
-static void
-grl_plugin_registry_load_plugin_infos (GrlPluginRegistry *registry)
+static GrlPluginInfo *
+grl_plugin_registry_load_plugin_info (GrlPluginRegistry *registry,
+                                      const gchar* plugin_id,
+                                      const gchar *file)
 {
-  GDir *dir;
-  GError *error = NULL;
   GHashTable *info;
   GrlPluginInfo *plugin_info;
+  gchar *library_filename;
+  gchar *path;
+  gchar *plugin_name;
+
+  /* Load plugin information */
+  info = get_info_from_plugin_xml (file);
+
+  if (!info) {
+    GRL_WARNING ("Invalid information file for '%s' plugin",
+                 plugin_id);
+    return NULL;
+  }
+
+  /* Build plugin library filename */
+  plugin_name = g_hash_table_lookup (info, GRL_PLUGIN_INFO_MODULE);
+  if (!plugin_name) {
+    GRL_WARNING ("Information about '%s' plugin has no reference to module; skipping",
+                 plugin_id);
+    g_hash_table_unref (info);
+    return NULL;
+  }
+  plugin_name = g_strconcat (plugin_name, "." G_MODULE_SUFFIX, NULL);
+  path = g_path_get_dirname (file);
+  library_filename = g_build_filename (path, plugin_name, NULL);
+  g_free (plugin_name);
+  g_free (path);
+
+  /* Build Plugin Info */
+  plugin_info = g_new0 (GrlPluginInfo, 1);
+  plugin_info->id = g_strdup (plugin_id);
+  plugin_info->filename = library_filename;
+  plugin_info->optional_info = info;
+
+  /* Set rank */
+  set_plugin_rank (registry, plugin_info);
+
+  return plugin_info;
+}
+
+static GList *
+grl_plugin_registry_load_plugin_info_directory (GrlPluginRegistry *registry,
+                                                const gchar *path,
+                                                GError **error)
+{
+  GDir *dir;
+  GrlPluginInfo *plugin_info;
   const gchar *entry;
   gchar *file;
+  gchar *id;
   gchar *suffix;
+  GList *loaded_infos = NULL;
 
-  dir = g_dir_open (GRL_PLUGINS_DIR, 0, &error);
+  dir = g_dir_open (path, 0, NULL);
   if (!dir) {
-    GRL_WARNING ("Could not open plugins' info directory '%s': %s",
-                 GRL_PLUGINS_DIR,
-                 error->message);
-    g_error_free (error);
-    return;
+    GRL_WARNING ("Could not open plugin directory: '%s'", path);
+    g_set_error (error,
+                 GRL_CORE_ERROR,
+                 GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
+                 "Failed to open plugin directory '%s'", path);
+    return NULL;
   }
 
   while ((entry = g_dir_read_name (dir)) != NULL) {
     if ((suffix = g_strrstr (entry, "." GRL_PLUGIN_INFO_SUFFIX)) != NULL) {
-      file = g_build_filename (GRL_PLUGINS_DIR, entry, NULL);
-      info = get_info_from_plugin_xml (file);
-      g_free (file);
-      if (info) {
-        plugin_info = g_new0 (GrlPluginInfo, 1);
-        plugin_info->id = g_strndup (entry, suffix - entry);
-        plugin_info->optional_info = info;
-        g_hash_table_insert (registry->priv->plugin_infos,
-                             plugin_info->id,
-                             plugin_info);
+      file = g_build_filename (path, entry, NULL);
+      id = g_strndup (entry, suffix - entry);
+      /* Skip plugin info if it is already loaded */
+      if (g_hash_table_lookup (registry->priv->plugin_infos, id)) {
+        GRL_DEBUG ("Information about '%s' plugin already loaded; skipping",
+                   id);
+        g_free (id);
+        g_free (file);
+        continue;
       }
+      plugin_info = grl_plugin_registry_load_plugin_info (registry, id, file);
+      g_free (id);
+      g_free (file);
+
+      g_hash_table_insert (registry->priv->plugin_infos,
+                           plugin_info->id,
+                           plugin_info);
+      loaded_infos = g_list_append (loaded_infos, plugin_info);
     }
   }
 
   g_dir_close (dir);
+  return loaded_infos;
+}
+
+static void
+grl_plugin_registry_load_plugin_info_all (GrlPluginRegistry *registry)
+{
+  GSList *plugin_dir;
+  GList *loaded_plugins;
+
+  for (plugin_dir = registry->priv->plugins_dir;
+       plugin_dir;
+       plugin_dir = g_slist_next (plugin_dir)) {
+    loaded_plugins =
+      grl_plugin_registry_load_plugin_info_directory (registry,
+                                                      plugin_dir->data,
+                                                      NULL);
+    g_list_free (loaded_plugins);
+  }
+}
+
+static gboolean
+grl_plugin_registry_load_list (GrlPluginRegistry *registry,
+                               GList *plugin_info_list)
+{
+  GrlPluginInfo *pinfo;
+  gboolean loaded_one = FALSE;
+
+  while (plugin_info_list) {
+    pinfo = (GrlPluginInfo *) plugin_info_list->data;
+    loaded_one |= grl_plugin_registry_load (registry, pinfo->filename, NULL);
+    plugin_info_list = g_list_next (plugin_info_list);
+  }
+
+  return loaded_one;
 }
 
 static void
@@ -558,7 +663,7 @@ grl_plugin_registry_add_directory (GrlPluginRegistry *registry,
 /**
  * grl_plugin_registry_load:
  * @registry: the registry instance
- * @path: the path to the so file
+ * @library_filename: the path to the so file
  * @error: error return location or @NULL to ignore
  *
  * Loads a module from shared object file stored in @path
@@ -569,54 +674,57 @@ grl_plugin_registry_add_directory (GrlPluginRegistry *registry,
  */
 gboolean
 grl_plugin_registry_load (GrlPluginRegistry *registry,
-                          const gchar *path,
+                          const gchar *library_filename,
                           GError **error)
 {
   GModule *module;
   GrlPluginDescriptor *plugin;
   GrlPluginInfo *plugin_info;
   GList *plugin_configs;
+  gchar *dirname;
+  gchar *plugin_info_filename;
+  gchar *plugin_info_fullpathname;
 
   g_return_val_if_fail (GRL_IS_PLUGIN_REGISTRY (registry), FALSE);
 
-  module = g_module_open (path, G_MODULE_BIND_LAZY);
+  module = g_module_open (library_filename, G_MODULE_BIND_LAZY);
   if (!module) {
-    GRL_WARNING ("Failed to open module: '%s'", path);
+    GRL_WARNING ("Failed to open module: '%s'", library_filename);
     g_set_error (error,
                  GRL_CORE_ERROR,
                  GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
-                 "Failed to load plugin at '%s'", path);
+                 "Failed to load plugin at '%s'", library_filename);
     return FALSE;
   }
 
   if (!g_module_symbol (module, "GRL_PLUGIN_DESCRIPTOR", (gpointer) &plugin)) {
-    GRL_WARNING ("Did not find plugin descriptor: '%s'", path);
+    GRL_WARNING ("Did not find plugin descriptor: '%s'", library_filename);
     g_set_error (error,
                  GRL_CORE_ERROR,
                  GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
-                 "'%s' is not a valid plugin file", path);
+                 "'%s' is not a valid plugin file", library_filename);
     g_module_close (module);
     return FALSE;
   }
 
   if (!plugin->plugin_init ||
       !plugin->plugin_id) {
-    GRL_WARNING ("Plugin descriptor is not valid: '%s'", path);
+    GRL_WARNING ("Plugin descriptor is not valid: '%s'", library_filename);
     g_set_error (error,
                  GRL_CORE_ERROR,
                  GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
-                 "'%s' is not a valid plugin file", path);
+                 "'%s' is not a valid plugin file", library_filename);
     g_module_close (module);
     return FALSE;
   }
 
   /* Check if plugin is already loaded */
   if (g_hash_table_lookup (registry->priv->plugins, plugin->plugin_id)) {
-    GRL_WARNING ("Plugin is already loaded: '%s'", path);
+    GRL_WARNING ("Plugin is already loaded: '%s'", library_filename);
     g_set_error (error,
                  GRL_CORE_ERROR,
                  GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
-                 "'%s' is already loaded", path);
+                 "'%s' is already loaded", library_filename);
     g_module_close (module);
     return FALSE;
   }
@@ -624,63 +732,66 @@ grl_plugin_registry_load (GrlPluginRegistry *registry,
   plugin_info = g_hash_table_lookup (registry->priv->plugin_infos,
                                      plugin->plugin_id);
 
+  /* This happens when the user invokes directly this function: plugin
+     information has not been loaded yet */
   if (!plugin_info) {
-    plugin_info = g_new0 (GrlPluginInfo, 1);
-    plugin_info->id = plugin->plugin_id;
-    g_hash_table_insert (registry->priv->plugin_infos,
-                         plugin_info->id,
-                         plugin_info);
+    plugin_info_filename = g_strconcat (plugin->plugin_id,
+                                        "." GRL_PLUGIN_INFO_SUFFIX,
+                                        NULL);
+    dirname = g_path_get_dirname (library_filename);
+    plugin_info_fullpathname = g_build_filename (dirname,
+                                                 plugin_info_filename,
+                                                 NULL);
+    plugin_info = grl_plugin_registry_load_plugin_info (registry,
+                                                        plugin->plugin_id,
+                                                        plugin_info_filename);
+    if (!plugin_info) {
+      GRL_WARNING ("Plugin '%s' does not have XML information file '%s'",
+                   plugin->plugin_id,
+                   plugin_info_fullpathname);
+      /* Create a default one */
+      plugin_info = g_new0 (GrlPluginInfo, 1);
+      plugin_info->id = g_strdup (plugin->plugin_id);
+      plugin_info->filename = g_strdup (library_filename);
+      g_hash_table_insert (registry->priv->plugin_infos,
+                           plugin_info->id,
+                           plugin_info);
+      plugin_info->optional_info =
+        g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+      g_hash_table_insert (plugin_info->optional_info,
+                           g_strdup (GRL_PLUGIN_INFO_MODULE),
+                           g_path_get_basename (plugin_info->filename));
+
+      set_plugin_rank (registry, plugin_info);
+    }
+    g_free (plugin_info_filename);
+    g_free (dirname);
+    g_free (plugin_info_fullpathname);
   }
 
-  set_plugin_rank (registry, plugin_info);
   g_hash_table_insert (registry->priv->plugins,
                        (gpointer) plugin->plugin_id, plugin);
 
-  /* Plugin info can have a valid filename if the plugin was loaded previously
-     (and unloaded afterwards). Note that we do not free filename when plugin is
-     unloaded because this way, if user tries to load the plugin with
-     load_by_id(), it will not need to search for the plugin throughout the list
-     of plugin directories: filename will be already pointing to the right
-     filename */
-  if (plugin_info->filename) {
-    g_free (plugin_info->filename);
-  }
-  plugin_info->filename = g_strdup (path);
-
   plugin_configs = g_hash_table_lookup (registry->priv->configs,
-					plugin->plugin_id);
+                                        plugin->plugin_id);
 
   if (!plugin->plugin_init (registry, plugin_info, plugin_configs)) {
-    g_free (plugin_info->filename);
-    plugin_info->filename = NULL;
     g_hash_table_remove (registry->priv->plugins, plugin->plugin_id);
-    GRL_INFO ("Failed to initialize plugin: '%s'", path);
+    GRL_INFO ("Failed to initialize plugin: '%s'", library_filename);
     g_set_error (error,
                  GRL_CORE_ERROR,
                  GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
-                 "Failed to initialize plugin at '%s'", path);
+                 "Failed to initialize plugin at '%s'", library_filename);
     g_module_close (module);
     return FALSE;
   }
 
-  /* Insert module name as part of plugin information */
-  if (!plugin_info->optional_info) {
-    plugin_info->optional_info =
-      g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
-  }
-  if (!g_hash_table_lookup (plugin_info->optional_info,
-                            GRL_PLUGIN_INFO_MODULE)) {
-    g_hash_table_insert (plugin_info->optional_info,
-                         g_strdup (GRL_PLUGIN_INFO_MODULE),
-                         g_path_get_basename (plugin_info->filename));
-  }
-
   /* Make plugin resident */
   g_module_make_resident (module);
 
   plugin->module = module;
 
-  GRL_DEBUG ("Loaded plugin '%s' from '%s'", plugin->plugin_id, path);
+  GRL_DEBUG ("Loaded plugin '%s' from '%s'", plugin->plugin_id, library_filename);
 
   return TRUE;
 }
@@ -703,39 +814,21 @@ grl_plugin_registry_load_directory (GrlPluginRegistry *registry,
                                     const gchar *path,
                                     GError **error)
 {
-  GDir *dir;
-  gchar *file;
-  const gchar *entry;
-  gboolean loaded_one = FALSE;
+  GList *plugin_infos;
 
   g_return_val_if_fail (GRL_IS_PLUGIN_REGISTRY (registry), FALSE);
 
-  dir = g_dir_open (path, 0, NULL);
-
-  if (!dir) {
-    GRL_WARNING ("Could not open plugin directory: '%s'", path);
-    g_set_error (error,
-                 GRL_CORE_ERROR,
-                 GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
-                 "Failed to open plugin directory '%s'", path);
-    return FALSE;
-  }
-
-  while ((entry = g_dir_read_name (dir)) != NULL) {
-    if (g_str_has_suffix (entry, "." G_MODULE_SUFFIX)) {
-      file = g_build_filename (path, entry, NULL);
-      if (grl_plugin_registry_load (registry, file, NULL)) {
-        loaded_one = TRUE;
-      }
-      g_free (file);
-    }
-  }
+  /* Load plugins information */
+  plugin_infos = grl_plugin_registry_load_plugin_info_directory (registry,
+                                                                 path,
+                                                                 error);
 
-  if (!loaded_one) {
+  /* Load the plugins */
+  if (!grl_plugin_registry_load_list (registry, plugin_infos)) {
     GRL_WARNING ("No plugins loaded from directory '%s'", path);
   }
+  g_list_free (plugin_infos);
 
-  g_dir_close (dir);
   return TRUE;
 }
 
@@ -758,24 +851,28 @@ grl_plugin_registry_load_directory (GrlPluginRegistry *registry,
 gboolean
 grl_plugin_registry_load_all (GrlPluginRegistry *registry, GError **error)
 {
-  GSList *plugin_dir;
-  gboolean loaded_one = FALSE;
+  GList *all_plugin_infos;
+  gboolean loaded_one;
 
   g_return_val_if_fail (GRL_IS_PLUGIN_REGISTRY (registry), TRUE);
 
-  for (plugin_dir = registry->priv->plugins_dir;
-       plugin_dir;
-       plugin_dir = g_slist_next (plugin_dir)) {
-    if (grl_plugin_registry_load_directory (registry, plugin_dir->data, NULL)) {
-      loaded_one = TRUE;
-    }
+  /* Preload all plugin infos */
+  if (!registry->priv->all_plugin_info_loaded) {
+    grl_plugin_registry_load_plugin_info_all (registry);
+    registry->priv->all_plugin_info_loaded = TRUE;
   }
 
+  /* Now load all plugins */
+  all_plugin_infos = g_hash_table_get_values (registry->priv->plugin_infos);
+  loaded_one = grl_plugin_registry_load_list (registry, all_plugin_infos);
+
+  g_list_free (all_plugin_infos);
+
   if (!loaded_one) {
     g_set_error (error,
                  GRL_CORE_ERROR,
                  GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
-                 "All configured plugin paths are invalid. "    \
+                 "All configured plugin paths are invalid. "   \
                  "Failed to load plugins.");
   }
 
@@ -803,17 +900,21 @@ grl_plugin_registry_load_by_id (GrlPluginRegistry *registry,
                                 const gchar *plugin_id,
                                 GError **error)
 {
-  GSList *plugin_dir;
   GrlPluginInfo *plugin_info;
-  const gchar *module_name;
-  gboolean result;
-  gchar *module_filename;
-  gchar *module_fullpathname;
 
   g_return_val_if_fail (GRL_IS_PLUGIN_REGISTRY (registry), FALSE);
 
-  /* Check if there is information preloaded */
+  /* Check if there is information loaded */
   plugin_info = g_hash_table_lookup (registry->priv->plugin_infos, plugin_id);
+    /* Maybe we need to load all plugins before */
+  if (!plugin_info &&
+      !registry->priv->all_plugin_info_loaded) {
+    grl_plugin_registry_load_plugin_info_all (registry);
+    registry->priv->all_plugin_info_loaded = TRUE;
+    /* Search again */
+    plugin_info = g_hash_table_lookup (registry->priv->plugin_infos, plugin_id);
+  }
+
   if (!plugin_info) {
     GRL_WARNING ("There is no information about a plugin with id '%s'", plugin_id);
     g_set_error (error,
@@ -823,62 +924,7 @@ grl_plugin_registry_load_by_id (GrlPluginRegistry *registry,
     return FALSE;
   }
 
-  /* Check if we know the module full pathname */
-  if (plugin_info->filename) {
-    return grl_plugin_registry_load (registry, plugin_info->filename, error);
-  }
-
-  /* Check the module name */
-  if (plugin_info->optional_info) {
-    module_name = g_hash_table_lookup (plugin_info->optional_info,
-                                       GRL_PLUGIN_INFO_MODULE);
-  }
-
-  if (!module_name) {
-    GRL_WARNING ("Unknown module file for plugin with id '%s'", plugin_id);
-    g_set_error (error,
-                 GRL_CORE_ERROR,
-                 GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
-                 "Unknown module file for plugin with id '%s'", plugin_id);
-    return FALSE;
-  }
-
-  /* Check if module name is actually a full pathname */
-  if (g_path_is_absolute (module_name)) {
-    return grl_plugin_registry_load (registry, module_name, error);
-  }
-
-  if (g_str_has_suffix (module_name, "." G_MODULE_SUFFIX)) {
-    module_filename = g_strdup (module_name);
-  } else {
-    module_filename = g_strconcat (module_name, "." G_MODULE_SUFFIX, NULL);
-  }
-
-  /* Search the module in default directory paths */
-  for (plugin_dir = registry->priv->plugins_dir;
-       plugin_dir;
-       plugin_dir = g_slist_next (plugin_dir)) {
-    module_fullpathname = g_build_filename (plugin_dir->data,
-                                            module_filename,
-                                            NULL);
-    if (g_file_test (module_fullpathname, G_FILE_TEST_EXISTS)) {
-      result = grl_plugin_registry_load (registry, module_fullpathname, error);
-      g_free (module_fullpathname);
-      g_free (module_filename);
-      return result;
-    }
-    g_free (module_fullpathname);
-  }
-
-  g_free (module_filename);
-
-  GRL_WARNING ("Module file for plugin '%s' not found", plugin_id);
-  g_set_error (error,
-               GRL_CORE_ERROR,
-               GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
-               "Module file for plugin '%s' not found", plugin_id);
-
-  return FALSE;
+  return grl_plugin_registry_load (registry, plugin_info->filename, error);
 }
 
 /**
diff --git a/src/grl-plugin-registry.h b/src/grl-plugin-registry.h
index 56be7da..22ae36f 100644
--- a/src/grl-plugin-registry.h
+++ b/src/grl-plugin-registry.h
@@ -206,7 +206,7 @@ void grl_plugin_registry_add_directory (GrlPluginRegistry *registry,
                                         const gchar *path);
 
 gboolean grl_plugin_registry_load (GrlPluginRegistry *registry,
-                                   const gchar *path,
+                                   const gchar *library_filename,
                                    GError **error);
 
 gboolean grl_plugin_registry_load_directory (GrlPluginRegistry *registry,
-- 
1.7.5.4



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