# HG changeset patch # User Sebastien Jodogne # Date 1423648305 -3600 # Node ID d1a43017640178c40742ada7504d64ddb99d8809 # Parent f796207e3df1eccbf7917d16e5b15b56f876e962# Parent 61ce8147f30dc97e2d55a9f6e9ef5df363459fdb integration db-changes->default diff -r f796207e3df1 -r d1a430176401 CMakeLists.txt --- a/CMakeLists.txt Wed Feb 11 10:26:17 2015 +0100 +++ b/CMakeLists.txt Wed Feb 11 10:51:45 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 f796207e3df1 -r d1a430176401 Core/DicomFormat/DicomMap.cpp --- a/Core/DicomFormat/DicomMap.cpp Wed Feb 11 10:26:17 2015 +0100 +++ b/Core/DicomFormat/DicomMap.cpp Wed Feb 11 10:51:45 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 f796207e3df1 -r d1a430176401 Core/DicomFormat/DicomMap.h --- a/Core/DicomFormat/DicomMap.h Wed Feb 11 10:26:17 2015 +0100 +++ b/Core/DicomFormat/DicomMap.h Wed Feb 11 10:51:45 2015 +0100 @@ -77,8 +77,10 @@ { Clear(); } + + DicomMap* Clone() const; - DicomMap* Clone() const; + void Assign(const DicomMap& other); void Clear(); diff -r f796207e3df1 -r d1a430176401 OrthancServer/ServerIndex.cpp --- a/OrthancServer/ServerIndex.cpp Wed Feb 11 10:26:17 2015 +0100 +++ b/OrthancServer/ServerIndex.cpp Wed Feb 11 10:51:45 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 f796207e3df1 -r d1a430176401 OrthancServer/main.cpp --- a/OrthancServer/main.cpp Wed Feb 11 10:26:17 2015 +0100 +++ b/OrthancServer/main.cpp Wed Feb 11 10:51:45 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 f796207e3df1 -r d1a430176401 Plugins/Engine/OrthancPluginDatabase.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Engine/OrthancPluginDatabase.cpp Wed Feb 11 10:51:45 2015 +0100 @@ -0,0 +1,1099 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, 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 . + **/ + + +#include "OrthancPluginDatabase.h" + +#include "../../Core/OrthancException.h" + +#include +#include + +namespace Orthanc +{ + static OrthancPluginResourceType Convert(ResourceType type) + { + switch (type) + { + case ResourceType_Patient: + return OrthancPluginResourceType_Patient; + + case ResourceType_Study: + return OrthancPluginResourceType_Study; + + case ResourceType_Series: + return OrthancPluginResourceType_Series; + + case ResourceType_Instance: + return OrthancPluginResourceType_Instance; + + default: + throw OrthancException(ErrorCode_InternalError); + } + } + + + static ResourceType Convert(OrthancPluginResourceType type) + { + switch (type) + { + case OrthancPluginResourceType_Patient: + return ResourceType_Patient; + + case OrthancPluginResourceType_Study: + return ResourceType_Study; + + case OrthancPluginResourceType_Series: + return ResourceType_Series; + + case OrthancPluginResourceType_Instance: + return ResourceType_Instance; + + default: + throw OrthancException(ErrorCode_InternalError); + } + } + + + static FileInfo Convert(const OrthancPluginAttachment& attachment) + { + return FileInfo(attachment.uuid, + static_cast(attachment.contentType), + attachment.uncompressedSize, + attachment.uncompressedHash, + static_cast(attachment.compressionType), + attachment.compressedSize, + attachment.compressedHash); + } + + + void OrthancPluginDatabase::ResetAnswers() + { + type_ = _OrthancPluginDatabaseAnswerType_None; + + answerDicomMap_ = NULL; + answerChanges_ = NULL; + answerExportedResources_ = NULL; + answerDone_ = NULL; + } + + + void OrthancPluginDatabase::ForwardAnswers(std::list& target) + { + if (type_ != _OrthancPluginDatabaseAnswerType_None && + type_ != _OrthancPluginDatabaseAnswerType_Int64) + { + throw OrthancException(ErrorCode_Plugin); + } + + target.clear(); + + if (type_ == _OrthancPluginDatabaseAnswerType_Int64) + { + for (std::list::const_iterator + it = answerInt64_.begin(); it != answerInt64_.end(); it++) + { + target.push_back(*it); + } + } + } + + + void OrthancPluginDatabase::ForwardAnswers(std::list& target) + { + if (type_ != _OrthancPluginDatabaseAnswerType_None && + type_ != _OrthancPluginDatabaseAnswerType_String) + { + throw OrthancException(ErrorCode_Plugin); + } + + target.clear(); + + if (type_ == _OrthancPluginDatabaseAnswerType_String) + { + for (std::list::const_iterator + it = answerStrings_.begin(); it != answerStrings_.end(); it++) + { + target.push_back(*it); + } + } + } + + + bool OrthancPluginDatabase::ForwardSingleAnswer(std::string& target) + { + if (type_ == _OrthancPluginDatabaseAnswerType_None) + { + return false; + } + else if (type_ == _OrthancPluginDatabaseAnswerType_String && + answerStrings_.size() == 1) + { + target = answerStrings_.front(); + return true; + } + else + { + throw OrthancException(ErrorCode_Plugin); + } + } + + + bool OrthancPluginDatabase::ForwardSingleAnswer(int64_t& target) + { + if (type_ == _OrthancPluginDatabaseAnswerType_None) + { + return false; + } + else if (type_ == _OrthancPluginDatabaseAnswerType_Int64 && + answerInt64_.size() == 1) + { + target = answerInt64_.front(); + return true; + } + else + { + throw OrthancException(ErrorCode_Plugin); + } + } + + + void OrthancPluginDatabase::AddAttachment(int64_t id, + const FileInfo& attachment) + { + OrthancPluginAttachment tmp; + tmp.uuid = attachment.GetUuid().c_str(); + tmp.contentType = static_cast(attachment.GetContentType()); + tmp.uncompressedSize = attachment.GetUncompressedSize(); + tmp.uncompressedHash = attachment.GetUncompressedMD5().c_str(); + tmp.compressionType = static_cast(attachment.GetCompressionType()); + tmp.compressedSize = attachment.GetCompressedSize(); + tmp.compressedHash = attachment.GetCompressedMD5().c_str(); + + if (backend_.addAttachment(payload_, id, &tmp) != 0) + { + throw OrthancException(ErrorCode_Plugin); + } + } + + + void OrthancPluginDatabase::AttachChild(int64_t parent, + int64_t child) + { + if (backend_.attachChild(payload_, parent, child) != 0) + { + throw OrthancException(ErrorCode_Plugin); + } + } + + + void OrthancPluginDatabase::ClearChanges() + { + if (backend_.clearChanges(payload_) != 0) + { + throw OrthancException(ErrorCode_Plugin); + } + } + + + void OrthancPluginDatabase::ClearExportedResources() + { + if (backend_.clearExportedResources(payload_) != 0) + { + throw OrthancException(ErrorCode_Plugin); + } + } + + + int64_t OrthancPluginDatabase::CreateResource(const std::string& publicId, + ResourceType type) + { + int64_t id; + + if (backend_.createResource(&id, payload_, publicId.c_str(), Convert(type)) != 0) + { + throw OrthancException(ErrorCode_Plugin); + } + + return id; + } + + + void OrthancPluginDatabase::DeleteAttachment(int64_t id, + FileContentType attachment) + { + if (backend_.deleteAttachment(payload_, id, static_cast(attachment)) != 0) + { + throw OrthancException(ErrorCode_Plugin); + } + } + + + void OrthancPluginDatabase::DeleteMetadata(int64_t id, + MetadataType type) + { + if (backend_.deleteMetadata(payload_, id, static_cast(type)) != 0) + { + throw OrthancException(ErrorCode_Plugin); + } + } + + + void OrthancPluginDatabase::DeleteResource(int64_t id) + { + if (backend_.deleteResource(payload_, id) != 0) + { + throw OrthancException(ErrorCode_Plugin); + } + } + + + void OrthancPluginDatabase::GetAllMetadata(std::map& target, + int64_t id) + { + std::list metadata; + ListAvailableMetadata(metadata, id); + + target.clear(); + + for (std::list::const_iterator + it = metadata.begin(); it != metadata.end(); it++) + { + std::string value; + if (!LookupMetadata(value, id, *it)) + { + throw OrthancException(ErrorCode_Plugin); + } + + target[*it] = value; + } + } + + + void OrthancPluginDatabase::GetAllPublicIds(std::list& target, + ResourceType resourceType) + { + ResetAnswers(); + + if (backend_.getAllPublicIds(GetContext(), payload_, Convert(resourceType)) != 0) + { + throw OrthancException(ErrorCode_Plugin); + } + + ForwardAnswers(target); + } + + + + void OrthancPluginDatabase::GetChanges(std::list& target /*out*/, + bool& done /*out*/, + int64_t since, + uint32_t maxResults) + { + ResetAnswers(); + answerChanges_ = ⌖ + answerDone_ = &done; + done = false; + + if (backend_.getChanges(GetContext(), payload_, since, maxResults) != 0) + { + throw OrthancException(ErrorCode_Plugin); + } + } + + + void OrthancPluginDatabase::GetChildrenInternalId(std::list& target, + int64_t id) + { + ResetAnswers(); + + if (backend_.getChildrenInternalId(GetContext(), payload_, id) != 0) + { + throw OrthancException(ErrorCode_Plugin); + } + + ForwardAnswers(target); + } + + + void OrthancPluginDatabase::GetChildrenPublicId(std::list& target, + int64_t id) + { + ResetAnswers(); + + if (backend_.getChildrenPublicId(GetContext(), payload_, id) != 0) + { + throw OrthancException(ErrorCode_Plugin); + } + + ForwardAnswers(target); + } + + + void OrthancPluginDatabase::GetExportedResources(std::list& target /*out*/, + bool& done /*out*/, + int64_t since, + uint32_t maxResults) + { + ResetAnswers(); + answerExportedResources_ = ⌖ + answerDone_ = &done; + done = false; + + if (backend_.getExportedResources(GetContext(), payload_, since, maxResults) != 0) + { + throw OrthancException(ErrorCode_Plugin); + } + } + + + void OrthancPluginDatabase::GetLastChange(std::list& target /*out*/) + { + bool ignored = false; + + ResetAnswers(); + answerChanges_ = ⌖ + answerDone_ = &ignored; + + if (backend_.getLastChange(GetContext(), payload_) != 0) + { + throw OrthancException(ErrorCode_Plugin); + } + } + + + void OrthancPluginDatabase::GetLastExportedResource(std::list& target /*out*/) + { + bool ignored = false; + + ResetAnswers(); + answerExportedResources_ = ⌖ + answerDone_ = &ignored; + + if (backend_.getLastExportedResource(GetContext(), payload_) != 0) + { + throw OrthancException(ErrorCode_Plugin); + } + } + + + void OrthancPluginDatabase::GetMainDicomTags(DicomMap& map, + int64_t id) + { + ResetAnswers(); + answerDicomMap_ = ↦ + + if (backend_.getMainDicomTags(GetContext(), payload_, id) != 0) + { + throw OrthancException(ErrorCode_Plugin); + } + } + + + std::string OrthancPluginDatabase::GetPublicId(int64_t resourceId) + { + ResetAnswers(); + std::string s; + + if (backend_.getPublicId(GetContext(), payload_, resourceId) != 0 || + !ForwardSingleAnswer(s)) + { + throw OrthancException(ErrorCode_Plugin); + } + + return s; + } + + + uint64_t OrthancPluginDatabase::GetResourceCount(ResourceType resourceType) + { + uint64_t count; + + if (backend_.getResourceCount(&count, payload_, Convert(resourceType)) != 0) + { + throw OrthancException(ErrorCode_Plugin); + } + + return count; + } + + + ResourceType OrthancPluginDatabase::GetResourceType(int64_t resourceId) + { + OrthancPluginResourceType type; + + if (backend_.getResourceType(&type, payload_, resourceId) != 0) + { + throw OrthancException(ErrorCode_Plugin); + } + + return Convert(type); + } + + + uint64_t OrthancPluginDatabase::GetTotalCompressedSize() + { + uint64_t size; + + if (backend_.getTotalCompressedSize(&size, payload_) != 0) + { + throw OrthancException(ErrorCode_Plugin); + } + + return size; + } + + + uint64_t OrthancPluginDatabase::GetTotalUncompressedSize() + { + uint64_t size; + + if (backend_.getTotalUncompressedSize(&size, payload_) != 0) + { + throw OrthancException(ErrorCode_Plugin); + } + + return size; + } + + + bool OrthancPluginDatabase::IsExistingResource(int64_t internalId) + { + int32_t existing; + + if (backend_.isExistingResource(&existing, payload_, internalId) != 0) + { + throw OrthancException(ErrorCode_Plugin); + } + + return existing; + } + + + bool OrthancPluginDatabase::IsProtectedPatient(int64_t internalId) + { + int32_t isProtected; + + if (backend_.isProtectedPatient(&isProtected, payload_, internalId) != 0) + { + throw OrthancException(ErrorCode_Plugin); + } + + return isProtected; + } + + + void OrthancPluginDatabase::ListAvailableMetadata(std::list& target, + int64_t id) + { + ResetAnswers(); + + if (backend_.listAvailableMetadata(GetContext(), payload_, id) != 0) + { + throw OrthancException(ErrorCode_Plugin); + } + + if (type_ != _OrthancPluginDatabaseAnswerType_None && + type_ != _OrthancPluginDatabaseAnswerType_Int32) + { + throw OrthancException(ErrorCode_Plugin); + } + + target.clear(); + + if (type_ == _OrthancPluginDatabaseAnswerType_Int32) + { + for (std::list::const_iterator + it = answerInt32_.begin(); it != answerInt32_.end(); it++) + { + target.push_back(static_cast(*it)); + } + } + } + + + void OrthancPluginDatabase::ListAvailableAttachments(std::list& target, + int64_t id) + { + ResetAnswers(); + + if (backend_.listAvailableAttachments(GetContext(), payload_, id) != 0) + { + throw OrthancException(ErrorCode_Plugin); + } + + if (type_ != _OrthancPluginDatabaseAnswerType_None && + type_ != _OrthancPluginDatabaseAnswerType_Int32) + { + throw OrthancException(ErrorCode_Plugin); + } + + target.clear(); + + if (type_ == _OrthancPluginDatabaseAnswerType_Int32) + { + for (std::list::const_iterator + it = answerInt32_.begin(); it != answerInt32_.end(); it++) + { + target.push_back(static_cast(*it)); + } + } + } + + + void OrthancPluginDatabase::LogChange(int64_t internalId, + const ServerIndexChange& change) + { + OrthancPluginChange tmp; + tmp.seq = change.GetSeq(); + tmp.changeType = static_cast(change.GetChangeType()); + tmp.resourceType = Convert(change.GetResourceType()); + tmp.publicId = change.GetPublicId().c_str(); + tmp.date = change.GetDate().c_str(); + + if (backend_.logChange(payload_, &tmp) != 0) + { + throw OrthancException(ErrorCode_Plugin); + } + } + + + void OrthancPluginDatabase::LogExportedResource(const ExportedResource& resource) + { + OrthancPluginExportedResource tmp; + tmp.seq = resource.GetSeq(); + tmp.resourceType = Convert(resource.GetResourceType()); + tmp.publicId = resource.GetPublicId().c_str(); + tmp.modality = resource.GetModality().c_str(); + tmp.date = resource.GetDate().c_str(); + tmp.patientId = resource.GetPatientId().c_str(); + tmp.studyInstanceUid = resource.GetStudyInstanceUid().c_str(); + tmp.seriesInstanceUid = resource.GetSeriesInstanceUid().c_str(); + tmp.sopInstanceUid = resource.GetSopInstanceUid().c_str(); + + if (backend_.logExportedResource(payload_, &tmp) != 0) + { + throw OrthancException(ErrorCode_Plugin); + } + } + + + bool OrthancPluginDatabase::LookupAttachment(FileInfo& attachment, + int64_t id, + FileContentType contentType) + { + ResetAnswers(); + + if (backend_.lookupAttachment(GetContext(), payload_, id, static_cast(contentType))) + { + throw OrthancException(ErrorCode_Plugin); + } + + if (type_ == _OrthancPluginDatabaseAnswerType_None) + { + return false; + } + else if (type_ == _OrthancPluginDatabaseAnswerType_Attachment && + answerAttachments_.size() == 1) + { + attachment = answerAttachments_.front(); + return true; + } + else + { + throw OrthancException(ErrorCode_Plugin); + } + } + + + bool OrthancPluginDatabase::LookupGlobalProperty(std::string& target, + GlobalProperty property) + { + ResetAnswers(); + + if (backend_.lookupGlobalProperty(GetContext(), payload_, + static_cast(property))) + { + throw OrthancException(ErrorCode_Plugin); + } + + return ForwardSingleAnswer(target); + } + + + void OrthancPluginDatabase::LookupIdentifier(std::list& target, + const DicomTag& tag, + const std::string& value) + { + ResetAnswers(); + + OrthancPluginDicomTag tmp; + tmp.group = tag.GetGroup(); + tmp.element = tag.GetElement(); + tmp.value = value.c_str(); + + if (backend_.lookupIdentifier(GetContext(), payload_, &tmp) != 0) + { + throw OrthancException(ErrorCode_Plugin); + } + + ForwardAnswers(target); + } + + + void OrthancPluginDatabase::LookupIdentifier(std::list& target, + const std::string& value) + { + ResetAnswers(); + + if (backend_.lookupIdentifier2(GetContext(), payload_, value.c_str()) != 0) + { + throw OrthancException(ErrorCode_Plugin); + } + + ForwardAnswers(target); + } + + + bool OrthancPluginDatabase::LookupMetadata(std::string& target, + int64_t id, + MetadataType type) + { + ResetAnswers(); + + if (backend_.lookupMetadata(GetContext(), payload_, id, static_cast(type))) + { + throw OrthancException(ErrorCode_Plugin); + } + + return ForwardSingleAnswer(target); + } + + + bool OrthancPluginDatabase::LookupParent(int64_t& parentId, + int64_t resourceId) + { + ResetAnswers(); + + if (backend_.lookupParent(GetContext(), payload_, resourceId)) + { + throw OrthancException(ErrorCode_Plugin); + } + + return ForwardSingleAnswer(parentId); + } + + + bool OrthancPluginDatabase::LookupResource(int64_t& id, + ResourceType& type, + const std::string& publicId) + { + ResetAnswers(); + + if (backend_.lookupResource(GetContext(), payload_, publicId.c_str())) + { + throw OrthancException(ErrorCode_Plugin); + } + + if (type_ == _OrthancPluginDatabaseAnswerType_None) + { + return false; + } + else if (type_ == _OrthancPluginDatabaseAnswerType_Resource && + answerResources_.size() == 1) + { + id = answerResources_.front().first; + type = answerResources_.front().second; + return true; + } + else + { + throw OrthancException(ErrorCode_Plugin); + } + } + + + bool OrthancPluginDatabase::SelectPatientToRecycle(int64_t& internalId) + { + ResetAnswers(); + + if (backend_.selectPatientToRecycle(GetContext(), payload_)) + { + throw OrthancException(ErrorCode_Plugin); + } + + return ForwardSingleAnswer(internalId); + } + + + bool OrthancPluginDatabase::SelectPatientToRecycle(int64_t& internalId, + int64_t patientIdToAvoid) + { + ResetAnswers(); + + if (backend_.selectPatientToRecycle2(GetContext(), payload_, patientIdToAvoid)) + { + throw OrthancException(ErrorCode_Plugin); + } + + return ForwardSingleAnswer(internalId); + } + + + void OrthancPluginDatabase::SetGlobalProperty(GlobalProperty property, + const std::string& value) + { + if (backend_.setGlobalProperty(payload_, static_cast(property), + value.c_str()) != 0) + { + throw OrthancException(ErrorCode_Plugin); + } + } + + + void OrthancPluginDatabase::SetMainDicomTag(int64_t id, + const DicomTag& tag, + const std::string& value) + { + int32_t status; + OrthancPluginDicomTag tmp; + tmp.group = tag.GetGroup(); + tmp.element = tag.GetElement(); + tmp.value = value.c_str(); + + if (tag.IsIdentifier()) + { + status = backend_.setIdentifierTag(payload_, id, &tmp); + } + else + { + status = backend_.setMainDicomTag(payload_, id, &tmp); + } + + if (status != 0) + { + throw OrthancException(ErrorCode_Plugin); + } + } + + + void OrthancPluginDatabase::SetMetadata(int64_t id, + MetadataType type, + const std::string& value) + { + if (backend_.setMetadata(payload_, id, static_cast(type), + value.c_str()) != 0) + { + throw OrthancException(ErrorCode_Plugin); + } + } + + + void OrthancPluginDatabase::SetProtectedPatient(int64_t internalId, + bool isProtected) + { + if (backend_.setProtectedPatient(payload_, internalId, isProtected) != 0) + { + throw OrthancException(ErrorCode_Plugin); + } + } + + + class OrthancPluginDatabase::Transaction : public SQLite::ITransaction + { + private: + const OrthancPluginDatabaseBackend& backend_; + void* payload_; + + public: + Transaction(const OrthancPluginDatabaseBackend& backend, + void* payload) : + backend_(backend), + payload_(payload) + { + } + + virtual void Begin() + { + if (backend_.startTransaction(payload_) != 0) + { + throw OrthancException(ErrorCode_Plugin); + } + } + + virtual void Rollback() + { + if (backend_.rollbackTransaction(payload_) != 0) + { + throw OrthancException(ErrorCode_Plugin); + } + } + + virtual void Commit() + { + if (backend_.commitTransaction(payload_) != 0) + { + throw OrthancException(ErrorCode_Plugin); + } + } + }; + + + SQLite::ITransaction* OrthancPluginDatabase::StartTransaction() + { + return new Transaction(backend_, payload_); + } + + + static void ProcessEvent(IServerIndexListener& listener, + const _OrthancPluginDatabaseAnswer& answer) + { + switch (answer.type) + { + case _OrthancPluginDatabaseAnswerType_DeletedAttachment: + { + const OrthancPluginAttachment& attachment = + *reinterpret_cast(answer.valueGeneric); + listener.SignalFileDeleted(Convert(attachment)); + break; + } + + case _OrthancPluginDatabaseAnswerType_RemainingAncestor: + { + ResourceType type = Convert(static_cast(answer.valueInt32)); + listener.SignalRemainingAncestor(type, answer.valueString); + break; + } + + case _OrthancPluginDatabaseAnswerType_DeletedResource: + { + ResourceType type = Convert(static_cast(answer.valueInt32)); + ServerIndexChange change(ChangeType_Deleted, type, answer.valueString); + listener.SignalChange(change); + break; + } + + default: + throw OrthancException(ErrorCode_Plugin); + } + } + + + void OrthancPluginDatabase::AnswerReceived(const _OrthancPluginDatabaseAnswer& answer) + { + if (answer.type == _OrthancPluginDatabaseAnswerType_None) + { + throw OrthancException(ErrorCode_Plugin); + } + + if (answer.type == _OrthancPluginDatabaseAnswerType_DeletedAttachment || + answer.type == _OrthancPluginDatabaseAnswerType_DeletedResource || + answer.type == _OrthancPluginDatabaseAnswerType_RemainingAncestor) + { + assert(listener_ != NULL); + ProcessEvent(*listener_, answer); + return; + } + + if (type_ == _OrthancPluginDatabaseAnswerType_None) + { + type_ = answer.type; + + switch (type_) + { + case _OrthancPluginDatabaseAnswerType_Int32: + answerInt32_.clear(); + break; + + case _OrthancPluginDatabaseAnswerType_Int64: + answerInt64_.clear(); + break; + + case _OrthancPluginDatabaseAnswerType_Resource: + answerResources_.clear(); + break; + + case _OrthancPluginDatabaseAnswerType_Attachment: + answerAttachments_.clear(); + break; + + case _OrthancPluginDatabaseAnswerType_String: + answerStrings_.clear(); + break; + + case _OrthancPluginDatabaseAnswerType_DicomTag: + assert(answerDicomMap_ != NULL); + answerDicomMap_->Clear(); + break; + + case _OrthancPluginDatabaseAnswerType_Change: + assert(answerChanges_ != NULL); + answerChanges_->clear(); + break; + + case _OrthancPluginDatabaseAnswerType_ExportedResource: + assert(answerExportedResources_ != NULL); + answerExportedResources_->clear(); + break; + + default: + LOG(ERROR) << "Unhandled type of answer for custom index plugin: " << answer.type; + throw OrthancException(ErrorCode_Plugin); + } + } + else if (type_ != answer.type) + { + LOG(ERROR) << "Error in the plugin protocol: Cannot change the answer type"; + throw OrthancException(ErrorCode_Plugin); + } + + switch (answer.type) + { + case _OrthancPluginDatabaseAnswerType_Int32: + { + answerInt32_.push_back(answer.valueInt32); + break; + } + + case _OrthancPluginDatabaseAnswerType_Int64: + { + answerInt64_.push_back(answer.valueInt64); + break; + } + + case _OrthancPluginDatabaseAnswerType_Resource: + { + OrthancPluginResourceType type = static_cast(answer.valueInt32); + answerResources_.push_back(std::make_pair(answer.valueInt64, Convert(type))); + break; + } + + case _OrthancPluginDatabaseAnswerType_Attachment: + { + const OrthancPluginAttachment& attachment = + *reinterpret_cast(answer.valueGeneric); + + answerAttachments_.push_back(Convert(attachment)); + break; + } + + case _OrthancPluginDatabaseAnswerType_DicomTag: + { + const OrthancPluginDicomTag& tag = *reinterpret_cast(answer.valueGeneric); + assert(answerDicomMap_ != NULL); + answerDicomMap_->SetValue(tag.group, tag.element, std::string(tag.value)); + break; + } + + case _OrthancPluginDatabaseAnswerType_String: + { + if (answer.valueString == NULL) + { + throw OrthancException(ErrorCode_Plugin); + } + + if (type_ == _OrthancPluginDatabaseAnswerType_None) + { + type_ = _OrthancPluginDatabaseAnswerType_String; + answerStrings_.clear(); + } + else if (type_ != _OrthancPluginDatabaseAnswerType_String) + { + throw OrthancException(ErrorCode_Plugin); + } + + answerStrings_.push_back(std::string(answer.valueString)); + break; + } + + case _OrthancPluginDatabaseAnswerType_Change: + { + assert(answerDone_ != NULL); + if (answer.valueUint32 == 1) + { + *answerDone_ = true; + } + else if (*answerDone_) + { + throw OrthancException(ErrorCode_Plugin); + } + else + { + const OrthancPluginChange& change = *reinterpret_cast(answer.valueGeneric); + assert(answerChanges_ != NULL); + answerChanges_->push_back + (ServerIndexChange(change.seq, + static_cast(change.changeType), + Convert(change.resourceType), + change.publicId, + change.date)); + } + + break; + } + + case _OrthancPluginDatabaseAnswerType_ExportedResource: + { + assert(answerDone_ != NULL); + if (answer.valueUint32 == 1) + { + *answerDone_ = true; + } + else if (*answerDone_) + { + throw OrthancException(ErrorCode_Plugin); + } + else + { + const OrthancPluginExportedResource& exported = + *reinterpret_cast(answer.valueGeneric); + assert(answerExportedResources_ != NULL); + answerExportedResources_->push_back + (ExportedResource(exported.seq, + Convert(exported.resourceType), + exported.publicId, + exported.modality, + exported.date, + exported.patientId, + exported.studyInstanceUid, + exported.seriesInstanceUid, + exported.sopInstanceUid)); + } + + break; + } + + default: + LOG(ERROR) << "Unhandled type of answer for custom index plugin: " << answer.type; + throw OrthancException(ErrorCode_Plugin); + } + } +} diff -r f796207e3df1 -r d1a430176401 Plugins/Engine/OrthancPluginDatabase.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Engine/OrthancPluginDatabase.h Wed Feb 11 10:51:45 2015 +0100 @@ -0,0 +1,226 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, 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 . + **/ + + +#pragma once + +#include "../../OrthancServer/IDatabaseWrapper.h" +#include "../Include/OrthancCDatabasePlugin.h" + +namespace Orthanc +{ + class OrthancPluginDatabase : public IDatabaseWrapper + { + private: + class Transaction; + + typedef std::pair AnswerResource; + + _OrthancPluginDatabaseAnswerType type_; + OrthancPluginDatabaseBackend backend_; + void* payload_; + IServerIndexListener* listener_; + + std::list answerStrings_; + std::list answerInt32_; + std::list answerInt64_; + std::list answerResources_; + std::list answerAttachments_; + + DicomMap* answerDicomMap_; + std::list* answerChanges_; + std::list* answerExportedResources_; + bool* answerDone_; + + OrthancPluginDatabaseContext* GetContext() + { + return reinterpret_cast(this); + } + + void ResetAnswers(); + + void ForwardAnswers(std::list& target); + + void ForwardAnswers(std::list& target); + + bool ForwardSingleAnswer(std::string& target); + + bool ForwardSingleAnswer(int64_t& target); + + public: + OrthancPluginDatabase(const OrthancPluginDatabaseBackend& backend, + void *payload) : + type_(_OrthancPluginDatabaseAnswerType_None), + backend_(backend), + payload_(payload), + listener_(NULL) + { + } + + virtual void AddAttachment(int64_t id, + const FileInfo& attachment); + + virtual void AttachChild(int64_t parent, + int64_t child); + + virtual void ClearChanges(); + + virtual void ClearExportedResources(); + + virtual int64_t CreateResource(const std::string& publicId, + ResourceType type); + + virtual void DeleteAttachment(int64_t id, + FileContentType attachment); + + virtual void DeleteMetadata(int64_t id, + MetadataType type); + + virtual void DeleteResource(int64_t id); + + virtual void FlushToDisk() + { + } + + virtual bool HasFlushToDisk() const + { + return false; + } + + virtual void GetAllMetadata(std::map& target, + int64_t id); + + virtual void GetAllPublicIds(std::list& target, + ResourceType resourceType); + + + virtual void GetChanges(std::list& target /*out*/, + bool& done /*out*/, + int64_t since, + uint32_t maxResults); + + virtual void GetChildrenInternalId(std::list& target, + int64_t id); + + virtual void GetChildrenPublicId(std::list& target, + int64_t id); + + virtual void GetExportedResources(std::list& target /*out*/, + bool& done /*out*/, + int64_t since, + uint32_t maxResults); + + virtual void GetLastChange(std::list& target /*out*/); + + virtual void GetLastExportedResource(std::list& target /*out*/); + + virtual void GetMainDicomTags(DicomMap& map, + int64_t id); + + virtual std::string GetPublicId(int64_t resourceId); + + virtual uint64_t GetResourceCount(ResourceType resourceType); + + virtual ResourceType GetResourceType(int64_t resourceId); + + virtual uint64_t GetTotalCompressedSize(); + + virtual uint64_t GetTotalUncompressedSize(); + + virtual bool IsExistingResource(int64_t internalId); + + virtual bool IsProtectedPatient(int64_t internalId); + + virtual void ListAvailableMetadata(std::list& target, + int64_t id); + + virtual void ListAvailableAttachments(std::list& target, + int64_t id); + + virtual void LogChange(int64_t internalId, + const ServerIndexChange& change); + + virtual void LogExportedResource(const ExportedResource& resource); + + virtual bool LookupAttachment(FileInfo& attachment, + int64_t id, + FileContentType contentType); + + virtual bool LookupGlobalProperty(std::string& target, + GlobalProperty property); + + virtual void LookupIdentifier(std::list& target, + const DicomTag& tag, + const std::string& value); + + virtual void LookupIdentifier(std::list& target, + const std::string& value); + + virtual bool LookupMetadata(std::string& target, + int64_t id, + MetadataType type); + + virtual bool LookupParent(int64_t& parentId, + int64_t resourceId); + + virtual bool LookupResource(int64_t& id, + ResourceType& type, + const std::string& publicId); + + virtual bool SelectPatientToRecycle(int64_t& internalId); + + virtual bool SelectPatientToRecycle(int64_t& internalId, + int64_t patientIdToAvoid); + + virtual void SetGlobalProperty(GlobalProperty property, + const std::string& value); + + virtual void SetMainDicomTag(int64_t id, + const DicomTag& tag, + const std::string& value); + + virtual void SetMetadata(int64_t id, + MetadataType type, + const std::string& value); + + virtual void SetProtectedPatient(int64_t internalId, + bool isProtected); + + virtual SQLite::ITransaction* StartTransaction(); + + virtual void SetListener(IServerIndexListener& listener) + { + listener_ = &listener; + } + + void AnswerReceived(const _OrthancPluginDatabaseAnswer& answer); + }; +} diff -r f796207e3df1 -r d1a430176401 Plugins/Engine/OrthancPlugins.cpp --- a/Plugins/Engine/OrthancPlugins.cpp Wed Feb 11 10:26:17 2015 +0100 +++ b/Plugins/Engine/OrthancPlugins.cpp Wed Feb 11 10:51:45 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 f796207e3df1 -r d1a430176401 Plugins/Engine/OrthancPlugins.h --- a/Plugins/Engine/OrthancPlugins.h Wed Feb 11 10:26:17 2015 +0100 +++ b/Plugins/Engine/OrthancPlugins.h Wed Feb 11 10:51:45 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 f796207e3df1 -r d1a430176401 Plugins/Include/OrthancCDatabasePlugin.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Include/OrthancCDatabasePlugin.h Wed Feb 11 10:51:45 2015 +0100 @@ -0,0 +1,664 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, 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 . + **/ + + + +#pragma once + +#include "OrthancCPlugin.h" + + +/** @{ */ + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct _OrthancPluginDatabaseContext_t OrthancPluginDatabaseContext; + + + typedef enum + { + _OrthancPluginDatabaseAnswerType_None = 0, + + /* Events */ + _OrthancPluginDatabaseAnswerType_DeletedAttachment = 1, + _OrthancPluginDatabaseAnswerType_DeletedResource = 2, + _OrthancPluginDatabaseAnswerType_RemainingAncestor = 3, + + /* Return value */ + _OrthancPluginDatabaseAnswerType_Attachment = 10, + _OrthancPluginDatabaseAnswerType_Change = 11, + _OrthancPluginDatabaseAnswerType_DicomTag = 12, + _OrthancPluginDatabaseAnswerType_ExportedResource = 13, + _OrthancPluginDatabaseAnswerType_Int32 = 14, + _OrthancPluginDatabaseAnswerType_Int64 = 15, + _OrthancPluginDatabaseAnswerType_Resource = 16, + _OrthancPluginDatabaseAnswerType_String = 17 + } _OrthancPluginDatabaseAnswerType; + + + typedef struct + { + const char* uuid; + int32_t contentType; + uint64_t uncompressedSize; + const char* uncompressedHash; + int32_t compressionType; + uint64_t compressedSize; + const char* compressedHash; + } OrthancPluginAttachment; + + typedef struct + { + uint16_t group; + uint16_t element; + const char* value; + } OrthancPluginDicomTag; + + typedef struct + { + int64_t seq; + int32_t changeType; + OrthancPluginResourceType resourceType; + const char* publicId; + const char* date; + } OrthancPluginChange; + + typedef struct + { + int64_t seq; + OrthancPluginResourceType resourceType; + const char* publicId; + const char* modality; + const char* date; + const char* patientId; + const char* studyInstanceUid; + const char* seriesInstanceUid; + const char* sopInstanceUid; + } OrthancPluginExportedResource; + + + typedef struct + { + OrthancPluginDatabaseContext* database; + _OrthancPluginDatabaseAnswerType type; + int32_t valueInt32; + uint32_t valueUint32; + int64_t valueInt64; + const char *valueString; + const void *valueGeneric; + } _OrthancPluginDatabaseAnswer; + + ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseAnswerString( + OrthancPluginContext* context, + OrthancPluginDatabaseContext* database, + const char* value) + { + _OrthancPluginDatabaseAnswer params; + memset(¶ms, 0, sizeof(params)); + params.database = database; + params.type = _OrthancPluginDatabaseAnswerType_String; + params.valueString = value; + context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, ¶ms); + } + + ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseAnswerChange( + OrthancPluginContext* context, + OrthancPluginDatabaseContext* database, + const OrthancPluginChange* change) + { + _OrthancPluginDatabaseAnswer params; + memset(¶ms, 0, sizeof(params)); + + params.database = database; + params.type = _OrthancPluginDatabaseAnswerType_Change; + params.valueUint32 = 0; + params.valueGeneric = change; + + context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, ¶ms); + } + + ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseAnswerChangesDone( + OrthancPluginContext* context, + OrthancPluginDatabaseContext* database) + { + _OrthancPluginDatabaseAnswer params; + memset(¶ms, 0, sizeof(params)); + + params.database = database; + params.type = _OrthancPluginDatabaseAnswerType_Change; + params.valueUint32 = 1; + params.valueGeneric = NULL; + + context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, ¶ms); + } + + ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseAnswerInt32( + OrthancPluginContext* context, + OrthancPluginDatabaseContext* database, + int32_t value) + { + _OrthancPluginDatabaseAnswer params; + memset(¶ms, 0, sizeof(params)); + params.database = database; + params.type = _OrthancPluginDatabaseAnswerType_Int32; + params.valueInt32 = value; + context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, ¶ms); + } + + ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseAnswerInt64( + OrthancPluginContext* context, + OrthancPluginDatabaseContext* database, + int64_t value) + { + _OrthancPluginDatabaseAnswer params; + memset(¶ms, 0, sizeof(params)); + params.database = database; + params.type = _OrthancPluginDatabaseAnswerType_Int64; + params.valueInt64 = value; + context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, ¶ms); + } + + ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseAnswerExportedResource( + OrthancPluginContext* context, + OrthancPluginDatabaseContext* database, + const OrthancPluginExportedResource* exported) + { + _OrthancPluginDatabaseAnswer params; + memset(¶ms, 0, sizeof(params)); + + params.database = database; + params.type = _OrthancPluginDatabaseAnswerType_ExportedResource; + params.valueUint32 = 0; + params.valueGeneric = exported; + context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, ¶ms); + } + + ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseAnswerExportedResourcesDone( + OrthancPluginContext* context, + OrthancPluginDatabaseContext* database) + { + _OrthancPluginDatabaseAnswer params; + memset(¶ms, 0, sizeof(params)); + + params.database = database; + params.type = _OrthancPluginDatabaseAnswerType_ExportedResource; + params.valueUint32 = 1; + params.valueGeneric = NULL; + context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, ¶ms); + } + + ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseAnswerDicomTag( + OrthancPluginContext* context, + OrthancPluginDatabaseContext* database, + const OrthancPluginDicomTag* tag) + { + _OrthancPluginDatabaseAnswer params; + memset(¶ms, 0, sizeof(params)); + params.database = database; + params.type = _OrthancPluginDatabaseAnswerType_DicomTag; + params.valueGeneric = tag; + context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, ¶ms); + } + + ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseAnswerAttachment( + OrthancPluginContext* context, + OrthancPluginDatabaseContext* database, + const OrthancPluginAttachment* attachment) + { + _OrthancPluginDatabaseAnswer params; + memset(¶ms, 0, sizeof(params)); + params.database = database; + params.type = _OrthancPluginDatabaseAnswerType_Attachment; + params.valueGeneric = attachment; + context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, ¶ms); + } + + ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseAnswerResource( + OrthancPluginContext* context, + OrthancPluginDatabaseContext* database, + int64_t id, + OrthancPluginResourceType resourceType) + { + _OrthancPluginDatabaseAnswer params; + memset(¶ms, 0, sizeof(params)); + params.database = database; + params.type = _OrthancPluginDatabaseAnswerType_Resource; + params.valueInt64 = id; + params.valueInt32 = (int32_t) resourceType; + context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, ¶ms); + } + + ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseSignalDeletedAttachment( + OrthancPluginContext* context, + OrthancPluginDatabaseContext* database, + const OrthancPluginAttachment* attachment) + { + _OrthancPluginDatabaseAnswer params; + memset(¶ms, 0, sizeof(params)); + params.database = database; + params.type = _OrthancPluginDatabaseAnswerType_DeletedAttachment; + params.valueGeneric = attachment; + context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, ¶ms); + } + + ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseSignalDeletedResource( + OrthancPluginContext* context, + OrthancPluginDatabaseContext* database, + const char* publicId, + OrthancPluginResourceType resourceType) + { + _OrthancPluginDatabaseAnswer params; + memset(¶ms, 0, sizeof(params)); + params.database = database; + params.type = _OrthancPluginDatabaseAnswerType_DeletedResource; + params.valueString = publicId; + params.valueInt32 = (int32_t) resourceType; + context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, ¶ms); + } + + ORTHANC_PLUGIN_INLINE void OrthancPluginDatabaseSignalRemainingAncestor( + OrthancPluginContext* context, + OrthancPluginDatabaseContext* database, + const char* ancestorId, + OrthancPluginResourceType ancestorType) + { + _OrthancPluginDatabaseAnswer params; + memset(¶ms, 0, sizeof(params)); + params.database = database; + params.type = _OrthancPluginDatabaseAnswerType_RemainingAncestor; + params.valueString = ancestorId; + params.valueInt32 = (int32_t) ancestorType; + context->InvokeService(context, _OrthancPluginService_DatabaseAnswer, ¶ms); + } + + + + + + typedef struct + { + int32_t (*addAttachment) ( + /* inputs */ + void* payload, + int64_t id, + const OrthancPluginAttachment* attachment); + + int32_t (*attachChild) ( + /* inputs */ + void* payload, + int64_t parent, + int64_t child); + + int32_t (*clearChanges) ( + /* inputs */ + void* payload); + + int32_t (*clearExportedResources) ( + /* inputs */ + void* payload); + + int32_t (*createResource) ( + /* outputs */ + int64_t* id, + /* inputs */ + void* payload, + const char* publicId, + OrthancPluginResourceType resourceType); + + int32_t (*deleteAttachment) ( + /* inputs */ + void* payload, + int64_t id, + int32_t contentType); + + int32_t (*deleteMetadata) ( + /* inputs */ + void* payload, + int64_t id, + int32_t metadataType); + + int32_t (*deleteResource) ( + /* inputs */ + void* payload, + int64_t id); + + /* Output: Use OrthancPluginDatabaseAnswerString() */ + int32_t (*getAllPublicIds) ( + /* outputs */ + OrthancPluginDatabaseContext* context, + /* inputs */ + void* payload, + OrthancPluginResourceType resourceType); + + /* Output: Use OrthancPluginDatabaseAnswerChange() and + * OrthancPluginDatabaseAnswerChangesDone() */ + int32_t (*getChanges) ( + /* outputs */ + OrthancPluginDatabaseContext* context, + /* inputs */ + void* payload, + int64_t since, + uint32_t maxResult); + + /* Output: Use OrthancPluginDatabaseAnswerInt64() */ + int32_t (*getChildrenInternalId) ( + /* outputs */ + OrthancPluginDatabaseContext* context, + /* inputs */ + void* payload, + int64_t id); + + /* Output: Use OrthancPluginDatabaseAnswerString() */ + int32_t (*getChildrenPublicId) ( + /* outputs */ + OrthancPluginDatabaseContext* context, + /* inputs */ + void* payload, + int64_t id); + + /* Output: Use OrthancPluginDatabaseAnswerExportedResource() and + * OrthancPluginDatabaseAnswerExportedResourcesDone() */ + int32_t (*getExportedResources) ( + /* outputs */ + OrthancPluginDatabaseContext* context, + /* inputs */ + void* payload, + int64_t since, + uint32_t maxResult); + + /* Output: Use OrthancPluginDatabaseAnswerChange() */ + int32_t (*getLastChange) ( + /* outputs */ + OrthancPluginDatabaseContext* context, + /* inputs */ + void* payload); + + /* Output: Use OrthancPluginDatabaseAnswerExportedResource() */ + int32_t (*getLastExportedResource) ( + /* outputs */ + OrthancPluginDatabaseContext* context, + /* inputs */ + void* payload); + + /* Output: Use OrthancPluginDatabaseAnswerDicomTag() */ + int32_t (*getMainDicomTags) ( + /* outputs */ + OrthancPluginDatabaseContext* context, + /* inputs */ + void* payload, + int64_t id); + + /* Output: Use OrthancPluginDatabaseAnswerString() */ + int32_t (*getPublicId) ( + /* outputs */ + OrthancPluginDatabaseContext* context, + /* inputs */ + void* payload, + int64_t id); + + int32_t (*getResourceCount) ( + /* outputs */ + uint64_t* target, + /* inputs */ + void* payload, + OrthancPluginResourceType resourceType); + + int32_t (*getResourceType) ( + /* outputs */ + OrthancPluginResourceType* resourceType, + /* inputs */ + void* payload, + int64_t id); + + int32_t (*getTotalCompressedSize) ( + /* outputs */ + uint64_t* target, + /* inputs */ + void* payload); + + int32_t (*getTotalUncompressedSize) ( + /* outputs */ + uint64_t* target, + /* inputs */ + void* payload); + + int32_t (*isExistingResource) ( + /* outputs */ + int32_t* existing, + /* inputs */ + void* payload, + int64_t id); + + int32_t (*isProtectedPatient) ( + /* outputs */ + int32_t* isProtected, + /* inputs */ + void* payload, + int64_t id); + + /* Output: Use OrthancPluginDatabaseAnswerInt32() */ + int32_t (*listAvailableMetadata) ( + /* outputs */ + OrthancPluginDatabaseContext* context, + /* inputs */ + void* payload, + int64_t id); + + /* Output: Use OrthancPluginDatabaseAnswerInt32() */ + int32_t (*listAvailableAttachments) ( + /* outputs */ + OrthancPluginDatabaseContext* context, + /* inputs */ + void* payload, + int64_t id); + + int32_t (*logChange) ( + /* inputs */ + void* payload, + const OrthancPluginChange* change); + + int32_t (*logExportedResource) ( + /* inputs */ + void* payload, + const OrthancPluginExportedResource* exported); + + /* Output: Use OrthancPluginDatabaseAnswerAttachment() */ + int32_t (*lookupAttachment) ( + /* outputs */ + OrthancPluginDatabaseContext* context, + /* inputs */ + void* payload, + int64_t id, + int32_t contentType); + + /* Output: Use OrthancPluginDatabaseAnswerString() */ + int32_t (*lookupGlobalProperty) ( + /* outputs */ + OrthancPluginDatabaseContext* context, + /* inputs */ + void* payload, + int32_t property); + + /* Output: Use OrthancPluginDatabaseAnswerInt64() */ + int32_t (*lookupIdentifier) ( + /* outputs */ + OrthancPluginDatabaseContext* context, + /* inputs */ + void* payload, + const OrthancPluginDicomTag* tag); + + /* Output: Use OrthancPluginDatabaseAnswerInt64() */ + int32_t (*lookupIdentifier2) ( + /* outputs */ + OrthancPluginDatabaseContext* context, + /* inputs */ + void* payload, + const char* value); + + /* Output: Use OrthancPluginDatabaseAnswerString() */ + int32_t (*lookupMetadata) ( + /* outputs */ + OrthancPluginDatabaseContext* context, + /* inputs */ + void* payload, + int64_t id, + int32_t metadata); + + /* Output: Use OrthancPluginDatabaseAnswerInt64() */ + int32_t (*lookupParent) ( + /* outputs */ + OrthancPluginDatabaseContext* context, + /* inputs */ + void* payload, + int64_t id); + + /* Output: Use OrthancPluginDatabaseAnswerResource() */ + int32_t (*lookupResource) ( + /* outputs */ + OrthancPluginDatabaseContext* context, + /* inputs */ + void* payload, + const char* publicId); + + /* Output: Use OrthancPluginDatabaseAnswerInt64() */ + int32_t (*selectPatientToRecycle) ( + /* outputs */ + OrthancPluginDatabaseContext* context, + /* inputs */ + void* payload); + + /* Output: Use OrthancPluginDatabaseAnswerInt64() */ + int32_t (*selectPatientToRecycle2) ( + /* outputs */ + OrthancPluginDatabaseContext* context, + /* inputs */ + void* payload, + int64_t patientIdToAvoid); + + int32_t (*setGlobalProperty) ( + /* inputs */ + void* payload, + int32_t property, + const char* value); + + int32_t (*setMainDicomTag) ( + /* inputs */ + void* payload, + int64_t id, + const OrthancPluginDicomTag* tag); + + int32_t (*setIdentifierTag) ( + /* inputs */ + void* payload, + int64_t id, + const OrthancPluginDicomTag* tag); + + int32_t (*setMetadata) ( + /* inputs */ + void* payload, + int64_t id, + int32_t metadata, + const char* value); + + int32_t (*setProtectedPatient) ( + /* inputs */ + void* payload, + int64_t id, + int32_t isProtected); + + int32_t (*startTransaction) ( + /* inputs */ + void* payload); + + int32_t (*rollbackTransaction) ( + /* inputs */ + void* payload); + + int32_t (*commitTransaction) ( + /* inputs */ + void* payload); + + int32_t (*open) ( + /* inputs */ + void* payload); + + int32_t (*close) ( + /* inputs */ + void* payload); + + } OrthancPluginDatabaseBackend; + + + + typedef struct + { + OrthancPluginDatabaseContext** result; + const OrthancPluginDatabaseBackend* backend; + void* payload; + } _OrthancPluginRegisterDatabaseBackend; + + ORTHANC_PLUGIN_INLINE OrthancPluginDatabaseContext* OrthancPluginRegisterDatabaseBackend( + OrthancPluginContext* context, + const OrthancPluginDatabaseBackend* backend, + void* payload) + { + OrthancPluginDatabaseContext* result = NULL; + + _OrthancPluginRegisterDatabaseBackend params; + memset(¶ms, 0, sizeof(params)); + params.backend = backend; + params.result = &result; + params.payload = payload; + + if (context->InvokeService(context, _OrthancPluginService_RegisterDatabaseBackend, ¶ms) || + result == NULL) + { + /* Error */ + return NULL; + } + else + { + return result; + } + } + + + +#ifdef __cplusplus +} +#endif + + +/** @} */ + diff -r f796207e3df1 -r d1a430176401 Plugins/Include/OrthancCPlugin.h --- a/Plugins/Include/OrthancCPlugin.h Wed Feb 11 10:26:17 2015 +0100 +++ b/Plugins/Include/OrthancCPlugin.h Wed Feb 11 10:51:45 2015 +0100 @@ -16,6 +16,7 @@ * - Register all its REST callbacks using ::OrthancPluginRegisterRestCallback(). * - Register all its callbacks for received instances using ::OrthancPluginRegisterOnStoredInstanceCallback(). * - Possibly register a custom storage area using ::OrthancPluginRegisterStorageArea(). + * - Possibly register a custom database back-end area using ::OrthancPluginRegisterDatabaseBackend(). * -# void OrthancPluginFinalize(): * This function is invoked by Orthanc during its shutdown. The plugin * must free all its memory. @@ -293,7 +294,12 @@ _OrthancPluginService_GetInstanceJson = 4003, _OrthancPluginService_GetInstanceSimplifiedJson = 4004, _OrthancPluginService_HasInstanceMetadata = 4005, - _OrthancPluginService_GetInstanceMetadata = 4006 + _OrthancPluginService_GetInstanceMetadata = 4006, + + /* Services for plugins implementing a database back-end */ + _OrthancPluginService_RegisterDatabaseBackend = 5000, + _OrthancPluginService_DatabaseAnswer = 5001 + } _OrthancPluginService; diff -r f796207e3df1 -r d1a430176401 Plugins/Include/OrthancCppDatabasePlugin.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Include/OrthancCppDatabasePlugin.h Wed Feb 11 10:51:45 2015 +0100 @@ -0,0 +1,1532 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, 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 . + **/ + + + +#pragma once + +#include "OrthancCDatabasePlugin.h" + +#include +#include +#include + +namespace OrthancPlugins +{ + // This class mimics "boost::noncopyable" + class NonCopyable + { + private: + NonCopyable(const NonCopyable&); + + NonCopyable& operator= (const NonCopyable&); + + protected: + NonCopyable() + { + } + + ~NonCopyable() + { + } + }; + + + + class DatabaseBackendOutput : public NonCopyable + { + friend class DatabaseBackendAdapter; + + private: + enum AllowedAnswers + { + AllowedAnswers_All, + AllowedAnswers_None, + AllowedAnswers_Attachment, + AllowedAnswers_Change, + AllowedAnswers_DicomTag, + AllowedAnswers_ExportedResource + }; + + OrthancPluginContext* context_; + OrthancPluginDatabaseContext* database_; + AllowedAnswers allowedAnswers_; + + void SetAllowedAnswers(AllowedAnswers allowed) + { + allowedAnswers_ = allowed; + } + + public: + DatabaseBackendOutput(OrthancPluginContext* context, + OrthancPluginDatabaseContext* database) : + context_(context), + database_(database), + allowedAnswers_(AllowedAnswers_All /* for unit tests */) + { + } + + void LogError(const std::string& message) + { + OrthancPluginLogError(context_, message.c_str()); + } + + void LogWarning(const std::string& message) + { + OrthancPluginLogWarning(context_, message.c_str()); + } + + void LogInfo(const std::string& message) + { + OrthancPluginLogInfo(context_, message.c_str()); + } + + void SignalDeletedAttachment(const std::string& uuid, + int32_t contentType, + uint64_t uncompressedSize, + const std::string& uncompressedHash, + int32_t compressionType, + uint64_t compressedSize, + const std::string& compressedHash) + { + OrthancPluginAttachment attachment; + attachment.uuid = uuid.c_str(); + attachment.contentType = contentType; + attachment.uncompressedSize = uncompressedSize; + attachment.uncompressedHash = uncompressedHash.c_str(); + attachment.compressionType = compressionType; + attachment.compressedSize = compressedSize; + attachment.compressedHash = compressedHash.c_str(); + + OrthancPluginDatabaseSignalDeletedAttachment(context_, database_, &attachment); + } + + void SignalDeletedResource(const std::string& publicId, + OrthancPluginResourceType resourceType) + { + OrthancPluginDatabaseSignalDeletedResource(context_, database_, publicId.c_str(), resourceType); + } + + void SignalRemainingAncestor(const std::string& ancestorId, + OrthancPluginResourceType ancestorType) + { + OrthancPluginDatabaseSignalRemainingAncestor(context_, database_, ancestorId.c_str(), ancestorType); + } + + void AnswerAttachment(const std::string& uuid, + int32_t contentType, + uint64_t uncompressedSize, + const std::string& uncompressedHash, + int32_t compressionType, + uint64_t compressedSize, + const std::string& compressedHash) + { + if (allowedAnswers_ != AllowedAnswers_All && + allowedAnswers_ != AllowedAnswers_Attachment) + { + throw std::runtime_error("Cannot answer with an attachment in the current state"); + } + + OrthancPluginAttachment attachment; + attachment.uuid = uuid.c_str(); + attachment.contentType = contentType; + attachment.uncompressedSize = uncompressedSize; + attachment.uncompressedHash = uncompressedHash.c_str(); + attachment.compressionType = compressionType; + attachment.compressedSize = compressedSize; + attachment.compressedHash = compressedHash.c_str(); + + OrthancPluginDatabaseAnswerAttachment(context_, database_, &attachment); + } + + void AnswerChange(int64_t seq, + int32_t changeType, + OrthancPluginResourceType resourceType, + const std::string& publicId, + const std::string& date) + { + if (allowedAnswers_ != AllowedAnswers_All && + allowedAnswers_ != AllowedAnswers_Change) + { + throw std::runtime_error("Cannot answer with a change in the current state"); + } + + OrthancPluginChange change; + change.seq = seq; + change.changeType = changeType; + change.resourceType = resourceType; + change.publicId = publicId.c_str(); + change.date = date.c_str(); + + OrthancPluginDatabaseAnswerChange(context_, database_, &change); + } + + void AnswerDicomTag(uint16_t group, + uint16_t element, + const std::string& value) + { + if (allowedAnswers_ != AllowedAnswers_All && + allowedAnswers_ != AllowedAnswers_DicomTag) + { + throw std::runtime_error("Cannot answer with a DICOM tag in the current state"); + } + + OrthancPluginDicomTag tag; + tag.group = group; + tag.element = element; + tag.value = value.c_str(); + + OrthancPluginDatabaseAnswerDicomTag(context_, database_, &tag); + } + + void AnswerExportedResource(int64_t seq, + OrthancPluginResourceType resourceType, + const std::string& publicId, + const std::string& modality, + const std::string& date, + const std::string& patientId, + const std::string& studyInstanceUid, + const std::string& seriesInstanceUid, + const std::string& sopInstanceUid) + { + if (allowedAnswers_ != AllowedAnswers_All && + allowedAnswers_ != AllowedAnswers_ExportedResource) + { + throw std::runtime_error("Cannot answer with an exported resource in the current state"); + } + + OrthancPluginExportedResource exported; + exported.seq = seq; + exported.resourceType = resourceType; + exported.publicId = publicId.c_str(); + exported.modality = modality.c_str(); + exported.date = date.c_str(); + exported.patientId = patientId.c_str(); + exported.studyInstanceUid = studyInstanceUid.c_str(); + exported.seriesInstanceUid = seriesInstanceUid.c_str(); + exported.sopInstanceUid = sopInstanceUid.c_str(); + + OrthancPluginDatabaseAnswerExportedResource(context_, database_, &exported); + } + }; + + + + class IDatabaseBackend : public NonCopyable + { + friend class DatabaseBackendAdapter; + + private: + DatabaseBackendOutput* output_; + + void Finalize() + { + if (output_ != NULL) + { + delete output_; + output_ = NULL; + } + } + + protected: + DatabaseBackendOutput& GetOutput() + { + return *output_; + } + + public: + IDatabaseBackend() : output_(NULL) + { + } + + virtual ~IDatabaseBackend() + { + Finalize(); + } + + // This takes the ownership + void RegisterOutput(DatabaseBackendOutput* output) + { + Finalize(); + output_ = output; + } + + virtual void Open() = 0; + + virtual void Close() = 0; + + virtual void AddAttachment(int64_t id, + const OrthancPluginAttachment& attachment) = 0; + + virtual void AttachChild(int64_t parent, + int64_t child) = 0; + + virtual void ClearChanges() = 0; + + virtual void ClearExportedResources() = 0; + + virtual int64_t CreateResource(const char* publicId, + OrthancPluginResourceType type) = 0; + + virtual void DeleteAttachment(int64_t id, + int32_t attachment) = 0; + + virtual void DeleteMetadata(int64_t id, + int32_t metadataType) = 0; + + virtual void DeleteResource(int64_t id) = 0; + + virtual void GetAllPublicIds(std::list& target, + OrthancPluginResourceType resourceType) = 0; + + /* Use GetOutput().AnswerChange() */ + virtual void GetChanges(bool& done /*out*/, + int64_t since, + uint32_t maxResults) = 0; + + virtual void GetChildrenInternalId(std::list& target /*out*/, + int64_t id) = 0; + + virtual void GetChildrenPublicId(std::list& target /*out*/, + int64_t id) = 0; + + /* Use GetOutput().AnswerExportedResource() */ + virtual void GetExportedResources(bool& done /*out*/, + int64_t since, + uint32_t maxResults) = 0; + + /* Use GetOutput().AnswerChange() */ + virtual void GetLastChange() = 0; + + /* Use GetOutput().AnswerExportedResource() */ + virtual void GetLastExportedResource() = 0; + + /* Use GetOutput().AnswerDicomTag() */ + virtual void GetMainDicomTags(int64_t id) = 0; + + virtual std::string GetPublicId(int64_t resourceId) = 0; + + virtual uint64_t GetResourceCount(OrthancPluginResourceType resourceType) = 0; + + virtual OrthancPluginResourceType GetResourceType(int64_t resourceId) = 0; + + virtual uint64_t GetTotalCompressedSize() = 0; + + virtual uint64_t GetTotalUncompressedSize() = 0; + + virtual bool IsExistingResource(int64_t internalId) = 0; + + virtual bool IsProtectedPatient(int64_t internalId) = 0; + + virtual void ListAvailableMetadata(std::list& target /*out*/, + int64_t id) = 0; + + virtual void ListAvailableAttachments(std::list& target /*out*/, + int64_t id) = 0; + + virtual void LogChange(const OrthancPluginChange& change) = 0; + + virtual void LogExportedResource(const OrthancPluginExportedResource& resource) = 0; + + /* Use GetOutput().AnswerAttachment() */ + virtual bool LookupAttachment(int64_t id, + int32_t contentType) = 0; + + virtual bool LookupGlobalProperty(std::string& target /*out*/, + int32_t property) = 0; + + /** + * "Identifiers" are necessarily one of the following tags: + * PatientID (0x0010, 0x0020), StudyInstanceUID (0x0020, 0x000d), + * SeriesInstanceUID (0x0020, 0x000e), SOPInstanceUID (0x0008, + * 0x0018) or AccessionNumber (0x0008, 0x0050). + **/ + virtual void LookupIdentifier(std::list& target /*out*/, + uint16_t group, + uint16_t element, + const char* value) = 0; + + virtual void LookupIdentifier(std::list& target /*out*/, + const char* value) = 0; + + virtual bool LookupMetadata(std::string& target /*out*/, + int64_t id, + int32_t metadataType) = 0; + + virtual bool LookupParent(int64_t& parentId /*out*/, + int64_t resourceId) = 0; + + virtual bool LookupResource(int64_t& id /*out*/, + OrthancPluginResourceType& type /*out*/, + const char* publicId) = 0; + + virtual bool SelectPatientToRecycle(int64_t& internalId /*out*/) = 0; + + virtual bool SelectPatientToRecycle(int64_t& internalId /*out*/, + int64_t patientIdToAvoid) = 0; + + virtual void SetGlobalProperty(int32_t property, + const char* value) = 0; + + virtual void SetMainDicomTag(int64_t id, + uint16_t group, + uint16_t element, + const char* value) = 0; + + virtual void SetIdentifierTag(int64_t id, + uint16_t group, + uint16_t element, + const char* value) = 0; + + virtual void SetMetadata(int64_t id, + int32_t metadataType, + const char* value) = 0; + + virtual void SetProtectedPatient(int64_t internalId, + bool isProtected) = 0; + + virtual void StartTransaction() = 0; + + virtual void RollbackTransaction() = 0; + + virtual void CommitTransaction() = 0; + }; + + + + class DatabaseBackendAdapter + { + private: + // This class cannot be instantiated + DatabaseBackendAdapter() + { + } + + static void LogError(IDatabaseBackend* backend, + const std::runtime_error& e) + { + backend->GetOutput().LogError("Exception in database back-end: " + std::string(e.what())); + } + + + static int32_t AddAttachment(void* payload, + int64_t id, + const OrthancPluginAttachment* attachment) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + backend->AddAttachment(id, *attachment); + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t AttachChild(void* payload, + int64_t parent, + int64_t child) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + backend->AttachChild(parent, child); + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t ClearChanges(void* payload) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + backend->ClearChanges(); + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t ClearExportedResources(void* payload) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + backend->ClearExportedResources(); + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t CreateResource(int64_t* id, + void* payload, + const char* publicId, + OrthancPluginResourceType resourceType) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + *id = backend->CreateResource(publicId, resourceType); + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t DeleteAttachment(void* payload, + int64_t id, + int32_t contentType) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + backend->DeleteAttachment(id, contentType); + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t DeleteMetadata(void* payload, + int64_t id, + int32_t metadataType) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + backend->DeleteMetadata(id, metadataType); + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t DeleteResource(void* payload, + int64_t id) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + backend->DeleteResource(id); + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t GetAllPublicIds(OrthancPluginDatabaseContext* context, + void* payload, + OrthancPluginResourceType resourceType) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + std::list ids; + backend->GetAllPublicIds(ids, resourceType); + + for (std::list::const_iterator + it = ids.begin(); it != ids.end(); ++it) + { + OrthancPluginDatabaseAnswerString(backend->GetOutput().context_, + backend->GetOutput().database_, + it->c_str()); + } + + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t GetChanges(OrthancPluginDatabaseContext* context, + void* payload, + int64_t since, + uint32_t maxResult) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_Change); + + try + { + bool done; + backend->GetChanges(done, since, maxResult); + + if (done) + { + OrthancPluginDatabaseAnswerChangesDone(backend->GetOutput().context_, + backend->GetOutput().database_); + } + + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t GetChildrenInternalId(OrthancPluginDatabaseContext* context, + void* payload, + int64_t id) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + std::list target; + backend->GetChildrenInternalId(target, id); + + for (std::list::const_iterator + it = target.begin(); it != target.end(); ++it) + { + OrthancPluginDatabaseAnswerInt64(backend->GetOutput().context_, + backend->GetOutput().database_, *it); + } + + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t GetChildrenPublicId(OrthancPluginDatabaseContext* context, + void* payload, + int64_t id) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + std::list ids; + backend->GetChildrenPublicId(ids, id); + + for (std::list::const_iterator + it = ids.begin(); it != ids.end(); ++it) + { + OrthancPluginDatabaseAnswerString(backend->GetOutput().context_, + backend->GetOutput().database_, + it->c_str()); + } + + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t GetExportedResources(OrthancPluginDatabaseContext* context, + void* payload, + int64_t since, + uint32_t maxResult) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_ExportedResource); + + try + { + bool done; + backend->GetExportedResources(done, since, maxResult); + + if (done) + { + OrthancPluginDatabaseAnswerExportedResourcesDone(backend->GetOutput().context_, + backend->GetOutput().database_); + } + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t GetLastChange(OrthancPluginDatabaseContext* context, + void* payload) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_Change); + + try + { + backend->GetLastChange(); + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t GetLastExportedResource(OrthancPluginDatabaseContext* context, + void* payload) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_ExportedResource); + + try + { + backend->GetLastExportedResource(); + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t GetMainDicomTags(OrthancPluginDatabaseContext* context, + void* payload, + int64_t id) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_DicomTag); + + try + { + backend->GetMainDicomTags(id); + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t GetPublicId(OrthancPluginDatabaseContext* context, + void* payload, + int64_t id) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + std::string s = backend->GetPublicId(id); + OrthancPluginDatabaseAnswerString(backend->GetOutput().context_, + backend->GetOutput().database_, + s.c_str()); + + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t GetResourceCount(uint64_t* target, + void* payload, + OrthancPluginResourceType resourceType) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + *target = backend->GetResourceCount(resourceType); + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t GetResourceType(OrthancPluginResourceType* resourceType, + void* payload, + int64_t id) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + *resourceType = backend->GetResourceType(id); + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t GetTotalCompressedSize(uint64_t* target, + void* payload) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + *target = backend->GetTotalCompressedSize(); + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t GetTotalUncompressedSize(uint64_t* target, + void* payload) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + *target = backend->GetTotalUncompressedSize(); + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t IsExistingResource(int32_t* existing, + void* payload, + int64_t id) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + *existing = backend->IsExistingResource(id); + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t IsProtectedPatient(int32_t* isProtected, + void* payload, + int64_t id) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + *isProtected = backend->IsProtectedPatient(id); + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t ListAvailableMetadata(OrthancPluginDatabaseContext* context, + void* payload, + int64_t id) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + std::list target; + backend->ListAvailableMetadata(target, id); + + for (std::list::const_iterator + it = target.begin(); it != target.end(); ++it) + { + OrthancPluginDatabaseAnswerInt32(backend->GetOutput().context_, + backend->GetOutput().database_, + *it); + } + + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t ListAvailableAttachments(OrthancPluginDatabaseContext* context, + void* payload, + int64_t id) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + std::list target; + backend->ListAvailableAttachments(target, id); + + for (std::list::const_iterator + it = target.begin(); it != target.end(); ++it) + { + OrthancPluginDatabaseAnswerInt32(backend->GetOutput().context_, + backend->GetOutput().database_, + *it); + } + + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t LogChange(void* payload, + const OrthancPluginChange* change) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + backend->LogChange(*change); + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t LogExportedResource(void* payload, + const OrthancPluginExportedResource* exported) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + backend->LogExportedResource(*exported); + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t LookupAttachment(OrthancPluginDatabaseContext* context, + void* payload, + int64_t id, + int32_t contentType) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_Attachment); + + try + { + backend->LookupAttachment(id, contentType); + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t LookupGlobalProperty(OrthancPluginDatabaseContext* context, + void* payload, + int32_t property) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + std::string s; + if (backend->LookupGlobalProperty(s, property)) + { + OrthancPluginDatabaseAnswerString(backend->GetOutput().context_, + backend->GetOutput().database_, + s.c_str()); + } + + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t LookupIdentifier(OrthancPluginDatabaseContext* context, + void* payload, + const OrthancPluginDicomTag* tag) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + std::list target; + backend->LookupIdentifier(target, tag->group, tag->element, tag->value); + + for (std::list::const_iterator + it = target.begin(); it != target.end(); ++it) + { + OrthancPluginDatabaseAnswerInt64(backend->GetOutput().context_, + backend->GetOutput().database_, *it); + } + + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t LookupIdentifier2(OrthancPluginDatabaseContext* context, + void* payload, + const char* value) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + std::list target; + backend->LookupIdentifier(target, value); + + for (std::list::const_iterator + it = target.begin(); it != target.end(); ++it) + { + OrthancPluginDatabaseAnswerInt64(backend->GetOutput().context_, + backend->GetOutput().database_, *it); + } + + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t LookupMetadata(OrthancPluginDatabaseContext* context, + void* payload, + int64_t id, + int32_t metadata) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + std::string s; + if (backend->LookupMetadata(s, id, metadata)) + { + OrthancPluginDatabaseAnswerString(backend->GetOutput().context_, + backend->GetOutput().database_, s.c_str()); + } + + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t LookupParent(OrthancPluginDatabaseContext* context, + void* payload, + int64_t id) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + int64_t parent; + if (backend->LookupParent(parent, id)) + { + OrthancPluginDatabaseAnswerInt64(backend->GetOutput().context_, + backend->GetOutput().database_, parent); + } + + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t LookupResource(OrthancPluginDatabaseContext* context, + void* payload, + const char* publicId) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + int64_t id; + OrthancPluginResourceType type; + if (backend->LookupResource(id, type, publicId)) + { + OrthancPluginDatabaseAnswerResource(backend->GetOutput().context_, + backend->GetOutput().database_, + id, type); + } + + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t SelectPatientToRecycle(OrthancPluginDatabaseContext* context, + void* payload) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + int64_t id; + if (backend->SelectPatientToRecycle(id)) + { + OrthancPluginDatabaseAnswerInt64(backend->GetOutput().context_, + backend->GetOutput().database_, id); + } + + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t SelectPatientToRecycle2(OrthancPluginDatabaseContext* context, + void* payload, + int64_t patientIdToAvoid) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + int64_t id; + if (backend->SelectPatientToRecycle(id, patientIdToAvoid)) + { + OrthancPluginDatabaseAnswerInt64(backend->GetOutput().context_, + backend->GetOutput().database_, id); + } + + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t SetGlobalProperty(void* payload, + int32_t property, + const char* value) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + backend->SetGlobalProperty(property, value); + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t SetMainDicomTag(void* payload, + int64_t id, + const OrthancPluginDicomTag* tag) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + backend->SetMainDicomTag(id, tag->group, tag->element, tag->value); + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t SetIdentifierTag(void* payload, + int64_t id, + const OrthancPluginDicomTag* tag) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + backend->SetIdentifierTag(id, tag->group, tag->element, tag->value); + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t SetMetadata(void* payload, + int64_t id, + int32_t metadata, + const char* value) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + backend->SetMetadata(id, metadata, value); + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t SetProtectedPatient(void* payload, + int64_t id, + int32_t isProtected) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + backend->SetProtectedPatient(id, isProtected); + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t StartTransaction(void* payload) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + backend->StartTransaction(); + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t RollbackTransaction(void* payload) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + backend->RollbackTransaction(); + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t CommitTransaction(void* payload) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + backend->CommitTransaction(); + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t Open(void* payload) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + backend->Open(); + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + static int32_t Close(void* payload) + { + IDatabaseBackend* backend = reinterpret_cast(payload); + backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); + + try + { + backend->Close(); + return 0; + } + catch (std::runtime_error& e) + { + LogError(backend, e); + return -1; + } + } + + + public: + static void Register(OrthancPluginContext* context, + IDatabaseBackend& backend) + { + OrthancPluginDatabaseBackend params; + memset(¶ms, 0, sizeof(params)); + + params.addAttachment = AddAttachment; + params.attachChild = AttachChild; + params.clearChanges = ClearChanges; + params.clearExportedResources = ClearExportedResources; + params.createResource = CreateResource; + params.deleteAttachment = DeleteAttachment; + params.deleteMetadata = DeleteMetadata; + params.deleteResource = DeleteResource; + params.getAllPublicIds = GetAllPublicIds; + params.getChanges = GetChanges; + params.getChildrenInternalId = GetChildrenInternalId; + params.getChildrenPublicId = GetChildrenPublicId; + params.getExportedResources = GetExportedResources; + params.getLastChange = GetLastChange; + params.getLastExportedResource = GetLastExportedResource; + params.getMainDicomTags = GetMainDicomTags; + params.getPublicId = GetPublicId; + params.getResourceCount = GetResourceCount; + params.getResourceType = GetResourceType; + params.getTotalCompressedSize = GetTotalCompressedSize; + params.getTotalUncompressedSize = GetTotalUncompressedSize; + params.isExistingResource = IsExistingResource; + params.isProtectedPatient = IsProtectedPatient; + params.listAvailableMetadata = ListAvailableMetadata; + params.listAvailableAttachments = ListAvailableAttachments; + params.logChange = LogChange; + params.logExportedResource = LogExportedResource; + params.lookupAttachment = LookupAttachment; + params.lookupGlobalProperty = LookupGlobalProperty; + params.lookupIdentifier = LookupIdentifier; + params.lookupIdentifier2 = LookupIdentifier2; + params.lookupMetadata = LookupMetadata; + params.lookupParent = LookupParent; + params.lookupResource = LookupResource; + params.selectPatientToRecycle = SelectPatientToRecycle; + params.selectPatientToRecycle2 = SelectPatientToRecycle2; + params.setGlobalProperty = SetGlobalProperty; + params.setMainDicomTag = SetMainDicomTag; + params.setIdentifierTag = SetIdentifierTag; + params.setMetadata = SetMetadata; + params.setProtectedPatient = SetProtectedPatient; + params.startTransaction = StartTransaction; + params.rollbackTransaction = RollbackTransaction; + params.commitTransaction = CommitTransaction; + params.open = Open; + params.close = Close; + + OrthancPluginDatabaseContext* database = OrthancPluginRegisterDatabaseBackend(context, ¶ms, &backend); + if (!context) + { + throw std::runtime_error("Unable to register the database backend"); + } + + backend.RegisterOutput(new DatabaseBackendOutput(context, database)); + } + }; +} diff -r f796207e3df1 -r d1a430176401 Resources/OrthancPlugin.doxygen --- a/Resources/OrthancPlugin.doxygen Wed Feb 11 10:26:17 2015 +0100 +++ b/Resources/OrthancPlugin.doxygen Wed Feb 11 10:51:45 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