# HG changeset patch # User Sebastien Jodogne # Date 1423647608 -3600 # Node ID 61ce8147f30dc97e2d55a9f6e9ef5df363459fdb # Parent 8f4487d8f79e3baa41a72a91be49a30367b5ba41 custom database back-end diff -r 8f4487d8f79e -r 61ce8147f30d CMakeLists.txt --- 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() diff -r 8f4487d8f79e -r 61ce8147f30d Core/DicomFormat/DicomMap.cpp --- 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); diff -r 8f4487d8f79e -r 61ce8147f30d Core/DicomFormat/DicomMap.h --- 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(); diff -r 8f4487d8f79e -r 61ce8147f30d OrthancServer/ServerIndex.cpp --- 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(cs); target["TotalUncompressedSize"] = boost::lexical_cast(us); - target["TotalDiskSizeMB"] = boost::lexical_cast(cs / MEGA_BYTES); - target["TotalUncompressedSizeMB"] = boost::lexical_cast(us / MEGA_BYTES); + target["TotalDiskSizeMB"] = static_cast(cs / MEGA_BYTES); + target["TotalUncompressedSizeMB"] = static_cast(us / MEGA_BYTES); target["CountPatients"] = static_cast(db_.GetResourceCount(ResourceType_Patient)); target["CountStudies"] = static_cast(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 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 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(compressedSize); - target["DiskSizeMB"] = boost::lexical_cast(compressedSize / MEGA_BYTES); + target["DiskSizeMB"] = static_cast(compressedSize / MEGA_BYTES); target["UncompressedSize"] = boost::lexical_cast(uncompressedSize); - target["UncompressedSizeMB"] = boost::lexical_cast(uncompressedSize / MEGA_BYTES); + target["UncompressedSizeMB"] = static_cast(uncompressedSize / MEGA_BYTES); switch (type) { @@ -1977,7 +1981,6 @@ FileContentType type) { boost::mutex::scoped_lock lock(mutex_); - Transaction t(*this); ResourceType rtype; diff -r 8f4487d8f79e -r 61ce8147f30d OrthancServer/main.cpp --- 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 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 database; std::auto_ptr storage; - - ServerContext context(*database); + std::auto_ptr 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 diff -r 8f4487d8f79e -r 61ce8147f30d Plugins/Engine/OrthancPlugins.cpp --- 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 OnChangeCallbacks; typedef std::map Properties; - ServerContext& context_; + ServerContext* context_; RestCallbacks restCallbacks_; OrthancRestApi* restApi_; OnStoredCallbacks onStoredCallbacks_; @@ -194,9 +194,10 @@ Properties properties_; int argc_; char** argv_; + std::auto_ptr 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(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 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(parameters); @@ -1107,16 +1120,19 @@ } else { - pimpl_->context_.GetIndex().SetGlobalProperty(static_cast(p.property), p.value); + assert(pimpl_->context_ != NULL); + pimpl_->context_->GetIndex().SetGlobalProperty(static_cast(p.property), p.value); return true; } } case _OrthancPluginService_GetGlobalProperty: { + assert(pimpl_->context_ != NULL); + const _OrthancPluginGlobalProperty& p = *reinterpret_cast(parameters); - std::string result = pimpl_->context_.GetIndex().GetGlobalProperty(static_cast(p.property), p.value); + std::string result = pimpl_->context_->GetIndex().GetGlobalProperty(static_cast(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(parameters); + + pimpl_->database_.reset(new OrthancPluginDatabase(*p.backend, p.payload)); + *(p.result) = reinterpret_cast(pimpl_->database_.get()); + + return true; + } + + case _OrthancPluginService_DatabaseAnswer: + { + const _OrthancPluginDatabaseAnswer& p = + *reinterpret_cast(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 diff -r 8f4487d8f79e -r 61ce8147f30d Plugins/Engine/OrthancPlugins.h --- 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 #include @@ -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(); diff -r 8f4487d8f79e -r 61ce8147f30d Resources/OrthancPlugin.doxygen --- 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