1 /** 2 * Orthanc - A Lightweight, RESTful DICOM Store 3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics 4 * Department, University Hospital of Liege, Belgium 5 * Copyright (C) 2017-2021 Osimis S.A., Belgium 6 * 7 * This program is free software: you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License as 9 * published by the Free Software Foundation, either version 3 of the 10 * License, or (at your option) any later version. 11 * 12 * In addition, as a special exception, the copyright holders of this 13 * program give permission to link the code of its release with the 14 * OpenSSL project's "OpenSSL" library (or with modified versions of it 15 * that use the same license as the "OpenSSL" library), and distribute 16 * the linked executables. You must obey the GNU General Public License 17 * in all respects for all of the code used other than "OpenSSL". If you 18 * modify file(s) with this exception, you may extend this exception to 19 * your version of the file(s), but you are not obligated to do so. If 20 * you do not wish to do so, delete this exception statement from your 21 * version. If you delete this exception statement from all source files 22 * in the program, then also delete it here. 23 * 24 * This program is distributed in the hope that it will be useful, but 25 * WITHOUT ANY WARRANTY; without even the implied warranty of 26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 27 * General Public License for more details. 28 * 29 * You should have received a copy of the GNU General Public License 30 * along with this program. If not, see <http://www.gnu.org/licenses/>. 31 **/ 32 33 34 #pragma once 35 36 #include "../../../OrthancFramework/Sources/DicomFormat/DicomMap.h" 37 38 #include "IDatabaseWrapper.h" 39 #include "../DicomInstanceOrigin.h" 40 41 #include <boost/shared_ptr.hpp> 42 #include <boost/thread/shared_mutex.hpp> 43 44 45 namespace Orthanc 46 { 47 class DatabaseLookup; 48 class ParsedDicomFile; 49 struct ServerIndexChange; 50 51 class StatelessDatabaseOperations : public boost::noncopyable 52 { 53 public: 54 typedef std::list<FileInfo> Attachments; 55 typedef std::map<std::pair<ResourceType, MetadataType>, std::string> MetadataMap; 56 57 class ITransactionContext : public IDatabaseListener 58 { 59 public: ~ITransactionContext()60 virtual ~ITransactionContext() 61 { 62 } 63 64 virtual void Commit() = 0; 65 66 virtual int64_t GetCompressedSizeDelta() = 0; 67 68 virtual bool IsUnstableResource(int64_t id) = 0; 69 70 virtual bool LookupRemainingLevel(std::string& remainingPublicId /* out */, 71 ResourceType& remainingLevel /* out */) = 0; 72 73 virtual void MarkAsUnstable(int64_t id, 74 Orthanc::ResourceType type, 75 const std::string& publicId) = 0; 76 77 virtual void SignalAttachmentsAdded(uint64_t compressedSize) = 0; 78 79 virtual void SignalChange(const ServerIndexChange& change) = 0; 80 }; 81 82 83 class ITransactionContextFactory : public boost::noncopyable 84 { 85 public: ~ITransactionContextFactory()86 virtual ~ITransactionContextFactory() 87 { 88 } 89 90 // WARNING: This method can be invoked from several threads concurrently 91 virtual ITransactionContext* Create() = 0; 92 }; 93 94 95 class ReadOnlyTransaction : public boost::noncopyable 96 { 97 private: 98 ITransactionContext& context_; 99 100 protected: 101 IDatabaseWrapper::ITransaction& transaction_; 102 103 public: ReadOnlyTransaction(IDatabaseWrapper::ITransaction & transaction,ITransactionContext & context)104 explicit ReadOnlyTransaction(IDatabaseWrapper::ITransaction& transaction, 105 ITransactionContext& context) : 106 context_(context), 107 transaction_(transaction) 108 { 109 } 110 GetTransactionContext()111 ITransactionContext& GetTransactionContext() 112 { 113 return context_; 114 } 115 116 /** 117 * Higher-level constructions 118 **/ 119 120 SeriesStatus GetSeriesStatus(int64_t id, 121 int64_t expectedNumberOfInstances); 122 123 124 /** 125 * Read-only methods from "IDatabaseWrapper" 126 **/ 127 ApplyLookupResources(std::list<std::string> & resourcesId,std::list<std::string> * instancesId,const std::vector<DatabaseConstraint> & lookup,ResourceType queryLevel,size_t limit)128 void ApplyLookupResources(std::list<std::string>& resourcesId, 129 std::list<std::string>* instancesId, // Can be NULL if not needed 130 const std::vector<DatabaseConstraint>& lookup, 131 ResourceType queryLevel, 132 size_t limit) 133 { 134 return transaction_.ApplyLookupResources(resourcesId, instancesId, lookup, queryLevel, limit); 135 } 136 GetAllMetadata(std::map<MetadataType,std::string> & target,int64_t id)137 void GetAllMetadata(std::map<MetadataType, std::string>& target, 138 int64_t id) 139 { 140 transaction_.GetAllMetadata(target, id); 141 } 142 GetAllPublicIds(std::list<std::string> & target,ResourceType resourceType)143 void GetAllPublicIds(std::list<std::string>& target, 144 ResourceType resourceType) 145 { 146 return transaction_.GetAllPublicIds(target, resourceType); 147 } 148 GetAllPublicIds(std::list<std::string> & target,ResourceType resourceType,size_t since,size_t limit)149 void GetAllPublicIds(std::list<std::string>& target, 150 ResourceType resourceType, 151 size_t since, 152 size_t limit) 153 { 154 return transaction_.GetAllPublicIds(target, resourceType, since, limit); 155 } 156 GetChanges(std::list<ServerIndexChange> & target,bool & done,int64_t since,uint32_t maxResults)157 void GetChanges(std::list<ServerIndexChange>& target /*out*/, 158 bool& done /*out*/, 159 int64_t since, 160 uint32_t maxResults) 161 { 162 transaction_.GetChanges(target, done, since, maxResults); 163 } 164 GetChildrenInternalId(std::list<int64_t> & target,int64_t id)165 void GetChildrenInternalId(std::list<int64_t>& target, 166 int64_t id) 167 { 168 transaction_.GetChildrenInternalId(target, id); 169 } 170 GetChildrenPublicId(std::list<std::string> & target,int64_t id)171 void GetChildrenPublicId(std::list<std::string>& target, 172 int64_t id) 173 { 174 transaction_.GetChildrenPublicId(target, id); 175 } 176 GetExportedResources(std::list<ExportedResource> & target,bool & done,int64_t since,uint32_t maxResults)177 void GetExportedResources(std::list<ExportedResource>& target /*out*/, 178 bool& done /*out*/, 179 int64_t since, 180 uint32_t maxResults) 181 { 182 return transaction_.GetExportedResources(target, done, since, maxResults); 183 } 184 GetLastChange(std::list<ServerIndexChange> & target)185 void GetLastChange(std::list<ServerIndexChange>& target /*out*/) 186 { 187 transaction_.GetLastChange(target); 188 } 189 GetLastExportedResource(std::list<ExportedResource> & target)190 void GetLastExportedResource(std::list<ExportedResource>& target /*out*/) 191 { 192 return transaction_.GetLastExportedResource(target); 193 } 194 GetLastChangeIndex()195 int64_t GetLastChangeIndex() 196 { 197 return transaction_.GetLastChangeIndex(); 198 } 199 GetMainDicomTags(DicomMap & map,int64_t id)200 void GetMainDicomTags(DicomMap& map, 201 int64_t id) 202 { 203 transaction_.GetMainDicomTags(map, id); 204 } 205 GetPublicId(int64_t resourceId)206 std::string GetPublicId(int64_t resourceId) 207 { 208 return transaction_.GetPublicId(resourceId); 209 } 210 GetResourcesCount(ResourceType resourceType)211 uint64_t GetResourcesCount(ResourceType resourceType) 212 { 213 return transaction_.GetResourcesCount(resourceType); 214 } 215 GetResourceType(int64_t resourceId)216 ResourceType GetResourceType(int64_t resourceId) 217 { 218 return transaction_.GetResourceType(resourceId); 219 } 220 GetTotalCompressedSize()221 uint64_t GetTotalCompressedSize() 222 { 223 return transaction_.GetTotalCompressedSize(); 224 } 225 GetTotalUncompressedSize()226 uint64_t GetTotalUncompressedSize() 227 { 228 return transaction_.GetTotalUncompressedSize(); 229 } 230 IsProtectedPatient(int64_t internalId)231 bool IsProtectedPatient(int64_t internalId) 232 { 233 return transaction_.IsProtectedPatient(internalId); 234 } 235 ListAvailableAttachments(std::set<FileContentType> & target,int64_t id)236 void ListAvailableAttachments(std::set<FileContentType>& target, 237 int64_t id) 238 { 239 transaction_.ListAvailableAttachments(target, id); 240 } 241 LookupAttachment(FileInfo & attachment,int64_t & revision,int64_t id,FileContentType contentType)242 bool LookupAttachment(FileInfo& attachment, 243 int64_t& revision, 244 int64_t id, 245 FileContentType contentType) 246 { 247 return transaction_.LookupAttachment(attachment, revision, id, contentType); 248 } 249 LookupGlobalProperty(std::string & target,GlobalProperty property,bool shared)250 bool LookupGlobalProperty(std::string& target, 251 GlobalProperty property, 252 bool shared) 253 { 254 return transaction_.LookupGlobalProperty(target, property, shared); 255 } 256 LookupMetadata(std::string & target,int64_t & revision,int64_t id,MetadataType type)257 bool LookupMetadata(std::string& target, 258 int64_t& revision, 259 int64_t id, 260 MetadataType type) 261 { 262 return transaction_.LookupMetadata(target, revision, id, type); 263 } 264 LookupParent(int64_t & parentId,int64_t resourceId)265 bool LookupParent(int64_t& parentId, 266 int64_t resourceId) 267 { 268 return transaction_.LookupParent(parentId, resourceId); 269 } 270 LookupResource(int64_t & id,ResourceType & type,const std::string & publicId)271 bool LookupResource(int64_t& id, 272 ResourceType& type, 273 const std::string& publicId) 274 { 275 return transaction_.LookupResource(id, type, publicId); 276 } 277 LookupResourceAndParent(int64_t & id,ResourceType & type,std::string & parentPublicId,const std::string & publicId)278 bool LookupResourceAndParent(int64_t& id, 279 ResourceType& type, 280 std::string& parentPublicId, 281 const std::string& publicId) 282 { 283 return transaction_.LookupResourceAndParent(id, type, parentPublicId, publicId); 284 } 285 }; 286 287 288 class ReadWriteTransaction : public ReadOnlyTransaction 289 { 290 public: ReadWriteTransaction(IDatabaseWrapper::ITransaction & transaction,ITransactionContext & context)291 ReadWriteTransaction(IDatabaseWrapper::ITransaction& transaction, 292 ITransactionContext& context) : 293 ReadOnlyTransaction(transaction, context) 294 { 295 } 296 AddAttachment(int64_t id,const FileInfo & attachment,int64_t revision)297 void AddAttachment(int64_t id, 298 const FileInfo& attachment, 299 int64_t revision) 300 { 301 transaction_.AddAttachment(id, attachment, revision); 302 } 303 ClearChanges()304 void ClearChanges() 305 { 306 transaction_.ClearChanges(); 307 } 308 ClearExportedResources()309 void ClearExportedResources() 310 { 311 transaction_.ClearExportedResources(); 312 } 313 ClearMainDicomTags(int64_t id)314 void ClearMainDicomTags(int64_t id) 315 { 316 return transaction_.ClearMainDicomTags(id); 317 } 318 CreateInstance(IDatabaseWrapper::CreateInstanceResult & result,int64_t & instanceId,const std::string & patient,const std::string & study,const std::string & series,const std::string & instance)319 bool CreateInstance(IDatabaseWrapper::CreateInstanceResult& result, /* out */ 320 int64_t& instanceId, /* out */ 321 const std::string& patient, 322 const std::string& study, 323 const std::string& series, 324 const std::string& instance) 325 { 326 return transaction_.CreateInstance(result, instanceId, patient, study, series, instance); 327 } 328 DeleteAttachment(int64_t id,FileContentType attachment)329 void DeleteAttachment(int64_t id, 330 FileContentType attachment) 331 { 332 return transaction_.DeleteAttachment(id, attachment); 333 } 334 DeleteMetadata(int64_t id,MetadataType type)335 void DeleteMetadata(int64_t id, 336 MetadataType type) 337 { 338 transaction_.DeleteMetadata(id, type); 339 } 340 DeleteResource(int64_t id)341 void DeleteResource(int64_t id) 342 { 343 transaction_.DeleteResource(id); 344 } 345 346 void LogChange(int64_t internalId, 347 ChangeType changeType, 348 ResourceType resourceType, 349 const std::string& publicId); 350 LogExportedResource(const ExportedResource & resource)351 void LogExportedResource(const ExportedResource& resource) 352 { 353 transaction_.LogExportedResource(resource); 354 } 355 SetGlobalProperty(GlobalProperty property,bool shared,const std::string & value)356 void SetGlobalProperty(GlobalProperty property, 357 bool shared, 358 const std::string& value) 359 { 360 transaction_.SetGlobalProperty(property, shared, value); 361 } 362 SetMetadata(int64_t id,MetadataType type,const std::string & value,int64_t revision)363 void SetMetadata(int64_t id, 364 MetadataType type, 365 const std::string& value, 366 int64_t revision) 367 { 368 return transaction_.SetMetadata(id, type, value, revision); 369 } 370 SetProtectedPatient(int64_t internalId,bool isProtected)371 void SetProtectedPatient(int64_t internalId, 372 bool isProtected) 373 { 374 transaction_.SetProtectedPatient(internalId, isProtected); 375 } 376 SetResourcesContent(const ResourcesContent & content)377 void SetResourcesContent(const ResourcesContent& content) 378 { 379 transaction_.SetResourcesContent(content); 380 } 381 382 void Recycle(uint64_t maximumStorageSize, 383 unsigned int maximumPatients, 384 uint64_t addedInstanceSize, 385 const std::string& newPatientId); 386 }; 387 388 389 class IReadOnlyOperations : public boost::noncopyable 390 { 391 public: ~IReadOnlyOperations()392 virtual ~IReadOnlyOperations() 393 { 394 } 395 396 virtual void Apply(ReadOnlyTransaction& transaction) = 0; 397 }; 398 399 400 class IReadWriteOperations : public boost::noncopyable 401 { 402 public: ~IReadWriteOperations()403 virtual ~IReadWriteOperations() 404 { 405 } 406 407 virtual void Apply(ReadWriteTransaction& transaction) = 0; 408 }; 409 410 411 private: 412 class MainDicomTagsRegistry; 413 class Transaction; 414 415 IDatabaseWrapper& db_; 416 boost::shared_ptr<MainDicomTagsRegistry> mainDicomTagsRegistry_; // "shared_ptr" because of PImpl 417 bool hasFlushToDisk_; 418 419 // Mutex to protect the configuration options 420 boost::shared_mutex mutex_; 421 std::unique_ptr<ITransactionContextFactory> factory_; 422 unsigned int maxRetries_; 423 424 void NormalizeLookup(std::vector<DatabaseConstraint>& target, 425 const DatabaseLookup& source, 426 ResourceType level) const; 427 428 void ApplyInternal(IReadOnlyOperations* readOperations, 429 IReadWriteOperations* writeOperations); 430 431 protected: 432 void StandaloneRecycling(uint64_t maximumStorageSize, 433 unsigned int maximumPatientCount); 434 435 public: 436 explicit StatelessDatabaseOperations(IDatabaseWrapper& database); 437 438 void SetTransactionContextFactory(ITransactionContextFactory* factory /* takes ownership */); 439 440 // Only used to handle "ErrorCode_DatabaseCannotSerialize" in the 441 // case of collision between multiple writers 442 void SetMaxDatabaseRetries(unsigned int maxRetries); 443 444 // It is assumed that "GetDatabaseVersion()" can run out of a 445 // database transaction GetDatabaseVersion()446 unsigned int GetDatabaseVersion() 447 { 448 return db_.GetDatabaseVersion(); 449 } 450 451 void FlushToDisk(); 452 HasFlushToDisk()453 bool HasFlushToDisk() const 454 { 455 return hasFlushToDisk_; 456 } 457 458 void Apply(IReadOnlyOperations& operations); 459 460 void Apply(IReadWriteOperations& operations); 461 462 bool ExpandResource(Json::Value& target, 463 const std::string& publicId, 464 ResourceType level, 465 DicomToJsonFormat format); 466 467 void GetAllMetadata(std::map<MetadataType, std::string>& target, 468 const std::string& publicId, 469 ResourceType level); 470 471 void GetAllUuids(std::list<std::string>& target, 472 ResourceType resourceType); 473 474 void GetAllUuids(std::list<std::string>& target, 475 ResourceType resourceType, 476 size_t since, 477 size_t limit); 478 479 void GetGlobalStatistics(/* out */ uint64_t& diskSize, 480 /* out */ uint64_t& uncompressedSize, 481 /* out */ uint64_t& countPatients, 482 /* out */ uint64_t& countStudies, 483 /* out */ uint64_t& countSeries, 484 /* out */ uint64_t& countInstances); 485 486 bool LookupAttachment(FileInfo& attachment, 487 int64_t& revision, 488 const std::string& instancePublicId, 489 FileContentType contentType); 490 491 void GetChanges(Json::Value& target, 492 int64_t since, 493 unsigned int maxResults); 494 495 void GetLastChange(Json::Value& target); 496 497 void GetExportedResources(Json::Value& target, 498 int64_t since, 499 unsigned int maxResults); 500 501 void GetLastExportedResource(Json::Value& target); 502 503 bool IsProtectedPatient(const std::string& publicId); 504 505 void GetChildren(std::list<std::string>& result, 506 const std::string& publicId); 507 508 void GetChildInstances(std::list<std::string>& result, 509 const std::string& publicId); 510 511 bool LookupMetadata(std::string& target, 512 int64_t& revision, 513 const std::string& publicId, 514 ResourceType expectedType, 515 MetadataType type); 516 517 void ListAvailableAttachments(std::set<FileContentType>& target, 518 const std::string& publicId, 519 ResourceType expectedType); 520 521 bool LookupParent(std::string& target, 522 const std::string& publicId); 523 524 void GetResourceStatistics(/* out */ ResourceType& type, 525 /* out */ uint64_t& diskSize, 526 /* out */ uint64_t& uncompressedSize, 527 /* out */ unsigned int& countStudies, 528 /* out */ unsigned int& countSeries, 529 /* out */ unsigned int& countInstances, 530 /* out */ uint64_t& dicomDiskSize, 531 /* out */ uint64_t& dicomUncompressedSize, 532 const std::string& publicId); 533 534 void LookupIdentifierExact(std::vector<std::string>& result, 535 ResourceType level, 536 const DicomTag& tag, 537 const std::string& value); 538 539 bool LookupGlobalProperty(std::string& value, 540 GlobalProperty property, 541 bool shared); 542 543 std::string GetGlobalProperty(GlobalProperty property, 544 bool shared, 545 const std::string& defaultValue); 546 547 bool GetMainDicomTags(DicomMap& result, 548 const std::string& publicId, 549 ResourceType expectedType, 550 ResourceType levelOfInterest); 551 552 // Only applicable at the instance level 553 bool GetAllMainDicomTags(DicomMap& result, 554 const std::string& instancePublicId); 555 556 bool LookupResourceType(ResourceType& type, 557 const std::string& publicId); 558 559 bool LookupParent(std::string& target, 560 const std::string& publicId, 561 ResourceType parentType); 562 563 void ApplyLookupResources(std::vector<std::string>& resourcesId, 564 std::vector<std::string>* instancesId, // Can be NULL if not needed 565 const DatabaseLookup& lookup, 566 ResourceType queryLevel, 567 size_t limit); 568 569 bool DeleteResource(Json::Value& remainingAncestor /* out */, 570 const std::string& uuid, 571 ResourceType expectedType); 572 573 void LogExportedResource(const std::string& publicId, 574 const std::string& remoteModality); 575 576 void SetProtectedPatient(const std::string& publicId, 577 bool isProtected); 578 579 void SetMetadata(int64_t& newRevision /*out*/, 580 const std::string& publicId, 581 MetadataType type, 582 const std::string& value, 583 bool hasOldRevision, 584 int64_t oldRevision, 585 const std::string& oldMD5); 586 587 // Same as "SetMetadata()", but doesn't care about revisions 588 void OverwriteMetadata(const std::string& publicId, 589 MetadataType type, 590 const std::string& value); 591 592 bool DeleteMetadata(const std::string& publicId, 593 MetadataType type, 594 bool hasRevision, 595 int64_t revision, 596 const std::string& md5); 597 598 uint64_t IncrementGlobalSequence(GlobalProperty sequence, 599 bool shared); 600 601 void DeleteChanges(); 602 603 void DeleteExportedResources(); 604 605 void SetGlobalProperty(GlobalProperty property, 606 bool shared, 607 const std::string& value); 608 609 bool DeleteAttachment(const std::string& publicId, 610 FileContentType type, 611 bool hasRevision, 612 int64_t revision, 613 const std::string& md5); 614 615 void LogChange(int64_t internalId, 616 ChangeType changeType, 617 const std::string& publicId, 618 ResourceType level); 619 620 void ReconstructInstance(const ParsedDicomFile& dicom); 621 622 StoreStatus Store(std::map<MetadataType, std::string>& instanceMetadata, 623 const DicomMap& dicomSummary, 624 const Attachments& attachments, 625 const MetadataMap& metadata, 626 const DicomInstanceOrigin& origin, 627 bool overwrite, 628 bool hasTransferSyntax, 629 DicomTransferSyntax transferSyntax, 630 bool hasPixelDataOffset, 631 uint64_t pixelDataOffset, 632 uint64_t maximumStorageSize, 633 unsigned int maximumPatients); 634 635 StoreStatus AddAttachment(int64_t& newRevision /*out*/, 636 const FileInfo& attachment, 637 const std::string& publicId, 638 uint64_t maximumStorageSize, 639 unsigned int maximumPatients, 640 bool hasOldRevision, 641 int64_t oldRevision, 642 const std::string& oldMd5); 643 }; 644 } 645