# HG changeset patch # User Sebastien Jodogne # Date 1680534051 -7200 # Node ID 802e2e3d9bfc63b3fafeeb92ff471d3b8b3ad9c4 # Parent 1fa3bfa86f8ed73d50579a3bc4504efcfbbfbd06# Parent afa96af2eb5a92a0be348650fd1919fc10b9e206 integration db-protobuf->mainline diff -r 1fa3bfa86f8e -r 802e2e3d9bfc NEWS --- a/NEWS Fri Mar 31 16:32:02 2023 +0200 +++ b/NEWS Mon Apr 03 17:00:51 2023 +0200 @@ -5,18 +5,20 @@ -------- * API version upgraded to 20 -* /system: added UserMetadata +* /system: added "UserMetadata" + +Plugins +------- + +* Added "OrthancPluginRegisterDatabaseBackendV4()" to communicate using Google + Protocol Buffers between the Orthanc core and database plugins Maintenance ----------- * Enforce the existence of the patient/study/instance while creating its archive * Security: New configuration option "RestApiWriteToFileSystemEnabled" - to allow "/instances/../export" that is now disabled by default. - -Bug Fixes ---------- - + to allow "/instances/../export" that is now disabled by default * Fix issue 214: VOILUTSequence is not returned in Wado-RS * Fix /tools/reset crashing when ExtraMainDicomTags were defined diff -r 1fa3bfa86f8e -r 802e2e3d9bfc OrthancFramework/Resources/CMake/OrthancFrameworkConfiguration.cmake --- a/OrthancFramework/Resources/CMake/OrthancFrameworkConfiguration.cmake Fri Mar 31 16:32:02 2023 +0200 +++ b/OrthancFramework/Resources/CMake/OrthancFrameworkConfiguration.cmake Mon Apr 03 17:00:51 2023 +0200 @@ -141,6 +141,11 @@ unset(ENABLE_DCMTK_LOG CACHE) endif() +if (NOT ENABLE_PROTOBUF) + unset(USE_SYSTEM_PROTOBUF CACHE) + add_definitions(-DORTHANC_ENABLE_PROTOBUF=0) +endif() + ##################################################################### ## List of source files @@ -476,6 +481,16 @@ endif() +## +## Google Protocol Buffers +## + +if (ENABLE_PROTOBUF) + include(${CMAKE_CURRENT_LIST_DIR}/ProtobufConfiguration.cmake) + add_definitions(-DORTHANC_ENABLE_PROTOBUF=1) +endif() + + ##################################################################### ## Inclusion of mandatory third-party dependencies @@ -712,6 +727,7 @@ ${LUA_SOURCES} ${MONGOOSE_SOURCES} ${OPENSSL_SOURCES} + ${PROTOBUF_LIBRARY_SOURCES} ${PUGIXML_SOURCES} ${SQLITE_SOURCES} ${UUID_SOURCES} diff -r 1fa3bfa86f8e -r 802e2e3d9bfc OrthancFramework/Resources/CMake/OrthancFrameworkParameters.cmake --- a/OrthancFramework/Resources/CMake/OrthancFrameworkParameters.cmake Fri Mar 31 16:32:02 2023 +0200 +++ b/OrthancFramework/Resources/CMake/OrthancFrameworkParameters.cmake Mon Apr 03 17:00:51 2023 +0200 @@ -70,6 +70,7 @@ set(USE_SYSTEM_LUA ON CACHE BOOL "Use the system version of Lua") set(USE_SYSTEM_MONGOOSE ON CACHE BOOL "Use the system version of Mongoose") set(USE_SYSTEM_OPENSSL ON CACHE BOOL "Use the system version of OpenSSL") +set(USE_SYSTEM_PROTOBUF ON CACHE BOOL "Use the system version of Google Protocol Buffers") set(USE_SYSTEM_PUGIXML ON CACHE BOOL "Use the system version of Pugixml") set(USE_SYSTEM_SQLITE ON CACHE BOOL "Use the system version of SQLite") set(USE_SYSTEM_UUID ON CACHE BOOL "Use the system version of the uuid library from e2fsprogs") @@ -123,6 +124,8 @@ set(ENABLE_LOCALE OFF CACHE INTERNAL "Enable support for locales (notably in Boost)") set(ENABLE_LUA OFF CACHE INTERNAL "Enable support of Lua scripting") set(ENABLE_PNG OFF CACHE INTERNAL "Enable support of PNG") +set(ENABLE_PROTOBUF OFF CACHE INTERNAL "Enable support for Google Protocol Buffers' library") +set(ENABLE_PROTOBUF_COMPILER OFF CACHE INTERNAL "Enable support for Google Protocol Buffers' compiler") set(ENABLE_PUGIXML OFF CACHE INTERNAL "Enable support of XML through Pugixml") set(ENABLE_SQLITE OFF CACHE INTERNAL "Enable support of SQLite databases") set(ENABLE_ZLIB OFF CACHE INTERNAL "Enable support of zlib") diff -r 1fa3bfa86f8e -r 802e2e3d9bfc OrthancFramework/Resources/CMake/ProtobufConfiguration.cmake --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancFramework/Resources/CMake/ProtobufConfiguration.cmake Mon Apr 03 17:00:51 2023 +0200 @@ -0,0 +1,85 @@ +# Orthanc - A Lightweight, RESTful DICOM Store +# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics +# Department, University Hospital of Liege, Belgium +# Copyright (C) 2017-2023 Osimis S.A., Belgium +# Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium +# +# This program is free software: you can redistribute it and/or +# modify it under the terms of the GNU Lesser 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program. If not, see +# . + + +if (STATIC_BUILD OR NOT USE_SYSTEM_PROTOBUF) + if (ENABLE_PROTOBUF_COMPILER) + include(ExternalProject) + externalproject_add(ProtobufCompiler + SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../ProtocolBuffers" + BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/ProtobufCompiler-build" + # this helps triggering build when changing the external project + BUILD_ALWAYS 1 + CMAKE_ARGS + -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} + -DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR} + ) + + # The "protoc" compiler is built using "externalproject_add", + # which builds for the host platform, not for the target platform + if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") + set(Suffix ".exe") + else() + set(Suffix "") + endif() + + set(PROTOC_EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/protoc${Suffix}) + endif() + + include(${CMAKE_CURRENT_LIST_DIR}/../ProtocolBuffers/ProtobufLibrary.cmake) + source_group(ThirdParty\\Protobuf REGULAR_EXPRESSION ${PROTOBUF_SOURCE_DIR}/.*) + +else() + if (CMAKE_CROSSCOMPILING) + message(FATAL_ERROR "If cross-compiling, the static version of Protocol Buffers should be used to avoid version mismatch") + endif() + + if (ENABLE_PROTOBUF_COMPILER) + find_program(PROTOC_EXECUTABLE protoc) + if (${PROTOC_EXECUTABLE} MATCHES "PROTOC_EXECUTABLE-NOTFOUND") + message(FATAL_ERROR "Please install the 'protoc' compiler for Protocol Buffers (package 'protobuf-compiler' on Debian/Ubuntu)") + endif() + add_custom_target(ProtobufCompiler) + endif() + + check_include_file_cxx(google/protobuf/any.h HAVE_PROTOBUF_H) + if (NOT HAVE_PROTOBUF_H) + message(FATAL_ERROR "Please install the libprotobuf-dev package") + endif() + + set(CMAKE_REQUIRED_LIBRARIES "protobuf") + + include(CheckCXXSourceCompiles) + check_cxx_source_compiles( + " +#include +int main() +{ + google::protobuf::FieldDescriptor::TypeName(google::protobuf::FieldDescriptor::TYPE_FLOAT); +} +" HAVE_PROTOBUF_LIB) + if (NOT HAVE_PROTOBUF_LIB) + message(FATAL_ERROR "Cannot find the protobuf library") + endif() + + unset(CMAKE_REQUIRED_LIBRARIES) + + link_libraries(protobuf) +endif() diff -r 1fa3bfa86f8e -r 802e2e3d9bfc OrthancFramework/Resources/Patches/protobuf-3.5.1.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancFramework/Resources/Patches/protobuf-3.5.1.patch Mon Apr 03 17:00:51 2023 +0200 @@ -0,0 +1,17 @@ +diff -urEb protobuf-3.5.1.orig/src/google/protobuf/stubs/io_win32.cc protobuf-3.5.1/src/google/protobuf/stubs/io_win32.cc +--- protobuf-3.5.1.orig/src/google/protobuf/stubs/io_win32.cc 2023-03-26 20:13:45.095021011 +0200 ++++ protobuf-3.5.1/src/google/protobuf/stubs/io_win32.cc 2023-03-26 20:19:19.932920102 +0200 +@@ -91,7 +91,12 @@ + + template + bool null_or_empty(const char_type* s) { +- return s == nullptr || *s == 0; ++ /** ++ * "nullptr" is not known to Visual Studio 2008, because this is a ++ * C++11 construction, which shouldn't be present in protobuf 3.5.1 ++ * that is supposed to comply with C++98. ++ **/ ++ return s == NULL || *s == 0; + } + + // Returns true if the path starts with a drive letter, e.g. "c:". diff -r 1fa3bfa86f8e -r 802e2e3d9bfc OrthancFramework/Resources/ProtocolBuffers/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancFramework/Resources/ProtocolBuffers/CMakeLists.txt Mon Apr 03 17:00:51 2023 +0200 @@ -0,0 +1,149 @@ +# Orthanc - A Lightweight, RESTful DICOM Store +# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics +# Department, University Hospital of Liege, Belgium +# Copyright (C) 2017-2023 Osimis S.A., Belgium +# Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium +# +# This program is free software: you can redistribute it and/or +# modify it under the terms of the GNU Lesser 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program. If not, see +# . + + +cmake_minimum_required(VERSION 2.8.3) + +project(ProtocolBuffers) + +set(ALLOW_DOWNLOADS ON) + +include(${CMAKE_SOURCE_DIR}/../CMake/DownloadPackage.cmake) +include(${CMAKE_SOURCE_DIR}/../CMake/Compiler.cmake) + +include(${CMAKE_SOURCE_DIR}/ProtobufLibrary.cmake) + +set(PROTOBUF_COMPILER_SOURCES + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/code_generator.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/command_line_interface.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/cpp/cpp_enum.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/cpp/cpp_enum_field.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/cpp/cpp_extension.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/cpp/cpp_field.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/cpp/cpp_file.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/cpp/cpp_generator.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/cpp/cpp_helpers.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/cpp/cpp_map_field.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/cpp/cpp_message.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/cpp/cpp_message_field.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/cpp/cpp_padding_optimizer.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/cpp/cpp_service.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/cpp/cpp_string_field.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_doc_comment.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_enum.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_enum_field.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_field_base.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_generator.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_helpers.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_map_field.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_message.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_message_field.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_reflection_class.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_repeated_message_field.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_source_generator_base.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/importer.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/java/java_context.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/java/java_doc_comment.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/java/java_enum.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/java/java_enum_field.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/java/java_enum_field_lite.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/java/java_enum_lite.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/java/java_extension.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/java/java_extension_lite.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/java/java_field.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/java/java_file.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/java/java_generator.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/java/java_generator_factory.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/java/java_helpers.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/java/java_lazy_message_field.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/java/java_lazy_message_field_lite.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/java/java_map_field.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/java/java_map_field_lite.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/java/java_message.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/java/java_message_builder.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/java/java_message_builder_lite.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/java/java_message_field.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/java/java_message_field_lite.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/java/java_message_lite.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/java/java_name_resolver.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/java/java_primitive_field.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/java/java_primitive_field_lite.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/java/java_service.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/java/java_shared_code_generator.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/java/java_string_field.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/java/java_string_field_lite.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/javanano/javanano_enum.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/javanano/javanano_enum_field.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/javanano/javanano_extension.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/javanano/javanano_field.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/javanano/javanano_file.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/javanano/javanano_generator.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/javanano/javanano_helpers.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/javanano/javanano_map_field.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/javanano/javanano_message.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/javanano/javanano_message_field.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/javanano/javanano_primitive_field.cc + #${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/js/embed.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/js/js_generator.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/js/well_known_types_embed.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/main.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_enum.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_enum_field.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_extension.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_field.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_file.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_generator.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_map_field.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_message.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_message_field.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_oneof.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_primitive_field.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/parser.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/php/php_generator.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/plugin.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/plugin.pb.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/python/python_generator.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/ruby/ruby_generator.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/subprocess.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/compiler/zip_writer.cc + ) + +if (NOT CMAKE_SYSTEM_NAME STREQUAL "Windows") + set_property( + SOURCE ${PROTOBUF_COMPILER_SOURCES} + PROPERTY COMPILE_DEFINITIONS "HAVE_PTHREAD=1" + ) +endif() + +add_executable(protoc + ${PROTOBUF_LIBRARY_SOURCES} + ${PROTOBUF_COMPILER_SOURCES} + ) + +install( + TARGETS protoc + RUNTIME DESTINATION . + ) diff -r 1fa3bfa86f8e -r 802e2e3d9bfc OrthancFramework/Resources/ProtocolBuffers/NOTES.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancFramework/Resources/ProtocolBuffers/NOTES.txt Mon Apr 03 17:00:51 2023 +0200 @@ -0,0 +1,29 @@ + +Version +======= + +We use Google's Protocol Buffers version 3.5.1, as this is the last +release to be compatible with C++98, which is mandatory for Visual +Studio 2008 and Linux Standard Base. + +References: +https://github.com/protocolbuffers/protobuf/releases/tag/v3.5.1 +https://github.com/protocolbuffers/protobuf/issues/2780 + + +Linux Standard Base +=================== + +$ mkdir lsb +$ cd lsb +$ LSB_CC=gcc-4.8 LSB_CXX=g++-4.8 cmake .. -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON -DCMAKE_TOOLCHAIN_FILE=../../Toolchains/LinuxStandardBaseToolchain.cmake -G Ninja +$ ninja + + +MinGW for 32bits +================ + +$ mkdir w32 +$ cd w32 +$ LSB_CC=gcc-4.8 LSB_CXX=g++-4.8 cmake .. -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON -DCMAKE_TOOLCHAIN_FILE=../../Toolchains/MinGW-W64-Toolchain32.cmake -G Ninja +$ ninja diff -r 1fa3bfa86f8e -r 802e2e3d9bfc OrthancFramework/Resources/ProtocolBuffers/ProtobufLibrary.cmake --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancFramework/Resources/ProtocolBuffers/ProtobufLibrary.cmake Mon Apr 03 17:00:51 2023 +0200 @@ -0,0 +1,144 @@ +# Orthanc - A Lightweight, RESTful DICOM Store +# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics +# Department, University Hospital of Liege, Belgium +# Copyright (C) 2017-2023 Osimis S.A., Belgium +# Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium +# +# This program is free software: you can redistribute it and/or +# modify it under the terms of the GNU Lesser 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program. If not, see +# . + + +set(PROTOBUF_SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/protobuf-3.5.1) + +if (IS_DIRECTORY "${PROTOBUF_SOURCE_DIR}") + set(FirstRun OFF) +else() + set(FirstRun ON) +endif() + +DownloadPackage( + "ca0d9b243e649d398a6b419acd35103a" + "http://orthanc.uclouvain.be/third-party-downloads/protobuf-cpp-3.5.1.tar.gz" + "${CMAKE_CURRENT_BINARY_DIR}/protobuf-3.5.1") + +if (FirstRun) + # Apply the patches + execute_process( + COMMAND ${PATCH_EXECUTABLE} -p0 -N -i + ${CMAKE_CURRENT_LIST_DIR}/../Patches/protobuf-3.5.1.patch + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + RESULT_VARIABLE Failure + ) + + if (Failure) + message(FATAL_ERROR "Error while patching a file") + endif() +endif() + +include_directories( + ${PROTOBUF_SOURCE_DIR}/src + ) + +set(PROTOBUF_LIBRARY_SOURCES + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/any.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/any.pb.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/api.pb.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/arena.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/arenastring.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/descriptor.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/descriptor.pb.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/descriptor_database.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/duration.pb.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/dynamic_message.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/empty.pb.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/extension_set.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/extension_set_heavy.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/field_mask.pb.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/generated_message_reflection.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/generated_message_table_driven.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/generated_message_table_driven_lite.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/generated_message_util.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/io/coded_stream.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/io/gzip_stream.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/io/printer.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/io/strtod.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/io/tokenizer.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/io/zero_copy_stream.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/io/zero_copy_stream_impl.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/io/zero_copy_stream_impl_lite.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/map_field.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/message.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/message_lite.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/reflection_ops.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/repeated_field.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/service.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/source_context.pb.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/struct.pb.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/stubs/atomicops_internals_arm64_gcc.h + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/stubs/atomicops_internals_arm_gcc.h + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/stubs/atomicops_internals_generic_gcc.h + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/stubs/atomicops_internals_mips_gcc.h + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/stubs/atomicops_internals_ppc_gcc.h + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/stubs/atomicops_internals_x86_gcc.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/stubs/atomicops_internals_x86_gcc.h + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/stubs/atomicops_internals_x86_msvc.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/stubs/common.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/stubs/int128.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/stubs/io_win32.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/stubs/mathlimits.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/stubs/once.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/stubs/status.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/stubs/statusor.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/stubs/stringpiece.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/stubs/stringprintf.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/stubs/structurally_valid.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/stubs/strutil.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/stubs/substitute.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/stubs/time.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/stubs/bytestream.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/text_format.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/timestamp.pb.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/type.pb.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/unknown_field_set.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/util/delimited_message_util.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/util/field_comparator.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/util/field_mask_util.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/util/internal/datapiece.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/util/internal/default_value_objectwriter.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/util/internal/error_listener.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/util/internal/field_mask_utility.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/util/internal/json_escaping.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/util/internal/json_objectwriter.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/util/internal/json_stream_parser.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/util/internal/object_writer.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/util/internal/proto_writer.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/util/internal/protostream_objectsource.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/util/internal/protostream_objectwriter.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/util/internal/type_info.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/util/internal/utility.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/util/json_util.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/util/message_differencer.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/util/time_util.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/util/type_resolver_util.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/wire_format.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/wire_format_lite.cc + ${PROTOBUF_SOURCE_DIR}/src/google/protobuf/wrappers.pb.cc + ) + +if (NOT CMAKE_SYSTEM_NAME STREQUAL "Windows") + set_property( + SOURCE ${PROTOBUF_LIBRARY_SOURCES} + PROPERTY COMPILE_DEFINITIONS "HAVE_PTHREAD=1" + ) +endif() diff -r 1fa3bfa86f8e -r 802e2e3d9bfc OrthancServer/CMakeLists.txt --- a/OrthancServer/CMakeLists.txt Fri Mar 31 16:32:02 2023 +0200 +++ b/OrthancServer/CMakeLists.txt Mon Apr 03 17:00:51 2023 +0200 @@ -69,6 +69,11 @@ ## Configuration of the Orthanc framework ##################################################################### +if (ENABLE_PLUGINS) + set(ENABLE_PROTOBUF ON) + set(ENABLE_PROTOBUF_COMPILER ON) +endif() + include(${CMAKE_SOURCE_DIR}/../OrthancFramework/Resources/CMake/VisualStudioPrecompiledHeaders.cmake) include(${CMAKE_SOURCE_DIR}/../OrthancFramework/Resources/CMake/OrthancFrameworkConfiguration.cmake) @@ -181,6 +186,7 @@ list(APPEND ORTHANC_SERVER_SOURCES ${CMAKE_SOURCE_DIR}/Plugins/Engine/OrthancPluginDatabase.cpp ${CMAKE_SOURCE_DIR}/Plugins/Engine/OrthancPluginDatabaseV3.cpp + ${CMAKE_SOURCE_DIR}/Plugins/Engine/OrthancPluginDatabaseV4.cpp ${CMAKE_SOURCE_DIR}/Plugins/Engine/OrthancPlugins.cpp ${CMAKE_SOURCE_DIR}/Plugins/Engine/PluginsEnumerations.cpp ${CMAKE_SOURCE_DIR}/Plugins/Engine/PluginsErrorDictionary.cpp @@ -361,8 +367,13 @@ ## Build the core of Orthanc ##################################################################### +add_custom_target(AutogeneratedTarget + DEPENDS + ${AUTOGENERATED_SOURCES} + ) + # "CoreLibrary" contains all the third-party dependencies and the -# content of the "Core" folder +# content of the "OrthancFramework" folder add_library(CoreLibrary STATIC ${ORTHANC_CORE_PCH} @@ -371,6 +382,8 @@ ${AUTOGENERATED_SOURCES} ) +add_dependencies(CoreLibrary AutogeneratedTarget) + if (LIBICU_LIBRARIES) target_link_libraries(CoreLibrary ${LIBICU_LIBRARIES}) endif() @@ -380,6 +393,30 @@ ## Build the Orthanc server ##################################################################### +if (ENABLE_PLUGINS) + add_custom_command( + COMMAND + ${PROTOC_EXECUTABLE} ${CMAKE_SOURCE_DIR}/Plugins/Include/orthanc/OrthancDatabasePlugin.proto --cpp_out=${AUTOGENERATED_DIR} -I${CMAKE_SOURCE_DIR}/Plugins/Include/orthanc + DEPENDS + ProtobufCompiler + ${CMAKE_SOURCE_DIR}/Plugins/Include/orthanc/OrthancDatabasePlugin.proto + OUTPUT + ${AUTOGENERATED_DIR}/OrthancDatabasePlugin.pb.cc + ${AUTOGENERATED_DIR}/OrthancDatabasePlugin.pb.h + ) + + add_custom_target(OrthancDatabaseProtobuf + DEPENDS + ${AUTOGENERATED_DIR}/OrthancDatabasePlugin.pb.h + ) + + list(APPEND ORTHANC_SERVER_SOURCES + ${AUTOGENERATED_DIR}/OrthancDatabasePlugin.pb.cc + ) +else() + add_custom_target(OrthancDatabaseProtobuf) +endif() + add_library(ServerLibrary STATIC ${ORTHANC_SERVER_PCH} @@ -387,7 +424,7 @@ ) # Ensure autogenerated code is built before building ServerLibrary -add_dependencies(ServerLibrary CoreLibrary) +add_dependencies(ServerLibrary CoreLibrary OrthancDatabaseProtobuf) add_executable(Orthanc ${CMAKE_SOURCE_DIR}/Sources/main.cpp @@ -546,7 +583,6 @@ if (ENABLE_PLUGINS AND (BUILD_DELAYED_DELETION OR BUILD_CONNECTIVITY_CHECKS)) include(ExternalProject) - endif() @@ -825,8 +861,9 @@ if (ENABLE_PLUGINS) install( FILES - ${CMAKE_SOURCE_DIR}/Plugins/Include/orthanc/OrthancCPlugin.h - ${CMAKE_SOURCE_DIR}/Plugins/Include/orthanc/OrthancCDatabasePlugin.h + ${CMAKE_SOURCE_DIR}/Plugins/Include/orthanc/OrthancCPlugin.h + ${CMAKE_SOURCE_DIR}/Plugins/Include/orthanc/OrthancCDatabasePlugin.h + ${CMAKE_SOURCE_DIR}/Plugins/Include/orthanc/OrthancDatabasePlugin.proto DESTINATION include/orthanc ) endif() diff -r 1fa3bfa86f8e -r 802e2e3d9bfc OrthancServer/Plugins/Engine/OrthancPluginDatabase.cpp --- a/OrthancServer/Plugins/Engine/OrthancPluginDatabase.cpp Fri Mar 31 16:32:02 2023 +0200 +++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabase.cpp Mon Apr 03 17:00:51 2023 +0200 @@ -565,7 +565,7 @@ std::list* instancesId, const std::vector& lookup, ResourceType queryLevel, - size_t limit) ORTHANC_OVERRIDE + uint32_t limit) ORTHANC_OVERRIDE { if (that_.extensions_.lookupResources == NULL) { @@ -800,8 +800,8 @@ virtual void GetAllPublicIds(std::list& target, ResourceType resourceType, - size_t since, - size_t limit) ORTHANC_OVERRIDE + int64_t since, + uint32_t limit) ORTHANC_OVERRIDE { if (that_.extensions_.getAllPublicIdsWithLimit != NULL) { @@ -825,7 +825,7 @@ std::list tmp; GetAllPublicIds(tmp, resourceType); - if (tmp.size() <= since) + if (tmp.size() <= static_cast(since)) { // Not enough results => empty answer return; @@ -847,14 +847,14 @@ virtual void GetChanges(std::list& target /*out*/, bool& done /*out*/, int64_t since, - uint32_t maxResults) ORTHANC_OVERRIDE + uint32_t limit) ORTHANC_OVERRIDE { ResetAnswers(); answerChanges_ = ⌖ answerDone_ = &done; done = false; - CheckSuccess(that_.backend_.getChanges(that_.GetContext(), that_.payload_, since, maxResults)); + CheckSuccess(that_.backend_.getChanges(that_.GetContext(), that_.payload_, since, limit)); } @@ -897,14 +897,14 @@ virtual void GetExportedResources(std::list& target /*out*/, bool& done /*out*/, int64_t since, - uint32_t maxResults) ORTHANC_OVERRIDE + uint32_t limit) ORTHANC_OVERRIDE { ResetAnswers(); answerExportedResources_ = ⌖ answerDone_ = &done; done = false; - CheckSuccess(that_.backend_.getExportedResources(that_.GetContext(), that_.payload_, since, maxResults)); + CheckSuccess(that_.backend_.getExportedResources(that_.GetContext(), that_.payload_, since, limit)); } @@ -1021,14 +1021,6 @@ } - virtual bool IsExistingResource(int64_t internalId) ORTHANC_OVERRIDE - { - int32_t existing; - CheckSuccess(that_.backend_.isExistingResource(&existing, that_.payload_, internalId)); - return (existing != 0); - } - - virtual bool IsProtectedPatient(int64_t internalId) ORTHANC_OVERRIDE { int32_t isProtected; @@ -1063,15 +1055,18 @@ } - virtual void LogChange(int64_t internalId, - const ServerIndexChange& change) ORTHANC_OVERRIDE + virtual void LogChange(ChangeType changeType, + ResourceType resourceType, + int64_t internalId, + const std::string& publicId, + const std::string& date) ORTHANC_OVERRIDE { OrthancPluginChange tmp; - tmp.seq = change.GetSeq(); - tmp.changeType = static_cast(change.GetChangeType()); - tmp.resourceType = Plugins::Convert(change.GetResourceType()); - tmp.publicId = change.GetPublicId().c_str(); - tmp.date = change.GetDate().c_str(); + tmp.seq = -1; // Unused (it is attributed by the database engine) + tmp.changeType = static_cast(changeType); + tmp.resourceType = Plugins::Convert(resourceType); + tmp.publicId = publicId.c_str(); + tmp.date = date.c_str(); CheckSuccess(that_.backend_.logChange(that_.payload_, &tmp)); } @@ -1370,12 +1365,12 @@ it = content.GetListTags().begin(); it != content.GetListTags().end(); ++it) { OrthancPluginResourcesContentTags tmp; - tmp.resource = it->resourceId_; - tmp.group = it->tag_.GetGroup(); - tmp.element = it->tag_.GetElement(); - tmp.value = it->value_.c_str(); + tmp.resource = it->GetResourceId(); + tmp.group = it->GetTag().GetGroup(); + tmp.element = it->GetTag().GetElement(); + tmp.value = it->GetValue().c_str(); - if (it->isIdentifier_) + if (it->IsIdentifier()) { identifierTags.push_back(tmp); } @@ -1389,9 +1384,9 @@ it = content.GetListMetadata().begin(); it != content.GetListMetadata().end(); ++it) { OrthancPluginResourcesContentMetadata tmp; - tmp.resource = it->resourceId_; - tmp.metadata = it->metadata_; - tmp.value = it->value_.c_str(); + tmp.resource = it->GetResourceId(); + tmp.metadata = it->GetType(); + tmp.value = it->GetValue().c_str(); metadata.push_back(tmp); } diff -r 1fa3bfa86f8e -r 802e2e3d9bfc OrthancServer/Plugins/Engine/OrthancPluginDatabaseV3.cpp --- a/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV3.cpp Fri Mar 31 16:32:02 2023 +0200 +++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV3.cpp Mon Apr 03 17:00:51 2023 +0200 @@ -388,8 +388,8 @@ virtual void GetAllPublicIds(std::list& target, ResourceType resourceType, - size_t since, - size_t limit) ORTHANC_OVERRIDE + int64_t since, + uint32_t limit) ORTHANC_OVERRIDE { CheckSuccess(that_.backend_.getAllPublicIdsWithLimit( transaction_, Plugins::Convert(resourceType), @@ -403,10 +403,10 @@ virtual void GetChanges(std::list& target /*out*/, bool& done /*out*/, int64_t since, - uint32_t maxResults) ORTHANC_OVERRIDE + uint32_t limit) ORTHANC_OVERRIDE { uint8_t tmpDone = true; - CheckSuccess(that_.backend_.getChanges(transaction_, &tmpDone, since, maxResults)); + CheckSuccess(that_.backend_.getChanges(transaction_, &tmpDone, since, limit)); CheckNoEvent(); done = (tmpDone != 0); @@ -454,10 +454,10 @@ virtual void GetExportedResources(std::list& target /*out*/, bool& done /*out*/, int64_t since, - uint32_t maxResults) ORTHANC_OVERRIDE + uint32_t limit) ORTHANC_OVERRIDE { uint8_t tmpDone = true; - CheckSuccess(that_.backend_.getExportedResources(transaction_, &tmpDone, since, maxResults)); + CheckSuccess(that_.backend_.getExportedResources(transaction_, &tmpDone, since, limit)); CheckNoEvent(); done = (tmpDone != 0); @@ -594,15 +594,6 @@ } - virtual bool IsExistingResource(int64_t internalId) ORTHANC_OVERRIDE - { - uint8_t b; - CheckSuccess(that_.backend_.isExistingResource(transaction_, &b, internalId)); - CheckNoEvent(); - return (b != 0); - } - - virtual bool IsProtectedPatient(int64_t internalId) ORTHANC_OVERRIDE { uint8_t b; @@ -631,12 +622,15 @@ } - virtual void LogChange(int64_t internalId, - const ServerIndexChange& change) ORTHANC_OVERRIDE + virtual void LogChange(ChangeType changeType, + ResourceType resourceType, + int64_t internalId, + const std::string& /* publicId - unused */, + const std::string& date) ORTHANC_OVERRIDE { - CheckSuccess(that_.backend_.logChange(transaction_, static_cast(change.GetChangeType()), - internalId, Plugins::Convert(change.GetResourceType()), - change.GetDate().c_str())); + CheckSuccess(that_.backend_.logChange(transaction_, static_cast(changeType), + internalId, Plugins::Convert(resourceType), + date.c_str())); CheckNoEvent(); } @@ -806,7 +800,7 @@ std::list* instancesId, // Can be NULL if not needed const std::vector& lookup, ResourceType queryLevel, - size_t limit) ORTHANC_OVERRIDE + uint32_t limit) ORTHANC_OVERRIDE { std::vector constraints; std::vector< std::vector > constraintsValues; @@ -910,12 +904,12 @@ it = content.GetListTags().begin(); it != content.GetListTags().end(); ++it) { OrthancPluginResourcesContentTags tmp; - tmp.resource = it->resourceId_; - tmp.group = it->tag_.GetGroup(); - tmp.element = it->tag_.GetElement(); - tmp.value = it->value_.c_str(); + tmp.resource = it->GetResourceId(); + tmp.group = it->GetTag().GetGroup(); + tmp.element = it->GetTag().GetElement(); + tmp.value = it->GetValue().c_str(); - if (it->isIdentifier_) + if (it->IsIdentifier()) { identifierTags.push_back(tmp); } @@ -929,9 +923,9 @@ it = content.GetListMetadata().begin(); it != content.GetListMetadata().end(); ++it) { OrthancPluginResourcesContentMetadata tmp; - tmp.resource = it->resourceId_; - tmp.metadata = it->metadata_; - tmp.value = it->value_.c_str(); + tmp.resource = it->GetResourceId(); + tmp.metadata = it->GetType(); + tmp.value = it->GetValue().c_str(); metadata.push_back(tmp); } diff -r 1fa3bfa86f8e -r 802e2e3d9bfc OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp Mon Apr 03 17:00:51 2023 +0200 @@ -0,0 +1,1310 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2023 Osimis S.A., Belgium + * Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, 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. + * + * 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 "../../Sources/PrecompiledHeadersServer.h" +#include "OrthancPluginDatabaseV4.h" + +#if ORTHANC_ENABLE_PLUGINS != 1 +# error The plugin support is disabled +#endif + +#include "../../../OrthancFramework/Sources/Logging.h" +#include "../../../OrthancFramework/Sources/OrthancException.h" +#include "../../Sources/Database/ResourcesContent.h" +#include "../../Sources/Database/VoidDatabaseListener.h" +#include "PluginsEnumerations.h" + +#include "OrthancDatabasePlugin.pb.h" // Auto-generated file + +#include + + +namespace Orthanc +{ + static void CheckSuccess(PluginsErrorDictionary& errorDictionary, + OrthancPluginErrorCode code) + { + if (code != OrthancPluginErrorCode_Success) + { + errorDictionary.LogError(code, true); + throw OrthancException(static_cast(code)); + } + } + + + static ResourceType Convert(DatabasePluginMessages::ResourceType type) + { + switch (type) + { + case DatabasePluginMessages::RESOURCE_PATIENT: + return ResourceType_Patient; + + case DatabasePluginMessages::RESOURCE_STUDY: + return ResourceType_Study; + + case DatabasePluginMessages::RESOURCE_SERIES: + return ResourceType_Series; + + case DatabasePluginMessages::RESOURCE_INSTANCE: + return ResourceType_Instance; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + + + static DatabasePluginMessages::ResourceType Convert(ResourceType type) + { + switch (type) + { + case ResourceType_Patient: + return DatabasePluginMessages::RESOURCE_PATIENT; + + case ResourceType_Study: + return DatabasePluginMessages::RESOURCE_STUDY; + + case ResourceType_Series: + return DatabasePluginMessages::RESOURCE_SERIES; + + case ResourceType_Instance: + return DatabasePluginMessages::RESOURCE_INSTANCE; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + + + static FileInfo Convert(const DatabasePluginMessages::FileInfo& source) + { + return FileInfo(source.uuid(), + static_cast(source.content_type()), + source.uncompressed_size(), + source.uncompressed_hash(), + static_cast(source.compression_type()), + source.compressed_size(), + source.compressed_hash()); + } + + + static ServerIndexChange Convert(const DatabasePluginMessages::ServerIndexChange& source) + { + return ServerIndexChange(source.seq(), + static_cast(source.change_type()), + Convert(source.resource_type()), + source.public_id(), + source.date()); + } + + + static ExportedResource Convert(const DatabasePluginMessages::ExportedResource& source) + { + return ExportedResource(source.seq(), + Convert(source.resource_type()), + source.public_id(), + source.modality(), + source.date(), + source.patient_id(), + source.study_instance_uid(), + source.series_instance_uid(), + source.sop_instance_uid()); + } + + + static void Execute(DatabasePluginMessages::Response& response, + const OrthancPluginDatabaseV4& database, + const DatabasePluginMessages::Request& request) + { + std::string requestSerialized; + request.SerializeToString(&requestSerialized); + + OrthancPluginMemoryBuffer64 responseSerialized; + CheckSuccess(database.GetErrorDictionary(), database.GetDefinition().operations( + &responseSerialized, database.GetDefinition().backend, + requestSerialized.empty() ? NULL : requestSerialized.c_str(), + requestSerialized.size())); + + bool success = response.ParseFromArray(responseSerialized.data, responseSerialized.size); + + if (responseSerialized.size > 0) + { + free(responseSerialized.data); + } + + if (!success) + { + throw OrthancException(ErrorCode_DatabasePlugin, "Cannot unserialize protobuf originating from the database plugin"); + } + } + + + static void ExecuteDatabase(DatabasePluginMessages::DatabaseResponse& response, + const OrthancPluginDatabaseV4& database, + DatabasePluginMessages::DatabaseOperation operation, + const DatabasePluginMessages::DatabaseRequest& request) + { + DatabasePluginMessages::Request fullRequest; + fullRequest.set_type(DatabasePluginMessages::REQUEST_DATABASE); + fullRequest.mutable_database_request()->CopyFrom(request); + fullRequest.mutable_database_request()->set_operation(operation); + + DatabasePluginMessages::Response fullResponse; + Execute(fullResponse, database, fullRequest); + + response.CopyFrom(fullResponse.database_response()); + } + + + class OrthancPluginDatabaseV4::Transaction : public IDatabaseWrapper::ITransaction + { + private: + OrthancPluginDatabaseV4& database_; + IDatabaseListener& listener_; + void* transaction_; + + void ExecuteTransaction(DatabasePluginMessages::TransactionResponse& response, + DatabasePluginMessages::TransactionOperation operation, + const DatabasePluginMessages::TransactionRequest& request) + { + DatabasePluginMessages::Request fullRequest; + fullRequest.set_type(DatabasePluginMessages::REQUEST_TRANSACTION); + fullRequest.mutable_transaction_request()->CopyFrom(request); + fullRequest.mutable_transaction_request()->set_transaction(reinterpret_cast(transaction_)); + fullRequest.mutable_transaction_request()->set_operation(operation); + + DatabasePluginMessages::Response fullResponse; + Execute(fullResponse, database_, fullRequest); + + response.CopyFrom(fullResponse.transaction_response()); + } + + + void ExecuteTransaction(DatabasePluginMessages::TransactionResponse& response, + DatabasePluginMessages::TransactionOperation operation) + { + DatabasePluginMessages::TransactionRequest request; // Ignored + ExecuteTransaction(response, operation, request); + } + + + void ExecuteTransaction(DatabasePluginMessages::TransactionOperation operation, + const DatabasePluginMessages::TransactionRequest& request) + { + DatabasePluginMessages::TransactionResponse response; // Ignored + ExecuteTransaction(response, operation, request); + } + + + void ExecuteTransaction(DatabasePluginMessages::TransactionOperation operation) + { + DatabasePluginMessages::TransactionResponse response; // Ignored + DatabasePluginMessages::TransactionRequest request; // Ignored + ExecuteTransaction(response, operation, request); + } + + + public: + Transaction(OrthancPluginDatabaseV4& database, + IDatabaseListener& listener, + TransactionType type) : + database_(database), + listener_(listener), + transaction_(NULL) + { + DatabasePluginMessages::DatabaseRequest request; + + switch (type) + { + case TransactionType_ReadOnly: + request.mutable_start_transaction()->set_type(DatabasePluginMessages::TRANSACTION_READ_ONLY); + break; + + case TransactionType_ReadWrite: + request.mutable_start_transaction()->set_type(DatabasePluginMessages::TRANSACTION_READ_WRITE); + break; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + DatabasePluginMessages::DatabaseResponse response; + ExecuteDatabase(response, database, DatabasePluginMessages::OPERATION_START_TRANSACTION, request); + + transaction_ = reinterpret_cast(response.start_transaction().transaction()); + + if (transaction_ == NULL) + { + throw OrthancException(ErrorCode_NullPointer); + } + } + + + virtual ~Transaction() + { + try + { + DatabasePluginMessages::DatabaseRequest request; + request.mutable_finalize_transaction()->set_transaction(reinterpret_cast(transaction_)); + + DatabasePluginMessages::DatabaseResponse response; + ExecuteDatabase(response, database_, DatabasePluginMessages::OPERATION_FINALIZE_TRANSACTION, request); + } + catch (OrthancException& e) + { + // Destructors must not throw exceptions + LOG(ERROR) << "Cannot finalize the database engine: " << e.What(); + } + } + + + void* GetTransactionObject() + { + return transaction_; + } + + + virtual void Rollback() ORTHANC_OVERRIDE + { + ExecuteTransaction(DatabasePluginMessages::OPERATION_ROLLBACK); + } + + + virtual void Commit(int64_t fileSizeDelta) ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_commit()->set_file_size_delta(fileSizeDelta); + + ExecuteTransaction(DatabasePluginMessages::OPERATION_COMMIT, request); + } + + + virtual void AddAttachment(int64_t id, + const FileInfo& attachment, + int64_t revision) ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_add_attachment()->set_id(id); + request.mutable_add_attachment()->mutable_attachment()->set_uuid(attachment.GetUuid()); + request.mutable_add_attachment()->mutable_attachment()->set_content_type(attachment.GetContentType()); + request.mutable_add_attachment()->mutable_attachment()->set_uncompressed_size(attachment.GetUncompressedSize()); + request.mutable_add_attachment()->mutable_attachment()->set_uncompressed_hash(attachment.GetUncompressedMD5()); + request.mutable_add_attachment()->mutable_attachment()->set_compression_type(attachment.GetCompressionType()); + request.mutable_add_attachment()->mutable_attachment()->set_compressed_size(attachment.GetCompressedSize()); + request.mutable_add_attachment()->mutable_attachment()->set_compressed_hash(attachment.GetCompressedMD5()); + request.mutable_add_attachment()->set_revision(revision); + + ExecuteTransaction(DatabasePluginMessages::OPERATION_ADD_ATTACHMENT, request); + } + + + virtual void ClearChanges() ORTHANC_OVERRIDE + { + ExecuteTransaction(DatabasePluginMessages::OPERATION_CLEAR_CHANGES); + } + + + virtual void ClearExportedResources() ORTHANC_OVERRIDE + { + ExecuteTransaction(DatabasePluginMessages::OPERATION_CLEAR_EXPORTED_RESOURCES); + } + + + virtual void DeleteAttachment(int64_t id, + FileContentType attachment) ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_delete_attachment()->set_id(id); + request.mutable_delete_attachment()->set_type(attachment); + + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_DELETE_ATTACHMENT, request); + + listener_.SignalAttachmentDeleted(Convert(response.delete_attachment().deleted_attachment())); + } + + + virtual void DeleteMetadata(int64_t id, + MetadataType type) ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_delete_metadata()->set_id(id); + request.mutable_delete_metadata()->set_type(type); + + ExecuteTransaction(DatabasePluginMessages::OPERATION_DELETE_METADATA, request); + } + + + virtual void DeleteResource(int64_t id) ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_delete_resource()->set_id(id); + + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_DELETE_RESOURCE, request); + + for (int i = 0; i < response.delete_resource().deleted_attachments().size(); i++) + { + listener_.SignalAttachmentDeleted(Convert(response.delete_resource().deleted_attachments(i))); + } + + for (int i = 0; i < response.delete_resource().deleted_resources().size(); i++) + { + listener_.SignalResourceDeleted(Convert(response.delete_resource().deleted_resources(i).level()), + response.delete_resource().deleted_resources(i).public_id()); + } + + if (response.delete_resource().is_remaining_ancestor()) + { + listener_.SignalRemainingAncestor(Convert(response.delete_resource().remaining_ancestor().level()), + response.delete_resource().remaining_ancestor().public_id()); + } + } + + + virtual void GetAllMetadata(std::map& target, + int64_t id) ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_get_all_metadata()->set_id(id); + + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_ALL_METADATA, request); + + target.clear(); + for (int i = 0; i < response.get_all_metadata().metadata().size(); i++) + { + MetadataType key = static_cast(response.get_all_metadata().metadata(i).type()); + + if (target.find(key) == target.end()) + { + target[key] = response.get_all_metadata().metadata(i).value(); + } + else + { + throw OrthancException(ErrorCode_DatabasePlugin); + } + } + } + + + virtual void GetAllPublicIds(std::list& target, + ResourceType resourceType) ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_get_all_public_ids()->set_resource_type(Convert(resourceType)); + + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_ALL_PUBLIC_IDS, request); + + target.clear(); + for (int i = 0; i < response.get_all_public_ids().ids().size(); i++) + { + target.push_back(response.get_all_public_ids().ids(i)); + } + } + + + virtual void GetAllPublicIds(std::list& target, + ResourceType resourceType, + int64_t since, + uint32_t limit) ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_get_all_public_ids_with_limits()->set_resource_type(Convert(resourceType)); + request.mutable_get_all_public_ids_with_limits()->set_since(since); + request.mutable_get_all_public_ids_with_limits()->set_limit(limit); + + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_ALL_PUBLIC_IDS_WITH_LIMITS, request); + + target.clear(); + for (int i = 0; i < response.get_all_public_ids_with_limits().ids().size(); i++) + { + target.push_back(response.get_all_public_ids_with_limits().ids(i)); + } + } + + + virtual void GetChanges(std::list& target /*out*/, + bool& done /*out*/, + int64_t since, + uint32_t limit) ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_get_changes()->set_since(since); + request.mutable_get_changes()->set_limit(limit); + + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_CHANGES, request); + + done = response.get_changes().done(); + + target.clear(); + for (int i = 0; i < response.get_changes().changes().size(); i++) + { + target.push_back(Convert(response.get_changes().changes(i))); + } + } + + + virtual void GetChildrenInternalId(std::list& target, + int64_t id) ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_get_children_internal_id()->set_id(id); + + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_CHILDREN_INTERNAL_ID, request); + + target.clear(); + for (int i = 0; i < response.get_children_internal_id().ids().size(); i++) + { + target.push_back(response.get_children_internal_id().ids(i)); + } + } + + + virtual void GetChildrenPublicId(std::list& target, + int64_t id) ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_get_children_public_id()->set_id(id); + + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_CHILDREN_PUBLIC_ID, request); + + target.clear(); + for (int i = 0; i < response.get_children_public_id().ids().size(); i++) + { + target.push_back(response.get_children_public_id().ids(i)); + } + } + + + virtual void GetExportedResources(std::list& target /*out*/, + bool& done /*out*/, + int64_t since, + uint32_t limit) ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_get_exported_resources()->set_since(since); + request.mutable_get_exported_resources()->set_limit(limit); + + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_EXPORTED_RESOURCES, request); + + done = response.get_exported_resources().done(); + + target.clear(); + for (int i = 0; i < response.get_exported_resources().resources().size(); i++) + { + target.push_back(Convert(response.get_exported_resources().resources(i))); + } + } + + + virtual void GetLastChange(std::list& target /*out*/) ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_LAST_CHANGE); + + target.clear(); + if (response.get_last_change().found()) + { + target.push_back(Convert(response.get_last_change().change())); + } + } + + + virtual void GetLastExportedResource(std::list& target /*out*/) ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_LAST_EXPORTED_RESOURCE); + + target.clear(); + if (response.get_last_exported_resource().found()) + { + target.push_back(Convert(response.get_last_exported_resource().resource())); + } + } + + + virtual void GetMainDicomTags(DicomMap& target, + int64_t id) ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_get_main_dicom_tags()->set_id(id); + + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_MAIN_DICOM_TAGS, request); + + target.Clear(); + + for (int i = 0; i < response.get_main_dicom_tags().tags().size(); i++) + { + const DatabasePluginMessages::GetMainDicomTags_Response_Tag& tag = response.get_main_dicom_tags().tags(i); + if (tag.group() > 0xffffu || + tag.element() > 0xffffu) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + else + { + target.SetValue(tag.group(), tag.element(), tag.value(), false); + } + } + } + + + virtual std::string GetPublicId(int64_t resourceId) ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_get_public_id()->set_id(resourceId); + + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_PUBLIC_ID, request); + return response.get_public_id().id(); + } + + + virtual uint64_t GetResourcesCount(ResourceType resourceType) ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_get_resources_count()->set_type(Convert(resourceType)); + + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_RESOURCES_COUNT, request); + return response.get_resources_count().count(); + } + + + virtual ResourceType GetResourceType(int64_t resourceId) ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_get_resource_type()->set_id(resourceId); + + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_RESOURCE_TYPE, request); + return Convert(response.get_resource_type().type()); + } + + + virtual uint64_t GetTotalCompressedSize() ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_TOTAL_COMPRESSED_SIZE); + return response.get_total_compressed_size().size(); + } + + + virtual uint64_t GetTotalUncompressedSize() ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_TOTAL_UNCOMPRESSED_SIZE); + return response.get_total_uncompressed_size().size(); + } + + + virtual bool IsProtectedPatient(int64_t internalId) ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_is_protected_patient()->set_patient_id(internalId); + + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_IS_PROTECTED_PATIENT, request); + return response.is_protected_patient().protected_patient(); + } + + + virtual void ListAvailableAttachments(std::set& target, + int64_t id) ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_list_available_attachments()->set_id(id); + + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_LIST_AVAILABLE_ATTACHMENTS, request); + + target.clear(); + for (int i = 0; i < response.list_available_attachments().attachments().size(); i++) + { + FileContentType attachment = static_cast(response.list_available_attachments().attachments(i)); + + if (target.find(attachment) == target.end()) + { + target.insert(attachment); + } + else + { + throw OrthancException(ErrorCode_DatabasePlugin); + } + } + } + + + virtual void LogChange(ChangeType changeType, + ResourceType resourceType, + int64_t internalId, + const std::string& /* publicId - unused */, + const std::string& date) ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_log_change()->set_change_type(changeType); + request.mutable_log_change()->set_resource_type(Convert(resourceType)); + request.mutable_log_change()->set_resource_id(internalId); + request.mutable_log_change()->set_date(date); + + ExecuteTransaction(DatabasePluginMessages::OPERATION_LOG_CHANGE, request); + } + + + virtual void LogExportedResource(const ExportedResource& resource) ORTHANC_OVERRIDE + { + // TODO: "seq" is ignored, could be simplified in "ExportedResource" + + DatabasePluginMessages::TransactionRequest request; + request.mutable_log_exported_resource()->set_resource_type(Convert(resource.GetResourceType())); + request.mutable_log_exported_resource()->set_public_id(resource.GetPublicId()); + request.mutable_log_exported_resource()->set_modality(resource.GetModality()); + request.mutable_log_exported_resource()->set_date(resource.GetDate()); + request.mutable_log_exported_resource()->set_patient_id(resource.GetPatientId()); + request.mutable_log_exported_resource()->set_study_instance_uid(resource.GetStudyInstanceUid()); + request.mutable_log_exported_resource()->set_series_instance_uid(resource.GetSeriesInstanceUid()); + request.mutable_log_exported_resource()->set_sop_instance_uid(resource.GetSopInstanceUid()); + + ExecuteTransaction(DatabasePluginMessages::OPERATION_LOG_EXPORTED_RESOURCE, request); + } + + + virtual bool LookupAttachment(FileInfo& attachment, + int64_t& revision, + int64_t id, + FileContentType contentType) ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_lookup_attachment()->set_id(id); + request.mutable_lookup_attachment()->set_content_type(contentType); + + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_LOOKUP_ATTACHMENT, request); + + if (response.lookup_attachment().found()) + { + attachment = Convert(response.lookup_attachment().attachment()); + revision = response.lookup_attachment().revision(); + return true; + } + else + { + return false; + } + } + + + virtual bool LookupGlobalProperty(std::string& target, + GlobalProperty property, + bool shared) ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_lookup_global_property()->set_server_id(shared ? "" : database_.GetServerIdentifier()); + request.mutable_lookup_global_property()->set_property(property); + + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_LOOKUP_GLOBAL_PROPERTY, request); + + if (response.lookup_global_property().found()) + { + target = response.lookup_global_property().value(); + return true; + } + else + { + return false; + } + } + + + virtual bool LookupMetadata(std::string& target, + int64_t& revision, + int64_t id, + MetadataType type) ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_lookup_metadata()->set_id(id); + request.mutable_lookup_metadata()->set_metadata_type(type); + + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_LOOKUP_METADATA, request); + + if (response.lookup_metadata().found()) + { + target = response.lookup_metadata().value(); + revision = response.lookup_metadata().revision(); + return true; + } + else + { + return false; + } + } + + + virtual bool LookupParent(int64_t& parentId, + int64_t resourceId) ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_lookup_parent()->set_id(resourceId); + + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_LOOKUP_PARENT, request); + + if (response.lookup_parent().found()) + { + parentId = response.lookup_parent().parent(); + return true; + } + else + { + return false; + } + } + + + virtual bool LookupResource(int64_t& id, + ResourceType& type, + const std::string& publicId) ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_lookup_resource()->set_public_id(publicId); + + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_LOOKUP_RESOURCE, request); + + if (response.lookup_resource().found()) + { + id = response.lookup_resource().internal_id(); + type = Convert(response.lookup_resource().type()); + return true; + } + else + { + return false; + } + } + + + virtual bool SelectPatientToRecycle(int64_t& internalId) ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_SELECT_PATIENT_TO_RECYCLE); + + if (response.select_patient_to_recycle().found()) + { + internalId = response.select_patient_to_recycle().patient_id(); + return true; + } + else + { + return false; + } + } + + + virtual bool SelectPatientToRecycle(int64_t& internalId, + int64_t patientIdToAvoid) ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_select_patient_to_recycle_with_avoid()->set_patient_id_to_avoid(patientIdToAvoid); + + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_SELECT_PATIENT_TO_RECYCLE_WITH_AVOID, request); + + if (response.select_patient_to_recycle_with_avoid().found()) + { + internalId = response.select_patient_to_recycle_with_avoid().patient_id(); + return true; + } + else + { + return false; + } + } + + + virtual void SetGlobalProperty(GlobalProperty property, + bool shared, + const std::string& value) ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_set_global_property()->set_server_id(shared ? "" : database_.GetServerIdentifier()); + request.mutable_set_global_property()->set_property(property); + request.mutable_set_global_property()->set_value(value); + + ExecuteTransaction(DatabasePluginMessages::OPERATION_SET_GLOBAL_PROPERTY, request); + } + + + virtual void ClearMainDicomTags(int64_t id) ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_clear_main_dicom_tags()->set_id(id); + + ExecuteTransaction(DatabasePluginMessages::OPERATION_CLEAR_MAIN_DICOM_TAGS, request); + } + + + virtual void SetMetadata(int64_t id, + MetadataType type, + const std::string& value, + int64_t revision) ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_set_metadata()->set_id(id); + request.mutable_set_metadata()->set_metadata_type(type); + request.mutable_set_metadata()->set_value(value); + request.mutable_set_metadata()->set_revision(revision); + + ExecuteTransaction(DatabasePluginMessages::OPERATION_SET_METADATA, request); + } + + + virtual void SetProtectedPatient(int64_t internalId, + bool isProtected) ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_set_protected_patient()->set_patient_id(internalId); + request.mutable_set_protected_patient()->set_protected_patient(isProtected); + + ExecuteTransaction(DatabasePluginMessages::OPERATION_SET_PROTECTED_PATIENT, request); + } + + + virtual bool IsDiskSizeAbove(uint64_t threshold) ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_is_disk_size_above()->set_threshold(threshold); + + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_IS_DISK_SIZE_ABOVE, request); + + return response.is_disk_size_above().result(); + } + + + virtual void ApplyLookupResources(std::list& resourcesId, + std::list* instancesId, // Can be NULL if not needed + const std::vector& lookup, + ResourceType queryLevel, + uint32_t limit) ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_lookup_resources()->set_query_level(Convert(queryLevel)); + request.mutable_lookup_resources()->set_limit(limit); + request.mutable_lookup_resources()->set_retrieve_instances_ids(instancesId != NULL); + + request.mutable_lookup_resources()->mutable_lookup()->Reserve(lookup.size()); + + for (size_t i = 0; i < lookup.size(); i++) + { + DatabasePluginMessages::DatabaseConstraint* constraint = request.mutable_lookup_resources()->add_lookup(); + constraint->set_level(Convert(lookup[i].GetLevel())); + constraint->set_tag_group(lookup[i].GetTag().GetGroup()); + constraint->set_tag_element(lookup[i].GetTag().GetElement()); + constraint->set_is_identifier_tag(lookup[i].IsIdentifier()); + constraint->set_is_case_sensitive(lookup[i].IsCaseSensitive()); + constraint->set_is_mandatory(lookup[i].IsMandatory()); + + constraint->mutable_values()->Reserve(lookup[i].GetValuesCount()); + for (size_t j = 0; j < lookup[i].GetValuesCount(); j++) + { + constraint->add_values(lookup[i].GetValue(j)); + } + + switch (lookup[i].GetConstraintType()) + { + case ConstraintType_Equal: + constraint->set_type(DatabasePluginMessages::CONSTRAINT_EQUAL); + break; + + case ConstraintType_SmallerOrEqual: + constraint->set_type(DatabasePluginMessages::CONSTRAINT_SMALLER_OR_EQUAL); + break; + + case ConstraintType_GreaterOrEqual: + constraint->set_type(DatabasePluginMessages::CONSTRAINT_GREATER_OR_EQUAL); + break; + + case ConstraintType_Wildcard: + constraint->set_type(DatabasePluginMessages::CONSTRAINT_WILDCARD); + break; + + case ConstraintType_List: + constraint->set_type(DatabasePluginMessages::CONSTRAINT_LIST); + break; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_LOOKUP_RESOURCES, request); + + for (int i = 0; i < response.lookup_resources().resources_ids().size(); i++) + { + resourcesId.push_back(response.lookup_resources().resources_ids(i)); + } + + if (instancesId != NULL) + { + if (response.lookup_resources().resources_ids().size() != response.lookup_resources().instances_ids().size()) + { + throw OrthancException(ErrorCode_DatabasePlugin); + } + else + { + for (int i = 0; i < response.lookup_resources().instances_ids().size(); i++) + { + instancesId->push_back(response.lookup_resources().instances_ids(i)); + } + } + } + } + + + virtual bool CreateInstance(CreateInstanceResult& result, /* out */ + int64_t& instanceId, /* out */ + const std::string& patient, + const std::string& study, + const std::string& series, + const std::string& instance) ORTHANC_OVERRIDE + { + // TODO: "CreateInstanceResult" => constructor and getters + + DatabasePluginMessages::TransactionRequest request; + request.mutable_create_instance()->set_patient(patient); + request.mutable_create_instance()->set_study(study); + request.mutable_create_instance()->set_series(series); + request.mutable_create_instance()->set_instance(instance); + + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_CREATE_INSTANCE, request); + + instanceId = response.create_instance().instance_id(); + + if (response.create_instance().is_new_instance()) + { + result.isNewPatient_ = response.create_instance().is_new_patient(); + result.isNewStudy_ = response.create_instance().is_new_study(); + result.isNewSeries_ = response.create_instance().is_new_series(); + result.patientId_ = response.create_instance().patient_id(); + result.studyId_ = response.create_instance().study_id(); + result.seriesId_ = response.create_instance().series_id(); + return true; + } + else + { + return false; + } + } + + + virtual void SetResourcesContent(const ResourcesContent& content) ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionRequest request; + + request.mutable_set_resources_content()->mutable_tags()->Reserve(content.GetListTags().size()); + for (ResourcesContent::ListTags::const_iterator it = content.GetListTags().begin(); it != content.GetListTags().end(); ++it) + { + DatabasePluginMessages::SetResourcesContent_Request_Tag* tag = request.mutable_set_resources_content()->add_tags(); + tag->set_resource_id(it->GetResourceId()); + tag->set_is_identifier(it->IsIdentifier()); + tag->set_group(it->GetTag().GetGroup()); + tag->set_element(it->GetTag().GetElement()); + tag->set_value(it->GetValue()); + } + + request.mutable_set_resources_content()->mutable_metadata()->Reserve(content.GetListMetadata().size()); + for (ResourcesContent::ListMetadata::const_iterator it = content.GetListMetadata().begin(); it != content.GetListMetadata().end(); ++it) + { + DatabasePluginMessages::SetResourcesContent_Request_Metadata* metadata = request.mutable_set_resources_content()->add_metadata(); + metadata->set_resource_id(it->GetResourceId()); + metadata->set_metadata(it->GetType()); + metadata->set_value(it->GetValue()); + } + + ExecuteTransaction(DatabasePluginMessages::OPERATION_SET_RESOURCES_CONTENT, request); + } + + + virtual void GetChildrenMetadata(std::list& target, + int64_t resourceId, + MetadataType metadata) ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_get_children_metadata()->set_id(resourceId); + request.mutable_get_children_metadata()->set_metadata(metadata); + + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_CHILDREN_METADATA, request); + + for (int i = 0; i < response.get_children_metadata().values().size(); i++) + { + target.push_back(response.get_children_metadata().values(i)); + } + } + + + virtual int64_t GetLastChangeIndex() ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_LAST_CHANGE_INDEX); + return response.get_last_change_index().result(); + } + + + virtual bool LookupResourceAndParent(int64_t& id, + ResourceType& type, + std::string& parentPublicId, + const std::string& publicId) ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_lookup_resource_and_parent()->set_public_id(publicId); + + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_LOOKUP_RESOURCE_AND_PARENT, request); + + if (response.lookup_resource_and_parent().found()) + { + id = response.lookup_resource_and_parent().id(); + type = Convert(response.lookup_resource_and_parent().type()); + + switch (type) + { + case ResourceType_Patient: + if (!response.lookup_resource_and_parent().parent_public_id().empty()) + { + throw OrthancException(ErrorCode_DatabasePlugin); + } + break; + + case ResourceType_Study: + case ResourceType_Series: + case ResourceType_Instance: + if (response.lookup_resource_and_parent().parent_public_id().empty()) + { + throw OrthancException(ErrorCode_DatabasePlugin); + } + else + { + parentPublicId = response.lookup_resource_and_parent().parent_public_id(); + } + break; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + return true; + } + else + { + return false; + } + } + }; + + + OrthancPluginDatabaseV4::OrthancPluginDatabaseV4(SharedLibrary& library, + PluginsErrorDictionary& errorDictionary, + const _OrthancPluginRegisterDatabaseBackendV4& database, + const std::string& serverIdentifier) : + library_(library), + errorDictionary_(errorDictionary), + definition_(database), + serverIdentifier_(serverIdentifier), + open_(false), + databaseVersion_(0), + hasFlushToDisk_(false), + hasRevisionsSupport_(false) + { + CLOG(INFO, PLUGINS) << "Identifier of this Orthanc server for the global properties " + << "of the custom database: \"" << serverIdentifier << "\""; + + if (definition_.backend == NULL || + definition_.operations == NULL || + definition_.finalize == NULL) + { + throw OrthancException(ErrorCode_NullPointer); + } + } + + + OrthancPluginDatabaseV4::~OrthancPluginDatabaseV4() + { + definition_.finalize(definition_.backend); + } + + + void OrthancPluginDatabaseV4::Open() + { + if (open_) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + + { + DatabasePluginMessages::DatabaseRequest request; + DatabasePluginMessages::DatabaseResponse response; + ExecuteDatabase(response, *this, DatabasePluginMessages::OPERATION_OPEN, request); + } + + { + DatabasePluginMessages::DatabaseRequest request; + DatabasePluginMessages::DatabaseResponse response; + ExecuteDatabase(response, *this, DatabasePluginMessages::OPERATION_GET_SYSTEM_INFORMATION, request); + databaseVersion_ = response.get_system_information().database_version(); + hasFlushToDisk_ = response.get_system_information().supports_flush_to_disk(); + hasRevisionsSupport_ = response.get_system_information().supports_revisions(); + } + + open_ = true; + } + + + void OrthancPluginDatabaseV4::Close() + { + if (!open_) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + else + { + DatabasePluginMessages::DatabaseRequest request; + DatabasePluginMessages::DatabaseResponse response; + ExecuteDatabase(response, *this, DatabasePluginMessages::OPERATION_CLOSE, request); + } + } + + + bool OrthancPluginDatabaseV4::HasFlushToDisk() const + { + if (!open_) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + else + { + return hasFlushToDisk_; + } + } + + + void OrthancPluginDatabaseV4::FlushToDisk() + { + if (!open_ || + !hasFlushToDisk_) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + else + { + DatabasePluginMessages::DatabaseRequest request; + DatabasePluginMessages::DatabaseResponse response; + ExecuteDatabase(response, *this, DatabasePluginMessages::OPERATION_FLUSH_TO_DISK, request); + } + } + + + IDatabaseWrapper::ITransaction* OrthancPluginDatabaseV4::StartTransaction(TransactionType type, + IDatabaseListener& listener) + { + if (!open_) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + else + { + return new Transaction(*this, listener, type); + } + } + + + unsigned int OrthancPluginDatabaseV4::GetDatabaseVersion() + { + if (!open_) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + else + { + return databaseVersion_; + } + } + + + void OrthancPluginDatabaseV4::Upgrade(unsigned int targetVersion, + IStorageArea& storageArea) + { + if (!open_) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + else + { + VoidDatabaseListener listener; + Transaction transaction(*this, listener, TransactionType_ReadWrite); + + try + { + DatabasePluginMessages::DatabaseRequest request; + request.mutable_upgrade()->set_target_version(targetVersion); + request.mutable_upgrade()->set_storage_area(reinterpret_cast(&storageArea)); + request.mutable_upgrade()->set_transaction(reinterpret_cast(transaction.GetTransactionObject())); + + DatabasePluginMessages::DatabaseResponse response; + + ExecuteDatabase(response, *this, DatabasePluginMessages::OPERATION_UPGRADE, request); + transaction.Commit(0); + } + catch (OrthancException& e) + { + transaction.Rollback(); + throw; + } + } + } + + + bool OrthancPluginDatabaseV4::HasRevisionsSupport() const + { + if (!open_) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + else + { + return hasRevisionsSupport_; + } + } +} diff -r 1fa3bfa86f8e -r 802e2e3d9bfc OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.h Mon Apr 03 17:00:51 2023 +0200 @@ -0,0 +1,99 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2023 Osimis S.A., Belgium + * Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, 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. + * + * 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 + +#if ORTHANC_ENABLE_PLUGINS == 1 + +#include "../../../OrthancFramework/Sources/SharedLibrary.h" +#include "../../Sources/Database/IDatabaseWrapper.h" +#include "../Include/orthanc/OrthancCPlugin.h" +#include "PluginsErrorDictionary.h" + +namespace Orthanc +{ + class OrthancPluginDatabaseV4 : public IDatabaseWrapper + { + private: + class Transaction; + + SharedLibrary& library_; + PluginsErrorDictionary& errorDictionary_; + _OrthancPluginRegisterDatabaseBackendV4 definition_; + std::string serverIdentifier_; + bool open_; + unsigned int databaseVersion_; + bool hasFlushToDisk_; + bool hasRevisionsSupport_; + + void CheckSuccess(OrthancPluginErrorCode code) const; + + public: + OrthancPluginDatabaseV4(SharedLibrary& library, + PluginsErrorDictionary& errorDictionary, + const _OrthancPluginRegisterDatabaseBackendV4& database, + const std::string& serverIdentifier); + + virtual ~OrthancPluginDatabaseV4(); + + const _OrthancPluginRegisterDatabaseBackendV4& GetDefinition() const + { + return definition_; + } + + PluginsErrorDictionary& GetErrorDictionary() const + { + return errorDictionary_; + } + + const std::string& GetServerIdentifier() const + { + return serverIdentifier_; + } + + virtual void Open() ORTHANC_OVERRIDE; + + virtual void Close() ORTHANC_OVERRIDE; + + const SharedLibrary& GetSharedLibrary() const + { + return library_; + } + + virtual void FlushToDisk() ORTHANC_OVERRIDE; + + virtual bool HasFlushToDisk() const ORTHANC_OVERRIDE; + + virtual IDatabaseWrapper::ITransaction* StartTransaction(TransactionType type, + IDatabaseListener& listener) + ORTHANC_OVERRIDE; + + virtual unsigned int GetDatabaseVersion() ORTHANC_OVERRIDE; + + virtual void Upgrade(unsigned int targetVersion, + IStorageArea& storageArea) ORTHANC_OVERRIDE; + + virtual bool HasRevisionsSupport() const ORTHANC_OVERRIDE; + }; +} + +#endif diff -r 1fa3bfa86f8e -r 802e2e3d9bfc OrthancServer/Plugins/Engine/OrthancPlugins.cpp --- a/OrthancServer/Plugins/Engine/OrthancPlugins.cpp Fri Mar 31 16:32:02 2023 +0200 +++ b/OrthancServer/Plugins/Engine/OrthancPlugins.cpp Mon Apr 03 17:00:51 2023 +0200 @@ -63,6 +63,7 @@ #include "../../Sources/ServerToolbox.h" #include "OrthancPluginDatabase.h" #include "OrthancPluginDatabaseV3.h" +#include "OrthancPluginDatabaseV4.h" #include "PluginsEnumerations.h" #include "PluginsJob.h" @@ -1544,8 +1545,9 @@ Properties properties_; int argc_; char** argv_; - std::unique_ptr database_; + std::unique_ptr database_; std::unique_ptr databaseV3_; // New in Orthanc 1.9.2 + std::unique_ptr databaseV4_; // New in Orthanc 1.12.0 PluginsErrorDictionary dictionary_; std::string databaseServerIdentifier_; // New in Orthanc 1.9.2 unsigned int maxDatabaseRetries_; // New in Orthanc 1.9.2 @@ -5487,14 +5489,15 @@ case _OrthancPluginService_RegisterDatabaseBackend: { - LOG(WARNING) << "Performance warning: Plugin has registered a custom database back-end with an old API"; + LOG(WARNING) << "Performance warning: Plugin has registered a custom database back-end with an old API (version 1)"; LOG(WARNING) << "The database backend has *no* support for revisions of metadata and attachments"; const _OrthancPluginRegisterDatabaseBackend& p = *reinterpret_cast(parameters); if (pimpl_->database_.get() == NULL && - pimpl_->databaseV3_.get() == NULL) + pimpl_->databaseV3_.get() == NULL && + pimpl_->databaseV4_.get() == NULL) { pimpl_->database_.reset(new OrthancPluginDatabase(plugin, GetErrorDictionary(), *p.backend, NULL, 0, p.payload)); @@ -5511,14 +5514,15 @@ case _OrthancPluginService_RegisterDatabaseBackendV2: { - LOG(WARNING) << "Performance warning: Plugin has registered a custom database back-end with an old API"; + LOG(WARNING) << "Performance warning: Plugin has registered a custom database back-end with an old API (version 2)"; LOG(WARNING) << "The database backend has *no* support for revisions of metadata and attachments"; const _OrthancPluginRegisterDatabaseBackendV2& p = *reinterpret_cast(parameters); if (pimpl_->database_.get() == NULL && - pimpl_->databaseV3_.get() == NULL) + pimpl_->databaseV3_.get() == NULL && + pimpl_->databaseV4_.get() == NULL) { pimpl_->database_.reset(new OrthancPluginDatabase(plugin, GetErrorDictionary(), *p.backend, p.extensions, @@ -5536,13 +5540,14 @@ case _OrthancPluginService_RegisterDatabaseBackendV3: { - CLOG(INFO, PLUGINS) << "Plugin has registered a custom database back-end"; + LOG(WARNING) << "Performance warning: Plugin has registered a custom database back-end with an old API (version 3)"; const _OrthancPluginRegisterDatabaseBackendV3& p = *reinterpret_cast(parameters); if (pimpl_->database_.get() == NULL && - pimpl_->databaseV3_.get() == NULL) + pimpl_->databaseV3_.get() == NULL && + pimpl_->databaseV4_.get() == NULL) { pimpl_->databaseV3_.reset(new OrthancPluginDatabaseV3(plugin, GetErrorDictionary(), p.backend, p.backendSize, p.database, pimpl_->databaseServerIdentifier_)); @@ -5556,6 +5561,28 @@ return true; } + case _OrthancPluginService_RegisterDatabaseBackendV4: + { + CLOG(INFO, PLUGINS) << "Plugin has registered a custom database back-end"; + + const _OrthancPluginRegisterDatabaseBackendV4& p = + *reinterpret_cast(parameters); + + if (pimpl_->database_.get() == NULL && + pimpl_->databaseV3_.get() == NULL && + pimpl_->databaseV4_.get() == NULL) + { + pimpl_->databaseV4_.reset(new OrthancPluginDatabaseV4(plugin, GetErrorDictionary(), p, pimpl_->databaseServerIdentifier_)); + pimpl_->maxDatabaseRetries_ = p.maxDatabaseRetries; + } + else + { + throw OrthancException(ErrorCode_DatabaseBackendAlreadyRegistered); + } + + return true; + } + case _OrthancPluginService_DatabaseAnswer: throw OrthancException(ErrorCode_InternalError); // Implemented before locking (*) @@ -5695,7 +5722,8 @@ { boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_); return (pimpl_->database_.get() != NULL || - pimpl_->databaseV3_.get() != NULL); + pimpl_->databaseV3_.get() != NULL || + pimpl_->databaseV4_.get() != NULL); } @@ -5735,6 +5763,10 @@ { return *pimpl_->databaseV3_; } + else if (pimpl_->databaseV4_.get() != NULL) + { + return *pimpl_->databaseV4_; + } else { throw OrthancException(ErrorCode_BadSequenceOfCalls); @@ -5752,6 +5784,10 @@ { return pimpl_->databaseV3_->GetSharedLibrary(); } + else if (pimpl_->databaseV4_.get() != NULL) + { + return pimpl_->databaseV4_->GetSharedLibrary(); + } else { throw OrthancException(ErrorCode_BadSequenceOfCalls); diff -r 1fa3bfa86f8e -r 802e2e3d9bfc OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h --- a/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h Fri Mar 31 16:32:02 2023 +0200 +++ b/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h Mon Apr 03 17:00:51 2023 +0200 @@ -17,7 +17,7 @@ * - Possibly register its callback for received DICOM instances using ::OrthancPluginRegisterOnStoredInstanceCallback(). * - Possibly register its callback for changes to the DICOM store using ::OrthancPluginRegisterOnChangeCallback(). * - Possibly register a custom storage area using ::OrthancPluginRegisterStorageArea2(). - * - Possibly register a custom database back-end area using OrthancPluginRegisterDatabaseBackendV3(). + * - Possibly register a custom database back-end area using OrthancPluginRegisterDatabaseBackendV4(). * - Possibly register a handler for C-Find SCP using OrthancPluginRegisterFindCallback(). * - Possibly register a handler for C-Find SCP against DICOM worklists using OrthancPluginRegisterWorklistCallback(). * - Possibly register a handler for C-Move SCP using OrthancPluginRegisterMoveCallback(). @@ -119,8 +119,8 @@ #endif #define ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER 1 -#define ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER 11 -#define ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER 3 +#define ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER 12 +#define ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER 0 #if !defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) @@ -535,7 +535,8 @@ _OrthancPluginService_StorageAreaRead = 5004, _OrthancPluginService_StorageAreaRemove = 5005, _OrthancPluginService_RegisterDatabaseBackendV3 = 5006, /* New in Orthanc 1.9.2 */ - + _OrthancPluginService_RegisterDatabaseBackendV4 = 5007, /* New in Orthanc 1.12.0 */ + /* Primitives for handling images */ _OrthancPluginService_GetImagePixelFormat = 6000, _OrthancPluginService_GetImageWidth = 6001, @@ -9164,6 +9165,66 @@ } + /** + * @brief Signature of a callback function that is triggered when + * the Orthanc core requests an operation from the database plugin. + * Both request and response are encoded as protobuf buffers. + * @ingroup Callbacks + **/ + typedef OrthancPluginErrorCode (*OrthancPluginCallDatabaseBackendV4) ( + OrthancPluginMemoryBuffer64* response, + void* backend, + const void* request, + uint64_t requestSize); + + /** + * @brief Signature of a callback function that is triggered when + * the database plugin must be finalized. + * @ingroup Callbacks + **/ + typedef void (*OrthancPluginFinalizeDatabaseBackendV4) (void* backend); + + typedef struct + { + void* backend; + uint32_t maxDatabaseRetries; + OrthancPluginCallDatabaseBackendV4 operations; + OrthancPluginFinalizeDatabaseBackendV4 finalize; + } _OrthancPluginRegisterDatabaseBackendV4; + + /** + * Register a custom database back-end. + * + * This function was added in Orthanc SDK 1.12.0. It uses Google + * Protocol Buffers for the communications between the Orthanc core + * and database plugins. Check out "OrthancDatabasePlugin.proto" for + * the definition of the protobuf messages. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param backend Pointer to the custom database backend. + * @param maxDatabaseRetries Maximum number of retries if transaction doesn't succeed. + * If no retry is successful, OrthancPluginErrorCode_DatabaseCannotSerialize is generated. + * @param operations Access to the operations of the custom database backend. + * @param finalize Callback to deallocate the custom database backend. + * @return 0 if success, other value if error. + * @ingroup Callbacks + **/ + ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginRegisterDatabaseBackendV4( + OrthancPluginContext* context, + void* backend, + uint32_t maxDatabaseRetries, + OrthancPluginCallDatabaseBackendV4 operations, + OrthancPluginFinalizeDatabaseBackendV4 finalize) + { + _OrthancPluginRegisterDatabaseBackendV4 params; + params.backend = backend; + params.maxDatabaseRetries = maxDatabaseRetries; + params.operations = operations; + params.finalize = finalize; + + return context->InvokeService(context, _OrthancPluginService_RegisterDatabaseBackendV4, ¶ms); + } + #ifdef __cplusplus } #endif diff -r 1fa3bfa86f8e -r 802e2e3d9bfc OrthancServer/Plugins/Include/orthanc/OrthancDatabasePlugin.proto --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Plugins/Include/orthanc/OrthancDatabasePlugin.proto Mon Apr 03 17:00:51 2023 +0200 @@ -0,0 +1,851 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2023 Osimis S.A., Belgium + * Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, 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. + * + * 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 . + **/ + + +/** + * This Protocol Buffers prototype describes the exchanges between the + * Orthanc core and its database plugins. The various calls correspond + * to the "IDatabaseWrapper" interface in the source code of Orthanc. + * + * WARNING: *NEVER* modify or remove existing entries. It is only + * allowed to *add* new stuff. + **/ + +syntax = "proto3"; + +/** + * Turn off protobuf reflection to avoid clashes between the Orthanc + * core and the database plugin, otherwise both will try to register + * the same messages in the process-wide descriptor pool, which would + * result in protobuf error "File already exists in database". + **/ +option optimize_for = LITE_RUNTIME; + +package Orthanc.DatabasePluginMessages; + + +/** + * Data structures that are common with the Orthanc core. + **/ + +message FileInfo { + string uuid = 1; + int32 content_type = 2; // opaque "FileContentType" in Orthanc + uint64 uncompressed_size = 3; + string uncompressed_hash = 4; + int32 compression_type = 5; // opaque "CompressionType" in Orthanc + uint64 compressed_size = 6; + string compressed_hash = 7; +} + +enum ResourceType { + RESOURCE_PATIENT = 0; + RESOURCE_STUDY = 1; + RESOURCE_SERIES = 2; + RESOURCE_INSTANCE = 3; +} + +enum ConstraintType { + CONSTRAINT_EQUAL = 0; + CONSTRAINT_SMALLER_OR_EQUAL = 1; + CONSTRAINT_GREATER_OR_EQUAL = 2; + CONSTRAINT_WILDCARD = 3; + CONSTRAINT_LIST = 4; +} + +message ServerIndexChange { + int64 seq = 1; + int32 change_type = 2; // opaque "ChangeType" in Orthanc + ResourceType resource_type = 3; + string public_id = 4; + string date = 5; +} + +message ExportedResource { + int64 seq = 1; + ResourceType resource_type = 2; + string public_id = 3; + string modality = 4; + string date = 5; + string patient_id = 6; + string study_instance_uid = 7; + string series_instance_uid = 8; + string sop_instance_uid = 9; +} + +message DatabaseConstraint { + ResourceType level = 1; + uint32 tag_group = 2; + uint32 tag_element = 3; + bool is_identifier_tag = 4; + bool is_case_sensitive = 5; + bool is_mandatory = 6; + ConstraintType type = 7; + repeated string values = 8; +} + + +/** + * Database-level operations. + **/ + +enum DatabaseOperation { + OPERATION_GET_SYSTEM_INFORMATION = 0; + OPERATION_OPEN = 1; + OPERATION_CLOSE = 2; + OPERATION_FLUSH_TO_DISK = 3; + OPERATION_START_TRANSACTION = 4; + OPERATION_UPGRADE = 5; + OPERATION_FINALIZE_TRANSACTION = 6; +} + +enum TransactionType { + TRANSACTION_READ_ONLY = 0; + TRANSACTION_READ_WRITE = 1; +} + +message GetSystemInformation { + message Request { + } + message Response { + uint32 database_version = 1; + bool supports_flush_to_disk = 2; + bool supports_revisions = 3; + } +} + +message Open { + message Request { + } + message Response { + } +} + +message Close { + message Request { + } + message Response { + } +} + +message FlushToDisk { + message Request { + } + message Response { + } +} + +message StartTransaction { + message Request { + TransactionType type = 1; + } + message Response { + sfixed64 transaction = 1; + } +} + +message Upgrade { + /** + * It is guaranteed that a read-write transaction is created by the + * Orthanc core before executing this operation. + **/ + message Request { + uint32 target_version = 1; + sfixed64 storage_area = 2; + sfixed64 transaction = 3; + } + message Response { + } +} + +message FinalizeTransaction { + message Request { + sfixed64 transaction = 1; + } + message Response { + } +} + +message DatabaseRequest { + sfixed64 database = 1; + DatabaseOperation operation = 2; + + GetSystemInformation.Request get_system_information = 100; + Open.Request open = 101; + Close.Request close = 102; + FlushToDisk.Request flush_to_disk = 103; + StartTransaction.Request start_transaction = 104; + Upgrade.Request upgrade = 105; + FinalizeTransaction.Request finalize_transaction = 106; +} + +message DatabaseResponse { + GetSystemInformation.Response get_system_information = 100; + Open.Response open = 101; + Close.Response close = 102; + FlushToDisk.Response flush_to_disk = 103; + StartTransaction.Response start_transaction = 104; + Upgrade.Response upgrade = 105; + FinalizeTransaction.Response finalize_transaction = 106; +} + + +/** + * Transaction-level operations. + **/ + +enum TransactionOperation { + OPERATION_ROLLBACK = 0; + OPERATION_COMMIT = 1; + OPERATION_ADD_ATTACHMENT = 2; + OPERATION_CLEAR_CHANGES = 3; + OPERATION_CLEAR_EXPORTED_RESOURCES = 4; + OPERATION_DELETE_ATTACHMENT = 5; + OPERATION_DELETE_METADATA = 6; + OPERATION_DELETE_RESOURCE = 7; + OPERATION_GET_ALL_METADATA = 8; + OPERATION_GET_ALL_PUBLIC_IDS = 9; + OPERATION_GET_ALL_PUBLIC_IDS_WITH_LIMITS = 10; + OPERATION_GET_CHANGES = 11; + OPERATION_GET_CHILDREN_INTERNAL_ID = 12; + OPERATION_GET_CHILDREN_PUBLIC_ID = 13; + OPERATION_GET_EXPORTED_RESOURCES = 14; + OPERATION_GET_LAST_CHANGE = 15; + OPERATION_GET_LAST_EXPORTED_RESOURCE = 16; + OPERATION_GET_MAIN_DICOM_TAGS = 17; + OPERATION_GET_PUBLIC_ID = 18; + OPERATION_GET_RESOURCES_COUNT = 19; + OPERATION_GET_RESOURCE_TYPE = 20; + OPERATION_GET_TOTAL_COMPRESSED_SIZE = 21; + OPERATION_GET_TOTAL_UNCOMPRESSED_SIZE = 22; + OPERATION_IS_PROTECTED_PATIENT = 23; + OPERATION_LIST_AVAILABLE_ATTACHMENTS = 24; + OPERATION_LOG_CHANGE = 25; + OPERATION_LOG_EXPORTED_RESOURCE = 26; + OPERATION_LOOKUP_ATTACHMENT = 27; + OPERATION_LOOKUP_GLOBAL_PROPERTY = 28; + OPERATION_LOOKUP_METADATA = 29; + OPERATION_LOOKUP_PARENT = 30; + OPERATION_LOOKUP_RESOURCE = 31; + OPERATION_SELECT_PATIENT_TO_RECYCLE = 32; + OPERATION_SELECT_PATIENT_TO_RECYCLE_WITH_AVOID = 33; + OPERATION_SET_GLOBAL_PROPERTY = 34; + OPERATION_CLEAR_MAIN_DICOM_TAGS = 35; + OPERATION_SET_METADATA = 36; + OPERATION_SET_PROTECTED_PATIENT = 37; + OPERATION_IS_DISK_SIZE_ABOVE = 38; + OPERATION_LOOKUP_RESOURCES = 39; + OPERATION_CREATE_INSTANCE = 40; + OPERATION_SET_RESOURCES_CONTENT = 41; + OPERATION_GET_CHILDREN_METADATA = 42; + OPERATION_GET_LAST_CHANGE_INDEX = 43; + OPERATION_LOOKUP_RESOURCE_AND_PARENT = 44; +} + +message Rollback { + message Request { + } + message Response { + } +} + +message Commit { + message Request { + int64 file_size_delta = 1; + } + message Response { + } +} + +message AddAttachment { + message Request { + int64 id = 1; + FileInfo attachment = 2; + int64 revision = 3; + } + message Response { + } +} + +message ClearChanges { + message Request { + } + message Response { + } +} + +message ClearExportedResources { + message Request { + } + message Response { + } +} + +message DeleteAttachment { + message Request { + int64 id = 1; + int32 type = 2; + } + message Response { + FileInfo deleted_attachment = 1; + } +} + +message DeleteMetadata { + message Request { + int64 id = 1; + int32 type = 2; + } + message Response { + } +} + +message DeleteResource { + message Request { + int64 id = 1; + } + message Response { + message Resource { + ResourceType level = 1; + string public_id = 2; + } + repeated FileInfo deleted_attachments = 1; + repeated Resource deleted_resources = 2; + bool is_remaining_ancestor = 3; + Resource remaining_ancestor = 4; + } +} + +message GetAllMetadata { + message Request { + int64 id = 1; + } + message Response { + message Metadata { + int32 type = 1; + string value = 2; + } + repeated Metadata metadata = 1; + } +} + +message GetAllPublicIds { + message Request { + ResourceType resource_type = 1; + } + message Response { + repeated string ids = 1; + } +} + +message GetAllPublicIdsWithLimits { + message Request { + ResourceType resource_type = 1; + int64 since = 2; + uint32 limit = 3; + } + message Response { + repeated string ids = 1; + } +} + +message GetChanges { + message Request { + int64 since = 1; + uint32 limit = 2; + } + message Response { + repeated ServerIndexChange changes = 1; + bool done = 2; + } +} + +message GetChildrenInternalId { + message Request { + int64 id = 1; + } + message Response { + repeated int64 ids = 1; + } +} + +message GetChildrenPublicId { + message Request { + int64 id = 1; + } + message Response { + repeated string ids = 1; + } +} + +message GetExportedResources { + message Request { + int64 since = 1; + uint32 limit = 2; + } + message Response { + repeated ExportedResource resources = 1; + bool done = 2; + } +} + +message GetLastChange { + message Request { + } + message Response { + bool found = 1; + ServerIndexChange change = 2; + } +} + +message GetLastExportedResource { + message Request { + } + message Response { + bool found = 1; + ExportedResource resource = 2; + } +} + +message GetMainDicomTags { + message Request { + int64 id = 1; + } + message Response { + message Tag { + uint32 group = 1; + uint32 element = 2; + string value = 3; + } + repeated Tag tags = 1; + } +} + +message GetPublicId { + message Request { + int64 id = 1; + } + message Response { + string id = 1; + } +} + +message GetResourcesCount { + message Request { + ResourceType type = 1; + } + message Response { + uint64 count = 1; + } +} + +message GetResourceType { + message Request { + int64 id = 1; + } + message Response { + ResourceType type = 1; + } +} + +message GetTotalCompressedSize { + message Request { + } + message Response { + uint64 size = 1; + } +} + +message GetTotalUncompressedSize { + message Request { + } + message Response { + uint64 size = 1; + } +} + +message IsProtectedPatient { + message Request { + int64 patient_id = 1; + } + message Response { + bool protected_patient = 1; + } +} + +message ListAvailableAttachments { + message Request { + int64 id = 1; + } + message Response { + repeated int32 attachments = 1; + } +} + +message LogChange { + message Request { + int32 change_type = 1; + ResourceType resource_type = 2; + int64 resource_id = 3; + string date = 4; + } + message Response { + } +} + +message LogExportedResource { + message Request { + ResourceType resource_type = 1; + string public_id = 2; + string modality = 3; + string date = 4; + string patient_id = 5; + string study_instance_uid = 6; + string series_instance_uid = 7; + string sop_instance_uid = 8; + } + message Response { + } +} + +message LookupAttachment { + message Request { + int64 id = 1; + int32 content_type = 2; + } + message Response { + bool found = 1; + FileInfo attachment = 2; + int64 revision = 3; + } +} + +message LookupGlobalProperty { + message Request { + string server_id = 1; + int32 property = 2; + } + message Response { + bool found = 1; + string value = 2; + } +} + +message LookupMetadata { + message Request { + int64 id = 1; + int32 metadata_type = 2; + } + message Response { + bool found = 1; + string value = 2; + int64 revision = 3; + } +} + +message LookupParent { + message Request { + int64 id = 1; + } + message Response { + bool found = 1; + int64 parent = 2; + } +} + +message LookupResource { + message Request { + string public_id = 1; + } + message Response { + bool found = 1; + int64 internal_id = 2; + ResourceType type = 3; + } +} + +message SelectPatientToRecycle { + message Request { + } + message Response { + bool found = 1; + int64 patient_id = 2; + } +} + +message SelectPatientToRecycleWithAvoid { + message Request { + int64 patient_id_to_avoid = 1; + } + message Response { + bool found = 1; + int64 patient_id = 2; + } +} + +message SetGlobalProperty { + message Request { + string server_id = 1; + int32 property = 2; + string value = 3; + } + message Response { + } +} + +message ClearMainDicomTags { + message Request { + int64 id = 1; + } + message Response { + } +} + +message SetMetadata { + message Request { + int64 id = 1; + int32 metadata_type = 2; + string value = 3; + int64 revision = 4; + } + message Response { + } +} + +message SetProtectedPatient { + message Request { + int64 patient_id = 1; + bool protected_patient = 2; + } + message Response { + } +} + +message IsDiskSizeAbove { + message Request { + uint64 threshold = 1; + } + message Response { + bool result = 1; + } +} + +message LookupResources { + message Request { + repeated DatabaseConstraint lookup = 1; + ResourceType query_level = 2; + uint32 limit = 3; + bool retrieve_instances_ids = 4; + } + message Response { + repeated string resources_ids = 1; + repeated string instances_ids = 2; // Only filled if "retrieve_instances" is true + } +} + +message CreateInstance { + message Request { + string patient = 1; + string study = 2; + string series = 3; + string instance = 4; + } + message Response { + bool is_new_instance = 1; + int64 instance_id = 2; + + // The fields below are only set if "is_new_instance" is true + bool is_new_patient = 3; + bool is_new_study = 4; + bool is_new_series = 5; + int64 patient_id = 6; + int64 study_id = 7; + int64 series_id = 8; + } +} + +message SetResourcesContent { + message Request { + message Tag { + int64 resource_id = 1; + bool is_identifier = 2; + uint32 group = 3; + uint32 element = 4; + string value = 5; + } + + message Metadata { + int64 resource_id = 1; + int32 metadata = 2; + string value = 3; + } + + repeated Tag tags = 1; + repeated Metadata metadata = 2; + } + message Response { + } +} + +message GetChildrenMetadata { + message Request { + int64 id = 1; + int32 metadata = 2; + } + message Response { + repeated string values = 1; + } +} + +message GetLastChangeIndex { + message Request { + } + message Response { + int64 result = 1; + } +} + +message LookupResourceAndParent { + message Request { + string public_id = 1; + } + message Response { + bool found = 1; + int64 id = 2; + ResourceType type = 3; + string parent_public_id = 4; // Only for study, series, or instance + } +} + +message TransactionRequest { + sfixed64 transaction = 1; + TransactionOperation operation = 2; + + Rollback.Request rollback = 100; + Commit.Request commit = 101; + AddAttachment.Request add_attachment = 102; + ClearChanges.Request clear_changes = 103; + ClearExportedResources.Request clear_exported_resources = 104; + DeleteAttachment.Request delete_attachment = 105; + DeleteMetadata.Request delete_metadata = 106; + DeleteResource.Request delete_resource = 107; + GetAllMetadata.Request get_all_metadata = 108; + GetAllPublicIds.Request get_all_public_ids = 109; + GetAllPublicIdsWithLimits.Request get_all_public_ids_with_limits = 110; + GetChanges.Request get_changes = 111; + GetChildrenInternalId.Request get_children_internal_id = 112; + GetChildrenPublicId.Request get_children_public_id = 113; + GetExportedResources.Request get_exported_resources = 114; + GetLastChange.Request get_last_change = 115; + GetLastExportedResource.Request get_last_exported_resource = 116; + GetMainDicomTags.Request get_main_dicom_tags = 117; + GetPublicId.Request get_public_id = 118; + GetResourcesCount.Request get_resources_count = 119; + GetResourceType.Request get_resource_type = 120; + GetTotalCompressedSize.Request get_total_compressed_size = 121; + GetTotalUncompressedSize.Request get_total_uncompressed_size = 122; + IsProtectedPatient.Request is_protected_patient = 123; + ListAvailableAttachments.Request list_available_attachments = 124; + LogChange.Request log_change = 125; + LogExportedResource.Request log_exported_resource = 126; + LookupAttachment.Request lookup_attachment = 127; + LookupGlobalProperty.Request lookup_global_property = 128; + LookupMetadata.Request lookup_metadata = 129; + LookupParent.Request lookup_parent = 130; + LookupResource.Request lookup_resource = 131; + SelectPatientToRecycle.Request select_patient_to_recycle = 132; + SelectPatientToRecycleWithAvoid.Request select_patient_to_recycle_with_avoid = 133; + SetGlobalProperty.Request set_global_property = 134; + ClearMainDicomTags.Request clear_main_dicom_tags = 135; + SetMetadata.Request set_metadata = 136; + SetProtectedPatient.Request set_protected_patient = 137; + IsDiskSizeAbove.Request is_disk_size_above = 138; + LookupResources.Request lookup_resources = 139; + CreateInstance.Request create_instance = 140; + SetResourcesContent.Request set_resources_content = 141; + GetChildrenMetadata.Request get_children_metadata = 142; + GetLastChangeIndex.Request get_last_change_index = 143; + LookupResourceAndParent.Request lookup_resource_and_parent = 144; +} + +message TransactionResponse { + Rollback.Response rollback = 100; + Commit.Response commit = 101; + AddAttachment.Response add_attachment = 102; + ClearChanges.Response clear_changes = 103; + ClearExportedResources.Response clear_exported_resources = 104; + DeleteAttachment.Response delete_attachment = 105; + DeleteMetadata.Response delete_metadata = 106; + DeleteResource.Response delete_resource = 107; + GetAllMetadata.Response get_all_metadata = 108; + GetAllPublicIds.Response get_all_public_ids = 109; + GetAllPublicIdsWithLimits.Response get_all_public_ids_with_limits = 110; + GetChanges.Response get_changes = 111; + GetChildrenInternalId.Response get_children_internal_id = 112; + GetChildrenPublicId.Response get_children_public_id = 113; + GetExportedResources.Response get_exported_resources = 114; + GetLastChange.Response get_last_change = 115; + GetLastExportedResource.Response get_last_exported_resource = 116; + GetMainDicomTags.Response get_main_dicom_tags = 117; + GetPublicId.Response get_public_id = 118; + GetResourcesCount.Response get_resources_count = 119; + GetResourceType.Response get_resource_type = 120; + GetTotalCompressedSize.Response get_total_compressed_size = 121; + GetTotalUncompressedSize.Response get_total_uncompressed_size = 122; + IsProtectedPatient.Response is_protected_patient = 123; + ListAvailableAttachments.Response list_available_attachments = 124; + LogChange.Response log_change = 125; + LogExportedResource.Response log_exported_resource = 126; + LookupAttachment.Response lookup_attachment = 127; + LookupGlobalProperty.Response lookup_global_property = 128; + LookupMetadata.Response lookup_metadata = 129; + LookupParent.Response lookup_parent = 130; + LookupResource.Response lookup_resource = 131; + SelectPatientToRecycle.Response select_patient_to_recycle = 132; + SelectPatientToRecycleWithAvoid.Response select_patient_to_recycle_with_avoid = 133; + SetGlobalProperty.Response set_global_property = 134; + ClearMainDicomTags.Response clear_main_dicom_tags = 135; + SetMetadata.Response set_metadata = 136; + SetProtectedPatient.Response set_protected_patient = 137; + IsDiskSizeAbove.Response is_disk_size_above = 138; + LookupResources.Response lookup_resources = 139; + CreateInstance.Response create_instance = 140; + SetResourcesContent.Response set_resources_content = 141; + GetChildrenMetadata.Response get_children_metadata = 142; + GetLastChangeIndex.Response get_last_change_index = 143; + LookupResourceAndParent.Response lookup_resource_and_parent = 144; +} + +enum RequestType { + REQUEST_DATABASE = 0; + REQUEST_TRANSACTION = 1; +} + +message Request { + RequestType type = 1; + DatabaseRequest database_request = 2; + TransactionRequest transaction_request = 3; +} + +message Response { + DatabaseResponse database_response = 2; + TransactionResponse transaction_response = 3; +} diff -r 1fa3bfa86f8e -r 802e2e3d9bfc OrthancServer/Sources/Database/IDatabaseWrapper.h --- a/OrthancServer/Sources/Database/IDatabaseWrapper.h Fri Mar 31 16:32:02 2023 +0200 +++ b/OrthancServer/Sources/Database/IDatabaseWrapper.h Mon Apr 03 17:00:51 2023 +0200 @@ -42,7 +42,7 @@ class IDatabaseWrapper : public boost::noncopyable { public: - struct CreateInstanceResult + struct CreateInstanceResult : public boost::noncopyable { bool isNewPatient_; bool isNewStudy_; @@ -94,13 +94,13 @@ virtual void GetAllPublicIds(std::list& target, ResourceType resourceType, - size_t since, - size_t limit) = 0; + int64_t since, + uint32_t limit) = 0; virtual void GetChanges(std::list& target /*out*/, bool& done /*out*/, int64_t since, - uint32_t maxResults) = 0; + uint32_t limit) = 0; virtual void GetChildrenInternalId(std::list& target, int64_t id) = 0; @@ -111,7 +111,7 @@ virtual void GetExportedResources(std::list& target /*out*/, bool& done /*out*/, int64_t since, - uint32_t maxResults) = 0; + uint32_t limit) = 0; virtual void GetLastChange(std::list& target /*out*/) = 0; @@ -130,15 +130,16 @@ virtual uint64_t GetTotalUncompressedSize() = 0; - virtual bool IsExistingResource(int64_t internalId) = 0; - virtual bool IsProtectedPatient(int64_t internalId) = 0; virtual void ListAvailableAttachments(std::set& target, int64_t id) = 0; - virtual void LogChange(int64_t internalId, - const ServerIndexChange& change) = 0; + virtual void LogChange(ChangeType changeType, + ResourceType resourceType, + int64_t internalId, + const std::string& publicId, /* only for compatibility with V1 and V2 plugins */ + const std::string& date) = 0; virtual void LogExportedResource(const ExportedResource& resource) = 0; @@ -199,7 +200,7 @@ std::list* instancesId, // Can be NULL if not needed const std::vector& lookup, ResourceType queryLevel, - size_t limit) = 0; + uint32_t limit) = 0; // Returns "true" iff. the instance is new and has been inserted // into the database. If "false" is returned, the content of diff -r 1fa3bfa86f8e -r 802e2e3d9bfc OrthancServer/Sources/Database/ResourcesContent.cpp --- a/OrthancServer/Sources/Database/ResourcesContent.cpp Fri Mar 31 16:32:02 2023 +0200 +++ b/OrthancServer/Sources/Database/ResourcesContent.cpp Mon Apr 03 17:00:51 2023 +0200 @@ -140,13 +140,13 @@ for (std::list::const_iterator it = tags_.begin(); it != tags_.end(); ++it) { - if (it->isIdentifier_) + if (it->IsIdentifier()) { - compatibility.SetIdentifierTag(it->resourceId_, it->tag_, it->value_); + compatibility.SetIdentifierTag(it->GetResourceId(), it->GetTag(), it->GetValue()); } else { - compatibility.SetMainDicomTag(it->resourceId_, it->tag_, it->value_); + compatibility.SetMainDicomTag(it->GetResourceId(), it->GetTag(), it->GetValue()); } } @@ -154,7 +154,7 @@ it = metadata_.begin(); it != metadata_.end(); ++it) { assert(isNewResource_); - compatibility.SetMetadata(it->resourceId_, it->metadata_, it->value_, 0 /* initial revision number */); + compatibility.SetMetadata(it->GetResourceId(), it->GetType(), it->GetValue(), 0 /* initial revision number */); } } } diff -r 1fa3bfa86f8e -r 802e2e3d9bfc OrthancServer/Sources/Database/ResourcesContent.h --- a/OrthancServer/Sources/Database/ResourcesContent.h Fri Mar 31 16:32:02 2023 +0200 +++ b/OrthancServer/Sources/Database/ResourcesContent.h Mon Apr 03 17:00:51 2023 +0200 @@ -39,13 +39,15 @@ class ResourcesContent : public boost::noncopyable { public: - struct TagValue + class TagValue { + private: int64_t resourceId_; bool isIdentifier_; DicomTag tag_; std::string value_; + public: TagValue(int64_t resourceId, bool isIdentifier, const DicomTag& tag, @@ -56,22 +58,59 @@ value_(value) { } + + int64_t GetResourceId() const + { + return resourceId_; + } + + bool IsIdentifier() const + { + return isIdentifier_; + } + + const DicomTag& GetTag() const + { + return tag_; + } + + const std::string& GetValue() const + { + return value_; + } }; - struct Metadata + class Metadata { + private: int64_t resourceId_; - MetadataType metadata_; + MetadataType type_; std::string value_; + public: Metadata(int64_t resourceId, - MetadataType metadata, + MetadataType type, const std::string& value) : resourceId_(resourceId), - metadata_(metadata), + type_(type), value_(value) { } + + int64_t GetResourceId() const + { + return resourceId_; + } + + MetadataType GetType() const + { + return type_; + } + + const std::string& GetValue() const + { + return value_; + } }; typedef std::list ListTags; diff -r 1fa3bfa86f8e -r 802e2e3d9bfc OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp --- a/OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp Fri Mar 31 16:32:02 2023 +0200 +++ b/OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp Mon Apr 03 17:00:51 2023 +0200 @@ -233,11 +233,11 @@ void GetChangesInternal(std::list& target, bool& done, SQLite::Statement& s, - uint32_t maxResults) + uint32_t limit) { target.clear(); - while (target.size() < maxResults && s.Step()) + while (target.size() < limit && s.Step()) { int64_t seq = s.ColumnInt64(0); ChangeType changeType = static_cast(s.ColumnInt(1)); @@ -250,18 +250,18 @@ target.push_back(ServerIndexChange(seq, changeType, resourceType, publicId, date)); } - done = !(target.size() == maxResults && s.Step()); + done = !(target.size() == limit && s.Step()); } void GetExportedResourcesInternal(std::list& target, bool& done, SQLite::Statement& s, - uint32_t maxResults) + uint32_t limit) { target.clear(); - while (target.size() < maxResults && s.Step()) + while (target.size() < limit && s.Step()) { int64_t seq = s.ColumnInt64(0); ResourceType resourceType = static_cast(s.ColumnInt(1)); @@ -280,7 +280,7 @@ target.push_back(resource); } - done = !(target.size() == maxResults && s.Step()); + done = !(target.size() == limit && s.Step()); } @@ -341,7 +341,7 @@ std::list* instancesId, const std::vector& lookup, ResourceType queryLevel, - size_t limit) ORTHANC_OVERRIDE + uint32_t limit) ORTHANC_OVERRIDE { LookupFormatter formatter; @@ -509,8 +509,8 @@ virtual void GetAllPublicIds(std::list& target, ResourceType resourceType, - size_t since, - size_t limit) ORTHANC_OVERRIDE + int64_t since, + uint32_t limit) ORTHANC_OVERRIDE { if (limit == 0) { @@ -536,12 +536,12 @@ virtual void GetChanges(std::list& target /*out*/, bool& done /*out*/, int64_t since, - uint32_t maxResults) ORTHANC_OVERRIDE + uint32_t limit) ORTHANC_OVERRIDE { SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT * FROM Changes WHERE seq>? ORDER BY seq LIMIT ?"); s.BindInt64(0, since); - s.BindInt(1, maxResults + 1); - GetChangesInternal(target, done, s, maxResults); + s.BindInt(1, limit + 1); + GetChangesInternal(target, done, s, limit); } @@ -588,13 +588,13 @@ virtual void GetExportedResources(std::list& target, bool& done, int64_t since, - uint32_t maxResults) ORTHANC_OVERRIDE + uint32_t limit) ORTHANC_OVERRIDE { SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT * FROM ExportedResources WHERE seq>? ORDER BY seq LIMIT ?"); s.BindInt64(0, since); - s.BindInt(1, maxResults + 1); - GetExportedResourcesInternal(target, done, s, maxResults); + s.BindInt(1, limit + 1); + GetExportedResourcesInternal(target, done, s, limit); } @@ -731,15 +731,6 @@ } - virtual bool IsExistingResource(int64_t internalId) ORTHANC_OVERRIDE - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, - "SELECT * FROM Resources WHERE internalId=?"); - s.BindInt64(0, internalId); - return s.Step(); - } - - virtual bool IsProtectedPatient(int64_t internalId) ORTHANC_OVERRIDE { SQLite::Statement s(db_, SQLITE_FROM_HERE, @@ -765,14 +756,17 @@ } - virtual void LogChange(int64_t internalId, - const ServerIndexChange& change) ORTHANC_OVERRIDE + virtual void LogChange(ChangeType changeType, + ResourceType resourceType, + int64_t internalId, + const std::string& /* publicId - unused */, + const std::string& date) ORTHANC_OVERRIDE { SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Changes (seq, changeType, internalId, resourceType, date) VALUES(NULL, ?, ?, ?, ?)"); - s.BindInt(0, change.GetChangeType()); + s.BindInt(0, changeType); s.BindInt64(1, internalId); - s.BindInt(2, change.GetResourceType()); - s.BindString(3, change.GetDate()); + s.BindInt(2, resourceType); + s.BindString(3, date); s.Run(); } diff -r 1fa3bfa86f8e -r 802e2e3d9bfc OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp --- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp Fri Mar 31 16:32:02 2023 +0200 +++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp Mon Apr 03 17:00:51 2023 +0200 @@ -415,7 +415,7 @@ if (changeType <= ChangeType_INTERNAL_LastLogged) { - transaction_.LogChange(internalId, change); + transaction_.LogChange(changeType, resourceType, internalId, publicId, change.GetDate()); } GetTransactionContext().SignalChange(change); diff -r 1fa3bfa86f8e -r 802e2e3d9bfc OrthancServer/Sources/OrthancInitialization.cpp --- a/OrthancServer/Sources/OrthancInitialization.cpp Fri Mar 31 16:32:02 2023 +0200 +++ b/OrthancServer/Sources/OrthancInitialization.cpp Mon Apr 03 17:00:51 2023 +0200 @@ -51,6 +51,10 @@ #include // For DCM_dcmnetLogger +#if ORTHANC_ENABLE_PLUGINS == 1 +# include +#endif + static const char* const STORAGE_DIRECTORY = "StorageDirectory"; static const char* const ORTHANC_STORAGE = "OrthancStorage"; @@ -315,6 +319,10 @@ OrthancConfiguration::WriterLock lock; +#if ORTHANC_ENABLE_PLUGINS == 1 + GOOGLE_PROTOBUF_VERIFY_VERSION; +#endif + InitializeServerEnumerations(); // Read the user-provided configuration @@ -391,6 +399,10 @@ { OrthancConfiguration::WriterLock lock; Orthanc::FinalizeFramework(); + +#if ORTHANC_ENABLE_PLUGINS == 1 + google::protobuf::ShutdownProtobufLibrary(); +#endif }