====== 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 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 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 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 getLoadedPlugins() const; /*! \brief Return the list of plugins that were not loaded. */ std::vector getUnloadedPlugins() const; /*! \brief Return the list of plugins that could not be loaded. */ std::vector 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 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 // STL #include 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 #include // STL #include #include 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 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 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 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 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(e); std::cerr << *err << std::endl; } return EXIT_SUCCESS; } ===== Additional References =====