changeset 373:be7de633695c db-protobuf

started DatabaseBackendAdapterV4
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 28 Mar 2023 12:47:54 +0200
parents b5e2c1e48828
children 4a3985088723
files Framework/Plugins/DatabaseBackendAdapterV2.cpp Framework/Plugins/DatabaseBackendAdapterV3.cpp Framework/Plugins/DatabaseBackendAdapterV4.cpp Framework/Plugins/DatabaseBackendAdapterV4.h Framework/Plugins/IndexBackend.cpp Resources/CMake/DatabasesPluginConfiguration.cmake
diffstat 6 files changed, 307 insertions(+), 20 deletions(-) [+]
line wrap: on
line diff
--- 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<IDatabaseBackend> 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(&params, 0, sizeof(params));
--- 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<IndexBackend> 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, &params, 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");
     }
 
--- /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 <http://www.gnu.org/licenses/>.
+ **/
+
+
+#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 <OrthancDatabasePlugin.pb.h>  // Include protobuf messages
+
+#include <Logging.h>
+#include <MultiThreading/SharedMessageQueue.h>
+#include <OrthancException.h>
+
+#include <stdexcept>
+#include <list>
+#include <string>
+#include <cassert>
+
+
+#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<IndexBackend*>(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<OrthancPluginErrorCode>(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<IndexBackend*>(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<IndexBackend> 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
--- /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 <http://www.gnu.org/licenses/>.
+ **/
+
+
+
+#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
--- 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 <Compatibility.h>  // For std::unique_ptr<>
@@ -35,13 +36,6 @@
 #include <OrthancException.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 "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;
     }
--- 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