# HG changeset patch # User Sebastien Jodogne # Date 1354281747 -3600 # Node ID 5368bbe813cf4546fae44c5b3ac4640240b8be10 # Parent 8098448bd8273b769c2faf48afca49ddf73cccdc refactoring of attachments diff -r 8098448bd827 -r 5368bbe813cf CMakeLists.txt --- a/CMakeLists.txt Fri Nov 30 12:18:44 2012 +0100 +++ b/CMakeLists.txt Fri Nov 30 14:22:27 2012 +0100 @@ -106,6 +106,7 @@ Core/FileStorage.cpp Core/FileStorage/StorageAccessor.cpp Core/FileStorage/CompressedFileStorageAccessor.cpp + Core/FileStorage/FileStorageAccessor.cpp Core/HttpServer/EmbeddedResourceHttpHandler.cpp Core/HttpServer/FilesystemHttpHandler.cpp Core/HttpServer/HttpHandler.cpp diff -r 8098448bd827 -r 5368bbe813cf Core/Enumerations.h --- a/Core/Enumerations.h Fri Nov 30 12:18:44 2012 +0100 +++ b/Core/Enumerations.h Fri Nov 30 14:22:27 2012 +0100 @@ -64,9 +64,22 @@ PixelFormat_Grayscale16 }; + + /** + * WARNING: Do not change the explicit values in the enumerations + * below this point. This would result in incompatible databases + * between versions of Orthanc! + **/ + enum CompressionType { CompressionType_None = 1, CompressionType_Zlib = 2 }; + + enum FileType + { + FileType_Dicom = 1, + FileType_Json = 2 + }; } diff -r 8098448bd827 -r 5368bbe813cf Core/FileStorage/CompressedFileStorageAccessor.cpp --- a/Core/FileStorage/CompressedFileStorageAccessor.cpp Fri Nov 30 12:18:44 2012 +0100 +++ b/Core/FileStorage/CompressedFileStorageAccessor.cpp Fri Nov 30 14:22:27 2012 +0100 @@ -38,19 +38,25 @@ namespace Orthanc { - std::string CompressedFileStorageAccessor::WriteInternal(const void* data, - size_t size) + FileInfo CompressedFileStorageAccessor::WriteInternal(const void* data, + size_t size, + FileType type) { switch (compressionType_) { case CompressionType_None: - return storage_.Create(data, size); + { + std::string uuid = storage_.Create(data, size); + return FileInfo(uuid, type, size); + } case CompressionType_Zlib: { std::string compressed; zlib_.Compress(compressed, data, size); - return storage_.Create(compressed); + std::string uuid = storage_.Create(compressed); + return FileInfo(uuid, type, size, + CompressionType_Zlib, compressed.size()); } default: @@ -61,7 +67,7 @@ CompressedFileStorageAccessor::CompressedFileStorageAccessor(FileStorage& storage) : storage_(storage) { - compressionType_ = CompressionType_Zlib; + compressionType_ = CompressionType_None; } void CompressedFileStorageAccessor::Read(std::string& content, diff -r 8098448bd827 -r 5368bbe813cf Core/FileStorage/CompressedFileStorageAccessor.h --- a/Core/FileStorage/CompressedFileStorageAccessor.h Fri Nov 30 12:18:44 2012 +0100 +++ b/Core/FileStorage/CompressedFileStorageAccessor.h Fri Nov 30 14:22:27 2012 +0100 @@ -46,8 +46,9 @@ CompressionType compressionType_; protected: - virtual std::string WriteInternal(const void* data, - size_t size); + virtual FileInfo WriteInternal(const void* data, + size_t size, + FileType type); public: CompressedFileStorageAccessor(FileStorage& storage); diff -r 8098448bd827 -r 5368bbe813cf Core/FileStorage/FileInfo.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/FileStorage/FileInfo.h Fri Nov 30 14:22:27 2012 +0100 @@ -0,0 +1,110 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * 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. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * 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 + +#include +#include +#include "../Enumerations.h" + +namespace Orthanc +{ + struct FileInfo + { + private: + std::string uuid_; + FileType type_; + uint64_t uncompressedSize_; + CompressionType compression_; + uint64_t compressedSize_; + + public: + FileInfo() + { + } + + /** + * Constructor for an uncompressed attachment. + **/ + FileInfo(const std::string& uuid, + FileType type, + uint64_t size) : + uuid_(uuid), + type_(type), + uncompressedSize_(size), + compression_(CompressionType_None), + compressedSize_(size) + { + } + + /** + * Constructor for a compressed attachment. + **/ + FileInfo(const std::string& uuid, + FileType type, + uint64_t uncompressedSize, + CompressionType compression, + uint64_t compressedSize) : + uuid_(uuid), + type_(type), + uncompressedSize_(uncompressedSize), + compression_(compression), + compressedSize_(compressedSize) + { + } + + const std::string& GetUuid() const + { + return uuid_; + } + + FileType GetFileType() const + { + return type_; + } + + uint64_t GetUncompressedSize() const + { + return uncompressedSize_; + } + + CompressionType GetCompressionType() const + { + return compression_; + } + + uint64_t GetCompressedSize() const + { + return compressedSize_; + } + }; +} diff -r 8098448bd827 -r 5368bbe813cf Core/FileStorage/FileStorageAccessor.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/FileStorage/FileStorageAccessor.cpp Fri Nov 30 14:22:27 2012 +0100 @@ -0,0 +1,43 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012 Medical Physics Department, CHU of Liege, + * 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. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * 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 "FileStorageAccessor.h" + +namespace Orthanc +{ + FileInfo FileStorageAccessor::WriteInternal(const void* data, + size_t size, + FileType type) + { + return FileInfo(storage_.Create(data, size), type, size); + } +} diff -r 8098448bd827 -r 5368bbe813cf Core/FileStorage/FileStorageAccessor.h --- a/Core/FileStorage/FileStorageAccessor.h Fri Nov 30 12:18:44 2012 +0100 +++ b/Core/FileStorage/FileStorageAccessor.h Fri Nov 30 14:22:27 2012 +0100 @@ -44,11 +44,9 @@ FileStorage& storage_; protected: - virtual std::string WriteInternal(const void* data, - size_t size) - { - return storage_.Create(data, size); - } + virtual FileInfo WriteInternal(const void* data, + size_t size, + FileType type); public: FileStorageAccessor(FileStorage& storage) : storage_(storage) diff -r 8098448bd827 -r 5368bbe813cf Core/FileStorage/StorageAccessor.cpp --- a/Core/FileStorage/StorageAccessor.cpp Fri Nov 30 12:18:44 2012 +0100 +++ b/Core/FileStorage/StorageAccessor.cpp Fri Nov 30 14:22:27 2012 +0100 @@ -34,27 +34,29 @@ namespace Orthanc { - std::string StorageAccessor::Write(const std::vector& content) + FileInfo StorageAccessor::Write(const std::vector& content, + FileType type) { if (content.size() == 0) { - return WriteInternal(NULL, 0); + return WriteInternal(NULL, 0, type); } else { - return WriteInternal(&content[0], content.size()); + return WriteInternal(&content[0], content.size(), type); } } - std::string StorageAccessor::Write(const std::string& content) + FileInfo StorageAccessor::Write(const std::string& content, + FileType type) { if (content.size() == 0) { - return WriteInternal(NULL, 0); + return WriteInternal(NULL, 0, type); } else { - return WriteInternal(&content[0], content.size()); + return WriteInternal(&content[0], content.size(), type); } } } diff -r 8098448bd827 -r 5368bbe813cf Core/FileStorage/StorageAccessor.h --- a/Core/FileStorage/StorageAccessor.h Fri Nov 30 12:18:44 2012 +0100 +++ b/Core/FileStorage/StorageAccessor.h Fri Nov 30 14:22:27 2012 +0100 @@ -32,6 +32,7 @@ #pragma once +#include "FileInfo.h" #include "../HttpServer/HttpFileSender.h" #include @@ -44,23 +45,27 @@ class StorageAccessor : boost::noncopyable { protected: - virtual std::string WriteInternal(const void* data, - size_t size) = 0; + virtual FileInfo WriteInternal(const void* data, + size_t size, + FileType type) = 0; public: virtual ~StorageAccessor() { } - std::string Write(const void* data, - size_t size) + FileInfo Write(const void* data, + size_t size, + FileType type) { - return WriteInternal(data, size); + return WriteInternal(data, size, type); } - std::string Write(const std::vector& content); + FileInfo Write(const std::vector& content, + FileType type); - std::string Write(const std::string& content); + FileInfo Write(const std::string& content, + FileType type); virtual void Read(std::string& content, const std::string& uuid) = 0; diff -r 8098448bd827 -r 5368bbe813cf NEWS --- a/NEWS Fri Nov 30 12:18:44 2012 +0100 +++ b/NEWS Fri Nov 30 14:22:27 2012 +0100 @@ -17,7 +17,7 @@ * Generate a sample configuration file from command line * "CompletedSeries" event in the changes API * Thread to continuously flush DB to disk (SQLite checkpoints for - robustness against reboots) + improved robustness) Version 0.2.3 (2012/10/26) diff -r 8098448bd827 -r 5368bbe813cf OrthancServer/DatabaseWrapper.cpp --- a/OrthancServer/DatabaseWrapper.cpp Fri Nov 30 12:18:44 2012 +0100 +++ b/OrthancServer/DatabaseWrapper.cpp Fri Nov 30 14:22:27 2012 +0100 @@ -376,32 +376,25 @@ - void DatabaseWrapper::AttachFile(int64_t id, - AttachedFileType contentType, - const std::string& fileUuid, - uint64_t compressedSize, - uint64_t uncompressedSize, - CompressionType compressionType) + void DatabaseWrapper::AddAttachment(int64_t id, + const FileInfo& attachment) { SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO AttachedFiles VALUES(?, ?, ?, ?, ?, ?)"); s.BindInt(0, id); - s.BindInt(1, contentType); - s.BindString(2, fileUuid); - s.BindInt(3, compressedSize); - s.BindInt(4, uncompressedSize); - s.BindInt(5, compressionType); + s.BindInt(1, attachment.GetFileType()); + s.BindString(2, attachment.GetUuid()); + s.BindInt(3, attachment.GetCompressedSize()); + s.BindInt(4, attachment.GetUncompressedSize()); + s.BindInt(5, attachment.GetCompressionType()); s.Run(); } - bool DatabaseWrapper::LookupFile(int64_t id, - AttachedFileType contentType, - std::string& fileUuid, - uint64_t& compressedSize, - uint64_t& uncompressedSize, - CompressionType& compressionType) + bool DatabaseWrapper::LookupAttachment(FileInfo& attachment, + int64_t id, + FileType contentType) { SQLite::Statement s(db_, SQLITE_FROM_HERE, - "SELECT uuid, compressedSize, uncompressedSize, compressionType FROM AttachedFiles WHERE id=? AND fileType=?"); + "SELECT uuid, uncompressedSize, compressionType, compressedSize FROM AttachedFiles WHERE id=? AND fileType=?"); s.BindInt(0, id); s.BindInt(1, contentType); @@ -411,10 +404,11 @@ } else { - fileUuid = s.ColumnString(0); - compressedSize = s.ColumnInt(1); - uncompressedSize = s.ColumnInt(2); - compressionType = static_cast(s.ColumnInt(3)); + attachment = FileInfo(s.ColumnString(0), + contentType, + s.ColumnInt(1), + static_cast(s.ColumnInt(2)), + s.ColumnInt(3)); return true; } } diff -r 8098448bd827 -r 5368bbe813cf OrthancServer/DatabaseWrapper.h --- a/OrthancServer/DatabaseWrapper.h Fri Nov 30 12:18:44 2012 +0100 +++ b/OrthancServer/DatabaseWrapper.h Fri Nov 30 14:22:27 2012 +0100 @@ -35,6 +35,7 @@ #include "../Core/SQLite/Connection.h" #include "../Core/SQLite/Transaction.h" #include "../Core/DicomFormat/DicomInstanceHasher.h" +#include "../Core/FileStorage/FileInfo.h" #include "IServerIndexListener.h" #include @@ -107,37 +108,12 @@ int64_t id, MetadataType type); - void AttachFile(int64_t id, - AttachedFileType contentType, - const std::string& fileUuid, - uint64_t compressedSize, - uint64_t uncompressedSize, - CompressionType compressionType); - - void AttachFile(int64_t id, - AttachedFileType contentType, - const std::string& fileUuid, - uint64_t fileSize) - { - AttachFile(id, contentType, fileUuid, fileSize, fileSize, CompressionType_None); - } + void AddAttachment(int64_t id, + const FileInfo& attachment); - bool LookupFile(int64_t id, - AttachedFileType contentType, - std::string& fileUuid, - uint64_t& compressedSize, - uint64_t& uncompressedSize, - CompressionType& compressionType); - - bool LookupFile(int64_t id, - AttachedFileType contentType, - std::string& fileUuid, - uint64_t& uncompressedSize) - { - uint64_t compressedSize; - CompressionType compressionType; - return LookupFile(id, contentType, fileUuid, compressedSize, uncompressedSize, compressionType); - } + bool LookupAttachment(FileInfo& attachment, + int64_t id, + FileType contentType); void SetMainDicomTags(int64_t id, const DicomMap& tags); diff -r 8098448bd827 -r 5368bbe813cf OrthancServer/OrthancRestApi.cpp --- a/OrthancServer/OrthancRestApi.cpp Fri Nov 30 12:18:44 2012 +0100 +++ b/OrthancServer/OrthancRestApi.cpp Fri Nov 30 14:22:27 2012 +0100 @@ -258,7 +258,7 @@ { std::string instanceId = found["Instances"][i].asString(); std::string dicom; - context.ReadFile(dicom, instanceId, AttachedFileType_Dicom); + context.ReadFile(dicom, instanceId, FileType_Dicom); connection.Store(dicom); } @@ -270,7 +270,7 @@ context.GetIndex().LogExportedResource(resourceId, remote); std::string dicom; - context.ReadFile(dicom, resourceId, AttachedFileType_Dicom); + context.ReadFile(dicom, resourceId, FileType_Dicom); connection.Store(dicom); call.GetOutput().AnswerBuffer("{}", "application/json"); @@ -410,7 +410,7 @@ RETRIEVE_CONTEXT(call); std::string publicId = call.GetUriComponent("id", ""); - context.AnswerFile(call.GetOutput(), publicId, AttachedFileType_Dicom); + context.AnswerFile(call.GetOutput(), publicId, FileType_Dicom); } @@ -485,7 +485,7 @@ std::string publicId = call.GetUriComponent("id", ""); std::string dicomContent, png; - context.ReadFile(dicomContent, publicId, AttachedFileType_Dicom); + context.ReadFile(dicomContent, publicId, FileType_Dicom); try { diff -r 8098448bd827 -r 5368bbe813cf OrthancServer/ServerContext.cpp --- a/OrthancServer/ServerContext.cpp Fri Nov 30 12:18:44 2012 +0100 +++ b/OrthancServer/ServerContext.cpp Fri Nov 30 14:22:27 2012 +0100 @@ -50,7 +50,8 @@ { ServerContext::ServerContext(const boost::filesystem::path& path) : storage_(path.string()), - index_(*this, path.string()) + index_(*this, path.string()), + accessor_(storage_) { } @@ -65,14 +66,21 @@ const Json::Value& dicomJson, const std::string& remoteAet) { - std::string fileUuid = storage_.Create(dicomFile, dicomSize); - std::string jsonUuid = storage_.Create(dicomJson.toStyledString()); - StoreStatus status = index_.Store(dicomSummary, fileUuid, dicomSize, jsonUuid, remoteAet); + //accessor_.SetCompressionForNextOperations(CompressionType_Zlib); + + FileInfo dicomInfo = accessor_.Write(dicomFile, dicomSize, FileType_Dicom); + FileInfo jsonInfo = accessor_.Write(dicomJson.toStyledString(), FileType_Json); + + ServerIndex::Attachments attachments; + attachments.push_back(dicomInfo); + attachments.push_back(jsonInfo); + + StoreStatus status = index_.Store(dicomSummary, attachments, remoteAet); if (status != StoreStatus_Success) { - storage_.Remove(fileUuid); - storage_.Remove(jsonUuid); + storage_.Remove(dicomInfo.GetUuid()); + storage_.Remove(jsonInfo.GetUuid()); } switch (status) @@ -96,18 +104,16 @@ void ServerContext::AnswerFile(RestApiOutput& output, const std::string& instancePublicId, - AttachedFileType content) + FileType content) { - CompressionType compressionType; - std::string fileUuid; + FileInfo attachment; + if (index_.LookupAttachment(attachment, instancePublicId, FileType_Dicom)) + { + assert(attachment.GetCompressionType() == CompressionType_None); + assert(attachment.GetFileType() == FileType_Dicom); - if (index_.GetFile(fileUuid, compressionType, - instancePublicId, AttachedFileType_Dicom)) - { - assert(compressionType == CompressionType_None); - - FilesystemHttpSender sender(storage_, fileUuid); - sender.SetDownloadFilename(fileUuid + ".dcm"); + FilesystemHttpSender sender(storage_, attachment.GetUuid()); + sender.SetDownloadFilename(attachment.GetUuid() + ".dcm"); sender.SetContentType("application/dicom"); output.AnswerFile(sender); } @@ -118,7 +124,7 @@ const std::string& instancePublicId) { std::string s; - ReadFile(s, instancePublicId, AttachedFileType_Json); + ReadFile(s, instancePublicId, FileType_Json); Json::Reader reader; if (!reader.parse(s, result)) @@ -130,16 +136,15 @@ void ServerContext::ReadFile(std::string& result, const std::string& instancePublicId, - AttachedFileType content) + FileType content) { - CompressionType compressionType; - std::string fileUuid; - if (!index_.GetFile(fileUuid, compressionType, instancePublicId, content)) + FileInfo attachment; + if (!index_.LookupAttachment(attachment, instancePublicId, content)) { throw OrthancException(ErrorCode_InternalError); } - assert(compressionType == CompressionType_None); - storage_.ReadFile(result, fileUuid); + accessor_.SetCompressionForNextOperations(attachment.GetCompressionType()); + accessor_.Read(result, attachment.GetUuid()); } } diff -r 8098448bd827 -r 5368bbe813cf OrthancServer/ServerContext.h --- a/OrthancServer/ServerContext.h Fri Nov 30 12:18:44 2012 +0100 +++ b/OrthancServer/ServerContext.h Fri Nov 30 14:22:27 2012 +0100 @@ -35,6 +35,7 @@ #include "ServerIndex.h" #include "../Core/FileStorage.h" #include "../Core/RestApi/RestApiOutput.h" +#include "../Core/FileStorage/CompressedFileStorageAccessor.h" namespace Orthanc { @@ -48,6 +49,7 @@ private: FileStorage storage_; ServerIndex index_; + CompressedFileStorageAccessor accessor_; public: ServerContext(const boost::filesystem::path& path); @@ -67,7 +69,7 @@ void AnswerFile(RestApiOutput& output, const std::string& instancePublicId, - AttachedFileType content); + FileType content); void ReadJson(Json::Value& result, const std::string& instancePublicId); @@ -75,6 +77,6 @@ // TODO CACHING MECHANISM AT THIS POINT void ReadFile(std::string& result, const std::string& instancePublicId, - AttachedFileType content); + FileType content); }; } diff -r 8098448bd827 -r 5368bbe813cf OrthancServer/ServerEnumerations.h --- a/OrthancServer/ServerEnumerations.h Fri Nov 30 12:18:44 2012 +0100 +++ b/OrthancServer/ServerEnumerations.h Fri Nov 30 14:22:27 2012 +0100 @@ -35,11 +35,6 @@ namespace Orthanc { - enum GlobalProperty - { - GlobalProperty_FlushSleep = 1 - }; - enum SeriesStatus { SeriesStatus_Complete, @@ -55,6 +50,18 @@ StoreStatus_Failure }; + + /** + * WARNING: Do not change the explicit values in the enumerations + * below this point. This would result in incompatible databases + * between versions of Orthanc! + **/ + + enum GlobalProperty + { + GlobalProperty_FlushSleep = 1 + }; + enum ResourceType { ResourceType_Patient = 1, @@ -65,32 +72,26 @@ enum MetadataType { - MetadataType_Instance_IndexInSeries = 2, - MetadataType_Instance_ReceptionDate = 4, - MetadataType_Instance_RemoteAet = 1, - MetadataType_Series_ExpectedNumberOfInstances = 3 + MetadataType_Instance_IndexInSeries = 1, + MetadataType_Instance_ReceptionDate = 2, + MetadataType_Instance_RemoteAet = 3, + MetadataType_Series_ExpectedNumberOfInstances = 4 }; enum ChangeType { ChangeType_CompletedSeries = 1, - ChangeType_NewInstance = 3, - ChangeType_NewPatient = 4, - ChangeType_NewSeries = 2, + ChangeType_NewInstance = 2, + ChangeType_NewPatient = 3, + ChangeType_NewSeries = 4, ChangeType_NewStudy = 5 }; - enum AttachedFileType - { - AttachedFileType_Dicom = 1, - AttachedFileType_Json = 2 - }; + std::string GetBasePath(ResourceType type, + const std::string& publicId); const char* ToString(ResourceType type); - std::string GetBasePath(ResourceType type, - const std::string& publicId); - const char* ToString(SeriesStatus status); const char* ToString(StoreStatus status); diff -r 8098448bd827 -r 5368bbe813cf OrthancServer/ServerIndex.cpp --- a/OrthancServer/ServerIndex.cpp Fri Nov 30 12:18:44 2012 +0100 +++ b/OrthancServer/ServerIndex.cpp Fri Nov 30 14:22:27 2012 +0100 @@ -228,9 +228,7 @@ StoreStatus ServerIndex::Store(const DicomMap& dicomSummary, - const std::string& fileUuid, - uint64_t uncompressedFileSize, - const std::string& jsonUuid, + const Attachments& attachments, const std::string& remoteAet) { boost::mutex::scoped_lock lock(mutex_); @@ -305,8 +303,11 @@ } // Attach the files to the newly created instance - db_->AttachFile(instance, AttachedFileType_Dicom, fileUuid, uncompressedFileSize); - db_->AttachFile(instance, AttachedFileType_Json, jsonUuid, 0); // TODO "0" + for (Attachments::const_iterator it = attachments.begin(); + it != attachments.end(); it++) + { + db_->AddAttachment(instance, *it); + } // Attach the metadata db_->SetMetadata(instance, MetadataType_Instance_ReceptionDate, Toolbox::GetNowIsoString()); @@ -546,15 +547,14 @@ { result["Type"] = "Instance"; - std::string fileUuid; - uint64_t uncompressedSize; - if (!db_->LookupFile(id, AttachedFileType_Dicom, fileUuid, uncompressedSize)) + FileInfo attachment; + if (!db_->LookupAttachment(attachment, id, FileType_Dicom)) { throw OrthancException(ErrorCode_InternalError); } - result["FileSize"] = static_cast(uncompressedSize); - result["FileUuid"] = fileUuid; + result["FileSize"] = static_cast(attachment.GetUncompressedSize()); + result["FileUuid"] = attachment.GetUuid(); int i; if (db_->GetMetadataAsInteger(i, id, MetadataType_Instance_IndexInSeries)) @@ -577,10 +577,9 @@ } - bool ServerIndex::GetFile(std::string& fileUuid, - CompressionType& compressionType, - const std::string& instanceUuid, - AttachedFileType contentType) + bool ServerIndex::LookupAttachment(FileInfo& attachment, + const std::string& instanceUuid, + FileType contentType) { boost::mutex::scoped_lock lock(mutex_); @@ -592,9 +591,15 @@ throw OrthancException(ErrorCode_InternalError); } - uint64_t compressedSize, uncompressedSize; - - return db_->LookupFile(id, contentType, fileUuid, compressedSize, uncompressedSize, compressionType); + if (db_->LookupAttachment(attachment, id, contentType)) + { + assert(attachment.GetFileType() == contentType); + return true; + } + else + { + return false; + } } diff -r 8098448bd827 -r 5368bbe813cf OrthancServer/ServerIndex.h --- a/OrthancServer/ServerIndex.h Fri Nov 30 12:18:44 2012 +0100 +++ b/OrthancServer/ServerIndex.h Fri Nov 30 14:22:27 2012 +0100 @@ -52,6 +52,7 @@ } + class ServerIndex : public boost::noncopyable { private: @@ -67,15 +68,15 @@ SeriesStatus GetSeriesStatus(int id); public: + typedef std::list Attachments; + ServerIndex(ServerContext& context, const std::string& dbPath); ~ServerIndex(); StoreStatus Store(const DicomMap& dicomSummary, - const std::string& fileUuid, - uint64_t uncompressedFileSize, - const std::string& jsonUuid, + const Attachments& attachments, const std::string& remoteAet); uint64_t GetTotalCompressedSize(); @@ -86,10 +87,9 @@ const std::string& publicId, ResourceType expectedType); - bool GetFile(std::string& fileUuid, - CompressionType& compressionType, - const std::string& instanceUuid, - AttachedFileType contentType); + bool LookupAttachment(FileInfo& attachment, + const std::string& instanceUuid, + FileType contentType); void GetAllUuids(Json::Value& target, ResourceType resourceType); diff -r 8098448bd827 -r 5368bbe813cf UnitTests/FileStorage.cpp --- a/UnitTests/FileStorage.cpp Fri Nov 30 12:18:44 2012 +0100 +++ b/UnitTests/FileStorage.cpp Fri Nov 30 14:22:27 2012 +0100 @@ -66,12 +66,16 @@ FileStorageAccessor accessor(s); std::string data = "Hello world"; - std::string id = accessor.Write(data); + FileInfo info = accessor.Write(data, FileType_Dicom); std::string r; - accessor.Read(r, id); + accessor.Read(r, info.GetUuid()); ASSERT_EQ(data, r); + ASSERT_EQ(CompressionType_None, info.GetCompressionType()); + ASSERT_EQ(11u, info.GetUncompressedSize()); + ASSERT_EQ(11u, info.GetCompressedSize()); + ASSERT_EQ(FileType_Dicom, info.GetFileType()); } @@ -82,12 +86,16 @@ accessor.SetCompressionForNextOperations(CompressionType_None); std::string data = "Hello world"; - std::string id = accessor.Write(data); + FileInfo info = accessor.Write(data, FileType_Dicom); std::string r; - accessor.Read(r, id); + accessor.Read(r, info.GetUuid()); ASSERT_EQ(data, r); + ASSERT_EQ(CompressionType_None, info.GetCompressionType()); + ASSERT_EQ(11u, info.GetUncompressedSize()); + ASSERT_EQ(11u, info.GetCompressedSize()); + ASSERT_EQ(FileType_Dicom, info.GetFileType()); } @@ -98,12 +106,15 @@ accessor.SetCompressionForNextOperations(CompressionType_Zlib); std::string data = "Hello world"; - std::string id = accessor.Write(data); + FileInfo info = accessor.Write(data, FileType_Dicom); std::string r; - accessor.Read(r, id); + accessor.Read(r, info.GetUuid()); ASSERT_EQ(data, r); + ASSERT_EQ(CompressionType_Zlib, info.GetCompressionType()); + ASSERT_EQ(11u, info.GetUncompressedSize()); + ASSERT_EQ(FileType_Dicom, info.GetFileType()); } @@ -117,24 +128,24 @@ std::string uncompressedData = "HelloWorld"; accessor.SetCompressionForNextOperations(CompressionType_Zlib); - std::string compressedId = accessor.Write(compressedData); + FileInfo compressedInfo = accessor.Write(compressedData, FileType_Dicom); accessor.SetCompressionForNextOperations(CompressionType_None); - std::string uncompressedId = accessor.Write(uncompressedData); + FileInfo uncompressedInfo = accessor.Write(uncompressedData, FileType_Dicom); accessor.SetCompressionForNextOperations(CompressionType_Zlib); - accessor.Read(r, compressedId); + accessor.Read(r, compressedInfo.GetUuid()); ASSERT_EQ(compressedData, r); accessor.SetCompressionForNextOperations(CompressionType_None); - accessor.Read(r, compressedId); + accessor.Read(r, compressedInfo.GetUuid()); ASSERT_NE(compressedData, r); - -#if defined(__linux) + + /* // This test is too slow on Windows accessor.SetCompressionForNextOperations(CompressionType_Zlib); - ASSERT_THROW(accessor.Read(r, uncompressedId), OrthancException); -#endif + ASSERT_THROW(accessor.Read(r, uncompressedInfo.GetUuid()), OrthancException); + */ } diff -r 8098448bd827 -r 5368bbe813cf UnitTests/ServerIndex.cpp --- a/UnitTests/ServerIndex.cpp Fri Nov 30 12:18:44 2012 +0100 +++ b/UnitTests/ServerIndex.cpp Fri Nov 30 14:22:27 2012 +0100 @@ -65,18 +65,18 @@ Json::Value t; index.GetAllPublicIds(t, ResourceType_Patient); - ASSERT_EQ(1, t.size()); + ASSERT_EQ(1u, t.size()); ASSERT_EQ("a", t[0u].asString()); index.GetAllPublicIds(t, ResourceType_Series); - ASSERT_EQ(1, t.size()); + ASSERT_EQ(1u, t.size()); ASSERT_EQ("c", t[0u].asString()); index.GetAllPublicIds(t, ResourceType_Study); - ASSERT_EQ(2, t.size()); + ASSERT_EQ(2u, t.size()); index.GetAllPublicIds(t, ResourceType_Instance); - ASSERT_EQ(3, t.size()); + ASSERT_EQ(3u, t.size()); } index.SetGlobalProperty(GlobalProperty_FlushSleep, "World"); @@ -107,14 +107,14 @@ ASSERT_TRUE(index.GetParentPublicId(s, a[5])); ASSERT_EQ("g", s); std::list l; - index.GetChildrenPublicId(l, a[0]); ASSERT_EQ(1, l.size()); ASSERT_EQ("b", l.front()); - index.GetChildrenPublicId(l, a[1]); ASSERT_EQ(1, l.size()); ASSERT_EQ("c", l.front()); - index.GetChildrenPublicId(l, a[3]); ASSERT_EQ(0, l.size()); - index.GetChildrenPublicId(l, a[4]); ASSERT_EQ(0, l.size()); - index.GetChildrenPublicId(l, a[5]); ASSERT_EQ(0, l.size()); - index.GetChildrenPublicId(l, a[6]); ASSERT_EQ(1, l.size()); ASSERT_EQ("f", l.front()); + index.GetChildrenPublicId(l, a[0]); ASSERT_EQ(1u, l.size()); ASSERT_EQ("b", l.front()); + index.GetChildrenPublicId(l, a[1]); ASSERT_EQ(1u, l.size()); ASSERT_EQ("c", l.front()); + index.GetChildrenPublicId(l, a[3]); ASSERT_EQ(0u, l.size()); + index.GetChildrenPublicId(l, a[4]); ASSERT_EQ(0u, l.size()); + index.GetChildrenPublicId(l, a[5]); ASSERT_EQ(0u, l.size()); + index.GetChildrenPublicId(l, a[6]); ASSERT_EQ(1u, l.size()); ASSERT_EQ("f", l.front()); - index.GetChildrenPublicId(l, a[2]); ASSERT_EQ(2, l.size()); + index.GetChildrenPublicId(l, a[2]); ASSERT_EQ(2u, l.size()); if (l.front() == "d") { ASSERT_EQ("e", l.back()); @@ -125,13 +125,13 @@ ASSERT_EQ("e", l.front()); } - index.AttachFile(a[4], AttachedFileType_Json, "my json file", 21, 42, CompressionType_Zlib); - index.AttachFile(a[4], AttachedFileType_Dicom, "my dicom file", 42); - index.AttachFile(a[6], AttachedFileType_Dicom, "world", 44); + index.AddAttachment(a[4], FileInfo("my json file", FileType_Json, 42, CompressionType_Zlib, 21)); + index.AddAttachment(a[4], FileInfo("my dicom file", FileType_Dicom, 42)); + index.AddAttachment(a[6], FileInfo("world", FileType_Dicom, 44)); index.SetMetadata(a[4], MetadataType_Instance_RemoteAet, "PINNACLE"); - ASSERT_EQ(21 + 42 + 44, index.GetTotalCompressedSize()); - ASSERT_EQ(42 + 42 + 44, index.GetTotalUncompressedSize()); + ASSERT_EQ(21u + 42u + 44u, index.GetTotalCompressedSize()); + ASSERT_EQ(42u + 42u + 44u, index.GetTotalUncompressedSize()); DicomMap m; m.SetValue(0x0010, 0x0010, "PatientName"); @@ -155,35 +155,34 @@ ASSERT_EQ("World", index.GetGlobalProperty(GlobalProperty_FlushSleep)); ASSERT_EQ("None", index.GetGlobalProperty(static_cast(42), "None")); - uint64_t us, cs; - CompressionType ct; - ASSERT_TRUE(index.LookupFile(a[4], AttachedFileType_Json, s, cs, us, ct)); - ASSERT_EQ("my json file", s); - ASSERT_EQ(21, cs); - ASSERT_EQ(42, us); - ASSERT_EQ(CompressionType_Zlib, ct); + FileInfo att; + ASSERT_TRUE(index.LookupAttachment(att, a[4], FileType_Json)); + ASSERT_EQ("my json file", att.GetUuid()); + ASSERT_EQ(21u, att.GetCompressedSize()); + ASSERT_EQ(42u, att.GetUncompressedSize()); + ASSERT_EQ(CompressionType_Zlib, att.GetCompressionType()); ASSERT_EQ(0u, listener.deletedFiles_.size()); - ASSERT_EQ(7, index.GetTableRecordCount("Resources")); - ASSERT_EQ(3, index.GetTableRecordCount("AttachedFiles")); - ASSERT_EQ(1, index.GetTableRecordCount("Metadata")); - ASSERT_EQ(1, index.GetTableRecordCount("MainDicomTags")); + ASSERT_EQ(7u, index.GetTableRecordCount("Resources")); + ASSERT_EQ(3u, index.GetTableRecordCount("AttachedFiles")); + ASSERT_EQ(1u, index.GetTableRecordCount("Metadata")); + ASSERT_EQ(1u, index.GetTableRecordCount("MainDicomTags")); index.DeleteResource(a[0]); - ASSERT_EQ(2, listener.deletedFiles_.size()); + ASSERT_EQ(2u, listener.deletedFiles_.size()); ASSERT_FALSE(listener.deletedFiles_.find("my json file") == listener.deletedFiles_.end()); ASSERT_FALSE(listener.deletedFiles_.find("my dicom file") == listener.deletedFiles_.end()); - ASSERT_EQ(2, index.GetTableRecordCount("Resources")); - ASSERT_EQ(0, index.GetTableRecordCount("Metadata")); - ASSERT_EQ(1, index.GetTableRecordCount("AttachedFiles")); - ASSERT_EQ(0, index.GetTableRecordCount("MainDicomTags")); + ASSERT_EQ(2u, index.GetTableRecordCount("Resources")); + ASSERT_EQ(0u, index.GetTableRecordCount("Metadata")); + ASSERT_EQ(1u, index.GetTableRecordCount("AttachedFiles")); + ASSERT_EQ(0u, index.GetTableRecordCount("MainDicomTags")); index.DeleteResource(a[5]); - ASSERT_EQ(0, index.GetTableRecordCount("Resources")); - ASSERT_EQ(0, index.GetTableRecordCount("AttachedFiles")); - ASSERT_EQ(1, index.GetTableRecordCount("GlobalProperties")); + ASSERT_EQ(0u, index.GetTableRecordCount("Resources")); + ASSERT_EQ(0u, index.GetTableRecordCount("AttachedFiles")); + ASSERT_EQ(1u, index.GetTableRecordCount("GlobalProperties")); - ASSERT_EQ(3, listener.deletedFiles_.size()); + ASSERT_EQ(3u, listener.deletedFiles_.size()); ASSERT_FALSE(listener.deletedFiles_.find("world") == listener.deletedFiles_.end()); } @@ -217,25 +216,25 @@ { Json::Value j; index.GetChildren(j, a[0]); - ASSERT_EQ(2, j.size()); + ASSERT_EQ(2u, j.size()); ASSERT_TRUE((j[0u] == "b" && j[1u] == "f") || (j[1u] == "b" && j[0u] == "f")); index.GetChildren(j, a[1]); - ASSERT_EQ(2, j.size()); + ASSERT_EQ(2u, j.size()); ASSERT_TRUE((j[0u] == "c" && j[1u] == "g") || (j[1u] == "c" && j[0u] == "g")); index.GetChildren(j, a[2]); - ASSERT_EQ(2, j.size()); + ASSERT_EQ(2u, j.size()); ASSERT_TRUE((j[0u] == "d" && j[1u] == "e") || (j[1u] == "d" && j[0u] == "e")); - index.GetChildren(j, a[3]); ASSERT_EQ(0, j.size()); - index.GetChildren(j, a[4]); ASSERT_EQ(0, j.size()); - index.GetChildren(j, a[5]); ASSERT_EQ(1, j.size()); ASSERT_EQ("h", j[0u].asString()); - index.GetChildren(j, a[6]); ASSERT_EQ(0, j.size()); - index.GetChildren(j, a[7]); ASSERT_EQ(0, j.size()); + index.GetChildren(j, a[3]); ASSERT_EQ(0u, j.size()); + index.GetChildren(j, a[4]); ASSERT_EQ(0u, j.size()); + index.GetChildren(j, a[5]); ASSERT_EQ(1u, j.size()); ASSERT_EQ("h", j[0u].asString()); + index.GetChildren(j, a[6]); ASSERT_EQ(0u, j.size()); + index.GetChildren(j, a[7]); ASSERT_EQ(0u, j.size()); } listener.Reset();