changeset 1630:ffd23c0104af

"/system" URI gives information about the plugins used for storage area and DB back-end
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 21 Sep 2015 13:26:45 +0200
parents bad4772b605c
children 0d074f5f6069
files Core/Enumerations.cpp Core/Enumerations.h NEWS OrthancServer/OrthancRestApi/OrthancRestSystem.cpp OrthancServer/main.cpp Plugins/Engine/IPluginServiceProvider.h Plugins/Engine/OrthancPluginDatabase.cpp Plugins/Engine/OrthancPluginDatabase.h Plugins/Engine/OrthancPlugins.cpp Plugins/Engine/OrthancPlugins.h Plugins/Engine/PluginsEnumerations.cpp Plugins/Engine/PluginsManager.cpp Plugins/Engine/PluginsManager.h Plugins/Engine/SharedLibrary.cpp Plugins/Engine/SharedLibrary.h Plugins/Include/orthanc/OrthancCPlugin.h Resources/ErrorCodes.json
diffstat 17 files changed, 333 insertions(+), 167 deletions(-) [+]
line wrap: on
line diff
--- a/Core/Enumerations.cpp	Fri Sep 18 17:45:59 2015 +0200
+++ b/Core/Enumerations.cpp	Mon Sep 21 13:26:45 2015 +0200
@@ -298,6 +298,15 @@
       case ErrorCode_LuaReturnsNoString:
         return "The Lua function does not return a string";
 
+      case ErrorCode_StorageAreaAlreadyRegistered:
+        return "Another plugin has already registered a custom storage area";
+
+      case ErrorCode_DatabaseBackendAlreadyRegistered:
+        return "Another plugin has already registered a custom database back-end";
+
+      case ErrorCode_DatabasePlugin:
+        return "The plugin implementing a custom database back-end does not fulfill the proper interface";
+
       default:
         return "Unknown error code";
     }
--- a/Core/Enumerations.h	Fri Sep 18 17:45:59 2015 +0200
+++ b/Core/Enumerations.h	Mon Sep 21 13:26:45 2015 +0200
@@ -128,7 +128,10 @@
     ErrorCode_LuaAlreadyExecuted = 2032    /*!< Arguments cannot be pushed after the Lua function is executed */,
     ErrorCode_LuaBadOutput = 2033    /*!< The Lua function does not give the expected number of outputs */,
     ErrorCode_NotLuaPredicate = 2034    /*!< The Lua function is not a predicate (only true/false outputs allowed) */,
-    ErrorCode_LuaReturnsNoString = 2035    /*!< The Lua function does not return a string */
+    ErrorCode_LuaReturnsNoString = 2035    /*!< The Lua function does not return a string */,
+    ErrorCode_StorageAreaAlreadyRegistered = 2036    /*!< Another plugin has already registered a custom storage area */,
+    ErrorCode_DatabaseBackendAlreadyRegistered = 2037    /*!< Another plugin has already registered a custom database back-end */,
+    ErrorCode_DatabasePlugin = 2038    /*!< The plugin implementing a custom database back-end does not fulfill the proper interface */
   };
 
   enum LogLevel
--- a/NEWS	Fri Sep 18 17:45:59 2015 +0200
+++ b/NEWS	Mon Sep 21 13:26:45 2015 +0200
@@ -6,6 +6,7 @@
 Maintenance
 -----------
 
+* "/system" URI gives information about the plugins used for storage area and DB back-end
 * Plugin callbacks must now return explicit "OrthancPluginErrorCode" instead of integers
 
 
--- a/OrthancServer/OrthancRestApi/OrthancRestSystem.cpp	Fri Sep 18 17:45:59 2015 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestSystem.cpp	Mon Sep 21 13:26:45 2015 +0200
@@ -62,6 +62,21 @@
     result["Name"] = Configuration::GetGlobalStringParameter("Name", "");
     result["Version"] = ORTHANC_VERSION;
 
+    result["StorageAreaPlugin"] = Json::nullValue;
+    result["DatabaseBackendPlugin"] = Json::nullValue;
+
+    const OrthancPlugins& plugins = OrthancRestApi::GetContext(call).GetPlugins();
+
+    if (plugins.HasStorageArea())
+    {
+      result["StorageAreaPlugin"] = plugins.GetStorageAreaLibrary().GetPath();
+    }
+
+    if (plugins.HasDatabaseBackend())
+    {
+      result["DatabaseBackendPlugin"] = plugins.GetDatabaseBackendLibrary().GetPath();
+    }
+
     call.GetOutput().AnswerJson(result);
   }
 
--- a/OrthancServer/main.cpp	Fri Sep 18 17:45:59 2015 +0200
+++ b/OrthancServer/main.cpp	Mon Sep 21 13:26:45 2015 +0200
@@ -624,10 +624,10 @@
   LoadPlugins(plugins);
 
   IDatabaseWrapper* database = NULL;
-  if (plugins.HasDatabase())
+  if (plugins.HasDatabaseBackend())
   {
     LOG(WARNING) << "Using a custom database from plugins";
-    database = &plugins.GetDatabase();
+    database = &plugins.GetDatabaseBackend();
   }
   else
   {
--- a/Plugins/Engine/IPluginServiceProvider.h	Fri Sep 18 17:45:59 2015 +0200
+++ b/Plugins/Engine/IPluginServiceProvider.h	Mon Sep 21 13:26:45 2015 +0200
@@ -34,7 +34,7 @@
 
 #include "../Include/orthanc/OrthancCPlugin.h"
 
-#include <boost/noncopyable.hpp>
+#include "SharedLibrary.h"
 
 namespace Orthanc
 {
@@ -45,7 +45,8 @@
     {
     }
 
-    virtual bool InvokeService(_OrthancPluginService service,
+    virtual bool InvokeService(SharedLibrary& plugin,
+                               _OrthancPluginService service,
                                const void* parameters) = 0;
   };
 }
--- a/Plugins/Engine/OrthancPluginDatabase.cpp	Fri Sep 18 17:45:59 2015 +0200
+++ b/Plugins/Engine/OrthancPluginDatabase.cpp	Mon Sep 21 13:26:45 2015 +0200
@@ -113,7 +113,7 @@
     if (type_ != _OrthancPluginDatabaseAnswerType_None &&
         type_ != _OrthancPluginDatabaseAnswerType_Int64)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      throw OrthancException(ErrorCode_DatabasePlugin);
     }
 
     target.clear();
@@ -134,7 +134,7 @@
     if (type_ != _OrthancPluginDatabaseAnswerType_None &&
         type_ != _OrthancPluginDatabaseAnswerType_String)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      throw OrthancException(ErrorCode_DatabasePlugin);
     }
 
     target.clear();
@@ -164,7 +164,7 @@
     }
     else
     {
-      throw OrthancException(ErrorCode_Plugin);
+      throw OrthancException(ErrorCode_DatabasePlugin);
     }
   }
 
@@ -183,15 +183,17 @@
     }
     else
     {
-      throw OrthancException(ErrorCode_Plugin);
+      throw OrthancException(ErrorCode_DatabasePlugin);
     }
   }
 
 
-  OrthancPluginDatabase::OrthancPluginDatabase(const OrthancPluginDatabaseBackend& backend,
+  OrthancPluginDatabase::OrthancPluginDatabase(SharedLibrary& library,
+                                               const OrthancPluginDatabaseBackend& backend,
                                                const OrthancPluginDatabaseExtensions* extensions,
                                                size_t extensionsSize,
                                                void *payload) : 
+    library_(library),
     type_(_OrthancPluginDatabaseAnswerType_None),
     backend_(backend),
     payload_(payload),
@@ -333,7 +335,7 @@
       std::string value;
       if (!LookupMetadata(value, id, *it))
       {
-        throw OrthancException(ErrorCode_Plugin);
+        throw OrthancException(ErrorCode_DatabasePlugin);
       }
 
       target[*it] = value;
@@ -544,7 +546,7 @@
     
     if (!ForwardSingleAnswer(s))
     {
-      throw OrthancException(ErrorCode_Plugin);
+      throw OrthancException(ErrorCode_DatabasePlugin);
     }
 
     return s;
@@ -656,7 +658,7 @@
     if (type_ != _OrthancPluginDatabaseAnswerType_None &&
         type_ != _OrthancPluginDatabaseAnswerType_Int32)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      throw OrthancException(ErrorCode_DatabasePlugin);
     }
 
     target.clear();
@@ -687,7 +689,7 @@
     if (type_ != _OrthancPluginDatabaseAnswerType_None &&
         type_ != _OrthancPluginDatabaseAnswerType_Int32)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      throw OrthancException(ErrorCode_DatabasePlugin);
     }
 
     target.clear();
@@ -770,7 +772,7 @@
     }
     else
     {
-      throw OrthancException(ErrorCode_Plugin);
+      throw OrthancException(ErrorCode_DatabasePlugin);
     }
   }
 
@@ -889,7 +891,7 @@
     }
     else
     {
-      throw OrthancException(ErrorCode_Plugin);
+      throw OrthancException(ErrorCode_DatabasePlugin);
     }
   }
 
@@ -1072,7 +1074,7 @@
       }
 
       default:
-        throw OrthancException(ErrorCode_Plugin);
+        throw OrthancException(ErrorCode_DatabasePlugin);
     }
   }
 
@@ -1122,7 +1124,7 @@
   {
     if (answer.type == _OrthancPluginDatabaseAnswerType_None)
     {
-      throw OrthancException(ErrorCode_Plugin);
+      throw OrthancException(ErrorCode_DatabasePlugin);
     }
 
     if (answer.type == _OrthancPluginDatabaseAnswerType_DeletedAttachment ||
@@ -1177,13 +1179,13 @@
 
         default:
           LOG(ERROR) << "Unhandled type of answer for custom index plugin: " << answer.type;
-          throw OrthancException(ErrorCode_Plugin);
+          throw OrthancException(ErrorCode_DatabasePlugin);
       }
     }
     else if (type_ != answer.type)
     {
       LOG(ERROR) << "Error in the plugin protocol: Cannot change the answer type";
-      throw OrthancException(ErrorCode_Plugin);
+      throw OrthancException(ErrorCode_DatabasePlugin);
     }
 
     switch (answer.type)
@@ -1228,7 +1230,7 @@
       {
         if (answer.valueString == NULL)
         {
-          throw OrthancException(ErrorCode_Plugin);
+          throw OrthancException(ErrorCode_DatabasePlugin);
         }
 
         if (type_ == _OrthancPluginDatabaseAnswerType_None)
@@ -1238,7 +1240,7 @@
         }
         else if (type_ != _OrthancPluginDatabaseAnswerType_String)
         {
-          throw OrthancException(ErrorCode_Plugin);
+          throw OrthancException(ErrorCode_DatabasePlugin);
         }
 
         answerStrings_.push_back(std::string(answer.valueString));
@@ -1254,7 +1256,7 @@
         }
         else if (*answerDone_)
         {
-          throw OrthancException(ErrorCode_Plugin);
+          throw OrthancException(ErrorCode_DatabasePlugin);
         }
         else
         {
@@ -1280,7 +1282,7 @@
         }
         else if (*answerDone_)
         {
-          throw OrthancException(ErrorCode_Plugin);
+          throw OrthancException(ErrorCode_DatabasePlugin);
         }
         else
         {
@@ -1304,7 +1306,7 @@
 
       default:
         LOG(ERROR) << "Unhandled type of answer for custom index plugin: " << answer.type;
-        throw OrthancException(ErrorCode_Plugin);
+        throw OrthancException(ErrorCode_DatabasePlugin);
     }
   }
 }
--- a/Plugins/Engine/OrthancPluginDatabase.h	Fri Sep 18 17:45:59 2015 +0200
+++ b/Plugins/Engine/OrthancPluginDatabase.h	Mon Sep 21 13:26:45 2015 +0200
@@ -34,6 +34,7 @@
 
 #include "../../OrthancServer/IDatabaseWrapper.h"
 #include "../Include/orthanc/OrthancCDatabasePlugin.h"
+#include "SharedLibrary.h"
 
 namespace Orthanc
 {
@@ -44,6 +45,7 @@
 
     typedef std::pair<int64_t, ResourceType>  AnswerResource;
 
+    SharedLibrary&  library_;
     _OrthancPluginDatabaseAnswerType type_;
     OrthancPluginDatabaseBackend backend_;
     OrthancPluginDatabaseExtensions extensions_;
@@ -77,11 +79,17 @@
     bool ForwardSingleAnswer(int64_t& target);
 
   public:
-    OrthancPluginDatabase(const OrthancPluginDatabaseBackend& backend,
+    OrthancPluginDatabase(SharedLibrary& library,
+                          const OrthancPluginDatabaseBackend& backend,
                           const OrthancPluginDatabaseExtensions* extensions,
                           size_t extensionsSize,
                           void *payload);
 
+    const SharedLibrary& GetSharedLibrary() const
+    {
+      return library_;
+    }
+
     virtual void AddAttachment(int64_t id,
                                const FileInfo& attachment);
 
--- a/Plugins/Engine/OrthancPlugins.cpp	Fri Sep 18 17:45:59 2015 +0200
+++ b/Plugins/Engine/OrthancPlugins.cpp	Mon Sep 21 13:26:45 2015 +0200
@@ -55,6 +55,117 @@
 
 namespace Orthanc
 {
+  namespace
+  {
+    class PluginStorageArea : public IStorageArea
+    {
+    private:
+      _OrthancPluginRegisterStorageArea callbacks_;
+
+      void Free(void* buffer) const
+      {
+        if (buffer != NULL)
+        {
+          callbacks_.free(buffer);
+        }
+      }
+
+    public:
+      PluginStorageArea(const _OrthancPluginRegisterStorageArea& callbacks) : callbacks_(callbacks)
+      {
+      }
+
+
+      virtual void Create(const std::string& uuid,
+                          const void* content, 
+                          size_t size,
+                          FileContentType type)
+      {
+        OrthancPluginErrorCode error = callbacks_.create
+          (uuid.c_str(), content, size, Plugins::Convert(type));
+
+        if (error != OrthancPluginErrorCode_Success)
+        {
+          throw OrthancException(Plugins::Convert(error));
+        }
+      }
+
+
+      virtual void Read(std::string& content,
+                        const std::string& uuid,
+                        FileContentType type)
+      {
+        void* buffer = NULL;
+        int64_t size = 0;
+
+        OrthancPluginErrorCode error = callbacks_.read
+          (&buffer, &size, uuid.c_str(), Plugins::Convert(type));
+
+        if (error != OrthancPluginErrorCode_Success)
+        {
+          throw OrthancException(Plugins::Convert(error));
+        }
+
+        try
+        {
+          content.resize(static_cast<size_t>(size));
+        }
+        catch (...)
+        {
+          Free(buffer);
+          throw;
+        }
+
+        if (size > 0)
+        {
+          memcpy(&content[0], buffer, static_cast<size_t>(size));
+        }
+
+        Free(buffer);
+      }
+
+
+      virtual void Remove(const std::string& uuid,
+                          FileContentType type) 
+      {
+        OrthancPluginErrorCode error = callbacks_.remove
+          (uuid.c_str(), Plugins::Convert(type));
+
+        if (error != OrthancPluginErrorCode_Success)
+        {
+          throw OrthancException(Plugins::Convert(error));
+        }
+      }
+    };
+
+
+    class StorageAreaFactory : public boost::noncopyable
+    {
+    private:
+      SharedLibrary&   sharedLibrary_;
+      _OrthancPluginRegisterStorageArea  callbacks_;
+
+    public:
+      StorageAreaFactory(SharedLibrary& sharedLibrary,
+                         const _OrthancPluginRegisterStorageArea& callbacks) :
+        sharedLibrary_(sharedLibrary),
+        callbacks_(callbacks)
+      {
+      }
+
+      SharedLibrary&  GetSharedLibrary()
+      {
+        return sharedLibrary_;
+      }
+
+      IStorageArea* Create() const
+      {
+        return new PluginStorageArea(callbacks_);
+      }
+    };
+  }
+
+
   struct OrthancPlugins::PImpl
   {
     class RestCallback : public boost::noncopyable
@@ -105,6 +216,7 @@
       }
     };
 
+
     typedef std::pair<std::string, _OrthancPluginProperty>  Property;
     typedef std::list<RestCallback*>  RestCallbacks;
     typedef std::list<OrthancPluginOnStoredInstanceCallback>  OnStoredCallbacks;
@@ -116,8 +228,7 @@
     RestCallbacks restCallbacks_;
     OnStoredCallbacks  onStoredCallbacks_;
     OnChangeCallbacks  onChangeCallbacks_;
-    bool hasStorageArea_;
-    _OrthancPluginRegisterStorageArea storageArea_;
+    std::auto_ptr<StorageAreaFactory>  storageArea_;
     boost::recursive_mutex restCallbackMutex_;
     boost::recursive_mutex storedCallbackMutex_;
     boost::recursive_mutex changeCallbackMutex_;
@@ -129,11 +240,9 @@
 
     PImpl() : 
       context_(NULL), 
-      hasStorageArea_(false),
       argc_(1),
       argv_(NULL)
     {
-      memset(&storageArea_, 0, sizeof(storageArea_));
     }
   };
 
@@ -1102,10 +1211,11 @@
   }
         
 
-  bool OrthancPlugins::InvokeService(_OrthancPluginService service,
+  bool OrthancPlugins::InvokeService(SharedLibrary& plugin,
+                                     _OrthancPluginService service,
                                      const void* parameters)
   {
-    VLOG(1) << "Calling plugin service: " << service;
+    VLOG(1) << "Calling service " << service << " from plugin " << plugin.GetPath();
 
     boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_);
 
@@ -1261,8 +1371,15 @@
         const _OrthancPluginRegisterStorageArea& p = 
           *reinterpret_cast<const _OrthancPluginRegisterStorageArea*>(parameters);
         
-        pimpl_->storageArea_ = p;
-        pimpl_->hasStorageArea_ = true;
+        if (pimpl_->storageArea_.get() == NULL)
+        {
+          pimpl_->storageArea_.reset(new StorageAreaFactory(plugin, p));
+        }
+        else
+        {
+          throw OrthancException(ErrorCode_StorageAreaAlreadyRegistered);
+        }
+
         return true;
       }
 
@@ -1332,7 +1449,15 @@
 
         const _OrthancPluginRegisterDatabaseBackend& p =
           *reinterpret_cast<const _OrthancPluginRegisterDatabaseBackend*>(parameters);
-        pimpl_->database_.reset(new OrthancPluginDatabase(*p.backend, NULL, 0, p.payload));
+
+        if (pimpl_->database_.get() == NULL)
+        {
+          pimpl_->database_.reset(new OrthancPluginDatabase(plugin, *p.backend, NULL, 0, p.payload));
+        }
+        else
+        {
+          throw OrthancException(ErrorCode_DatabaseBackendAlreadyRegistered);
+        }
 
         *(p.result) = reinterpret_cast<OrthancPluginDatabaseContext*>(pimpl_->database_.get());
 
@@ -1345,7 +1470,16 @@
 
         const _OrthancPluginRegisterDatabaseBackendV2& p =
           *reinterpret_cast<const _OrthancPluginRegisterDatabaseBackendV2*>(parameters);
-        pimpl_->database_.reset(new OrthancPluginDatabase(*p.backend, p.extensions, p.extensionsSize, p.payload));
+
+        if (pimpl_->database_.get() == NULL)
+        {
+          pimpl_->database_.reset(new OrthancPluginDatabase(plugin, *p.backend, p.extensions,
+                                                            p.extensionsSize, p.payload));
+        }
+        else
+        {
+          throw OrthancException(ErrorCode_DatabaseBackendAlreadyRegistered);
+        }
 
         *(p.result) = reinterpret_cast<OrthancPluginDatabaseContext*>(pimpl_->database_.get());
 
@@ -1356,6 +1490,7 @@
       {
         const _OrthancPluginDatabaseAnswer& p =
           *reinterpret_cast<const _OrthancPluginDatabaseAnswer*>(parameters);
+
         if (pimpl_->database_.get() != NULL)
         {
           pimpl_->database_->AnswerReceived(p);
@@ -1545,124 +1680,65 @@
 
   bool OrthancPlugins::HasStorageArea() const
   {
-    return pimpl_->hasStorageArea_;
+    return pimpl_->storageArea_.get() != NULL;
   }
   
-  bool OrthancPlugins::HasDatabase() const
+  bool OrthancPlugins::HasDatabaseBackend() const
   {
     return pimpl_->database_.get() != NULL;
   }
 
 
-
-  namespace
-  {
-    class PluginStorageArea : public IStorageArea
-    {
-    private:
-      _OrthancPluginRegisterStorageArea params_;
-
-      void Free(void* buffer) const
-      {
-        if (buffer != NULL)
-        {
-          params_.free(buffer);
-        }
-      }
-
-    public:
-      PluginStorageArea(const _OrthancPluginRegisterStorageArea& params) : params_(params)
-      {
-      }
-
-
-      virtual void Create(const std::string& uuid,
-                          const void* content, 
-                          size_t size,
-                          FileContentType type)
-      {
-        OrthancPluginErrorCode error = params_.create
-          (uuid.c_str(), content, size, Plugins::Convert(type));
-
-        if (error != OrthancPluginErrorCode_Success)
-        {
-          throw OrthancException(Plugins::Convert(error));
-        }
-      }
-
-
-      virtual void Read(std::string& content,
-                        const std::string& uuid,
-                        FileContentType type)
-      {
-        void* buffer = NULL;
-        int64_t size = 0;
-
-        OrthancPluginErrorCode error = params_.read
-          (&buffer, &size, uuid.c_str(), Plugins::Convert(type));
-
-        if (error != OrthancPluginErrorCode_Success)
-        {
-          throw OrthancException(Plugins::Convert(error));
-        }
-
-        try
-        {
-          content.resize(static_cast<size_t>(size));
-        }
-        catch (...)
-        {
-          Free(buffer);
-          throw;
-        }
-
-        if (size > 0)
-        {
-          memcpy(&content[0], buffer, static_cast<size_t>(size));
-        }
-
-        Free(buffer);
-      }
-
-
-      virtual void Remove(const std::string& uuid,
-                          FileContentType type) 
-      {
-        OrthancPluginErrorCode error = params_.remove
-          (uuid.c_str(), Plugins::Convert(type));
-
-        if (error != OrthancPluginErrorCode_Success)
-        {
-          throw OrthancException(Plugins::Convert(error));
-        }
-      }
-    };
-  }
-
-
   IStorageArea* OrthancPlugins::CreateStorageArea()
   {
     if (!HasStorageArea())
     {
       throw OrthancException(ErrorCode_BadSequenceOfCalls);
     }
+    else
+    {
+      return pimpl_->storageArea_->Create();
+    }
+  }
 
-    return new PluginStorageArea(pimpl_->storageArea_);
+
+  const SharedLibrary& OrthancPlugins::GetStorageAreaLibrary() const
+  {
+    if (!HasStorageArea())
+    {
+      throw OrthancException(ErrorCode_BadSequenceOfCalls);
+    }
+    else
+    {
+      return pimpl_->storageArea_->GetSharedLibrary();
+    }
   }
 
 
-  IDatabaseWrapper& OrthancPlugins::GetDatabase()
+  IDatabaseWrapper& OrthancPlugins::GetDatabaseBackend()
   {
-    if (!HasDatabase())
+    if (!HasDatabaseBackend())
     {
       throw OrthancException(ErrorCode_BadSequenceOfCalls);
     }
-
-    return *pimpl_->database_;
+    else
+    {
+      return *pimpl_->database_;
+    }
   }
 
 
-
+  const SharedLibrary& OrthancPlugins::GetDatabaseBackendLibrary() const
+  {
+    if (!HasDatabaseBackend())
+    {
+      throw OrthancException(ErrorCode_BadSequenceOfCalls);
+    }
+    else
+    {
+      return pimpl_->database_->GetSharedLibrary();
+    }
+  }
 
 
   const char* OrthancPlugins::GetProperty(const char* plugin,
--- a/Plugins/Engine/OrthancPlugins.h	Fri Sep 18 17:45:59 2015 +0200
+++ b/Plugins/Engine/OrthancPlugins.h	Mon Sep 21 13:26:45 2015 +0200
@@ -130,7 +130,8 @@
                         const char* bodyData,
                         size_t bodySize);
 
-    virtual bool InvokeService(_OrthancPluginService service,
+    virtual bool InvokeService(SharedLibrary& plugin,
+                               _OrthancPluginService service,
                                const void* parameters);
 
     virtual void SignalChange(const ServerIndexChange& change);
@@ -149,9 +150,13 @@
 
     IStorageArea* CreateStorageArea();  // To be freed after use
 
-    bool HasDatabase() const;
+    const SharedLibrary& GetStorageAreaLibrary() const;
+
+    bool HasDatabaseBackend() const;
 
-    IDatabaseWrapper& GetDatabase();
+    IDatabaseWrapper& GetDatabaseBackend();
+
+    const SharedLibrary& GetDatabaseBackendLibrary() const;
 
     const char* GetProperty(const char* plugin,
                             _OrthancPluginProperty property) const;
--- a/Plugins/Engine/PluginsEnumerations.cpp	Fri Sep 18 17:45:59 2015 +0200
+++ b/Plugins/Engine/PluginsEnumerations.cpp	Mon Sep 21 13:26:45 2015 +0200
@@ -295,6 +295,15 @@
         case OrthancPluginErrorCode_LuaReturnsNoString:
           return ErrorCode_LuaReturnsNoString;
 
+        case OrthancPluginErrorCode_StorageAreaAlreadyRegistered:
+          return ErrorCode_StorageAreaAlreadyRegistered;
+
+        case OrthancPluginErrorCode_DatabaseBackendAlreadyRegistered:
+          return ErrorCode_DatabaseBackendAlreadyRegistered;
+
+        case OrthancPluginErrorCode_DatabasePlugin:
+          return ErrorCode_DatabasePlugin;
+
         default:
           return ErrorCode_Plugin;
       }
--- a/Plugins/Engine/PluginsManager.cpp	Fri Sep 18 17:45:59 2015 +0200
+++ b/Plugins/Engine/PluginsManager.cpp	Mon Sep 21 13:26:45 2015 +0200
@@ -54,6 +54,19 @@
 
 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)
   {
@@ -157,15 +170,15 @@
         break;
     }
 
-    PluginsManager* that = reinterpret_cast<PluginsManager*>(context->pluginsManager);
+    Plugin* that = reinterpret_cast<Plugin*>(context->pluginsManager);
 
     for (std::list<IPluginServiceProvider*>::iterator
-           it = that->serviceProviders_.begin(); 
-         it != that->serviceProviders_.end(); ++it)
+           it = that->GetPluginManager().serviceProviders_.begin(); 
+         it != that->GetPluginManager().serviceProviders_.end(); ++it)
     {
       try
       {
-        if ((*it)->InvokeService(service, params))
+        if ((*it)->InvokeService(that->GetSharedLibrary(), service, params))
         {
           return OrthancPluginErrorCode_Success;
         }
@@ -185,11 +198,6 @@
 
   PluginsManager::PluginsManager()
   {
-    memset(&context_, 0, sizeof(context_));
-    context_.pluginsManager = this;
-    context_.orthancVersion = ORTHANC_VERSION;
-    context_.Free = ::free;
-    context_.InvokeService = InvokeService;
   }
 
   PluginsManager::~PluginsManager()
@@ -201,7 +209,7 @@
         LOG(WARNING) << "Unregistering plugin '" << it->first
                      << "' (version " << it->second->GetVersion() << ")";
 
-        CallFinalize(it->second->GetLibrary());
+        CallFinalize(it->second->GetSharedLibrary());
         delete it->second;
       }
     }
@@ -231,27 +239,27 @@
       return;
     }
 
-    std::auto_ptr<Plugin> plugin(new Plugin(path));
+    std::auto_ptr<Plugin> plugin(new Plugin(*this, path));
 
-    if (!IsOrthancPlugin(plugin->GetLibrary()))
+    if (!IsOrthancPlugin(plugin->GetSharedLibrary()))
     {
-      LOG(ERROR) << "Plugin " << plugin->GetLibrary().GetPath()
+      LOG(ERROR) << "Plugin " << plugin->GetSharedLibrary().GetPath()
                  << " does not declare the proper entry functions";
       throw OrthancException(ErrorCode_SharedLibrary);
     }
 
-    std::string name(CallGetName(plugin->GetLibrary()));
+    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->GetLibrary()));
+    plugin->SetVersion(CallGetVersion(plugin->GetSharedLibrary()));
     LOG(WARNING) << "Registering plugin '" << name
                  << "' (version " << plugin->GetVersion() << ")";
 
-    CallInitialize(plugin->GetLibrary(), context_);
+    CallInitialize(plugin->GetSharedLibrary(), plugin->GetContext());
 
     plugins_[name] = plugin.release();
   }
@@ -333,5 +341,4 @@
       return it->second->GetVersion();
     }
   }
-
 }
--- a/Plugins/Engine/PluginsManager.h	Fri Sep 18 17:45:59 2015 +0200
+++ b/Plugins/Engine/PluginsManager.h	Mon Sep 21 13:26:45 2015 +0200
@@ -40,21 +40,22 @@
 
 namespace Orthanc
 {
-  class PluginsManager : boost::noncopyable
+  class PluginsManager : public boost::noncopyable
   {
   private:
-    class Plugin
+    class Plugin : public boost::noncopyable
     {
     private:
-      SharedLibrary library_;
-      std::string  version_;
+      OrthancPluginContext  context_;
+      SharedLibrary         library_;
+      std::string           version_;
+      PluginsManager&       pluginManager_;
 
     public:
-      Plugin(const std::string& path) : library_(path)
-      {
-      }
+      Plugin(PluginsManager& pluginManager,
+             const std::string& path);
 
-      SharedLibrary& GetLibrary()
+      SharedLibrary& GetSharedLibrary()
       {
         return library_;
       }
@@ -68,11 +69,20 @@
       {
         return version_;
       }
+
+      PluginsManager& GetPluginManager()
+      {
+        return pluginManager_;
+      }
+
+      OrthancPluginContext& GetContext()
+      {
+        return context_;
+      }
     };
 
     typedef std::map<std::string, Plugin*>  Plugins;
 
-    OrthancPluginContext  context_;
     Plugins  plugins_;
     std::list<IPluginServiceProvider*> serviceProviders_;
 
--- a/Plugins/Engine/SharedLibrary.cpp	Fri Sep 18 17:45:59 2015 +0200
+++ b/Plugins/Engine/SharedLibrary.cpp	Mon Sep 21 13:26:45 2015 +0200
@@ -36,6 +36,8 @@
 #include "../../Core/Logging.h"
 #include "../../Core/Toolbox.h"
 
+#include <boost/filesystem.hpp>
+
 #if defined(_WIN32)
 #include <windows.h>
 #elif defined(__linux) || (defined(__APPLE__) && defined(__MACH__)) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
@@ -46,20 +48,20 @@
 
 namespace Orthanc
 {
-  SharedLibrary::SharedLibrary(const std::string& path) : 
-    path_(path),
-    handle_(NULL)
+  SharedLibrary::SharedLibrary(const std::string& path) : handle_(NULL)
   {
+    path_ = boost::filesystem::canonical(path).string();
+
 #if defined(_WIN32)
-    handle_ = ::LoadLibraryA(path.c_str());
+    handle_ = ::LoadLibraryA(path_.c_str());
     if (handle_ == NULL)
     {
-      LOG(ERROR) << "LoadLibrary(" << path << ") failed: Error " << ::GetLastError();
+      LOG(ERROR) << "LoadLibrary(" << path_ << ") failed: Error " << ::GetLastError();
       throw OrthancException(ErrorCode_SharedLibrary);
     }
 
 #elif defined(__linux) || (defined(__APPLE__) && defined(__MACH__)) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
-    handle_ = ::dlopen(path.c_str(), RTLD_NOW);
+    handle_ = ::dlopen(path_.c_str(), RTLD_NOW);
     if (handle_ == NULL) 
     {
       std::string explanation;
@@ -69,7 +71,7 @@
         explanation = ": Error " + std::string(tmp);
       }
 
-      LOG(ERROR) << "dlopen(" << path << ") failed" << explanation;
+      LOG(ERROR) << "dlopen(" << path_ << ") failed" << explanation;
       throw OrthancException(ErrorCode_SharedLibrary);
     }
 
--- a/Plugins/Engine/SharedLibrary.h	Fri Sep 18 17:45:59 2015 +0200
+++ b/Plugins/Engine/SharedLibrary.h	Mon Sep 21 13:26:45 2015 +0200
@@ -42,7 +42,7 @@
 
 namespace Orthanc
 {
-  class SharedLibrary : boost::noncopyable
+  class SharedLibrary : public boost::noncopyable
   {
   public:
 #if defined(_WIN32)
--- a/Plugins/Include/orthanc/OrthancCPlugin.h	Fri Sep 18 17:45:59 2015 +0200
+++ b/Plugins/Include/orthanc/OrthancCPlugin.h	Mon Sep 21 13:26:45 2015 +0200
@@ -262,6 +262,9 @@
     OrthancPluginErrorCode_LuaBadOutput = 2033    /*!< The Lua function does not give the expected number of outputs */,
     OrthancPluginErrorCode_NotLuaPredicate = 2034    /*!< The Lua function is not a predicate (only true/false outputs allowed) */,
     OrthancPluginErrorCode_LuaReturnsNoString = 2035    /*!< The Lua function does not return a string */,
+    OrthancPluginErrorCode_StorageAreaAlreadyRegistered = 2036    /*!< Another plugin has already registered a custom storage area */,
+    OrthancPluginErrorCode_DatabaseBackendAlreadyRegistered = 2037    /*!< Another plugin has already registered a custom database back-end */,
+    OrthancPluginErrorCode_DatabasePlugin = 2038    /*!< The plugin implementing a custom database back-end does not fulfill the proper interface */,
 
     _OrthancPluginErrorCode_INTERNAL = 0x7fffffff
   } OrthancPluginErrorCode;
--- a/Resources/ErrorCodes.json	Fri Sep 18 17:45:59 2015 +0200
+++ b/Resources/ErrorCodes.json	Mon Sep 21 13:26:45 2015 +0200
@@ -468,5 +468,20 @@
     "Code": 2035, 
     "Name": "LuaReturnsNoString", 
     "Description": "The Lua function does not return a string"
+  },
+  {
+    "Code": 2036,
+    "Name": "StorageAreaAlreadyRegistered",
+    "Description": "Another plugin has already registered a custom storage area"
+  },
+  {
+    "Code": 2037,
+    "Name": "DatabaseBackendAlreadyRegistered",
+    "Description": "Another plugin has already registered a custom database back-end"
+  }, 
+  {
+    "Code": 2038, 
+    "Name": "DatabasePlugin", 
+    "Description": "The plugin implementing a custom database back-end does not fulfill the proper interface"
   }
 ]