Mercurial > hg > orthanc
diff OrthancServer/Plugins/Engine/PluginsManager.cpp @ 4044:d25f4c0fa160 framework
splitting code into OrthancFramework and OrthancServer
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Wed, 10 Jun 2020 20:30:34 +0200 |
parents | Plugins/Engine/PluginsManager.cpp@2a170a8f1faf |
children | 05b8fd21089c |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Plugins/Engine/PluginsManager.cpp Wed Jun 10 20:30:34 2020 +0200 @@ -0,0 +1,360 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "../../OrthancServer/PrecompiledHeadersServer.h" +#include "PluginsManager.h" + +#if ORTHANC_ENABLE_PLUGINS != 1 +#error The plugin support is disabled +#endif + +#include "../../Core/HttpServer/HttpOutput.h" +#include "../../Core/Logging.h" +#include "../../Core/OrthancException.h" +#include "../../Core/Toolbox.h" + +#include <cassert> +#include <memory> +#include <boost/filesystem.hpp> + +#ifdef WIN32 +#define PLUGIN_EXTENSION ".dll" +#elif defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__) || defined(__OpenBSD__) +#define PLUGIN_EXTENSION ".so" +#elif defined(__APPLE__) && defined(__MACH__) +#define PLUGIN_EXTENSION ".dylib" +#else +#error Support your platform here +#endif + + +namespace Orthanc +{ + PluginsManager::Plugin::Plugin(PluginsManager& pluginManager, + const std::string& path) : + library_(path), + pluginManager_(pluginManager) + { + memset(&context_, 0, sizeof(context_)); + context_.pluginsManager = this; + context_.orthancVersion = ORTHANC_VERSION; + context_.Free = ::free; + context_.InvokeService = InvokeService; + } + + + static void CallInitialize(SharedLibrary& plugin, + const OrthancPluginContext& context) + { + typedef int32_t (*Initialize) (const OrthancPluginContext*); + +#if defined(_WIN32) + Initialize initialize = (Initialize) plugin.GetFunction("OrthancPluginInitialize"); +#else + /** + * gcc would complain about "ISO C++ forbids casting between + * pointer-to-function and pointer-to-object" without the trick + * below, that is known as "the POSIX.1-2003 (Technical Corrigendum + * 1) workaround". See the man page of "dlsym()". + * http://www.trilithium.com/johan/2004/12/problem-with-dlsym/ + * http://stackoverflow.com/a/14543811/881731 + **/ + + Initialize initialize; + *(void **) (&initialize) = plugin.GetFunction("OrthancPluginInitialize"); +#endif + + assert(initialize != NULL); + int32_t error = initialize(&context); + + if (error != 0) + { + LOG(ERROR) << "Error while initializing plugin " << plugin.GetPath() + << " (code " << error << ")"; + throw OrthancException(ErrorCode_SharedLibrary); + } + } + + + static void CallFinalize(SharedLibrary& plugin) + { + typedef void (*Finalize) (); + +#if defined(_WIN32) + Finalize finalize = (Finalize) plugin.GetFunction("OrthancPluginFinalize"); +#else + Finalize finalize; + *(void **) (&finalize) = plugin.GetFunction("OrthancPluginFinalize"); +#endif + + assert(finalize != NULL); + finalize(); + } + + + static const char* CallGetName(SharedLibrary& plugin) + { + typedef const char* (*GetName) (); + +#if defined(_WIN32) + GetName getName = (GetName) plugin.GetFunction("OrthancPluginGetName"); +#else + GetName getName; + *(void **) (&getName) = plugin.GetFunction("OrthancPluginGetName"); +#endif + + assert(getName != NULL); + return getName(); + } + + + static const char* CallGetVersion(SharedLibrary& plugin) + { + typedef const char* (*GetVersion) (); + +#if defined(_WIN32) + GetVersion getVersion = (GetVersion) plugin.GetFunction("OrthancPluginGetVersion"); +#else + GetVersion getVersion; + *(void **) (&getVersion) = plugin.GetFunction("OrthancPluginGetVersion"); +#endif + + assert(getVersion != NULL); + return getVersion(); + } + + + OrthancPluginErrorCode PluginsManager::InvokeService(OrthancPluginContext* context, + _OrthancPluginService service, + const void* params) + { + switch (service) + { + case _OrthancPluginService_LogError: + LOG(ERROR) << reinterpret_cast<const char*>(params); + return OrthancPluginErrorCode_Success; + + case _OrthancPluginService_LogWarning: + LOG(WARNING) << reinterpret_cast<const char*>(params); + return OrthancPluginErrorCode_Success; + + case _OrthancPluginService_LogInfo: + LOG(INFO) << reinterpret_cast<const char*>(params); + return OrthancPluginErrorCode_Success; + + default: + break; + } + + Plugin* that = reinterpret_cast<Plugin*>(context->pluginsManager); + + for (std::list<IPluginServiceProvider*>::iterator + it = that->GetPluginManager().serviceProviders_.begin(); + it != that->GetPluginManager().serviceProviders_.end(); ++it) + { + try + { + if ((*it)->InvokeService(that->GetSharedLibrary(), service, params)) + { + return OrthancPluginErrorCode_Success; + } + } + catch (OrthancException& e) + { + // This service provider has failed + if (e.GetErrorCode() != ErrorCode_UnknownResource) // This error code is valid in plugins + { + LOG(ERROR) << "Exception while invoking plugin service " << service << ": " << e.What(); + } + + return static_cast<OrthancPluginErrorCode>(e.GetErrorCode()); + } + } + + LOG(ERROR) << "Plugin invoking unknown service: " << service; + return OrthancPluginErrorCode_UnknownPluginService; + } + + + PluginsManager::PluginsManager() + { + } + + PluginsManager::~PluginsManager() + { + for (Plugins::iterator it = plugins_.begin(); it != plugins_.end(); ++it) + { + if (it->second != NULL) + { + LOG(WARNING) << "Unregistering plugin '" << it->first + << "' (version " << it->second->GetVersion() << ")"; + + CallFinalize(it->second->GetSharedLibrary()); + delete it->second; + } + } + } + + + static bool IsOrthancPlugin(SharedLibrary& library) + { + return (library.HasFunction("OrthancPluginInitialize") && + library.HasFunction("OrthancPluginFinalize") && + library.HasFunction("OrthancPluginGetName") && + library.HasFunction("OrthancPluginGetVersion")); + } + + + void PluginsManager::RegisterPlugin(const std::string& path) + { + if (!boost::filesystem::exists(path)) + { + LOG(ERROR) << "Inexistent path to plugins: " << path; + return; + } + + if (boost::filesystem::is_directory(path)) + { + ScanFolderForPlugins(path, false); + return; + } + + std::unique_ptr<Plugin> plugin(new Plugin(*this, path)); + + if (!IsOrthancPlugin(plugin->GetSharedLibrary())) + { + LOG(ERROR) << "Plugin " << plugin->GetSharedLibrary().GetPath() + << " does not declare the proper entry functions"; + throw OrthancException(ErrorCode_SharedLibrary); + } + + std::string name(CallGetName(plugin->GetSharedLibrary())); + if (plugins_.find(name) != plugins_.end()) + { + LOG(ERROR) << "Plugin '" << name << "' already registered"; + throw OrthancException(ErrorCode_SharedLibrary); + } + + plugin->SetVersion(CallGetVersion(plugin->GetSharedLibrary())); + LOG(WARNING) << "Registering plugin '" << name + << "' (version " << plugin->GetVersion() << ")"; + + CallInitialize(plugin->GetSharedLibrary(), plugin->GetContext()); + + plugins_[name] = plugin.release(); + } + + + void PluginsManager::ScanFolderForPlugins(const std::string& folder, + bool isRecursive) + { + using namespace boost::filesystem; + + if (!exists(folder)) + { + return; + } + + LOG(INFO) << "Scanning folder " << folder << " for plugins"; + + directory_iterator end_it; // default construction yields past-the-end + for (directory_iterator it(folder); + it != end_it; + ++it) + { + std::string path = it->path().string(); + + if (is_directory(it->status())) + { + if (isRecursive) + { + ScanFolderForPlugins(path, true); + } + } + else + { + std::string extension = boost::filesystem::extension(it->path()); + Toolbox::ToLowerCase(extension); + + if (extension == PLUGIN_EXTENSION) + { + LOG(INFO) << "Found a shared library: " << it->path(); + + SharedLibrary plugin(path); + if (IsOrthancPlugin(plugin)) + { + RegisterPlugin(path); + } + } + } + } + } + + + void PluginsManager::ListPlugins(std::list<std::string>& result) const + { + result.clear(); + + for (Plugins::const_iterator it = plugins_.begin(); + it != plugins_.end(); ++it) + { + result.push_back(it->first); + } + } + + + bool PluginsManager::HasPlugin(const std::string& name) const + { + return plugins_.find(name) != plugins_.end(); + } + + + const std::string& PluginsManager::GetPluginVersion(const std::string& name) const + { + Plugins::const_iterator it = plugins_.find(name); + if (it == plugins_.end()) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + else + { + return it->second->GetVersion(); + } + } + + + std::string PluginsManager::GetPluginName(SharedLibrary& library) + { + return CallGetName(library); + } +}