comparison Framework/Plugins/DatabaseBackendAdapterV3.cpp @ 569:f18e46d7dbf8 attach-custom-data

merged find-refactoring -> attach-custom-data
author Alain Mazy <am@orthanc.team>
date Tue, 24 Sep 2024 15:04:21 +0200
parents cd9521e04249 1a23f1ce3b98
children
comparison
equal deleted inserted replaced
368:82f73188b58d 569:f18e46d7dbf8
1 /** 1 /**
2 * Orthanc - A Lightweight, RESTful DICOM Store 2 * Orthanc - A Lightweight, RESTful DICOM Store
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics 3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4 * Department, University Hospital of Liege, Belgium 4 * Department, University Hospital of Liege, Belgium
5 * Copyright (C) 2017-2021 Osimis S.A., Belgium 5 * Copyright (C) 2017-2023 Osimis S.A., Belgium
6 * Copyright (C) 2024-2024 Orthanc Team SRL, Belgium
7 * Copyright (C) 2021-2024 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
6 * 8 *
7 * This program is free software: you can redistribute it and/or 9 * This program is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU Affero General Public License 10 * modify it under the terms of the GNU Affero General Public License
9 * as published by the Free Software Foundation, either version 3 of 11 * as published by the Free Software Foundation, either version 3 of
10 * the License, or (at your option) any later version. 12 * the License, or (at your option) any later version.
22 #include "DatabaseBackendAdapterV3.h" 24 #include "DatabaseBackendAdapterV3.h"
23 25
24 #if defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) // Macro introduced in Orthanc 1.3.1 26 #if defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) // Macro introduced in Orthanc 1.3.1
25 # if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 9, 2) 27 # if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 9, 2)
26 28
29 #include "IndexConnectionsPool.h"
30
27 #include <Logging.h> 31 #include <Logging.h>
28 #include <MultiThreading/SharedMessageQueue.h>
29 #include <OrthancException.h> 32 #include <OrthancException.h>
30 33
31 #include <stdexcept> 34 #include <stdexcept>
32 #include <list> 35 #include <list>
33 #include <string> 36 #include <string>
79 target.push_back(*it); 82 target.push_back(*it);
80 } 83 }
81 } 84 }
82 85
83 86
84 class DatabaseBackendAdapterV3::Adapter : public boost::noncopyable
85 {
86 private:
87 class ManagerReference : public Orthanc::IDynamicObject
88 {
89 private:
90 DatabaseManager* manager_;
91
92 public:
93 ManagerReference(DatabaseManager& manager) :
94 manager_(&manager)
95 {
96 }
97
98 DatabaseManager& GetManager()
99 {
100 assert(manager_ != NULL);
101 return *manager_;
102 }
103 };
104
105 std::unique_ptr<IndexBackend> backend_;
106 OrthancPluginContext* context_;
107 boost::shared_mutex connectionsMutex_;
108 size_t countConnections_;
109 std::list<DatabaseManager*> connections_;
110 Orthanc::SharedMessageQueue availableConnections_;
111
112 public:
113 Adapter(IndexBackend* backend,
114 size_t countConnections) :
115 backend_(backend),
116 countConnections_(countConnections)
117 {
118 if (countConnections == 0)
119 {
120 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange,
121 "There must be a non-zero number of connections to the database");
122 }
123 else if (backend == NULL)
124 {
125 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
126 }
127 else
128 {
129 context_ = backend_->GetContext();
130 }
131 }
132
133 ~Adapter()
134 {
135 for (std::list<DatabaseManager*>::iterator
136 it = connections_.begin(); it != connections_.end(); ++it)
137 {
138 assert(*it != NULL);
139 delete *it;
140 }
141 }
142
143 OrthancPluginContext* GetContext() const
144 {
145 return context_;
146 }
147
148 void OpenConnections()
149 {
150 boost::unique_lock<boost::shared_mutex> lock(connectionsMutex_);
151
152 if (connections_.size() == 0)
153 {
154 assert(backend_.get() != NULL);
155
156 {
157 std::unique_ptr<DatabaseManager> manager(new DatabaseManager(backend_->CreateDatabaseFactory()));
158 manager->GetDatabase(); // Make sure to open the database connection
159
160 backend_->ConfigureDatabase(*manager);
161 connections_.push_back(manager.release());
162 }
163
164 for (size_t i = 1; i < countConnections_; i++)
165 {
166 connections_.push_back(new DatabaseManager(backend_->CreateDatabaseFactory()));
167 connections_.back()->GetDatabase(); // Make sure to open the database connection
168 }
169
170 for (std::list<DatabaseManager*>::iterator
171 it = connections_.begin(); it != connections_.end(); ++it)
172 {
173 assert(*it != NULL);
174 availableConnections_.Enqueue(new ManagerReference(**it));
175 }
176 }
177 else
178 {
179 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
180 }
181 }
182
183 void CloseConnections()
184 {
185 boost::unique_lock<boost::shared_mutex> lock(connectionsMutex_);
186
187 if (connections_.size() != countConnections_)
188 {
189 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
190 }
191 else if (availableConnections_.GetSize() != countConnections_)
192 {
193 throw Orthanc::OrthancException(Orthanc::ErrorCode_Database, "Some connections are still in use, bug in the Orthanc core");
194 }
195 else
196 {
197 for (std::list<DatabaseManager*>::iterator
198 it = connections_.begin(); it != connections_.end(); ++it)
199 {
200 assert(*it != NULL);
201 (*it)->Close();
202 }
203 }
204 }
205
206 class DatabaseAccessor : public boost::noncopyable
207 {
208 private:
209 boost::shared_lock<boost::shared_mutex> lock_;
210 Adapter& adapter_;
211 DatabaseManager* manager_;
212
213 public:
214 DatabaseAccessor(Adapter& adapter) :
215 lock_(adapter.connectionsMutex_),
216 adapter_(adapter),
217 manager_(NULL)
218 {
219 for (;;)
220 {
221 std::unique_ptr<Orthanc::IDynamicObject> manager(adapter.availableConnections_.Dequeue(100));
222 if (manager.get() != NULL)
223 {
224 manager_ = &dynamic_cast<ManagerReference&>(*manager).GetManager();
225 return;
226 }
227 }
228 }
229
230 ~DatabaseAccessor()
231 {
232 assert(manager_ != NULL);
233 adapter_.availableConnections_.Enqueue(new ManagerReference(*manager_));
234 }
235
236 IndexBackend& GetBackend() const
237 {
238 return *adapter_.backend_;
239 }
240
241 DatabaseManager& GetManager() const
242 {
243 assert(manager_ != NULL);
244 return *manager_;
245 }
246 };
247 };
248
249
250 class DatabaseBackendAdapterV3::Output : public IDatabaseBackendOutput 87 class DatabaseBackendAdapterV3::Output : public IDatabaseBackendOutput
251 { 88 {
252 private: 89 private:
253 struct Metadata 90 struct Metadata
254 { 91 {
801 638
802 639
803 class DatabaseBackendAdapterV3::Transaction : public boost::noncopyable 640 class DatabaseBackendAdapterV3::Transaction : public boost::noncopyable
804 { 641 {
805 private: 642 private:
806 Adapter& adapter_; 643 IndexConnectionsPool& pool_;
807 std::unique_ptr<Adapter::DatabaseAccessor> accessor_; 644 std::unique_ptr<IndexConnectionsPool::Accessor> accessor_;
808 std::unique_ptr<Output> output_; 645 std::unique_ptr<Output> output_;
809 646
810 public: 647 public:
811 Transaction(Adapter& adapter) : 648 Transaction(IndexConnectionsPool& pool) :
812 adapter_(adapter), 649 pool_(pool),
813 accessor_(new Adapter::DatabaseAccessor(adapter)), 650 accessor_(new IndexConnectionsPool::Accessor(pool)),
814 output_(new Output) 651 output_(new Output)
815 { 652 {
816 } 653 }
817 654
818 ~Transaction() 655 ~Transaction()
960 } 797 }
961 798
962 799
963 static OrthancPluginErrorCode Open(void* database) 800 static OrthancPluginErrorCode Open(void* database)
964 { 801 {
965 DatabaseBackendAdapterV3::Adapter* adapter = reinterpret_cast<DatabaseBackendAdapterV3::Adapter*>(database); 802 IndexConnectionsPool* pool = reinterpret_cast<IndexConnectionsPool*>(database);
966 803
967 try 804 try
968 { 805 {
969 adapter->OpenConnections(); 806 std::list<IdentifierTag> identifierTags;
970 return OrthancPluginErrorCode_Success; 807 pool->OpenConnections(false, identifierTags);
971 } 808 return OrthancPluginErrorCode_Success;
972 ORTHANC_PLUGINS_DATABASE_CATCH(adapter->GetContext()); 809 }
810 ORTHANC_PLUGINS_DATABASE_CATCH(pool->GetContext());
973 } 811 }
974 812
975 813
976 static OrthancPluginErrorCode Close(void* database) 814 static OrthancPluginErrorCode Close(void* database)
977 { 815 {
978 DatabaseBackendAdapterV3::Adapter* adapter = reinterpret_cast<DatabaseBackendAdapterV3::Adapter*>(database); 816 IndexConnectionsPool* pool = reinterpret_cast<IndexConnectionsPool*>(database);
979 817
980 try 818 try
981 { 819 {
982 adapter->CloseConnections(); 820 pool->CloseConnections();
983 return OrthancPluginErrorCode_Success; 821 return OrthancPluginErrorCode_Success;
984 } 822 }
985 ORTHANC_PLUGINS_DATABASE_CATCH(adapter->GetContext()); 823 ORTHANC_PLUGINS_DATABASE_CATCH(pool->GetContext());
986 } 824 }
987 825
988 826
989 static OrthancPluginErrorCode DestructDatabase(void* database) 827 static OrthancPluginErrorCode DestructDatabase(void* database)
990 { 828 {
991 DatabaseBackendAdapterV3::Adapter* adapter = reinterpret_cast<DatabaseBackendAdapterV3::Adapter*>(database); 829 IndexConnectionsPool* pool = reinterpret_cast<IndexConnectionsPool*>(database);
992 830
993 if (adapter == NULL) 831 if (pool == NULL)
994 { 832 {
995 return OrthancPluginErrorCode_InternalError; 833 return OrthancPluginErrorCode_InternalError;
996 } 834 }
997 else 835 else
998 { 836 {
1000 { 838 {
1001 isBackendInUse_ = false; 839 isBackendInUse_ = false;
1002 } 840 }
1003 else 841 else
1004 { 842 {
1005 OrthancPluginLogError(adapter->GetContext(), "More than one index backend was registered, internal error"); 843 OrthancPluginLogError(pool->GetContext(), "More than one index backend was registered, internal error");
1006 } 844 }
1007 845
1008 delete adapter; 846 delete pool;
1009 847
1010 return OrthancPluginErrorCode_Success; 848 return OrthancPluginErrorCode_Success;
1011 } 849 }
1012 } 850 }
1013 851
1014 852
1015 static OrthancPluginErrorCode GetDatabaseVersion(void* database, 853 static OrthancPluginErrorCode GetDatabaseVersion(void* database,
1016 uint32_t* version) 854 uint32_t* version)
1017 { 855 {
1018 DatabaseBackendAdapterV3::Adapter* adapter = reinterpret_cast<DatabaseBackendAdapterV3::Adapter*>(database); 856 IndexConnectionsPool* pool = reinterpret_cast<IndexConnectionsPool*>(database);
1019 857
1020 try 858 try
1021 { 859 {
1022 DatabaseBackendAdapterV3::Adapter::DatabaseAccessor accessor(*adapter); 860 IndexConnectionsPool::Accessor accessor(*pool);
1023 *version = accessor.GetBackend().GetDatabaseVersion(accessor.GetManager()); 861 *version = accessor.GetBackend().GetDatabaseVersion(accessor.GetManager());
1024 return OrthancPluginErrorCode_Success; 862 return OrthancPluginErrorCode_Success;
1025 } 863 }
1026 ORTHANC_PLUGINS_DATABASE_CATCH(adapter->GetContext()); 864 ORTHANC_PLUGINS_DATABASE_CATCH(pool->GetContext());
1027 } 865 }
1028 866
1029 867
1030 static OrthancPluginErrorCode UpgradeDatabase(void* database, 868 static OrthancPluginErrorCode UpgradeDatabase(void* database,
1031 OrthancPluginStorageArea* storageArea, 869 OrthancPluginStorageArea* storageArea,
1032 uint32_t targetVersion) 870 uint32_t targetVersion)
1033 { 871 {
1034 DatabaseBackendAdapterV3::Adapter* adapter = reinterpret_cast<DatabaseBackendAdapterV3::Adapter*>(database); 872 IndexConnectionsPool* pool = reinterpret_cast<IndexConnectionsPool*>(database);
1035 873
1036 try 874 try
1037 { 875 {
1038 DatabaseBackendAdapterV3::Adapter::DatabaseAccessor accessor(*adapter); 876 IndexConnectionsPool::Accessor accessor(*pool);
1039 accessor.GetBackend().UpgradeDatabase(accessor.GetManager(), targetVersion, storageArea); 877 accessor.GetBackend().UpgradeDatabase(accessor.GetManager(), targetVersion, storageArea);
1040 return OrthancPluginErrorCode_Success; 878 return OrthancPluginErrorCode_Success;
1041 } 879 }
1042 ORTHANC_PLUGINS_DATABASE_CATCH(adapter->GetContext()); 880 ORTHANC_PLUGINS_DATABASE_CATCH(pool->GetContext());
1043 } 881 }
1044 882
1045 883
1046 static OrthancPluginErrorCode HasRevisionsSupport(void* database, 884 static OrthancPluginErrorCode HasRevisionsSupport(void* database,
1047 uint8_t* target) 885 uint8_t* target)
1048 { 886 {
1049 DatabaseBackendAdapterV3::Adapter* adapter = reinterpret_cast<DatabaseBackendAdapterV3::Adapter*>(database); 887 IndexConnectionsPool* pool = reinterpret_cast<IndexConnectionsPool*>(database);
1050 888
1051 try 889 try
1052 { 890 {
1053 DatabaseBackendAdapterV3::Adapter::DatabaseAccessor accessor(*adapter); 891 IndexConnectionsPool::Accessor accessor(*pool);
1054 *target = (accessor.GetBackend().HasRevisionsSupport() ? 1 : 0); 892 *target = (accessor.GetBackend().HasRevisionsSupport() ? 1 : 0);
1055 return OrthancPluginErrorCode_Success; 893 return OrthancPluginErrorCode_Success;
1056 } 894 }
1057 ORTHANC_PLUGINS_DATABASE_CATCH(adapter->GetContext()); 895 ORTHANC_PLUGINS_DATABASE_CATCH(pool->GetContext());
1058 } 896 }
1059 897
1060 898
1061 static OrthancPluginErrorCode StartTransaction(void* database, 899 static OrthancPluginErrorCode StartTransaction(void* database,
1062 OrthancPluginDatabaseTransaction** target /* out */, 900 OrthancPluginDatabaseTransaction** target /* out */,
1063 OrthancPluginDatabaseTransactionType type) 901 OrthancPluginDatabaseTransactionType type)
1064 { 902 {
1065 DatabaseBackendAdapterV3::Adapter* adapter = reinterpret_cast<DatabaseBackendAdapterV3::Adapter*>(database); 903 IndexConnectionsPool* pool = reinterpret_cast<IndexConnectionsPool*>(database);
1066 904
1067 try 905 try
1068 { 906 {
1069 std::unique_ptr<DatabaseBackendAdapterV3::Transaction> transaction(new DatabaseBackendAdapterV3::Transaction(*adapter)); 907 std::unique_ptr<DatabaseBackendAdapterV3::Transaction> transaction(new DatabaseBackendAdapterV3::Transaction(*pool));
1070 908
1071 switch (type) 909 switch (type)
1072 { 910 {
1073 case OrthancPluginDatabaseTransactionType_ReadOnly: 911 case OrthancPluginDatabaseTransactionType_ReadOnly:
1074 transaction->GetManager().StartTransaction(TransactionType_ReadOnly); 912 transaction->GetManager().StartTransaction(TransactionType_ReadOnly);
1084 922
1085 *target = reinterpret_cast<OrthancPluginDatabaseTransaction*>(transaction.release()); 923 *target = reinterpret_cast<OrthancPluginDatabaseTransaction*>(transaction.release());
1086 924
1087 return OrthancPluginErrorCode_Success; 925 return OrthancPluginErrorCode_Success;
1088 } 926 }
1089 ORTHANC_PLUGINS_DATABASE_CATCH(adapter->GetContext()); 927 ORTHANC_PLUGINS_DATABASE_CATCH(pool->GetContext());
1090 } 928 }
1091 929
1092 930
1093 static OrthancPluginErrorCode DestructTransaction(OrthancPluginDatabaseTransaction* transaction) 931 static OrthancPluginErrorCode DestructTransaction(OrthancPluginDatabaseTransaction* transaction)
1094 { 932 {
1665 { 1503 {
1666 DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction); 1504 DatabaseBackendAdapterV3::Transaction* t = reinterpret_cast<DatabaseBackendAdapterV3::Transaction*>(transaction);
1667 1505
1668 try 1506 try
1669 { 1507 {
1670 OrthancPluginExportedResource exported; 1508 t->GetOutput().Clear();
1671 exported.seq = 0; 1509 t->GetBackend().LogExportedResource(t->GetManager(), resourceType, publicId, modality, date,
1672 exported.resourceType = resourceType; 1510 patientId, studyInstanceUid, seriesInstanceUid, sopInstanceUid);
1673 exported.publicId = publicId;
1674 exported.modality = modality;
1675 exported.date = date;
1676 exported.patientId = patientId;
1677 exported.studyInstanceUid = studyInstanceUid;
1678 exported.seriesInstanceUid = seriesInstanceUid;
1679 exported.sopInstanceUid = sopInstanceUid;
1680
1681 t->GetOutput().Clear();
1682 t->GetBackend().LogExportedResource(t->GetManager(), exported);
1683 return OrthancPluginErrorCode_Success; 1511 return OrthancPluginErrorCode_Success;
1684 } 1512 }
1685 ORTHANC_PLUGINS_DATABASE_CATCH(t->GetBackend().GetContext()); 1513 ORTHANC_PLUGINS_DATABASE_CATCH(t->GetBackend().GetContext());
1686 } 1514 }
1687 1515
1812 1640
1813 try 1641 try
1814 { 1642 {
1815 t->GetOutput().Clear(); 1643 t->GetOutput().Clear();
1816 1644
1817 std::vector<Orthanc::DatabaseConstraint> lookup; 1645 DatabaseConstraints lookup;
1818 lookup.reserve(constraintsCount);
1819 1646
1820 for (uint32_t i = 0; i < constraintsCount; i++) 1647 for (uint32_t i = 0; i < constraintsCount; i++)
1821 { 1648 {
1822 lookup.push_back(Orthanc::DatabaseConstraint(constraints[i])); 1649 lookup.AddConstraint(new DatabaseConstraint(constraints[i]));
1823 } 1650 }
1824 1651
1825 t->GetBackend().LookupResources(t->GetOutput(), t->GetManager(), lookup, queryLevel, limit, (requestSomeInstanceId != 0)); 1652 std::set<std::string> noLabel;
1653 t->GetBackend().LookupResources(t->GetOutput(), t->GetManager(), lookup, queryLevel, noLabel,
1654 LabelsConstraint_All, limit, (requestSomeInstanceId != 0));
1826 return OrthancPluginErrorCode_Success; 1655 return OrthancPluginErrorCode_Success;
1827 } 1656 }
1828 ORTHANC_PLUGINS_DATABASE_CATCH(t->GetBackend().GetContext()); 1657 ORTHANC_PLUGINS_DATABASE_CATCH(t->GetBackend().GetContext());
1829 } 1658 }
1830 1659
1987 1816
1988 void DatabaseBackendAdapterV3::Register(IndexBackend* backend, 1817 void DatabaseBackendAdapterV3::Register(IndexBackend* backend,
1989 size_t countConnections, 1818 size_t countConnections,
1990 unsigned int maxDatabaseRetries) 1819 unsigned int maxDatabaseRetries)
1991 { 1820 {
1821 std::unique_ptr<IndexBackend> protection(backend);
1822
1992 if (isBackendInUse_) 1823 if (isBackendInUse_)
1993 { 1824 {
1994 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); 1825 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
1995 } 1826 }
1996 1827
2070 params.setGlobalProperty = SetGlobalProperty; 1901 params.setGlobalProperty = SetGlobalProperty;
2071 params.setMetadata = SetMetadata; 1902 params.setMetadata = SetMetadata;
2072 params.setProtectedPatient = SetProtectedPatient; 1903 params.setProtectedPatient = SetProtectedPatient;
2073 params.setResourcesContent = SetResourcesContent; 1904 params.setResourcesContent = SetResourcesContent;
2074 1905
2075 OrthancPluginContext* context = backend->GetContext(); 1906 OrthancPluginContext* context = protection->GetContext();
2076 1907
2077 if (OrthancPluginRegisterDatabaseBackendV3( 1908 if (OrthancPluginRegisterDatabaseBackendV3(
2078 context, &params, sizeof(params), maxDatabaseRetries, 1909 context, &params, sizeof(params), maxDatabaseRetries,
2079 new Adapter(backend, countConnections)) != OrthancPluginErrorCode_Success) 1910 new IndexConnectionsPool(protection.release(), countConnections)) != OrthancPluginErrorCode_Success)
2080 { 1911 {
1912 delete backend;
2081 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Unable to register the database backend"); 1913 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Unable to register the database backend");
2082 } 1914 }
2083 1915
2084 backend->SetOutputFactory(new Factory); 1916 backend->SetOutputFactory(new Factory);
2085 1917