# HG changeset patch # User Sebastien Jodogne # Date 1680000474 -7200 # Node ID be7de633695c6a051e9952439285ba33924a19a2 # Parent b5e2c1e488288aaf2e708a6359a506fe0d0bdd4b started DatabaseBackendAdapterV4 diff -r b5e2c1e48828 -r be7de633695c Framework/Plugins/DatabaseBackendAdapterV2.cpp --- a/Framework/Plugins/DatabaseBackendAdapterV2.cpp Tue Mar 28 11:40:07 2023 +0200 +++ b/Framework/Plugins/DatabaseBackendAdapterV2.cpp Tue Mar 28 12:47:54 2023 +0200 @@ -1632,17 +1632,21 @@ void DatabaseBackendAdapterV2::Register(IDatabaseBackend* backend) { - if (backend == NULL) { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } + std::unique_ptr protection(backend); + + if (backend == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } - if (adapter_.get() != NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + if (adapter_.get() != NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + adapter_.reset(new Adapter(protection.release())); } - - adapter_.reset(new Adapter(backend)); OrthancPluginDatabaseBackend params; memset(¶ms, 0, sizeof(params)); diff -r b5e2c1e48828 -r be7de633695c Framework/Plugins/DatabaseBackendAdapterV3.cpp --- a/Framework/Plugins/DatabaseBackendAdapterV3.cpp Tue Mar 28 11:40:07 2023 +0200 +++ b/Framework/Plugins/DatabaseBackendAdapterV3.cpp Tue Mar 28 12:47:54 2023 +0200 @@ -1988,6 +1988,8 @@ size_t countConnections, unsigned int maxDatabaseRetries) { + std::unique_ptr protection(backend); + if (isBackendInUse_) { throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); @@ -2071,12 +2073,13 @@ params.setProtectedPatient = SetProtectedPatient; params.setResourcesContent = SetResourcesContent; - OrthancPluginContext* context = backend->GetContext(); + OrthancPluginContext* context = protection->GetContext(); if (OrthancPluginRegisterDatabaseBackendV3( context, ¶ms, sizeof(params), maxDatabaseRetries, - new Adapter(backend, countConnections)) != OrthancPluginErrorCode_Success) + new Adapter(protection.release(), countConnections)) != OrthancPluginErrorCode_Success) { + delete backend; throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Unable to register the database backend"); } diff -r b5e2c1e48828 -r be7de633695c Framework/Plugins/DatabaseBackendAdapterV4.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Plugins/DatabaseBackendAdapterV4.cpp Tue Mar 28 12:47:54 2023 +0200 @@ -0,0 +1,216 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2022 Osimis S.A., Belgium + * Copyright (C) 2021-2022 Sebastien Jodogne, ICTEAM UCLouvain, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "DatabaseBackendAdapterV4.h" + +#if defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) // Macro introduced in Orthanc 1.3.1 +# if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 12, 0) + +#include // Include protobuf messages + +#include +#include +#include + +#include +#include +#include +#include + + +#define ORTHANC_PLUGINS_DATABASE_CATCH(context) \ + + +namespace OrthancDatabases +{ + static bool isBackendInUse_ = false; // Only for sanity checks + + + static void ProcessDatabaseOperation(Orthanc::DatabasePluginMessages::DatabaseResponse& response, + const Orthanc::DatabasePluginMessages::DatabaseRequest& request, + IndexBackend& backend) + { + switch (request.operation()) + { + case Orthanc::DatabasePluginMessages::OPERATION_GET_SYSTEM_INFORMATION: + response.mutable_get_system_information()->set_supports_revisions(backend.HasRevisionsSupport()); + break; + + default: + LOG(ERROR) << "Not implemented database operation from protobuf: " << request.operation(); + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + } + + + static void ProcessTransactionOperation(Orthanc::DatabasePluginMessages::TransactionResponse& response, + const Orthanc::DatabasePluginMessages::TransactionRequest& request, + IndexBackend& backend) + { + switch (request.operation()) + { + default: + LOG(ERROR) << "Not implemented transaction operation from protobuf: " << request.operation(); + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + } + + + static OrthancPluginErrorCode CallBackend(OrthancPluginMemoryBuffer64* serializedResponse, + void* rawBackend, + const void* requestData, + uint64_t requestSize) + { + Orthanc::DatabasePluginMessages::Request request; + if (!request.ParseFromArray(requestData, requestSize)) + { + LOG(ERROR) << "Cannot parse message from the Orthanc core using protobuf"; + return OrthancPluginErrorCode_InternalError; + } + + if (rawBackend == NULL) + { + LOG(ERROR) << "Received a NULL pointer from the database"; + return OrthancPluginErrorCode_InternalError; + } + + IndexBackend& backend = *reinterpret_cast(rawBackend); + + try + { + Orthanc::DatabasePluginMessages::Response response; + + switch (request.type()) + { + case Orthanc::DatabasePluginMessages::REQUEST_DATABASE: + ProcessDatabaseOperation(*response.mutable_database_response(), request.database_request(), backend); + break; + + case Orthanc::DatabasePluginMessages::REQUEST_TRANSACTION: + ProcessTransactionOperation(*response.mutable_transaction_response(), request.transaction_request(), backend); + break; + + default: + LOG(ERROR) << "Not implemented request type from protobuf: " << request.type(); + break; + } + + std::string s; + if (!response.SerializeToString(&s)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Cannot serialize to protobuf"); + } + + if (OrthancPluginCreateMemoryBuffer64(backend.GetContext(), serializedResponse, s.size()) != OrthancPluginErrorCode_Success) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotEnoughMemory, "Cannot allocate a memory buffer"); + } + + if (!s.empty()) + { + assert(serializedResponse->size == s.size()); + memcpy(serializedResponse->data, s.c_str(), s.size()); + } + + return OrthancPluginErrorCode_Success; + } + catch (::Orthanc::OrthancException& e) + { + LOG(ERROR) << "Exception in database back-end: " << e.What(); + return static_cast(e.GetErrorCode()); + } + catch (::std::runtime_error& e) + { + LOG(ERROR) << "Exception in database back-end: " << e.what(); + return OrthancPluginErrorCode_DatabasePlugin; + } + catch (...) + { + LOG(ERROR) << "Native exception"; + return OrthancPluginErrorCode_DatabasePlugin; + } + } + + static void FinalizeBackend(void* rawBackend) + { + if (rawBackend != NULL) + { + IndexBackend* backend = reinterpret_cast(rawBackend); + + if (isBackendInUse_) + { + isBackendInUse_ = false; + } + else + { + LOG(ERROR) << "More than one index backend was registered, internal error"; + } + + delete backend; + } + else + { + LOG(ERROR) << "Received a null pointer from the Orthanc core, internal error"; + } + } + + + void DatabaseBackendAdapterV4::Register(IndexBackend* backend, + size_t countConnections, + unsigned int maxDatabaseRetries) + { + std::unique_ptr protection(backend); + + if (isBackendInUse_) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + if (backend == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + + OrthancPluginContext* context = backend->GetContext(); + + if (OrthancPluginRegisterDatabaseBackendV4(context, protection.release(), maxDatabaseRetries, + CallBackend, FinalizeBackend) != OrthancPluginErrorCode_Success) + { + delete backend; + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Unable to register the database backend"); + } + + isBackendInUse_ = true; + } + + + void DatabaseBackendAdapterV4::Finalize() + { + if (isBackendInUse_) + { + LOG(ERROR) << "The Orthanc core has not destructed the index backend, internal error"; + } + } +} + +# endif +#endif diff -r b5e2c1e48828 -r be7de633695c Framework/Plugins/DatabaseBackendAdapterV4.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Plugins/DatabaseBackendAdapterV4.h Tue Mar 28 12:47:54 2023 +0200 @@ -0,0 +1,59 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2022 Osimis S.A., Belgium + * Copyright (C) 2021-2022 Sebastien Jodogne, ICTEAM UCLouvain, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + + +#pragma once + +#include "IndexBackend.h" + + +#if defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) // Macro introduced in Orthanc 1.3.1 +# if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 12, 0) + +namespace OrthancDatabases +{ + /** + * @brief Bridge between C and C++ database engines. + * + * Class creating the bridge between the C low-level primitives for + * custom database engines, and the high-level IDatabaseBackend C++ + * interface, through ProtocolBuffers for Orthanc >= 1.12.0. + **/ + class DatabaseBackendAdapterV4 + { + private: + // This class cannot be instantiated + DatabaseBackendAdapterV4() + { + } + + public: + static void Register(IndexBackend* backend, + size_t countConnections, + unsigned int maxDatabaseRetries); + + static void Finalize(); + }; +} + +# endif +#endif diff -r b5e2c1e48828 -r be7de633695c Framework/Plugins/IndexBackend.cpp --- a/Framework/Plugins/IndexBackend.cpp Tue Mar 28 11:40:07 2023 +0200 +++ b/Framework/Plugins/IndexBackend.cpp Tue Mar 28 12:47:54 2023 +0200 @@ -28,6 +28,7 @@ #include "../Common/Utf8StringValue.h" #include "DatabaseBackendAdapterV2.h" #include "DatabaseBackendAdapterV3.h" +#include "DatabaseBackendAdapterV4.h" #include "GlobalProperties.h" #include // For std::unique_ptr<> @@ -35,13 +36,6 @@ #include -#if defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) // Macro introduced in Orthanc 1.3.1 -# if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 12, 0) -# include "OrthancDatabasePlugin.pb.h" // Include protobuf messages -# endif -#endif - - namespace OrthancDatabases { static std::string ConvertWildcardToLike(const std::string& query) @@ -2617,13 +2611,23 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); } + LOG(WARNING) << "The index plugin will use " << countConnections << " connection(s) to the database, " + << "and will retry up to " << maxDatabaseRetries << " time(s) in the case of a collision"; + +#if defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) // Macro introduced in Orthanc 1.3.1 +# if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 12, 0) + if (OrthancPluginCheckVersionAdvanced(backend->GetContext(), 1, 12, 0) == 1) + { + OrthancDatabases::DatabaseBackendAdapterV4::Register(backend, countConnections, maxDatabaseRetries); + return; + } +# endif +#endif + #if defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) // Macro introduced in Orthanc 1.3.1 # if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 9, 2) if (OrthancPluginCheckVersionAdvanced(backend->GetContext(), 1, 9, 2) == 1) { - LOG(WARNING) << "The index plugin will use " << countConnections << " connection(s) to the database, " - << "and will retry up to " << maxDatabaseRetries << " time(s) in the case of a collision"; - OrthancDatabases::DatabaseBackendAdapterV3::Register(backend, countConnections, maxDatabaseRetries); return; } diff -r b5e2c1e48828 -r be7de633695c Resources/CMake/DatabasesPluginConfiguration.cmake --- a/Resources/CMake/DatabasesPluginConfiguration.cmake Tue Mar 28 11:40:07 2023 +0200 +++ b/Resources/CMake/DatabasesPluginConfiguration.cmake Tue Mar 28 12:47:54 2023 +0200 @@ -92,6 +92,7 @@ ${ORTHANC_CORE_SOURCES} ${ORTHANC_DATABASES_ROOT}/Framework/Plugins/DatabaseBackendAdapterV2.cpp ${ORTHANC_DATABASES_ROOT}/Framework/Plugins/DatabaseBackendAdapterV3.cpp + ${ORTHANC_DATABASES_ROOT}/Framework/Plugins/DatabaseBackendAdapterV4.cpp ${ORTHANC_DATABASES_ROOT}/Framework/Plugins/IndexBackend.cpp ${ORTHANC_DATABASES_ROOT}/Framework/Plugins/StorageBackend.cpp ${ORTHANC_DATABASES_ROOT}/Resources/Orthanc/Databases/DatabaseConstraint.cpp