changeset 1310:61ce8147f30d db-changes

custom database back-end
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 11 Feb 2015 10:40:08 +0100
parents 8f4487d8f79e
children d1a430176401 4a5c79e31b60
files CMakeLists.txt Core/DicomFormat/DicomMap.cpp Core/DicomFormat/DicomMap.h OrthancServer/ServerIndex.cpp OrthancServer/main.cpp Plugins/Engine/OrthancPlugins.cpp Plugins/Engine/OrthancPlugins.h Resources/OrthancPlugin.doxygen
diffstat 8 files changed, 153 insertions(+), 56 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Wed Feb 11 10:36:22 2015 +0100
+++ b/CMakeLists.txt	Wed Feb 11 10:40:08 2015 +0100
@@ -129,6 +129,7 @@
   Plugins/Engine/SharedLibrary.cpp
   Plugins/Engine/PluginsManager.cpp
   Plugins/Engine/OrthancPlugins.cpp
+  Plugins/Engine/OrthancPluginDatabase.cpp
   )
 
 
@@ -477,6 +478,8 @@
     FILES
     ${ORTHANC_ROOT}/OrthancCppClient/SharedLibrary/AUTOGENERATED/OrthancCppClient.h 
     ${ORTHANC_ROOT}/Plugins/Include/OrthancCPlugin.h 
+    ${ORTHANC_ROOT}/Plugins/Include/OrthancCDatabasePlugin.h 
+    ${ORTHANC_ROOT}/Plugins/Include/OrthancCppDatabasePlugin.h 
     DESTINATION include/orthanc
     )
 endif()
--- a/Core/DicomFormat/DicomMap.cpp	Wed Feb 11 10:36:22 2015 +0100
+++ b/Core/DicomFormat/DicomMap.cpp	Wed Feb 11 10:40:08 2015 +0100
@@ -191,6 +191,17 @@
   }
 
 
+  void DicomMap::Assign(const DicomMap& other)
+  {
+    Clear();
+
+    for (Map::const_iterator it = other.map_.begin(); it != other.map_.end(); ++it)
+    {
+      map_.insert(std::make_pair(it->first, it->second->Clone()));
+    }
+  }
+
+
   const DicomValue& DicomMap::GetValue(const DicomTag& tag) const
   {
     const DicomValue* value = TestAndGetValue(tag);
--- a/Core/DicomFormat/DicomMap.h	Wed Feb 11 10:36:22 2015 +0100
+++ b/Core/DicomFormat/DicomMap.h	Wed Feb 11 10:40:08 2015 +0100
@@ -77,8 +77,10 @@
     {
       Clear();
     }
+    
+    DicomMap* Clone() const;
 
-    DicomMap* Clone() const;
+    void Assign(const DicomMap& other);
 
     void Clear();
 
--- a/OrthancServer/ServerIndex.cpp	Wed Feb 11 10:36:22 2015 +0100
+++ b/OrthancServer/ServerIndex.cpp	Wed Feb 11 10:40:08 2015 +0100
@@ -232,6 +232,11 @@
     ~Transaction()
     {
       index_.listener_->EndTransaction();
+
+      if (!isCommitted_)
+      {
+        transaction_->Rollback();
+      }
     }
 
     void Commit(uint64_t sizeOfAddedFiles)
@@ -810,8 +815,8 @@
     uint64_t us = db_.GetTotalUncompressedSize();
     target["TotalDiskSize"] = boost::lexical_cast<std::string>(cs);
     target["TotalUncompressedSize"] = boost::lexical_cast<std::string>(us);
-    target["TotalDiskSizeMB"] = boost::lexical_cast<unsigned int>(cs / MEGA_BYTES);
-    target["TotalUncompressedSizeMB"] = boost::lexical_cast<unsigned int>(us / MEGA_BYTES);
+    target["TotalDiskSizeMB"] = static_cast<unsigned int>(cs / MEGA_BYTES);
+    target["TotalUncompressedSizeMB"] = static_cast<unsigned int>(us / MEGA_BYTES);
 
     target["CountPatients"] = static_cast<unsigned int>(db_.GetResourceCount(ResourceType_Patient));
     target["CountStudies"] = static_cast<unsigned int>(db_.GetResourceCount(ResourceType_Study));
@@ -1147,6 +1152,7 @@
                                         const std::string& remoteModality)
   {
     boost::mutex::scoped_lock lock(mutex_);
+    Transaction transaction(*this);
 
     int64_t id;
     ResourceType type;
@@ -1205,7 +1211,6 @@
       }
     }
 
-    // No need for a SQLite::ITransaction here, as we only insert 1 record
     ExportedResource resource(-1, 
                               type,
                               publicId,
@@ -1215,7 +1220,9 @@
                               studyInstanceUid,
                               seriesInstanceUid,
                               sopInstanceUid);
+
     db_.LogExportedResource(resource);
+    transaction.Commit(0);
   }
 
 
@@ -1384,6 +1391,7 @@
                                         bool isProtected)
   {
     boost::mutex::scoped_lock lock(mutex_);
+    Transaction transaction(*this);
 
     // Lookup for the requested resource
     int64_t id;
@@ -1394,8 +1402,8 @@
       throw OrthancException(ErrorCode_ParameterOutOfRange);
     }
 
-    // No need for a SQLite::ITransaction here, as we only make 1 write to the DB
     db_.SetProtectedPatient(id, isProtected);
+    transaction.Commit(0);
 
     if (isProtected)
       LOG(INFO) << "Patient " << publicId << " has been protected";
@@ -1597,12 +1605,10 @@
   uint64_t ServerIndex::IncrementGlobalSequence(GlobalProperty sequence)
   {
     boost::mutex::scoped_lock lock(mutex_);
-
-    std::auto_ptr<SQLite::ITransaction> transaction(db_.StartTransaction());
+    Transaction transaction(*this);
 
-    transaction->Begin();
     uint64_t seq = IncrementGlobalSequenceInternal(sequence);
-    transaction->Commit();
+    transaction.Commit(0);
 
     return seq;
   }
@@ -1613,8 +1619,7 @@
                               const std::string& publicId)
   {
     boost::mutex::scoped_lock lock(mutex_);
-    std::auto_ptr<SQLite::ITransaction> transaction(db_.StartTransaction());
-    transaction->Begin();
+    Transaction transaction(*this);
 
     int64_t id;
     ResourceType type;
@@ -1624,8 +1629,7 @@
     }
 
     LogChange(id, changeType, type, publicId);
-
-    transaction->Commit();
+    transaction.Commit(0);
   }
 
 
@@ -1747,9 +1751,9 @@
 
     target = Json::objectValue;
     target["DiskSize"] = boost::lexical_cast<std::string>(compressedSize);
-    target["DiskSizeMB"] = boost::lexical_cast<unsigned int>(compressedSize / MEGA_BYTES);
+    target["DiskSizeMB"] = static_cast<unsigned int>(compressedSize / MEGA_BYTES);
     target["UncompressedSize"] = boost::lexical_cast<std::string>(uncompressedSize);
-    target["UncompressedSizeMB"] = boost::lexical_cast<unsigned int>(uncompressedSize / MEGA_BYTES);
+    target["UncompressedSizeMB"] = static_cast<unsigned int>(uncompressedSize / MEGA_BYTES);
 
     switch (type)
     {
@@ -1977,7 +1981,6 @@
                                      FileContentType type)
   {
     boost::mutex::scoped_lock lock(mutex_);
-
     Transaction t(*this);
 
     ResourceType rtype;
--- a/OrthancServer/main.cpp	Wed Feb 11 10:36:22 2015 +0100
+++ b/OrthancServer/main.cpp	Wed Feb 11 10:40:08 2015 +0100
@@ -385,47 +385,61 @@
 
 static bool StartOrthanc(int argc, char *argv[])
 {
-  std::auto_ptr<IDatabaseWrapper> database;
-  database.reset(Configuration::CreateDatabaseWrapper());
+#if ENABLE_PLUGINS == 1
+  OrthancPlugins orthancPlugins;
+  orthancPlugins.SetCommandLineArguments(argc, argv);
+  PluginsManager pluginsManager;
+  pluginsManager.RegisterServiceProvider(orthancPlugins);
+  LoadPlugins(pluginsManager);
+#endif
 
-
-  // "storage" must be declared BEFORE "ServerContext context", to
-  // avoid mess in the invokation order of the destructors.
+  // "storage" and "database" must be declared BEFORE "ServerContext
+  // context", to avoid mess in the invokation order of the destructors.
+  std::auto_ptr<IDatabaseWrapper> database;
   std::auto_ptr<IStorageArea>  storage;
-
-  ServerContext context(*database);
+  std::auto_ptr<ServerContext> context;
 
-  context.SetCompressionEnabled(Configuration::GetGlobalBoolParameter("StorageCompression", false));
-  context.SetStoreMD5ForAttachments(Configuration::GetGlobalBoolParameter("StoreMD5ForAttachments", true));
+  if (orthancPlugins.HasDatabase())
+  {
+    context.reset(new ServerContext(orthancPlugins.GetDatabase()));
+  }
+  else
+  {
+    database.reset(Configuration::CreateDatabaseWrapper());
+    context.reset(new ServerContext(*database));
+  }
 
-  LoadLuaScripts(context);
+  context->SetCompressionEnabled(Configuration::GetGlobalBoolParameter("StorageCompression", false));
+  context->SetStoreMD5ForAttachments(Configuration::GetGlobalBoolParameter("StoreMD5ForAttachments", true));
+
+  LoadLuaScripts(*context);
 
   try
   {
-    context.GetIndex().SetMaximumPatientCount(Configuration::GetGlobalIntegerParameter("MaximumPatientCount", 0));
+    context->GetIndex().SetMaximumPatientCount(Configuration::GetGlobalIntegerParameter("MaximumPatientCount", 0));
   }
   catch (...)
   {
-    context.GetIndex().SetMaximumPatientCount(0);
+    context->GetIndex().SetMaximumPatientCount(0);
   }
 
   try
   {
     uint64_t size = Configuration::GetGlobalIntegerParameter("MaximumStorageSize", 0);
-    context.GetIndex().SetMaximumStorageSize(size * 1024 * 1024);
+    context->GetIndex().SetMaximumStorageSize(size * 1024 * 1024);
   }
   catch (...)
   {
-    context.GetIndex().SetMaximumStorageSize(0);
+    context->GetIndex().SetMaximumStorageSize(0);
   }
 
-  MyDicomServerFactory serverFactory(context);
+  MyDicomServerFactory serverFactory(*context);
   bool isReset = false;
     
   {
     // DICOM server
     DicomServer dicomServer;
-    OrthancApplicationEntityFilter dicomFilter(context);
+    OrthancApplicationEntityFilter dicomFilter(*context);
     dicomServer.SetCalledApplicationEntityTitleCheck(Configuration::GetGlobalBoolParameter("DicomCheckCalledAet", false));
     dicomServer.SetStoreRequestHandlerFactory(serverFactory);
     dicomServer.SetMoveRequestHandlerFactory(serverFactory);
@@ -435,7 +449,7 @@
     dicomServer.SetApplicationEntityFilter(dicomFilter);
 
     // HTTP server
-    MyIncomingHttpRequestFilter httpFilter(context);
+    MyIncomingHttpRequestFilter httpFilter(*context);
     MongooseServer httpServer;
     httpServer.SetPortNumber(Configuration::GetGlobalIntegerParameter("HttpPort", 8042));
     httpServer.SetRemoteAccessAllowed(Configuration::GetGlobalBoolParameter("RemoteAccessAllowed", false));
@@ -457,7 +471,7 @@
       httpServer.SetSslEnabled(false);
     }
 
-    OrthancRestApi restApi(context);
+    OrthancRestApi restApi(*context);
 
 #if ORTHANC_STANDALONE == 1
     EmbeddedResourceHttpHandler staticResources("/app", EmbeddedResources::ORTHANC_EXPLORER);
@@ -466,15 +480,10 @@
 #endif
 
 #if ENABLE_PLUGINS == 1
-    OrthancPlugins orthancPlugins(context);
-    orthancPlugins.SetCommandLineArguments(argc, argv);
+    orthancPlugins.SetServerContext(*context);
     orthancPlugins.SetOrthancRestApi(restApi);
-
-    PluginsManager pluginsManager;
-    pluginsManager.RegisterServiceProvider(orthancPlugins);
-    LoadPlugins(pluginsManager);
     httpServer.RegisterHandler(orthancPlugins);
-    context.SetOrthancPlugins(pluginsManager, orthancPlugins);
+    context->SetOrthancPlugins(pluginsManager, orthancPlugins);
 #endif
 
     httpServer.RegisterHandler(staticResources);
@@ -494,7 +503,7 @@
       storage.reset(Configuration::CreateStorageArea());
     }
     
-    context.SetStorageArea(*storage);
+    context->SetStorageArea(*storage);
 
 
     // GO !!! Start the requested servers
@@ -531,7 +540,7 @@
     LOG(WARNING) << "Orthanc is stopping";
 
 #if ENABLE_PLUGINS == 1
-    context.ResetOrthancPlugins();
+    context->ResetOrthancPlugins();
     orthancPlugins.Stop();
     LOG(WARNING) << "    Plugins have stopped";
 #endif
--- a/Plugins/Engine/OrthancPlugins.cpp	Wed Feb 11 10:36:22 2015 +0100
+++ b/Plugins/Engine/OrthancPlugins.cpp	Wed Feb 11 10:40:08 2015 +0100
@@ -180,7 +180,7 @@
     typedef std::list<OrthancPluginOnChangeCallback>  OnChangeCallbacks;
     typedef std::map<Property, std::string>  Properties;
 
-    ServerContext& context_;
+    ServerContext* context_;
     RestCallbacks restCallbacks_;
     OrthancRestApi* restApi_;
     OnStoredCallbacks  onStoredCallbacks_;
@@ -194,9 +194,10 @@
     Properties properties_;
     int argc_;
     char** argv_;
+    std::auto_ptr<OrthancPluginDatabase>  database_;
 
-    PImpl(ServerContext& context) : 
-      context_(context), 
+    PImpl() : 
+      context_(NULL), 
       restApi_(NULL),
       hasStorageArea_(false),
       done_(false),
@@ -246,13 +247,20 @@
   }
 
 
-  OrthancPlugins::OrthancPlugins(ServerContext& context)
+  OrthancPlugins::OrthancPlugins()
   {
-    pimpl_.reset(new PImpl(context));
+    pimpl_.reset(new PImpl());
     pimpl_->changeThread_ = boost::thread(PImpl::ChangeThread, pimpl_.get());
   }
 
   
+  void OrthancPlugins::SetServerContext(ServerContext& context)
+  {
+    pimpl_->context_ = &context;
+  }
+
+
+  
   OrthancPlugins::~OrthancPlugins()
   {
     Stop();
@@ -642,11 +650,13 @@
 
   void OrthancPlugins::GetDicomForInstance(const void* parameters)
   {
+    assert(pimpl_->context_ != NULL);
+
     const _OrthancPluginGetDicomForInstance& p = 
       *reinterpret_cast<const _OrthancPluginGetDicomForInstance*>(parameters);
 
     std::string dicom;
-    pimpl_->context_.ReadFile(dicom, p.instanceId, FileContentType_Dicom);
+    pimpl_->context_->ReadFile(dicom, p.instanceId, FileContentType_Dicom);
     CopyToMemoryBuffer(*p.target, dicom);
   }
 
@@ -829,8 +839,10 @@
         throw OrthancException(ErrorCode_InternalError);
     }
 
+    assert(pimpl_->context_ != NULL);
+
     std::list<std::string> result;
-    pimpl_->context_.GetIndex().LookupIdentifier(result, tag, p.argument, level);
+    pimpl_->context_->GetIndex().LookupIdentifier(result, tag, p.argument, level);
 
     if (result.size() == 1)
     {
@@ -1081,6 +1093,7 @@
 
       case _OrthancPluginService_RegisterStorageArea:
       {
+        LOG(INFO) << "Plugin has registered a custom storage area";
         const _OrthancPluginRegisterStorageArea& p = 
           *reinterpret_cast<const _OrthancPluginRegisterStorageArea*>(parameters);
         
@@ -1107,16 +1120,19 @@
         }
         else
         {
-          pimpl_->context_.GetIndex().SetGlobalProperty(static_cast<GlobalProperty>(p.property), p.value);
+          assert(pimpl_->context_ != NULL);
+          pimpl_->context_->GetIndex().SetGlobalProperty(static_cast<GlobalProperty>(p.property), p.value);
           return true;
         }
       }
 
       case _OrthancPluginService_GetGlobalProperty:
       {
+        assert(pimpl_->context_ != NULL);
+
         const _OrthancPluginGlobalProperty& p = 
           *reinterpret_cast<const _OrthancPluginGlobalProperty*>(parameters);
-        std::string result = pimpl_->context_.GetIndex().GetGlobalProperty(static_cast<GlobalProperty>(p.property), p.value);
+        std::string result = pimpl_->context_->GetIndex().GetGlobalProperty(static_cast<GlobalProperty>(p.property), p.value);
         *(p.result) = CopyString(result);
         return true;
       }
@@ -1146,6 +1162,34 @@
         }
       }
 
+      case _OrthancPluginService_RegisterDatabaseBackend:
+      {
+        LOG(INFO) << "Plugin has registered a custom database back-end";
+        const _OrthancPluginRegisterDatabaseBackend& p =
+          *reinterpret_cast<const _OrthancPluginRegisterDatabaseBackend*>(parameters);
+
+        pimpl_->database_.reset(new OrthancPluginDatabase(*p.backend, p.payload));
+        *(p.result) = reinterpret_cast<OrthancPluginDatabaseContext*>(pimpl_->database_.get());
+
+        return true;
+      }
+
+      case _OrthancPluginService_DatabaseAnswer:
+      {
+        const _OrthancPluginDatabaseAnswer& p =
+          *reinterpret_cast<const _OrthancPluginDatabaseAnswer*>(parameters);
+        if (pimpl_->database_.get() != NULL)
+        {
+          pimpl_->database_->AnswerReceived(p);
+          return true;
+        }
+        else
+        {
+          LOG(ERROR) << "Cannot invoke this service without a custom database back-end";
+          throw OrthancException(ErrorCode_BadRequest);
+        }
+      }
+
       default:
         return false;
     }
@@ -1162,6 +1206,12 @@
   {
     return pimpl_->hasStorageArea_;
   }
+  
+  bool OrthancPlugins::HasDatabase() const
+  {
+    return pimpl_->database_.get() != NULL;
+  }
+
 
 
   namespace
@@ -1263,6 +1313,19 @@
   }
 
 
+  IDatabaseWrapper& OrthancPlugins::GetDatabase()
+  {
+    if (!HasDatabase())
+    {
+      throw OrthancException(ErrorCode_BadSequenceOfCalls);
+    }
+
+    return *pimpl_->database_;
+  }
+
+
+
+
 
   const char* OrthancPlugins::GetProperty(const char* plugin,
                                           _OrthancPluginProperty property) const
--- a/Plugins/Engine/OrthancPlugins.h	Wed Feb 11 10:36:22 2015 +0100
+++ b/Plugins/Engine/OrthancPlugins.h	Wed Feb 11 10:40:08 2015 +0100
@@ -36,7 +36,7 @@
 #include "../../Core/HttpServer/HttpHandler.h"
 #include "../../OrthancServer/ServerContext.h"
 #include "../../OrthancServer/OrthancRestApi/OrthancRestApi.h"
-#include "../Include/OrthancCPlugin.h"
+#include "OrthancPluginDatabase.h"
 
 #include <list>
 #include <boost/shared_ptr.hpp>
@@ -87,10 +87,12 @@
     void SetHttpHeader(const void* parameters);
 
   public:
-    OrthancPlugins(ServerContext& context);
+    OrthancPlugins();
 
     virtual ~OrthancPlugins();
 
+    void SetServerContext(ServerContext& context);
+
     virtual bool Handle(HttpOutput& output,
                         HttpMethod method,
                         const UriComponents& uri,
@@ -110,7 +112,11 @@
 
     bool HasStorageArea() const;
 
-    IStorageArea* GetStorageArea();
+    IStorageArea* GetStorageArea();  // To be freed after use
+
+    bool HasDatabase() const;
+
+    IDatabaseWrapper& GetDatabase();
 
     void Stop();
 
--- a/Resources/OrthancPlugin.doxygen	Wed Feb 11 10:36:22 2015 +0100
+++ b/Resources/OrthancPlugin.doxygen	Wed Feb 11 10:40:08 2015 +0100
@@ -655,7 +655,7 @@
 # directories like "/usr/src/myproject". Separate the files or directories
 # with spaces.
 
-INPUT                  = @CMAKE_SOURCE_DIR@/Plugins/Include/OrthancCPlugin.h
+INPUT                  = @CMAKE_SOURCE_DIR@/Plugins/Include/
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is