TerraLib and TerraView Wiki Page

TerraLib.Core: Plugins

The class te::core::Plugins provides support for managing plugins.

A plugin is an extension of a program that adds new functionality to it, usually at runtime.

TerraLib has a framework to facilitate the management and dynamic loading of shared libraries and code scripts in languages such as Lua and Python, which need to be loaded by the applications at runtime to add a new functionality.

API

C++

The API for manage plugins in TerraLib is defined by the following classes:

  • AbstractPlugin
  • AbstractPluginEngine
  • CppPlugin
  • CppPluginProxy
  • CppPluginEngine
  • PluginEngineManager
  • PluginManager
  • PluginInfo

The AbstractPlugin class defines the interface of a plugin. Each programming language should provide a specific concrete class, which will be able to handle the details of the plugins built in that language. The C++ plugins must be created from the CppPlugin class specialization. Objects of this class will be handled by the TerraLib framework through a proxy, the CppPluginProxy class. This class (CppPluginProxy) helps to separate the actual plugin object from the manipulation of the shared library where the plugin is defined. This separation is important in the process of downloading the plugin from memory.

The AbstractPluginEngine class defines the interface of objects capable of performing the creation and loading of plugins from a set of information defined by a PluginInfo structure. Each programming language or structuring form of a plugin should provide a specific implementation of this class. This class eases how to prepare the load environment of a plugin.

The PluginInfo class is an object that is serialized from a JSON file and has attributes to model the basic information of a plugin, shown below:

  • name: an unique internal value used to identify the plugin in the system.
  • display_name: name to be displayed in the graphical interface.
  • description: brief description of the plugin.
  • version: plugin version.
  • release: the launch date of the plugin.
  • engine: the plugin engine type (C++, LUA, JAVA).
  • license_description: a brief description of the plugin license.
  • license_URL: an URL where you can find more information about the license.
  • site: an URL pointing to the plugin's website.
  • provider: information about the plugin developer.
    • name: provider name.
    • site: provider website.
    • email: provider email.
  • dependencies: a list of plugins required for the plugin to be used.
  • linked_libraries: a list of linked libraries.
  • resources: a list of features used by the plugin.
  • host_application: host system information.
    • name: system name.
    • version: system version.

The CppPluginEngine class derives from the AbstractPluginEngine class and is capable of loading dynamic libraries that have an entry point defined by the TERRALIB_PLUGIN_CALL_BACK_IMPL macro.

CppPluginEngine class:

namespace te
{
  namespace core
  {
 
    /*!
      \class CppPluginEgine
 
      \brief A plugin engine for plugins written in C++.
     */
    class CppPluginEngine : public AbstractPluginEngine
    {
      public:
 
        /*! \brief Default construtor. */
        CppPluginEngine();
 
        /*! \brief Destructor. */
        ~CppPluginEngine();
 
        /*! \brief It returns the id of the plugin engine */
        const std::string& id() const;
 
        /*! \brief It returns the name of the plugin engine */
        const std::string& name() const;
 
        /*!
          \brief It loads a cpp plugin from a give PluginInfo
 
          \exception PluginEngineLoadException If the plugin cannot be loaded.
          \return A unique_ptr of the loaded plugin.
         */
        std::unique_ptr<AbstractPlugin> load(const PluginInfo& pinfo);
 
        /*!
          \brief It unloads a cpp plugin from a give AbstractPlugin unique_ptr
 
          \exception PluginEngineUnloadException If the plugin cannot be unloaded.
         */
        void unload(std::unique_ptr<AbstractPlugin> plugin);
 
    };
 
  } // end namespace core
}   // end namespace te

The PluginEngineManager class is a singleton that will contain the record of all types of plugin loaders available in the system. The developer of new plugin loaders, classes derived from AbstractPluginEngine, should register objects in this singleton so TerraLib-based applications can load the plugins.

The PluginManager class is a singleton that will manage the registration of all the plugins, allowing the loading, downloading, initialization and finalization of the plugins.

PluginManager class:

namespace te
{
  namespace core
  {
    /*!
      \class PluginManager
      \brief A singleton for managing plugins.
 
      \note Methods in this class are not thread-safe.
     */
    class PluginManager
    {
     public:
      /*!
        \brief Return the list of plugins managed by PluginManager.
 
        \param plugins A vector to output the name of plugins managed by this
        singleton.
 
        \note The list will contain: loaded, not-loaded and broken plugins.
       */
      std::vector<std::string> getPlugins() const;
 
      /*!
        \brief Return information about a plugin identified by the given name.
 
        \param name The plugin name.
 
        \return A plugin identified by the given name.
 
        \exception te::OutOfRangeException It throws an exception if there isn't
         a plugin with the given name in the manager.
       */
      const PluginInfo& getPluginInfo(const std::string& name) const;
 
      /*! \brief Return the list of plugins that are loaded. */
      std::vector<PluginInfo> getLoadedPlugins() const;
 
      /*! \brief Return the list of plugins that were not loaded. */
      std::vector<PluginInfo> getUnloadedPlugins() const;
 
      /*! \brief Return the list of plugins that could not be loaded. */
      std::vector<PluginInfo> getBrokenPlugins() const;
 
 
      /*!
        \brief Return the list of plugins that depends of a given plugin.
 
        \param plugin_name Name of the plugin to be search.
 
        \exception OutOfRangeException if the plugin is not found.
       */
      std::vector<std::string> getDependents(const std::string plugin_name);
 
      /*! \brief Returns true if the plugin is in the broken list of plugins. */
      bool isBroken(const std::string& plugin_name) const;
 
      /*!
        \brief Returns true if the plugin is in the not-loaded list of
         plugins.
      */
      bool isUnloaded(const std::string& plugin_name) const;
 
      /*!
        \brief Returns true if the plugin is loaded otherwise returns false.
       */
      bool isLoaded(const std::string& plugin_name) const;
 
      /*!
        \brief Returns true if the plugin has been fixed and moves it to the
         unloaded list otherwise returns false.
       */
      bool isFixed(const std::string& plugin_name);
 
      /*! \brief Tells if a given plugin is registered or not. */
      bool exists(const std::string& plugin_name) const;
 
      /*!
        \brief Adds plugin with its plugin information to the list of unloaded
        plugins.
 
        \exception plugin_already_registered_error Throw an exception if a
        plugin with the same
                   name already exists and it is registered in the manager.
       */
      void insert(const PluginInfo& pinfo);
 
      /*!
        \brief Remove plugin from the manager.
 
        This method removes the plugin from the manager.
        If the plugin was loaded, unload it and remove it from the manager.
        If it was unloaded or broked, just removes it from the correct list.
        Note that all its dependents will be moved to the broken list.
 
        \param plugin_name Name of the plugin to be removed.
 
        \exception te::InvalidArgumentException If the plugin can not be removed
        an exception is raised.
 
        \note Don't change the type of parameter to a const reference! (guess?)
       */
      void remove(const std::string& plugin_name);
 
      /*!
        \brief It tries to load the informed plugin.
 
        The manager will check the plugin's dependencies before trying to load
        it.
 
        This method can be used to retry loading a broken plugin.
 
        \pre plugins must be in the manager before calling this method.
 
        \param plugin_name  The plugin to be loaded
        \param start        If true it will try to startup the plugin if false
        it doesn't automatically call plugin startup method.
 
        \exception te::plugin::exception It throws an exception if any plugin
        can not be loaded or started.
 
        \note If the plugin in the list is already loaded this method will throw
        an exception.
 
        \note If the plugin load fails this method will add it to the list of
        broken plugins and continues.
              In this case at the end an exception is thrown.
 
        \note If the plugin is in the broken list, on success load it will be
        removed from the broken list.
       */
      void load(const std::string& plugin_name, const bool start = true);
 
      /*!
        \brief Start a loaded plugin.
 
        \exception te::core::PluginStartupException It throws an exception if
        any plugin can not be started.
       */
      void start(const std::string& plugin_name);
 
      /*!
        \brief Stop a loaded plugin.
 
        \exception te::core::PluginShutdownException throws an exception if any
        plugin can not be stoped.
       */
      void stop(const std::string& plugin_name);
 
      /*!
        \brief Try to unload a given plugin.
 
        \param plugin_name The plugin to be unloaded.
 
        \exception PluginUnloadException It will raise an exception if
        plugin's code is not unloaded, if it is started or if the
        plugin's is not managed by PluginManager.
       */
      void unload(const std::string& plugin_name);
 
      /*!
        \brief Try to unload a given plugin and its dependents recursively.
 
        This method will call plugin's shutdown method if needed.
 
        \param plugin_name The plugin to be unloaded.
       */
 
      void recursiveUnload(const std::string& plugin_name);
 
      /*! \brief  Stop and unload all plugins, then clear the internal list of
       * plugins. */
      void clear();
 
      /*! \brief  Access the singleton. */
      static PluginManager& instance();
 
    };
 
  }  // end namespace core
}  // end namespace te

Examples

Here is a simple example using the functions provided by the Plugins classes:

plugin1.teplg.json:

{
    "name":"plugin1",
    "display_name":"Plugin 1",
    "description":"This is an example C++ Plugin",
    "version":"0.0.1",
    "release":"01/07/2016",
    "engine":"C++",
    "license_description":"",
    "license_URL":"",
    "site":"www.example.com/plugin1",
    "dependencies":[],
    "linked_libraries":[],
    "resources":{"shared_library_name":"terralib_example_core_plugin1"},
    "parameters":[],
    "provider":{
        "name":"example",
        "site":"www.example.com",
        "email":"example@example.com"
    },
    "host_application":{
        "name":"TerraLib",
        "version":"5.2.0"
    }
}

Plugin1.cpp:

/// TerraLib
#include <terralib/core/plugin/CppPlugin.h>
 
// STL
#include <iostream>
 
TERRALIB_CPP_PLUGIN_BEGIN(Plugin1)
 
TERRALIB_CPP_PLUGIN_STARTUP
{
  if(m_initialized)
    return;
 
  std::cout << "Plugin1 startup" << std::endl;
 
  m_initialized = true;
}
 
TERRALIB_CPP_PLUGIN_SHUTDOWN
{
  std::cout << "Plugin1 shutdown" << std::endl;
 
  m_initialized = false;
}
 
TERRALIB_CPP_PLUGIN_END(Plugin1)

main.cpp:

// TerraLib
#include <terralib/core/plugin.h>
#include <terralib/core/utils.h>
 
// STL
#include <cstdlib>
#include <iostream>
 
void WithoutPluginManager()
{
  // plugins will be loaded with C++ plugin engine
  te::core::AbstractPluginEngine& plugin_engine =
      te::core::PluginEngineManager::instance().get("C++");
 
  // find fisrt plugin manifest file, read the manifest, load plugin and start
  // it
  std::string p1_manifest_file =
      te::core::FindInTerraLibPath("example/plugins/plugin1.teplg.json");
 
  te::core::PluginInfo p1_info =
      te::core::JSONPluginInfoSerializer(p1_manifest_file);
 
  std::unique_ptr<te::core::AbstractPlugin> p1(plugin_engine.load(p1_info));
 
  p1->startup();
 
  // find second plugin manifest file, read the manifest, load plugin and start
  // it
  std::string p3_manifest_file =
      te::core::FindInTerraLibPath("example/plugins/plugin3.teplg.json");
 
  te::core::PluginInfo p3_info =
      te::core::JSONPluginInfoSerializer(p3_manifest_file);
 
  std::unique_ptr<te::core::AbstractPlugin> p3(plugin_engine.load(p3_info));
 
  p3->startup();
 
  // stop all plugins and unload them
  p3->shutdown();
  plugin_engine.unload(std::move(p3));
 
  p1->shutdown();
  plugin_engine.unload(std::move(p1));
}
 
void WithPluginManager()
{
  // Load all the config files for the plugins.
  std::vector<te::core::PluginInfo> v_pInfo;
 
  v_pInfo.push_back(te::core::JSONPluginInfoSerializer(
      te::core::FindInTerraLibPath("example/plugins/plugin1.teplg.json")));
  v_pInfo.push_back(te::core::JSONPluginInfoSerializer(
      te::core::FindInTerraLibPath("example/plugins/plugin2.teplg.json")));
  v_pInfo.push_back(te::core::JSONPluginInfoSerializer(
      te::core::FindInTerraLibPath("example/plugins/plugin3.teplg.json")));
  v_pInfo.push_back(te::core::JSONPluginInfoSerializer(
      te::core::FindInTerraLibPath("example/plugins/plugin4.teplg.json")));
 
  // Insert all the plugins stored in the vector from a given PluginInfo.
  v_pInfo = te::core::plugin::TopologicalSort(v_pInfo);
 
  for(const te::core::PluginInfo& pinfo : v_pInfo)
  {
    te::core::PluginManager::instance().insert(pinfo);
    te::core::PluginManager::instance().load(pinfo.name);
  }
 
  // get all the loaded plugins
  std::vector<te::core::PluginInfo> pVec =
      te::core::PluginManager::instance().getLoadedPlugins();
 
  // unload it in the reverse order
  for (auto plugin = pVec.rbegin(); plugin != pVec.rend(); ++plugin)
  {
    te::core::PluginManager::instance().stop(plugin->name);
    te::core::PluginManager::instance().unload(plugin->name);
  }
  te::core::PluginManager::instance().clear();
}
 
int main(int argc, char* argv[])
{
  try
  {
    te::core::plugin::InitializePluginSystem();
    WithoutPluginManager();
    WithPluginManager();
    te::core::plugin::FinalizePluginSystem();
  }
  catch(boost::exception& e)
  {
    std::string* err = boost::get_error_info<te::ErrorDescription>(e);
    std::cerr << *err << std::endl;
  }
 
  return EXIT_SUCCESS;
}

Additional References