# HG changeset patch # User am@osimis.io # Date 1543501607 -3600 # Node ID 9f6716008066f2789a91a1179dad6a1defc7ea30 # Parent e9613a09cf9e483c965590df01600ca5c060e12b# Parent e292798f99805da9983294a6b354f1c0ca3edc3b merge diff -r e9613a09cf9e -r 9f6716008066 OrthancServer/LuaScripting.cpp --- a/OrthancServer/LuaScripting.cpp Thu Nov 29 15:26:31 2018 +0100 +++ b/OrthancServer/LuaScripting.cpp Thu Nov 29 15:26:47 2018 +0100 @@ -34,7 +34,7 @@ #include "PrecompiledHeadersServer.h" #include "LuaScripting.h" -#include "OrthancInitialization.h" +#include "OrthancConfiguration.h" #include "OrthancRestApi/OrthancRestApi.h" #include "ServerContext.h" @@ -414,7 +414,11 @@ int LuaScripting::GetOrthancConfiguration(lua_State *state) { Json::Value configuration; - Configuration::GetConfiguration(configuration); + + { + OrthancConfiguration::ReaderLock lock; + configuration = lock.GetJson(); + } LuaContext::GetLuaContext(state).PushJson(configuration); @@ -445,7 +449,12 @@ } std::string name = parameters["Modality"].asString(); - RemoteModalityParameters modality = Configuration::GetModalityUsingSymbolicName(name); + RemoteModalityParameters modality; + + { + OrthancConfiguration::ReaderLock configLock; + modality = configLock.GetConfiguration().GetModalityUsingSymbolicName(name); + } // This is not a C-MOVE: No need to call "StoreScuCommand::SetMoveOriginator()" return lock.AddStoreScuOperation(localAet, modality); @@ -453,10 +462,11 @@ if (operation == "store-peer") { + OrthancConfiguration::ReaderLock configLock; std::string name = parameters["Peer"].asString(); WebServiceParameters peer; - if (Configuration::GetOrthancPeer(peer, name)) + if (configLock.GetConfiguration().GetOrthancPeer(peer, name)) { return lock.AddStorePeerOperation(peer); } @@ -742,17 +752,19 @@ void LuaScripting::LoadGlobalConfiguration() { + OrthancConfiguration::ReaderLock configLock; + lua_.Execute(Orthanc::EmbeddedResources::LUA_TOOLBOX); std::list luaScripts; - Configuration::GetGlobalListOfStringsParameter(luaScripts, "LuaScripts"); + configLock.GetConfiguration().GetListOfStringsParameter(luaScripts, "LuaScripts"); LuaScripting::Lock lock(*this); for (std::list::const_iterator it = luaScripts.begin(); it != luaScripts.end(); ++it) { - std::string path = Configuration::InterpretStringParameterAsPath(*it); + std::string path = configLock.GetConfiguration().InterpretStringParameterAsPath(*it); LOG(INFO) << "Installing the Lua scripts from: " << path; std::string script; SystemToolbox::ReadFile(script, path); diff -r e9613a09cf9e -r 9f6716008066 OrthancServer/OrthancConfiguration.cpp --- a/OrthancServer/OrthancConfiguration.cpp Thu Nov 29 15:26:31 2018 +0100 +++ b/OrthancServer/OrthancConfiguration.cpp Thu Nov 29 15:26:47 2018 +0100 @@ -40,6 +40,8 @@ #include "../Core/SystemToolbox.h" #include "../Core/Toolbox.h" +#include "ServerIndex.h" + namespace Orthanc { static void AddFileToConfiguration(Json::Value& target, @@ -474,9 +476,8 @@ } - void OrthancConfiguration::GetGlobalListOfStringsParameter( - std::list& target, - const std::string& key) const + void OrthancConfiguration::GetListOfStringsParameter(std::list& target, + const std::string& key) const { target.clear(); @@ -718,4 +719,16 @@ return a != b; } + + + void OrthancConfiguration::SetServerIndex(ServerIndex& index) + { + serverIndex_ = &index; + } + + + void OrthancConfiguration::ResetServerIndex() + { + serverIndex_ = NULL; + } } diff -r e9613a09cf9e -r 9f6716008066 OrthancServer/OrthancConfiguration.h --- a/OrthancServer/OrthancConfiguration.h Thu Nov 29 15:26:31 2018 +0100 +++ b/OrthancServer/OrthancConfiguration.h Thu Nov 29 15:26:47 2018 +0100 @@ -41,10 +41,12 @@ #include #include +#include namespace Orthanc { class MongooseServer; + class ServerIndex; class OrthancConfiguration : public boost::noncopyable { @@ -55,6 +57,7 @@ std::string configurationAbsolutePath_; FontRegistry fontRegistry_; const char* configurationFileArg_; + ServerIndex* serverIndex_; OrthancConfiguration() : configurationFileArg_(NULL) @@ -66,14 +69,14 @@ static OrthancConfiguration& GetInstance(); public: - class Reader : public boost::noncopyable + class ReaderLock : public boost::noncopyable { private: OrthancConfiguration& configuration_; boost::shared_lock lock_; public: - Reader() : + ReaderLock() : configuration_(GetInstance()), lock_(configuration_.mutex_) { @@ -91,14 +94,14 @@ }; - class Writer : public boost::noncopyable + class WriterLock : public boost::noncopyable { private: OrthancConfiguration& configuration_; boost::unique_lock lock_; public: - Writer() : + WriterLock() : configuration_(GetInstance()), lock_(configuration_.mutex_) { @@ -121,11 +124,6 @@ }; - const Json::Value& GetContent() const - { - return json_; - } - const std::string& GetConfigurationAbsolutePath() const { return configurationAbsolutePath_; @@ -173,8 +171,8 @@ std::string InterpretStringParameterAsPath(const std::string& parameter) const; - void GetGlobalListOfStringsParameter(std::list& target, - const std::string& key) const; + void GetListOfStringsParameter(std::list& target, + const std::string& key) const; bool IsSameAETitle(const std::string& aet1, const std::string& aet2) const; @@ -205,5 +203,9 @@ void SetDefaultEncoding(Encoding encoding); bool HasConfigurationChanged() const; + + void SetServerIndex(ServerIndex& index); + + void ResetServerIndex(); }; } diff -r e9613a09cf9e -r 9f6716008066 OrthancServer/OrthancFindRequestHandler.cpp --- a/OrthancServer/OrthancFindRequestHandler.cpp Thu Nov 29 15:26:31 2018 +0100 +++ b/OrthancServer/OrthancFindRequestHandler.cpp Thu Nov 29 15:26:47 2018 +0100 @@ -38,7 +38,7 @@ #include "../Core/Lua/LuaFunctionCall.h" #include "../Core/Logging.h" #include "../Core/DicomParsing/FromDcmtkBridge.h" -#include "OrthancInitialization.h" +#include "OrthancConfiguration.h" #include "Search/LookupResource.h" #include "ServerToolbox.h" @@ -258,16 +258,21 @@ // The metadata "SopClassUid" is available for each of these instances StoreSetOfStrings(result, DICOM_TAG_SOP_CLASSES_IN_STUDY, values); } - else if (Configuration::GetGlobalBoolParameter("AllowFindSopClassesInStudy", false)) - { - ExtractTagFromInstancesOnDisk(values, context, DICOM_TAG_SOP_CLASS_UID, instances); - StoreSetOfStrings(result, DICOM_TAG_SOP_CLASSES_IN_STUDY, values); - } else { - result.SetValue(DICOM_TAG_SOP_CLASSES_IN_STUDY, "", false); - LOG(WARNING) << "The handling of \"SOP Classes in Study\" (0008,0062) " - << "in C-FIND requests is disabled"; + OrthancConfiguration::ReaderLock lock; + + if (lock.GetConfiguration().GetBooleanParameter("AllowFindSopClassesInStudy", false)) + { + ExtractTagFromInstancesOnDisk(values, context, DICOM_TAG_SOP_CLASS_UID, instances); + StoreSetOfStrings(result, DICOM_TAG_SOP_CLASSES_IN_STUDY, values); + } + else + { + result.SetValue(DICOM_TAG_SOP_CLASSES_IN_STUDY, "", false); + LOG(WARNING) << "The handling of \"SOP Classes in Study\" (0008,0062) " + << "in C-FIND requests is disabled"; + } } } } @@ -590,7 +595,12 @@ LookupResource lookup(level); - const bool caseSensitivePN = Configuration::GetGlobalBoolParameter("CaseSensitivePN", false); + bool caseSensitivePN; + + { + OrthancConfiguration::ReaderLock lock; + caseSensitivePN = lock.GetConfiguration().GetBooleanParameter("CaseSensitivePN", false); + } for (size_t i = 0; i < query.GetSize(); i++) { diff -r e9613a09cf9e -r 9f6716008066 OrthancServer/OrthancInitialization.cpp --- a/OrthancServer/OrthancInitialization.cpp Thu Nov 29 15:26:31 2018 +0100 +++ b/OrthancServer/OrthancInitialization.cpp Thu Nov 29 15:26:47 2018 +0100 @@ -39,22 +39,14 @@ #endif #include "OrthancInitialization.h" -#include "ServerContext.h" -#include "../Core/HttpClient.h" +#include "../Core/DicomParsing/FromDcmtkBridge.h" +#include "../Core/FileStorage/FilesystemStorage.h" #include "../Core/Logging.h" #include "../Core/OrthancException.h" -#include "../Core/Toolbox.h" -#include "../Core/FileStorage/FilesystemStorage.h" -#include "ServerEnumerations.h" #include "DatabaseWrapper.h" -#include "../Core/DicomParsing/FromDcmtkBridge.h" - -#include -#include -#include -#include +#include "OrthancConfiguration.h" #include // For dcmDisableGethostbyaddr() @@ -62,245 +54,11 @@ namespace Orthanc { - static boost::recursive_mutex globalMutex_; - static Json::Value configuration_; - static boost::filesystem::path defaultDirectory_; - static std::string configurationAbsolutePath_; - static FontRegistry fontRegistry_; - static const char* configurationFileArg_ = NULL; - - - static std::string GetGlobalStringParameterInternal(const std::string& parameter, - const std::string& defaultValue) - { - if (configuration_.isMember(parameter)) - { - if (configuration_[parameter].type() != Json::stringValue) - { - LOG(ERROR) << "The configuration option \"" << parameter << "\" must be a string"; - throw OrthancException(ErrorCode_BadParameterType); - } - else - { - return configuration_[parameter].asString(); - } - } - else - { - return defaultValue; - } - } - - - static bool GetGlobalBoolParameterInternal(const std::string& parameter, - bool defaultValue) - { - if (configuration_.isMember(parameter)) - { - if (configuration_[parameter].type() != Json::booleanValue) - { - LOG(ERROR) << "The configuration option \"" << parameter << "\" must be a Boolean (true or false)"; - throw OrthancException(ErrorCode_BadParameterType); - } - else - { - return configuration_[parameter].asBool(); - } - } - else - { - return defaultValue; - } - } - - - - static void AddFileToConfiguration(Json::Value& target, - const boost::filesystem::path& path) + static void RegisterUserMetadata(const Json::Value& config) { - std::map env; - SystemToolbox::GetEnvironmentVariables(env); - - LOG(WARNING) << "Reading the configuration from: " << path; - - Json::Value config; - - { - std::string content; - SystemToolbox::ReadFile(content, path.string()); - - content = Toolbox::SubstituteVariables(content, env); - - Json::Value tmp; - Json::Reader reader; - if (!reader.parse(content, tmp) || - tmp.type() != Json::objectValue) - { - LOG(ERROR) << "The configuration file does not follow the JSON syntax: " << path; - throw OrthancException(ErrorCode_BadJson); - } - - Toolbox::CopyJsonWithoutComments(config, tmp); - } - - if (target.size() == 0) - { - target = config; - } - else - { - // Merge the newly-added file with the previous content of "target" - Json::Value::Members members = config.getMemberNames(); - for (Json::Value::ArrayIndex i = 0; i < members.size(); i++) - { - if (target.isMember(members[i])) - { - LOG(ERROR) << "The configuration section \"" << members[i] << "\" is defined in 2 different configuration files"; - throw OrthancException(ErrorCode_BadFileFormat); - } - else - { - target[members[i]] = config[members[i]]; - } - } - } - } - - - static void ScanFolderForConfiguration(Json::Value& target, - const char* folder) - { - using namespace boost::filesystem; - - LOG(WARNING) << "Scanning folder \"" << folder << "\" for configuration files"; - - directory_iterator end_it; // default construction yields past-the-end - for (directory_iterator it(folder); - it != end_it; - ++it) + if (config.isMember("UserMetadata")) { - if (!is_directory(it->status())) - { - std::string extension = boost::filesystem::extension(it->path()); - Toolbox::ToLowerCase(extension); - - if (extension == ".json") - { - AddFileToConfiguration(target, it->path().string()); - } - } - } - } - - - static void ReadConfiguration(Json::Value& target, - const char* configurationFile) - { - target = Json::objectValue; - - if (configurationFile) - { - if (!boost::filesystem::exists(configurationFile)) - { - LOG(ERROR) << "Inexistent path to configuration: " << configurationFile; - throw OrthancException(ErrorCode_InexistentFile); - } - - if (boost::filesystem::is_directory(configurationFile)) - { - ScanFolderForConfiguration(target, configurationFile); - } - else - { - AddFileToConfiguration(target, configurationFile); - } - } - else - { -#if ORTHANC_STANDALONE == 1 - // No default path for the standalone configuration - LOG(WARNING) << "Using the default Orthanc configuration"; - return; - -#else - // In a non-standalone build, we use the - // "Resources/Configuration.json" from the Orthanc source code - - boost::filesystem::path p = ORTHANC_PATH; - p /= "Resources"; - p /= "Configuration.json"; - - AddFileToConfiguration(target, p); -#endif - } - } - - - - static void ReadGlobalConfiguration(const char* configurationFile) - { - // Read the content of the configuration - configurationFileArg_ = configurationFile; - ReadConfiguration(configuration_, configurationFile); - - // Adapt the paths to the configurations - defaultDirectory_ = boost::filesystem::current_path(); - configurationAbsolutePath_ = ""; - - if (configurationFile) - { - if (boost::filesystem::is_directory(configurationFile)) - { - defaultDirectory_ = boost::filesystem::path(configurationFile); - configurationAbsolutePath_ = boost::filesystem::absolute(configurationFile).parent_path().string(); - } - else - { - defaultDirectory_ = boost::filesystem::path(configurationFile).parent_path(); - configurationAbsolutePath_ = boost::filesystem::absolute(configurationFile).string(); - } - } - else - { -#if ORTHANC_STANDALONE != 1 - // In a non-standalone build, we use the - // "Resources/Configuration.json" from the Orthanc source code - - boost::filesystem::path p = ORTHANC_PATH; - p /= "Resources"; - p /= "Configuration.json"; - configurationAbsolutePath_ = boost::filesystem::absolute(p).string(); -#endif - } - } - - - static void ValidateGlobalConfiguration() - { - std::set ids; - - Configuration::GetListOfOrthancPeers(ids); - for (std::set::const_iterator it = ids.begin(); it != ids.end(); ++it) - { - WebServiceParameters peer; - Configuration::GetOrthancPeer(peer, *it); - peer.CheckClientCertificate(); - } - - Configuration::GetListOfDicomModalities(ids); - for (std::set::const_iterator it = ids.begin(); it != ids.end(); ++it) - { - RemoteModalityParameters modality; - Configuration::GetDicomModalityUsingSymbolicName(modality, *it); - } - } - - - static void RegisterUserMetadata() - { - if (configuration_.isMember("UserMetadata")) - { - const Json::Value& parameter = configuration_["UserMetadata"]; + const Json::Value& parameter = config["UserMetadata"]; Json::Value::Members members = parameter.getMemberNames(); for (size_t i = 0; i < members.size(); i++) @@ -332,11 +90,11 @@ } - static void RegisterUserContentType() + static void RegisterUserContentType(const Json::Value& config) { - if (configuration_.isMember("UserContentType")) + if (config.isMember("UserContentType")) { - const Json::Value& parameter = configuration_["UserContentType"]; + const Json::Value& parameter = config["UserContentType"]; Json::Value::Members members = parameter.getMemberNames(); for (size_t i = 0; i < members.size(); i++) @@ -464,29 +222,28 @@ void OrthancInitialize(const char* configurationFile) { - boost::recursive_mutex::scoped_lock lock(globalMutex_); + OrthancConfiguration::WriterLock lock; Toolbox::InitializeOpenSsl(); InitializeServerEnumerations(); // Read the user-provided configuration - ReadGlobalConfiguration(configurationFile); - ValidateGlobalConfiguration(); + lock.GetConfiguration().Read(configurationFile); - if (configuration_.isMember("Locale")) + if (lock.GetJson().isMember("Locale")) { - std::string locale = GetGlobalStringParameterInternal("Locale", ""); - Toolbox::InitializeGlobalLocale(configuration_["Locale"].asCString()); + std::string locale = lock.GetConfiguration().GetStringParameter("Locale", ""); + Toolbox::InitializeGlobalLocale(lock.GetJson()["Locale"].asCString()); } else { Toolbox::InitializeGlobalLocale(NULL); } - if (configuration_.isMember("DefaultEncoding")) + if (lock.GetJson().isMember("DefaultEncoding")) { - std::string encoding = GetGlobalStringParameterInternal("DefaultEncoding", ""); + std::string encoding = lock.GetConfiguration().GetStringParameter("DefaultEncoding", ""); SetDefaultDicomEncoding(StringToEncoding(encoding.c_str())); } else @@ -494,22 +251,23 @@ SetDefaultDicomEncoding(ORTHANC_DEFAULT_DICOM_ENCODING); } - if (configuration_.isMember("Pkcs11")) + if (lock.GetJson().isMember("Pkcs11")) { - ConfigurePkcs11(configuration_["Pkcs11"]); + ConfigurePkcs11(lock.GetJson()["Pkcs11"]); } HttpClient::GlobalInitialize(); - RegisterUserMetadata(); - RegisterUserContentType(); + RegisterUserMetadata(lock.GetJson()); + RegisterUserContentType(lock.GetJson()); - FromDcmtkBridge::InitializeDictionary(GetGlobalBoolParameterInternal("LoadPrivateDictionary", true)); - LoadCustomDictionary(configuration_); + bool loadPrivate = lock.GetConfiguration().GetBooleanParameter("LoadPrivateDictionary", true); + FromDcmtkBridge::InitializeDictionary(loadPrivate); + LoadCustomDictionary(lock.GetJson()); FromDcmtkBridge::InitializeCodecs(); - fontRegistry_.AddFromResource(EmbeddedResources::FONT_UBUNTU_MONO_BOLD_16); + lock.GetConfiguration().RegisterFont(EmbeddedResources::FONT_UBUNTU_MONO_BOLD_16); /* Disable "gethostbyaddr" (which results in memory leaks) and use raw IP addresses */ dcmDisableGethostbyaddr.set(OFTrue); @@ -519,7 +277,8 @@ void OrthancFinalize() { - boost::recursive_mutex::scoped_lock lock(globalMutex_); + OrthancConfiguration::WriterLock lock; + HttpClient::GlobalFinalize(); FromDcmtkBridge::FinalizeCodecs(); Toolbox::FinalizeOpenSsl(); @@ -527,505 +286,16 @@ } - std::string Configuration::GetGlobalStringParameter(const std::string& parameter, - const std::string& defaultValue) - { - boost::recursive_mutex::scoped_lock lock(globalMutex_); - return GetGlobalStringParameterInternal(parameter, defaultValue); - } - - - int Configuration::GetGlobalIntegerParameter(const std::string& parameter, - int defaultValue) - { - boost::recursive_mutex::scoped_lock lock(globalMutex_); - - if (configuration_.isMember(parameter)) - { - if (configuration_[parameter].type() != Json::intValue) - { - LOG(ERROR) << "The configuration option \"" << parameter << "\" must be an integer"; - throw OrthancException(ErrorCode_BadParameterType); - } - else - { - return configuration_[parameter].asInt(); - } - } - else - { - return defaultValue; - } - } - - - unsigned int Configuration::GetGlobalUnsignedIntegerParameter(const std::string& parameter, - unsigned int defaultValue) - { - int v = GetGlobalIntegerParameter(parameter, defaultValue); - - if (v < 0) - { - LOG(ERROR) << "The configuration option \"" << parameter << "\" must be a positive integer"; - throw OrthancException(ErrorCode_ParameterOutOfRange); - } - else - { - return static_cast(v); - } - } - - - bool Configuration::GetGlobalBoolParameter(const std::string& parameter, - bool defaultValue) - { - boost::recursive_mutex::scoped_lock lock(globalMutex_); - return GetGlobalBoolParameterInternal(parameter, defaultValue); - } - - - void Configuration::GetDicomModalityUsingSymbolicName(RemoteModalityParameters& modality, - const std::string& name) - { - boost::recursive_mutex::scoped_lock lock(globalMutex_); - - if (!configuration_.isMember("DicomModalities")) - { - LOG(ERROR) << "No modality with symbolic name: " << name; - throw OrthancException(ErrorCode_InexistentItem); - } - - const Json::Value& modalities = configuration_["DicomModalities"]; - if (modalities.type() != Json::objectValue || - !modalities.isMember(name)) - { - LOG(ERROR) << "No modality with symbolic name: " << name; - throw OrthancException(ErrorCode_InexistentItem); - } - - try - { - modality.Unserialize(modalities[name]); - } - catch (OrthancException&) - { - LOG(ERROR) << "Syntax error in the definition of DICOM modality \"" << name - << "\". Please check your configuration file."; - throw; - } - } - - - bool Configuration::GetOrthancPeer(WebServiceParameters& peer, - const std::string& name) - { - boost::recursive_mutex::scoped_lock lock(globalMutex_); - - if (!configuration_.isMember("OrthancPeers")) - { - return false; - } - - try - { - const Json::Value& modalities = configuration_["OrthancPeers"]; - if (modalities.type() != Json::objectValue || - !modalities.isMember(name)) - { - return false; - } - else - { - peer.Unserialize(modalities[name]); - return true; - } - } - catch (OrthancException&) - { - LOG(ERROR) << "Syntax error in the definition of peer \"" << name - << "\". Please check your configuration file."; - throw; - } - } - - - static bool ReadKeys(std::set& target, - const char* parameter, - bool onlyAlphanumeric) - { - boost::recursive_mutex::scoped_lock lock(globalMutex_); - - target.clear(); - - if (!configuration_.isMember(parameter)) - { - return true; - } - - const Json::Value& modalities = configuration_[parameter]; - if (modalities.type() != Json::objectValue) - { - LOG(ERROR) << "Bad format of the \"DicomModalities\" configuration section"; - throw OrthancException(ErrorCode_BadFileFormat); - } - - Json::Value::Members members = modalities.getMemberNames(); - for (size_t i = 0; i < members.size(); i++) - { - if (onlyAlphanumeric) - { - for (size_t j = 0; j < members[i].size(); j++) - { - if (!isalnum(members[i][j]) && members[i][j] != '-') - { - return false; - } - } - } - - target.insert(members[i]); - } - - return true; - } - - - void Configuration::GetListOfDicomModalities(std::set& target) - { - target.clear(); - - if (!ReadKeys(target, "DicomModalities", true)) - { - LOG(ERROR) << "Only alphanumeric and dash characters are allowed in the names of the modalities"; - throw OrthancException(ErrorCode_BadFileFormat); - } - } - - - void Configuration::GetListOfOrthancPeers(std::set& target) - { - target.clear(); - - if (!ReadKeys(target, "OrthancPeers", true)) - { - LOG(ERROR) << "Only alphanumeric and dash characters are allowed in the names of Orthanc peers"; - throw OrthancException(ErrorCode_BadFileFormat); - } - } - - - - void Configuration::SetupRegisteredUsers(MongooseServer& httpServer) - { - boost::recursive_mutex::scoped_lock lock(globalMutex_); - - httpServer.ClearUsers(); - - if (!configuration_.isMember("RegisteredUsers")) - { - return; - } - - const Json::Value& users = configuration_["RegisteredUsers"]; - if (users.type() != Json::objectValue) - { - LOG(ERROR) << "Badly formatted list of users"; - throw OrthancException(ErrorCode_BadFileFormat); - } - - Json::Value::Members usernames = users.getMemberNames(); - for (size_t i = 0; i < usernames.size(); i++) - { - const std::string& username = usernames[i]; - std::string password = users[username].asString(); - httpServer.RegisterUser(username.c_str(), password.c_str()); - } - } - - - std::string Configuration::InterpretStringParameterAsPath(const std::string& parameter) - { - boost::recursive_mutex::scoped_lock lock(globalMutex_); - return SystemToolbox::InterpretRelativePath(defaultDirectory_.string(), parameter); - } - - - void Configuration::GetGlobalListOfStringsParameter(std::list& target, - const std::string& key) - { - boost::recursive_mutex::scoped_lock lock(globalMutex_); - - target.clear(); - - if (!configuration_.isMember(key)) - { - return; - } - - const Json::Value& lst = configuration_[key]; - - if (lst.type() != Json::arrayValue) - { - LOG(ERROR) << "Badly formatted list of strings"; - throw OrthancException(ErrorCode_BadFileFormat); - } - - for (Json::Value::ArrayIndex i = 0; i < lst.size(); i++) - { - target.push_back(lst[i].asString()); - } - } - - - bool Configuration::IsSameAETitle(const std::string& aet1, - const std::string& aet2) - { - if (GetGlobalBoolParameter("StrictAetComparison", false)) - { - // Case-sensitive matching - return aet1 == aet2; - } - else - { - // Case-insensitive matching (default) - std::string tmp1, tmp2; - Toolbox::ToLowerCase(tmp1, aet1); - Toolbox::ToLowerCase(tmp2, aet2); - return tmp1 == tmp2; - } - } - - - bool Configuration::LookupDicomModalityUsingAETitle(RemoteModalityParameters& modality, - const std::string& aet) - { - std::set modalities; - GetListOfDicomModalities(modalities); - - for (std::set::const_iterator - it = modalities.begin(); it != modalities.end(); ++it) - { - try - { - GetDicomModalityUsingSymbolicName(modality, *it); - - if (IsSameAETitle(aet, modality.GetApplicationEntityTitle())) - { - return true; - } - } - catch (OrthancException&) - { - } - } - - return false; - } - - - bool Configuration::IsKnownAETitle(const std::string& aet, - const std::string& ip) - { - RemoteModalityParameters modality; - - if (!LookupDicomModalityUsingAETitle(modality, aet)) - { - LOG(WARNING) << "Modality \"" << aet - << "\" is not listed in the \"DicomModalities\" configuration option"; - return false; - } - else if (!Configuration::GetGlobalBoolParameter("DicomCheckModalityHost", false) || - ip == modality.GetHost()) - { - return true; - } - else - { - LOG(WARNING) << "Forbidding access from AET \"" << aet - << "\" given its hostname (" << ip << ") does not match " - << "the \"DicomModalities\" configuration option (" - << modality.GetHost() << " was expected)"; - return false; - } - } - - - RemoteModalityParameters Configuration::GetModalityUsingSymbolicName(const std::string& name) - { - RemoteModalityParameters modality; - GetDicomModalityUsingSymbolicName(modality, name); - - return modality; - } - - - RemoteModalityParameters Configuration::GetModalityUsingAet(const std::string& aet) - { - RemoteModalityParameters modality; - - if (LookupDicomModalityUsingAETitle(modality, aet)) - { - return modality; - } - else - { - LOG(ERROR) << "Unknown modality for AET: " << aet; - throw OrthancException(ErrorCode_InexistentItem); - } - } - - - void Configuration::UpdateModality(ServerContext& context, - const std::string& symbolicName, - const RemoteModalityParameters& modality) - { - { - boost::recursive_mutex::scoped_lock lock(globalMutex_); - - if (!configuration_.isMember("DicomModalities")) - { - configuration_["DicomModalities"] = Json::objectValue; - } - - Json::Value& modalities = configuration_["DicomModalities"]; - if (modalities.type() != Json::objectValue) - { - LOG(ERROR) << "Bad file format for modality: " << symbolicName; - throw OrthancException(ErrorCode_BadFileFormat); - } - - modalities.removeMember(symbolicName); - - Json::Value v; - modality.Serialize(v, true /* force advanced format */); - modalities[symbolicName] = v; - } - -#if ORTHANC_ENABLE_PLUGINS == 1 - if (context.HasPlugins()) - { - context.GetPlugins().SignalUpdatedModalities(); - } -#endif - } - - - void Configuration::RemoveModality(ServerContext& context, - const std::string& symbolicName) - { - { - boost::recursive_mutex::scoped_lock lock(globalMutex_); - - if (!configuration_.isMember("DicomModalities")) - { - LOG(ERROR) << "No modality with symbolic name: " << symbolicName; - throw OrthancException(ErrorCode_BadFileFormat); - } - - Json::Value& modalities = configuration_["DicomModalities"]; - if (modalities.type() != Json::objectValue) - { - LOG(ERROR) << "Bad file format for the \"DicomModalities\" configuration section"; - throw OrthancException(ErrorCode_BadFileFormat); - } - - modalities.removeMember(symbolicName.c_str()); - } - -#if ORTHANC_ENABLE_PLUGINS == 1 - if (context.HasPlugins()) - { - context.GetPlugins().SignalUpdatedModalities(); - } -#endif - } - - - void Configuration::UpdatePeer(ServerContext& context, - const std::string& symbolicName, - const WebServiceParameters& peer) - { - peer.CheckClientCertificate(); - - { - boost::recursive_mutex::scoped_lock lock(globalMutex_); - - if (!configuration_.isMember("OrthancPeers")) - { - LOG(ERROR) << "No peer with symbolic name: " << symbolicName; - configuration_["OrthancPeers"] = Json::objectValue; - } - - Json::Value& peers = configuration_["OrthancPeers"]; - if (peers.type() != Json::objectValue) - { - LOG(ERROR) << "Bad file format for the \"OrthancPeers\" configuration section"; - throw OrthancException(ErrorCode_BadFileFormat); - } - - peers.removeMember(symbolicName); - - Json::Value v; - peer.Serialize(v, - false /* use simple format if possible */, - true /* include passwords */); - peers[symbolicName] = v; - } - -#if ORTHANC_ENABLE_PLUGINS == 1 - if (context.HasPlugins()) - { - context.GetPlugins().SignalUpdatedPeers(); - } -#endif - } - - - void Configuration::RemovePeer(ServerContext& context, - const std::string& symbolicName) - { - { - boost::recursive_mutex::scoped_lock lock(globalMutex_); - - if (!configuration_.isMember("OrthancPeers")) - { - LOG(ERROR) << "No peer with symbolic name: " << symbolicName; - throw OrthancException(ErrorCode_BadFileFormat); - } - - Json::Value& peers = configuration_["OrthancPeers"]; - if (peers.type() != Json::objectValue) - { - LOG(ERROR) << "Bad file format for the \"OrthancPeers\" configuration section"; - throw OrthancException(ErrorCode_BadFileFormat); - } - - peers.removeMember(symbolicName.c_str()); - } - -#if ORTHANC_ENABLE_PLUGINS == 1 - if (context.HasPlugins()) - { - context.GetPlugins().SignalUpdatedPeers(); - } -#endif - } - - - - const std::string& Configuration::GetConfigurationAbsolutePath() - { - return configurationAbsolutePath_; - } - - static IDatabaseWrapper* CreateSQLiteWrapper() { - std::string storageDirectoryStr = Configuration::GetGlobalStringParameter("StorageDirectory", "OrthancStorage"); + OrthancConfiguration::ReaderLock lock; + + std::string storageDirectoryStr = + lock.GetConfiguration().GetStringParameter("StorageDirectory", "OrthancStorage"); // Open the database - boost::filesystem::path indexDirectory = Configuration::InterpretStringParameterAsPath( - Configuration::GetGlobalStringParameter("IndexDirectory", storageDirectoryStr)); + boost::filesystem::path indexDirectory = lock.GetConfiguration().InterpretStringParameterAsPath( + lock.GetConfiguration().GetStringParameter("IndexDirectory", storageDirectoryStr)); LOG(WARNING) << "SQLite index directory: " << indexDirectory; @@ -1094,12 +364,17 @@ static IStorageArea* CreateFilesystemStorage() { - std::string storageDirectoryStr = Configuration::GetGlobalStringParameter("StorageDirectory", "OrthancStorage"); + OrthancConfiguration::ReaderLock lock; - boost::filesystem::path storageDirectory = Configuration::InterpretStringParameterAsPath(storageDirectoryStr); + std::string storageDirectoryStr = + lock.GetConfiguration().GetStringParameter("StorageDirectory", "OrthancStorage"); + + boost::filesystem::path storageDirectory = + lock.GetConfiguration().InterpretStringParameterAsPath(storageDirectoryStr); + LOG(WARNING) << "Storage directory: " << storageDirectory; - if (Configuration::GetGlobalBoolParameter("StoreDicom", true)) + if (lock.GetConfiguration().GetBooleanParameter("StoreDicom", true)) { return new FilesystemStorage(storageDirectory.string()); } @@ -1111,66 +386,14 @@ } - IDatabaseWrapper* Configuration::CreateDatabaseWrapper() + IDatabaseWrapper* CreateDatabaseWrapper() { return CreateSQLiteWrapper(); } - IStorageArea* Configuration::CreateStorageArea() + IStorageArea* CreateStorageArea() { return CreateFilesystemStorage(); } - - - void Configuration::GetConfiguration(Json::Value& result) - { - boost::recursive_mutex::scoped_lock lock(globalMutex_); - result = configuration_; - } - - - void Configuration::FormatConfiguration(std::string& result) - { - Json::Value config; - GetConfiguration(config); - - Json::StyledWriter w; - result = w.write(config); - } - - - const FontRegistry& Configuration::GetFontRegistry() - { - return fontRegistry_; - } - - - void Configuration::SetDefaultEncoding(Encoding encoding) - { - SetDefaultDicomEncoding(encoding); - - { - // Propagate the encoding to the configuration file that is - // stored in memory - boost::recursive_mutex::scoped_lock lock(globalMutex_); - configuration_["DefaultEncoding"] = EnumerationToString(encoding); - } - } - - - bool Configuration::HasConfigurationChanged() - { - Json::Value starting; - GetConfiguration(starting); - - Json::Value current; - ReadConfiguration(current, configurationFileArg_); - - Json::FastWriter writer; - std::string a = writer.write(starting); - std::string b = writer.write(current); - - return a != b; - } } diff -r e9613a09cf9e -r 9f6716008066 OrthancServer/OrthancInitialization.h --- a/OrthancServer/OrthancInitialization.h Thu Nov 29 15:26:31 2018 +0100 +++ b/OrthancServer/OrthancInitialization.h Thu Nov 29 15:26:47 2018 +0100 @@ -33,105 +33,16 @@ #pragma once -#include -#include -#include -#include - #include "../Core/FileStorage/IStorageArea.h" -#include "../Core/HttpServer/MongooseServer.h" -#include "../Core/Images/FontRegistry.h" -#include "../Core/WebServiceParameters.h" -#include "../Core/DicomNetworking/RemoteModalityParameters.h" - #include "IDatabaseWrapper.h" -#include "ServerEnumerations.h" - namespace Orthanc { - class ServerContext; - void OrthancInitialize(const char* configurationFile = NULL); void OrthancFinalize(); - class Configuration - { - private: - Configuration(); // Forbidden, this is a static class - - public: - static std::string GetGlobalStringParameter(const std::string& parameter, - const std::string& defaultValue); - - static int GetGlobalIntegerParameter(const std::string& parameter, - int defaultValue); - - static unsigned int GetGlobalUnsignedIntegerParameter(const std::string& parameter, - unsigned int defaultValue); - - static bool GetGlobalBoolParameter(const std::string& parameter, - bool defaultValue); - - static void GetDicomModalityUsingSymbolicName(RemoteModalityParameters& modality, - const std::string& name); - - static bool LookupDicomModalityUsingAETitle(RemoteModalityParameters& modality, - const std::string& aet); - - static bool GetOrthancPeer(WebServiceParameters& peer, - const std::string& name); - - static void GetListOfDicomModalities(std::set& target); - - static void GetListOfOrthancPeers(std::set& target); - - static void SetupRegisteredUsers(MongooseServer& httpServer); - - static std::string InterpretStringParameterAsPath(const std::string& parameter); - - static void GetGlobalListOfStringsParameter(std::list& target, - const std::string& key); + IDatabaseWrapper* CreateDatabaseWrapper(); - static bool IsKnownAETitle(const std::string& aet, - const std::string& ip); - - static bool IsSameAETitle(const std::string& aet1, - const std::string& aet2); - - static RemoteModalityParameters GetModalityUsingSymbolicName(const std::string& name); - - static RemoteModalityParameters GetModalityUsingAet(const std::string& aet); - - static void UpdateModality(ServerContext& context, - const std::string& symbolicName, - const RemoteModalityParameters& modality); - - static void RemoveModality(ServerContext& context, - const std::string& symbolicName); - - static void UpdatePeer(ServerContext& context, - const std::string& symbolicName, - const WebServiceParameters& peer); - - static void RemovePeer(ServerContext& context, - const std::string& symbolicName); - - static const std::string& GetConfigurationAbsolutePath(); - - static IDatabaseWrapper* CreateDatabaseWrapper(); - - static IStorageArea* CreateStorageArea(); - - static void GetConfiguration(Json::Value& result); - - static void FormatConfiguration(std::string& result); - - static const FontRegistry& GetFontRegistry(); - - static void SetDefaultEncoding(Encoding encoding); - - static bool HasConfigurationChanged(); - }; + IStorageArea* CreateStorageArea(); } diff -r e9613a09cf9e -r 9f6716008066 OrthancServer/OrthancMoveRequestHandler.cpp --- a/OrthancServer/OrthancMoveRequestHandler.cpp Thu Nov 29 15:26:31 2018 +0100 +++ b/OrthancServer/OrthancMoveRequestHandler.cpp Thu Nov 29 15:26:47 2018 +0100 @@ -34,7 +34,7 @@ #include "PrecompiledHeadersServer.h" #include "OrthancMoveRequestHandler.h" -#include "OrthancInitialization.h" +#include "OrthancConfiguration.h" #include "../../Core/DicomParsing/FromDcmtkBridge.h" #include "../Core/DicomFormat/DicomArray.h" #include "../Core/Logging.h" @@ -81,7 +81,10 @@ instances_.push_back(*it); } - remote_ = Configuration::GetModalityUsingAet(targetAet); + { + OrthancConfiguration::ReaderLock lock; + remote_ = lock.GetConfiguration().GetModalityUsingAet(targetAet); + } } virtual unsigned int GetSubOperationCount() const @@ -135,7 +138,11 @@ job_->SetDescription("C-MOVE"); job_->SetPermissive(true); job_->SetLocalAet(context.GetDefaultLocalApplicationEntityTitle()); - job_->SetRemoteModality(Configuration::GetModalityUsingAet(targetAet)); + + { + OrthancConfiguration::ReaderLock lock; + job_->SetRemoteModality(lock.GetConfiguration().GetModalityUsingAet(targetAet)); + } if (originatorId != 0) { @@ -238,7 +245,12 @@ const std::string& originatorAet, uint16_t originatorId) { - bool synchronous = Configuration::GetGlobalBoolParameter("SynchronousCMove", true); + bool synchronous; + + { + OrthancConfiguration::ReaderLock lock; + synchronous = lock.GetConfiguration().GetBooleanParameter("SynchronousCMove", true); + } if (synchronous) { diff -r e9613a09cf9e -r 9f6716008066 OrthancServer/OrthancRestApi/OrthancRestModalities.cpp --- a/OrthancServer/OrthancRestApi/OrthancRestModalities.cpp Thu Nov 29 15:26:31 2018 +0100 +++ b/OrthancServer/OrthancRestApi/OrthancRestModalities.cpp Thu Nov 29 15:26:47 2018 +0100 @@ -37,7 +37,7 @@ #include "../../Core/DicomParsing/FromDcmtkBridge.h" #include "../../Core/Logging.h" #include "../../Core/SerializationToolbox.h" -#include "../OrthancInitialization.h" +#include "../OrthancConfiguration.h" #include "../QueryRetrieveHandler.h" #include "../ServerJobs/DicomModalityStoreJob.h" #include "../ServerJobs/DicomMoveScuJob.h" @@ -47,6 +47,13 @@ namespace Orthanc { + static RemoteModalityParameters MyGetModalityUsingSymbolicName(const std::string& name) + { + OrthancConfiguration::ReaderLock lock; + return lock.GetConfiguration().GetModalityUsingSymbolicName(name); + } + + /*************************************************************************** * DICOM C-Echo SCU ***************************************************************************/ @@ -57,7 +64,7 @@ const std::string& localAet = context.GetDefaultLocalApplicationEntityTitle(); RemoteModalityParameters remote = - Configuration::GetModalityUsingSymbolicName(call.GetUriComponent("id", "")); + MyGetModalityUsingSymbolicName(call.GetUriComponent("id", "")); try { @@ -181,7 +188,7 @@ const std::string& localAet = context.GetDefaultLocalApplicationEntityTitle(); RemoteModalityParameters remote = - Configuration::GetModalityUsingSymbolicName(call.GetUriComponent("id", "")); + MyGetModalityUsingSymbolicName(call.GetUriComponent("id", "")); DicomFindAnswers answers(false); @@ -216,7 +223,7 @@ const std::string& localAet = context.GetDefaultLocalApplicationEntityTitle(); RemoteModalityParameters remote = - Configuration::GetModalityUsingSymbolicName(call.GetUriComponent("id", "")); + MyGetModalityUsingSymbolicName(call.GetUriComponent("id", "")); DicomFindAnswers answers(false); @@ -252,7 +259,7 @@ const std::string& localAet = context.GetDefaultLocalApplicationEntityTitle(); RemoteModalityParameters remote = - Configuration::GetModalityUsingSymbolicName(call.GetUriComponent("id", "")); + MyGetModalityUsingSymbolicName(call.GetUriComponent("id", "")); DicomFindAnswers answers(false); @@ -289,7 +296,7 @@ const std::string& localAet = context.GetDefaultLocalApplicationEntityTitle(); RemoteModalityParameters remote = - Configuration::GetModalityUsingSymbolicName(call.GetUriComponent("id", "")); + MyGetModalityUsingSymbolicName(call.GetUriComponent("id", "")); DicomFindAnswers answers(false); @@ -331,7 +338,7 @@ const std::string& localAet = context.GetDefaultLocalApplicationEntityTitle(); RemoteModalityParameters remote = - Configuration::GetModalityUsingSymbolicName(call.GetUriComponent("id", "")); + MyGetModalityUsingSymbolicName(call.GetUriComponent("id", "")); DicomUserConnection connection(localAet, remote); connection.Open(); @@ -696,6 +703,13 @@ } } + bool logExportedResources; + + { + OrthancConfiguration::ReaderLock lock; + logExportedResources = lock.GetConfiguration().GetBooleanParameter("LogExportedResources", false); + } + for (Json::Value::ArrayIndex i = 0; i < resources->size(); i++) { if (!(*resources) [i].isString()) @@ -709,7 +723,7 @@ return false; } - if (Configuration::GetGlobalBoolParameter("LogExportedResources", false)) + if (logExportedResources) { context.GetIndex().LogExportedResource(stripped, remote); } @@ -740,7 +754,7 @@ (request, "MoveOriginatorID", 0 /* By default, not a C-MOVE */); job->SetLocalAet(localAet); - job->SetRemoteModality(Configuration::GetModalityUsingSymbolicName(remote)); + job->SetRemoteModality(MyGetModalityUsingSymbolicName(remote)); if (moveOriginatorID != 0) { @@ -784,7 +798,7 @@ (request, "TargetAet", context.GetDefaultLocalApplicationEntityTitle()); const RemoteModalityParameters source = - Configuration::GetModalityUsingSymbolicName(call.GetUriComponent("id", "")); + MyGetModalityUsingSymbolicName(call.GetUriComponent("id", "")); DicomUserConnection connection(localAet, source); connection.Open(); @@ -815,8 +829,10 @@ static void ListPeers(RestApiGetCall& call) { + OrthancConfiguration::ReaderLock lock; + OrthancRestApi::SetOfStrings peers; - Configuration::GetListOfOrthancPeers(peers); + lock.GetConfiguration().GetListOfOrthancPeers(peers); if (call.HasArgument("expand")) { @@ -826,7 +842,7 @@ { WebServiceParameters peer; - if (Configuration::GetOrthancPeer(peer, *it)) + if (lock.GetConfiguration().GetOrthancPeer(peer, *it)) { Json::Value jsonPeer = Json::objectValue; // only return the minimum information to identify the @@ -857,8 +873,10 @@ static void ListPeerOperations(RestApiGetCall& call) { + OrthancConfiguration::ReaderLock lock; + OrthancRestApi::SetOfStrings peers; - Configuration::GetListOfOrthancPeers(peers); + lock.GetConfiguration().GetListOfOrthancPeers(peers); std::string id = call.GetUriComponent("id", ""); if (IsExistingPeer(peers, id)) @@ -878,8 +896,10 @@ if (GetInstancesToExport(request, *job, remote, call)) { + OrthancConfiguration::ReaderLock lock; + WebServiceParameters peer; - if (Configuration::GetOrthancPeer(peer, remote)) + if (lock.GetConfiguration().GetOrthancPeer(peer, remote)) { job->SetPeer(peer); OrthancRestApi::GetApi(call).SubmitCommandsJob @@ -904,8 +924,10 @@ static void ListModalities(RestApiGetCall& call) { + OrthancConfiguration::ReaderLock lock; + OrthancRestApi::SetOfStrings modalities; - Configuration::GetListOfDicomModalities(modalities); + lock.GetConfiguration().GetListOfDicomModalities(modalities); if (call.HasArgument("expand")) { @@ -913,7 +935,7 @@ for (OrthancRestApi::SetOfStrings::const_iterator it = modalities.begin(); it != modalities.end(); ++it) { - const RemoteModalityParameters& remote = Configuration::GetModalityUsingSymbolicName(*it); + const RemoteModalityParameters& remote = lock.GetConfiguration().GetModalityUsingSymbolicName(*it); Json::Value info; remote.Serialize(info, true /* force advanced format */); @@ -936,8 +958,10 @@ static void ListModalityOperations(RestApiGetCall& call) { + OrthancConfiguration::ReaderLock lock; + OrthancRestApi::SetOfStrings modalities; - Configuration::GetListOfDicomModalities(modalities); + lock.GetConfiguration().GetListOfDicomModalities(modalities); std::string id = call.GetUriComponent("id", ""); if (IsExistingModality(modalities, id)) @@ -957,7 +981,14 @@ { RemoteModalityParameters modality; modality.Unserialize(json); - Configuration::UpdateModality(context, call.GetUriComponent("id", ""), modality); + + { + OrthancConfiguration::WriterLock lock; + lock.GetConfiguration().UpdateModality(call.GetUriComponent("id", ""), modality); + } + + context.SignalUpdatedModalities(); + call.GetOutput().AnswerBuffer("", MimeType_PlainText); } } @@ -967,7 +998,13 @@ { ServerContext& context = OrthancRestApi::GetContext(call); - Configuration::RemoveModality(context, call.GetUriComponent("id", "")); + { + OrthancConfiguration::WriterLock lock; + lock.GetConfiguration().RemoveModality(call.GetUriComponent("id", "")); + } + + context.SignalUpdatedModalities(); + call.GetOutput().AnswerBuffer("", MimeType_PlainText); } @@ -982,7 +1019,14 @@ { WebServiceParameters peer; peer.Unserialize(json); - Configuration::UpdatePeer(context, call.GetUriComponent("id", ""), peer); + + { + OrthancConfiguration::WriterLock lock; + lock.GetConfiguration().UpdatePeer(call.GetUriComponent("id", ""), peer); + } + + context.SignalUpdatedPeers(); + call.GetOutput().AnswerBuffer("", MimeType_PlainText); } } @@ -992,7 +1036,13 @@ { ServerContext& context = OrthancRestApi::GetContext(call); - Configuration::RemovePeer(context, call.GetUriComponent("id", "")); + { + OrthancConfiguration::WriterLock lock; + lock.GetConfiguration().RemovePeer(call.GetUriComponent("id", "")); + } + + context.SignalUpdatedPeers(); + call.GetOutput().AnswerBuffer("", MimeType_PlainText); } @@ -1006,7 +1056,7 @@ { const std::string& localAet = context.GetDefaultLocalApplicationEntityTitle(); RemoteModalityParameters remote = - Configuration::GetModalityUsingSymbolicName(call.GetUriComponent("id", "")); + MyGetModalityUsingSymbolicName(call.GetUriComponent("id", "")); std::auto_ptr query(ParsedDicomFile::CreateFromJson(json, static_cast(0))); diff -r e9613a09cf9e -r 9f6716008066 OrthancServer/OrthancRestApi/OrthancRestResources.cpp --- a/OrthancServer/OrthancRestApi/OrthancRestResources.cpp Thu Nov 29 15:26:31 2018 +0100 +++ b/OrthancServer/OrthancRestApi/OrthancRestResources.cpp Thu Nov 29 15:26:47 2018 +0100 @@ -39,7 +39,7 @@ #include "../../Core/DicomParsing/Internals/DicomImageDecoder.h" #include "../../Core/HttpServer/HttpContentNegociation.h" #include "../../Core/Logging.h" -#include "../OrthancInitialization.h" +#include "../OrthancConfiguration.h" #include "../Search/LookupResource.h" #include "../ServerContext.h" #include "../ServerToolbox.h" @@ -987,17 +987,22 @@ { allowed = true; } - else if (Configuration::GetGlobalBoolParameter("StoreDicom", true) && - contentType == FileContentType_DicomAsJson) - { - allowed = true; - } else { - // It is forbidden to delete internal attachments, except for - // the "DICOM as JSON" summary as of Orthanc 1.2.0 (this summary - // would be automatically reconstructed on the next GET call) - allowed = false; + OrthancConfiguration::ReaderLock lock; + + if (lock.GetConfiguration().GetBooleanParameter("StoreDicom", true) && + contentType == FileContentType_DicomAsJson) + { + allowed = true; + } + else + { + // It is forbidden to delete internal attachments, except for + // the "DICOM as JSON" summary as of Orthanc 1.2.0 (this summary + // would be automatically reconstructed on the next GET call) + allowed = false; + } } if (allowed) diff -r e9613a09cf9e -r 9f6716008066 OrthancServer/OrthancRestApi/OrthancRestSystem.cpp --- a/OrthancServer/OrthancRestApi/OrthancRestSystem.cpp Thu Nov 29 15:26:31 2018 +0100 +++ b/OrthancServer/OrthancRestApi/OrthancRestSystem.cpp Thu Nov 29 15:26:47 2018 +0100 @@ -34,7 +34,7 @@ #include "../PrecompiledHeadersServer.h" #include "OrthancRestApi.h" -#include "../OrthancInitialization.h" +#include "../OrthancConfiguration.h" #include "../../Core/DicomParsing/FromDcmtkBridge.h" #include "../../Plugins/Engine/PluginsManager.h" #include "../../Plugins/Engine/OrthancPlugins.h" @@ -55,12 +55,16 @@ Json::Value result = Json::objectValue; result["ApiVersion"] = ORTHANC_API_VERSION; + result["Version"] = ORTHANC_VERSION; result["DatabaseVersion"] = OrthancRestApi::GetIndex(call).GetDatabaseVersion(); - result["DicomAet"] = Configuration::GetGlobalStringParameter("DicomAet", "ORTHANC"); - result["DicomPort"] = Configuration::GetGlobalUnsignedIntegerParameter("DicomPort", 4242); - result["HttpPort"] = Configuration::GetGlobalUnsignedIntegerParameter("HttpPort", 8042); - result["Name"] = Configuration::GetGlobalStringParameter("Name", ""); - result["Version"] = ORTHANC_VERSION; + + { + OrthancConfiguration::ReaderLock lock; + result["DicomAet"] = lock.GetConfiguration().GetStringParameter("DicomAet", "ORTHANC"); + result["DicomPort"] = lock.GetConfiguration().GetUnsignedIntegerParameter("DicomPort", 4242); + result["HttpPort"] = lock.GetConfiguration().GetUnsignedIntegerParameter("HttpPort", 8042); + result["Name"] = lock.GetConfiguration().GetStringParameter("Name", ""); + } result["StorageAreaPlugin"] = Json::nullValue; result["DatabaseBackendPlugin"] = Json::nullValue; @@ -157,7 +161,10 @@ { Encoding encoding = StringToEncoding(call.GetBodyData()); - Configuration::SetDefaultEncoding(encoding); + { + OrthancConfiguration::WriterLock lock; + lock.GetConfiguration().SetDefaultEncoding(encoding); + } call.GetOutput().AnswerBuffer(EnumerationToString(encoding), MimeType_PlainText); } diff -r e9613a09cf9e -r 9f6716008066 OrthancServer/QueryRetrieveHandler.cpp --- a/OrthancServer/QueryRetrieveHandler.cpp Thu Nov 29 15:26:31 2018 +0100 +++ b/OrthancServer/QueryRetrieveHandler.cpp Thu Nov 29 15:26:47 2018 +0100 @@ -34,7 +34,7 @@ #include "PrecompiledHeadersServer.h" #include "QueryRetrieveHandler.h" -#include "OrthancInitialization.h" +#include "OrthancConfiguration.h" #include "../Core/DicomParsing/FromDcmtkBridge.h" #include "../Core/Logging.h" @@ -119,7 +119,11 @@ { Invalidate(); modalityName_ = symbolicName; - Configuration::GetDicomModalityUsingSymbolicName(modality_, symbolicName); + + { + OrthancConfiguration::ReaderLock lock; + lock.GetConfiguration().GetDicomModalityUsingSymbolicName(modality_, symbolicName); + } } diff -r e9613a09cf9e -r 9f6716008066 OrthancServer/Search/HierarchicalMatcher.cpp --- a/OrthancServer/Search/HierarchicalMatcher.cpp Thu Nov 29 15:26:31 2018 +0100 +++ b/OrthancServer/Search/HierarchicalMatcher.cpp Thu Nov 29 15:26:47 2018 +0100 @@ -38,7 +38,7 @@ #include "../../Core/OrthancException.h" #include "../../Core/DicomParsing/FromDcmtkBridge.h" #include "../../Core/DicomParsing/ToDcmtkBridge.h" -#include "../OrthancInitialization.h" +#include "../OrthancConfiguration.h" #include @@ -46,9 +46,14 @@ { HierarchicalMatcher::HierarchicalMatcher(ParsedDicomFile& query) { - Setup(*query.GetDcmtkObject().getDataset(), - Configuration::GetGlobalBoolParameter("CaseSensitivePN", false), - query.GetEncoding()); + bool caseSensitivePN; + + { + OrthancConfiguration::ReaderLock lock; + caseSensitivePN = lock.GetConfiguration().GetBooleanParameter("CaseSensitivePN", false); + } + + Setup(*query.GetDcmtkObject().getDataset(), caseSensitivePN, query.GetEncoding()); } diff -r e9613a09cf9e -r 9f6716008066 OrthancServer/ServerContext.cpp --- a/OrthancServer/ServerContext.cpp Thu Nov 29 15:26:31 2018 +0100 +++ b/OrthancServer/ServerContext.cpp Thu Nov 29 15:26:47 2018 +0100 @@ -40,7 +40,7 @@ #include "../Core/HttpServer/HttpStreamTranscoder.h" #include "../Core/Logging.h" #include "../Plugins/Engine/OrthancPlugins.h" -#include "OrthancInitialization.h" +#include "OrthancConfiguration.h" #include "OrthancRestApi/OrthancRestApi.h" #include "Search/LookupResource.h" #include "ServerJobs/OrthancJobUnserializer.h" @@ -229,11 +229,16 @@ #endif done_(false), haveJobsChanged_(false), - isJobsEngineUnserialized_(false), - queryRetrieveArchive_(Configuration::GetGlobalUnsignedIntegerParameter("QueryRetrieveSize", 10)), - defaultLocalAet_(Configuration::GetGlobalStringParameter("DicomAet", "ORTHANC")) + isJobsEngineUnserialized_(false) { - jobsEngine_.SetWorkersCount(Configuration::GetGlobalUnsignedIntegerParameter("ConcurrentJobs", 2)); + { + OrthancConfiguration::ReaderLock lock; + queryRetrieveArchive_.reset( + new SharedArchive(lock.GetConfiguration().GetUnsignedIntegerParameter("QueryRetrieveSize", 10))); + defaultLocalAet_ = lock.GetConfiguration().GetStringParameter("DicomAet", "ORTHANC"); + jobsEngine_.SetWorkersCount(lock.GetConfiguration().GetUnsignedIntegerParameter("ConcurrentJobs", 2)); + } + jobsEngine_.SetThreadSleep(unitTesting ? 20 : 200); listeners_.push_back(ServerListener(luaListener_, "Lua")); @@ -819,4 +824,26 @@ job.AddInstance(*it); } } + + + void ServerContext::SignalUpdatedModalities() + { +#if ORTHANC_ENABLE_PLUGINS == 1 + if (HasPlugins()) + { + GetPlugins().SignalUpdatedModalities(); + } +#endif + } + + + void ServerContext::SignalUpdatedPeers() + { +#if ORTHANC_ENABLE_PLUGINS == 1 + if (HasPlugins()) + { + GetPlugins().SignalUpdatedPeers(); + } +#endif + } } diff -r e9613a09cf9e -r 9f6716008066 OrthancServer/ServerContext.h --- a/OrthancServer/ServerContext.h Thu Nov 29 15:26:31 2018 +0100 +++ b/OrthancServer/ServerContext.h Thu Nov 29 15:26:47 2018 +0100 @@ -180,7 +180,7 @@ boost::thread changeThread_; boost::thread saveJobsThread_; - SharedArchive queryRetrieveArchive_; + std::auto_ptr queryRetrieveArchive_; std::string defaultLocalAet_; OrthancHttpHandler httpHandler_; @@ -301,7 +301,7 @@ SharedArchive& GetQueryRetrieveArchive() { - return queryRetrieveArchive_; + return *queryRetrieveArchive_; } const std::string& GetDefaultLocalApplicationEntityTitle() const @@ -346,5 +346,9 @@ void AddChildInstances(SetOfInstancesJob& job, const std::string& publicId); + + void SignalUpdatedModalities(); + + void SignalUpdatedPeers(); }; } diff -r e9613a09cf9e -r 9f6716008066 OrthancServer/ServerIndex.cpp --- a/OrthancServer/ServerIndex.cpp Thu Nov 29 15:26:31 2018 +0100 +++ b/OrthancServer/ServerIndex.cpp Thu Nov 29 15:26:47 2018 +0100 @@ -40,7 +40,7 @@ #include "ServerIndexChange.h" #include "EmbeddedResources.h" -#include "OrthancInitialization.h" +#include "OrthancConfiguration.h" #include "../Core/DicomParsing/ParsedDicomFile.h" #include "ServerToolbox.h" #include "../Core/Toolbox.h" @@ -1931,7 +1931,13 @@ void ServerIndex::UnstableResourcesMonitorThread(ServerIndex* that, unsigned int threadSleep) { - int stableAge = Configuration::GetGlobalUnsignedIntegerParameter("StableAge", 60); + int stableAge; + + { + OrthancConfiguration::ReaderLock lock; + stableAge = lock.GetConfiguration().GetUnsignedIntegerParameter("StableAge", 60); + } + if (stableAge <= 0) { stableAge = 60; diff -r e9613a09cf9e -r 9f6716008066 OrthancServer/ServerJobs/LuaJobManager.cpp --- a/OrthancServer/ServerJobs/LuaJobManager.cpp Thu Nov 29 15:26:31 2018 +0100 +++ b/OrthancServer/ServerJobs/LuaJobManager.cpp Thu Nov 29 15:26:47 2018 +0100 @@ -34,7 +34,7 @@ #include "../PrecompiledHeadersServer.h" #include "LuaJobManager.h" -#include "../OrthancInitialization.h" +#include "../OrthancConfiguration.h" #include "../../Core/Logging.h" #include "../../Core/JobsEngine/Operations/LogJobOperation.h" @@ -68,7 +68,11 @@ priority_(0), trailingTimeout_(5000) { - dicomTimeout_ = Configuration::GetGlobalUnsignedIntegerParameter("DicomAssociationCloseDelay", 5); + { + OrthancConfiguration::ReaderLock lock; + dicomTimeout_ = lock.GetConfiguration().GetUnsignedIntegerParameter("DicomAssociationCloseDelay", 5); + } + LOG(INFO) << "Lua: DICOM associations will be closed after " << dicomTimeout_ << " seconds of inactivity"; } diff -r e9613a09cf9e -r 9f6716008066 OrthancServer/main.cpp --- a/OrthancServer/main.cpp Thu Nov 29 15:26:31 2018 +0100 +++ b/OrthancServer/main.cpp Thu Nov 29 15:26:47 2018 +0100 @@ -36,19 +36,21 @@ #include -#include "../Core/Logging.h" -#include "../Core/HttpServer/EmbeddedResourceHttpHandler.h" -#include "../Core/HttpServer/FilesystemHttpHandler.h" -#include "../Core/Lua/LuaFunctionCall.h" #include "../Core/DicomFormat/DicomArray.h" #include "../Core/DicomNetworking/DicomServer.h" +#include "../Core/DicomParsing/FromDcmtkBridge.h" +#include "../Core/HttpServer/EmbeddedResourceHttpHandler.h" +#include "../Core/HttpServer/FilesystemHttpHandler.h" +#include "../Core/HttpServer/MongooseServer.h" +#include "../Core/Logging.h" +#include "../Core/Lua/LuaFunctionCall.h" +#include "../Plugins/Engine/OrthancPlugins.h" +#include "OrthancConfiguration.h" +#include "OrthancFindRequestHandler.h" #include "OrthancInitialization.h" -#include "ServerContext.h" -#include "OrthancFindRequestHandler.h" #include "OrthancMoveRequestHandler.h" +#include "ServerContext.h" #include "ServerToolbox.h" -#include "../Plugins/Engine/OrthancPlugins.h" -#include "../Core/DicomParsing/FromDcmtkBridge.h" using namespace Orthanc; @@ -89,19 +91,21 @@ -class ModalitiesFromConfiguration : public Orthanc::DicomServer::IRemoteModalities +class ModalitiesFromConfiguration : public DicomServer::IRemoteModalities { public: virtual bool IsSameAETitle(const std::string& aet1, const std::string& aet2) { - return Orthanc::Configuration::IsSameAETitle(aet1, aet2); + OrthancConfiguration::ReaderLock lock; + return lock.GetConfiguration().IsSameAETitle(aet1, aet2); } virtual bool LookupAETitle(RemoteModalityParameters& modality, const std::string& aet) { - return Orthanc::Configuration::LookupDicomModalityUsingAETitle(modality, aet); + OrthancConfiguration::ReaderLock lock; + return lock.GetConfiguration().LookupDicomModalityUsingAETitle(modality, aet); } }; @@ -128,8 +132,11 @@ { std::auto_ptr result(new OrthancFindRequestHandler(context_)); - result->SetMaxResults(Configuration::GetGlobalUnsignedIntegerParameter("LimitFindResults", 0)); - result->SetMaxInstances(Configuration::GetGlobalUnsignedIntegerParameter("LimitFindInstances", 0)); + { + OrthancConfiguration::ReaderLock lock; + result->SetMaxResults(lock.GetConfiguration().GetUnsignedIntegerParameter("LimitFindResults", 0)); + result->SetMaxInstances(lock.GetConfiguration().GetUnsignedIntegerParameter("LimitFindInstances", 0)); + } if (result->GetMaxResults() == 0) { @@ -176,8 +183,9 @@ OrthancApplicationEntityFilter(ServerContext& context) : context_(context) { - alwaysAllowEcho_ = Configuration::GetGlobalBoolParameter("DicomAlwaysAllowEcho", true); - alwaysAllowStore_ = Configuration::GetGlobalBoolParameter("DicomAlwaysAllowStore", true); + OrthancConfiguration::ReaderLock lock; + alwaysAllowEcho_ = lock.GetConfiguration().GetBooleanParameter("DicomAlwaysAllowEcho", true); + alwaysAllowStore_ = lock.GetConfiguration().GetBooleanParameter("DicomAlwaysAllowStore", true); } virtual bool IsAllowedConnection(const std::string& remoteIp, @@ -187,9 +195,16 @@ LOG(INFO) << "Incoming connection from AET " << remoteAet << " on IP " << remoteIp << ", calling AET " << calledAet; - return (alwaysAllowEcho_ || - alwaysAllowStore_ || - Configuration::IsKnownAETitle(remoteAet, remoteIp)); + if (alwaysAllowEcho_ || + alwaysAllowStore_) + { + return true; + } + else + { + OrthancConfiguration::ReaderLock lock; + return lock.GetConfiguration().IsKnownAETitle(remoteAet, remoteIp); + } } virtual bool IsAllowedRequest(const std::string& remoteIp, @@ -197,7 +212,7 @@ const std::string& calledAet, DicomRequestType type) { - LOG(INFO) << "Incoming " << Orthanc::EnumerationToString(type) << " request from AET " + LOG(INFO) << "Incoming " << EnumerationToString(type) << " request from AET " << remoteAet << " on IP " << remoteIp << ", calling AET " << calledAet; if (type == DicomRequestType_Echo && @@ -214,9 +229,10 @@ } else { + OrthancConfiguration::ReaderLock lock; + RemoteModalityParameters modality; - - if (Configuration::LookupDicomModalityUsingAETitle(modality, remoteAet)) + if (lock.GetConfiguration().LookupDicomModalityUsingAETitle(modality, remoteAet)) { return modality.IsRequestAllowed(type); } @@ -283,7 +299,10 @@ } } - return Configuration::GetGlobalBoolParameter(configuration, true); + { + OrthancConfiguration::ReaderLock lock; + return lock.GetConfiguration().GetBooleanParameter(configuration, true); + } } @@ -308,7 +327,10 @@ } } - return Configuration::GetGlobalBoolParameter(configuration, false); + { + OrthancConfiguration::ReaderLock lock; + return lock.GetConfiguration().GetBooleanParameter(configuration, false); + } } }; @@ -652,11 +674,22 @@ static void LoadPlugins(OrthancPlugins& plugins) { std::list path; - Configuration::GetGlobalListOfStringsParameter(path, "Plugins"); + + { + OrthancConfiguration::ReaderLock lock; + lock.GetConfiguration().GetListOfStringsParameter(path, "Plugins"); + } + for (std::list::const_iterator it = path.begin(); it != path.end(); ++it) { - std::string path = Configuration::InterpretStringParameterAsPath(*it); + std::string path; + + { + OrthancConfiguration::ReaderLock lock; + path = lock.GetConfiguration().InterpretStringParameterAsPath(*it); + } + LOG(WARNING) << "Loading plugin(s) from: " << path; plugins.GetManager().RegisterPlugin(path); } @@ -693,7 +726,8 @@ { // Handling of SIGHUP - if (Configuration::HasConfigurationChanged()) + OrthancConfiguration::ReaderLock lock; + if (lock.GetConfiguration().HasConfigurationChanged()) { LOG(WARNING) << "A SIGHUP signal has been received, resetting Orthanc"; Logging::Flush(); @@ -740,58 +774,74 @@ OrthancRestApi& restApi, OrthancPlugins* plugins) { - if (!Configuration::GetGlobalBoolParameter("HttpServerEnabled", true)) + bool httpServerEnabled; + + { + OrthancConfiguration::ReaderLock lock; + httpServerEnabled = lock.GetConfiguration().GetBooleanParameter("HttpServerEnabled", true); + } + + if (!httpServerEnabled) { LOG(WARNING) << "The HTTP server is disabled"; return WaitForExit(context, restApi); } - - MyHttpExceptionFormatter exceptionFormatter(Configuration::GetGlobalBoolParameter("HttpDescribeErrors", true), plugins); - - - // HTTP server - MyIncomingHttpRequestFilter httpFilter(context, plugins); - MongooseServer httpServer; - //httpServer.SetThreadsCount(50); - httpServer.SetPortNumber(Configuration::GetGlobalUnsignedIntegerParameter("HttpPort", 8042)); - httpServer.SetRemoteAccessAllowed(Configuration::GetGlobalBoolParameter("RemoteAccessAllowed", false)); - httpServer.SetKeepAliveEnabled(Configuration::GetGlobalBoolParameter("KeepAlive", false)); - httpServer.SetHttpCompressionEnabled(Configuration::GetGlobalBoolParameter("HttpCompressionEnabled", true)); - httpServer.SetIncomingHttpRequestFilter(httpFilter); - httpServer.SetHttpExceptionFormatter(exceptionFormatter); - - httpServer.SetAuthenticationEnabled(Configuration::GetGlobalBoolParameter("AuthenticationEnabled", false)); - Configuration::SetupRegisteredUsers(httpServer); - - if (Configuration::GetGlobalBoolParameter("SslEnabled", false)) - { - std::string certificate = Configuration::InterpretStringParameterAsPath( - Configuration::GetGlobalStringParameter("SslCertificate", "certificate.pem")); - httpServer.SetSslEnabled(true); - httpServer.SetSslCertificate(certificate.c_str()); - } else { - httpServer.SetSslEnabled(false); - } + MyIncomingHttpRequestFilter httpFilter(context, plugins); + MongooseServer httpServer; + bool httpDescribeErrors; - httpServer.Register(context.GetHttpHandler()); + { + OrthancConfiguration::ReaderLock lock; + + httpDescribeErrors = lock.GetConfiguration().GetBooleanParameter("HttpDescribeErrors", true); + + // HTTP server + //httpServer.SetThreadsCount(50); + httpServer.SetPortNumber(lock.GetConfiguration().GetUnsignedIntegerParameter("HttpPort", 8042)); + httpServer.SetRemoteAccessAllowed(lock.GetConfiguration().GetBooleanParameter("RemoteAccessAllowed", false)); + httpServer.SetKeepAliveEnabled(lock.GetConfiguration().GetBooleanParameter("KeepAlive", false)); + httpServer.SetHttpCompressionEnabled(lock.GetConfiguration().GetBooleanParameter("HttpCompressionEnabled", true)); + httpServer.SetAuthenticationEnabled(lock.GetConfiguration().GetBooleanParameter("AuthenticationEnabled", false)); + + lock.GetConfiguration().SetupRegisteredUsers(httpServer); - if (httpServer.GetPortNumber() < 1024) - { - LOG(WARNING) << "The HTTP port is privileged (" - << httpServer.GetPortNumber() << " is below 1024), " - << "make sure you run Orthanc as root/administrator"; - } + if (lock.GetConfiguration().GetBooleanParameter("SslEnabled", false)) + { + std::string certificate = lock.GetConfiguration().InterpretStringParameterAsPath( + lock.GetConfiguration().GetStringParameter("SslCertificate", "certificate.pem")); + httpServer.SetSslEnabled(true); + httpServer.SetSslCertificate(certificate.c_str()); + } + else + { + httpServer.SetSslEnabled(false); + } + } - httpServer.Start(); - - bool restart = WaitForExit(context, restApi); + MyHttpExceptionFormatter exceptionFormatter(httpDescribeErrors, plugins); + + httpServer.SetIncomingHttpRequestFilter(httpFilter); + httpServer.SetHttpExceptionFormatter(exceptionFormatter); + httpServer.Register(context.GetHttpHandler()); - httpServer.Stop(); - LOG(WARNING) << " HTTP server has stopped"; + if (httpServer.GetPortNumber() < 1024) + { + LOG(WARNING) << "The HTTP port is privileged (" + << httpServer.GetPortNumber() << " is below 1024), " + << "make sure you run Orthanc as root/administrator"; + } - return restart; + httpServer.Start(); + + bool restart = WaitForExit(context, restApi); + + httpServer.Stop(); + LOG(WARNING) << " HTTP server has stopped"; + + return restart; + } } @@ -799,84 +849,96 @@ OrthancRestApi& restApi, OrthancPlugins* plugins) { - if (!Configuration::GetGlobalBoolParameter("DicomServerEnabled", true)) + bool dicomServerEnabled; + + { + OrthancConfiguration::ReaderLock lock; + dicomServerEnabled = lock.GetConfiguration().GetBooleanParameter("DicomServerEnabled", true); + } + + if (!dicomServerEnabled) { LOG(WARNING) << "The DICOM server is disabled"; return StartHttpServer(context, restApi, plugins); } - - MyDicomServerFactory serverFactory(context); - OrthancApplicationEntityFilter dicomFilter(context); - ModalitiesFromConfiguration modalities; + else + { + MyDicomServerFactory serverFactory(context); + OrthancApplicationEntityFilter dicomFilter(context); + ModalitiesFromConfiguration modalities; - // Setup the DICOM server - DicomServer dicomServer; - dicomServer.SetRemoteModalities(modalities); - dicomServer.SetCalledApplicationEntityTitleCheck(Configuration::GetGlobalBoolParameter("DicomCheckCalledAet", false)); - dicomServer.SetStoreRequestHandlerFactory(serverFactory); - dicomServer.SetMoveRequestHandlerFactory(serverFactory); - dicomServer.SetFindRequestHandlerFactory(serverFactory); - dicomServer.SetAssociationTimeout(Configuration::GetGlobalUnsignedIntegerParameter("DicomScpTimeout", 30)); + // Setup the DICOM server + DicomServer dicomServer; + dicomServer.SetRemoteModalities(modalities); + dicomServer.SetStoreRequestHandlerFactory(serverFactory); + dicomServer.SetMoveRequestHandlerFactory(serverFactory); + dicomServer.SetFindRequestHandlerFactory(serverFactory); + { + OrthancConfiguration::ReaderLock lock; + dicomServer.SetCalledApplicationEntityTitleCheck(lock.GetConfiguration().GetBooleanParameter("DicomCheckCalledAet", false)); + dicomServer.SetAssociationTimeout(lock.GetConfiguration().GetUnsignedIntegerParameter("DicomScpTimeout", 30)); + dicomServer.SetPortNumber(lock.GetConfiguration().GetUnsignedIntegerParameter("DicomPort", 4242)); + dicomServer.SetApplicationEntityTitle(lock.GetConfiguration().GetStringParameter("DicomAet", "ORTHANC")); + } #if ORTHANC_ENABLE_PLUGINS == 1 - if (plugins != NULL) - { - if (plugins->HasWorklistHandler()) - { - dicomServer.SetWorklistRequestHandlerFactory(*plugins); - } - - if (plugins->HasFindHandler()) + if (plugins != NULL) { - dicomServer.SetFindRequestHandlerFactory(*plugins); - } + if (plugins->HasWorklistHandler()) + { + dicomServer.SetWorklistRequestHandlerFactory(*plugins); + } - if (plugins->HasMoveHandler()) - { - dicomServer.SetMoveRequestHandlerFactory(*plugins); + if (plugins->HasFindHandler()) + { + dicomServer.SetFindRequestHandlerFactory(*plugins); + } + + if (plugins->HasMoveHandler()) + { + dicomServer.SetMoveRequestHandlerFactory(*plugins); + } } - } #endif - dicomServer.SetPortNumber(Configuration::GetGlobalUnsignedIntegerParameter("DicomPort", 4242)); - dicomServer.SetApplicationEntityTitle(Configuration::GetGlobalStringParameter("DicomAet", "ORTHANC")); - dicomServer.SetApplicationEntityFilter(dicomFilter); + dicomServer.SetApplicationEntityFilter(dicomFilter); - if (dicomServer.GetPortNumber() < 1024) - { - LOG(WARNING) << "The DICOM port is privileged (" - << dicomServer.GetPortNumber() << " is below 1024), " - << "make sure you run Orthanc as root/administrator"; - } + if (dicomServer.GetPortNumber() < 1024) + { + LOG(WARNING) << "The DICOM port is privileged (" + << dicomServer.GetPortNumber() << " is below 1024), " + << "make sure you run Orthanc as root/administrator"; + } - dicomServer.Start(); - LOG(WARNING) << "DICOM server listening with AET " << dicomServer.GetApplicationEntityTitle() - << " on port: " << dicomServer.GetPortNumber(); + dicomServer.Start(); + LOG(WARNING) << "DICOM server listening with AET " << dicomServer.GetApplicationEntityTitle() + << " on port: " << dicomServer.GetPortNumber(); - bool restart = false; - ErrorCode error = ErrorCode_Success; + bool restart = false; + ErrorCode error = ErrorCode_Success; - try - { - restart = StartHttpServer(context, restApi, plugins); - } - catch (OrthancException& e) - { - error = e.GetErrorCode(); - } + try + { + restart = StartHttpServer(context, restApi, plugins); + } + catch (OrthancException& e) + { + error = e.GetErrorCode(); + } - dicomServer.Stop(); - LOG(WARNING) << " DICOM server has stopped"; + dicomServer.Stop(); + LOG(WARNING) << " DICOM server has stopped"; - serverFactory.Done(); + serverFactory.Done(); - if (error != ErrorCode_Success) - { - throw OrthancException(error); + if (error != ErrorCode_Success) + { + throw OrthancException(error); + } + + return restart; } - - return restart; } @@ -969,86 +1031,108 @@ } + +namespace +{ + class ServerContextConfigurator : public boost::noncopyable + { + private: + ServerContext& context_; + OrthancPlugins* plugins_; + + public: + ServerContextConfigurator(ServerContext& context, + OrthancPlugins* plugins) : + context_(context), + plugins_(plugins) + { + { + OrthancConfiguration::WriterLock lock; + lock.GetConfiguration().SetServerIndex(context.GetIndex()); + } + +#if ORTHANC_ENABLE_PLUGINS == 1 + if (plugins_ != NULL) + { + plugins_->SetServerContext(context_); + context_.SetPlugins(*plugins_); + } +#endif + } + + ~ServerContextConfigurator() + { + { + OrthancConfiguration::WriterLock lock; + lock.GetConfiguration().ResetServerIndex(); + } + +#if ORTHANC_ENABLE_PLUGINS == 1 + if (plugins_ != NULL) + { + plugins_->ResetServerContext(); + context_.ResetPlugins(); + } +#endif + } + }; +} + + static bool ConfigureServerContext(IDatabaseWrapper& database, IStorageArea& storageArea, OrthancPlugins *plugins, bool loadJobsFromDatabase) { - // These configuration options must be set before creating the - // ServerContext, otherwise the possible Lua scripts will not be - // able to properly issue HTTP/HTTPS queries - HttpClient::ConfigureSsl(Configuration::GetGlobalBoolParameter("HttpsVerifyPeers", true), - Configuration::InterpretStringParameterAsPath - (Configuration::GetGlobalStringParameter("HttpsCACertificates", ""))); - HttpClient::SetDefaultVerbose(Configuration::GetGlobalBoolParameter("HttpVerbose", false)); - HttpClient::SetDefaultTimeout(Configuration::GetGlobalUnsignedIntegerParameter("HttpTimeout", 0)); - HttpClient::SetDefaultProxy(Configuration::GetGlobalStringParameter("HttpProxy", "")); + ServerContext context(database, storageArea, false /* not running unit tests */); + + { + OrthancConfiguration::ReaderLock lock; - DicomUserConnection::SetDefaultTimeout(Configuration::GetGlobalUnsignedIntegerParameter("DicomScuTimeout", 10)); + // These configuration options must be set before creating the + // ServerContext, otherwise the possible Lua scripts will not be + // able to properly issue HTTP/HTTPS queries + HttpClient::ConfigureSsl(lock.GetConfiguration().GetBooleanParameter("HttpsVerifyPeers", true), + lock.GetConfiguration().InterpretStringParameterAsPath + (lock.GetConfiguration().GetStringParameter("HttpsCACertificates", ""))); + HttpClient::SetDefaultVerbose(lock.GetConfiguration().GetBooleanParameter("HttpVerbose", false)); + HttpClient::SetDefaultTimeout(lock.GetConfiguration().GetUnsignedIntegerParameter("HttpTimeout", 0)); + HttpClient::SetDefaultProxy(lock.GetConfiguration().GetStringParameter("HttpProxy", "")); + + DicomUserConnection::SetDefaultTimeout(lock.GetConfiguration().GetUnsignedIntegerParameter("DicomScuTimeout", 10)); + context.SetCompressionEnabled(lock.GetConfiguration().GetBooleanParameter("StorageCompression", false)); + context.SetStoreMD5ForAttachments(lock.GetConfiguration().GetBooleanParameter("StoreMD5ForAttachments", true)); - ServerContext context(database, storageArea, false /* not running unit tests */); - context.SetCompressionEnabled(Configuration::GetGlobalBoolParameter("StorageCompression", false)); - context.SetStoreMD5ForAttachments(Configuration::GetGlobalBoolParameter("StoreMD5ForAttachments", true)); + // New option in Orthanc 1.4.2 + context.GetIndex().SetOverwriteInstances(lock.GetConfiguration().GetBooleanParameter("OverwriteInstances", false)); - // New option in Orthanc 1.4.2 - context.GetIndex().SetOverwriteInstances(Configuration::GetGlobalBoolParameter("OverwriteInstances", false)); + try + { + context.GetIndex().SetMaximumPatientCount(lock.GetConfiguration().GetUnsignedIntegerParameter("MaximumPatientCount", 0)); + } + catch (...) + { + context.GetIndex().SetMaximumPatientCount(0); + } - try - { - context.GetIndex().SetMaximumPatientCount(Configuration::GetGlobalUnsignedIntegerParameter("MaximumPatientCount", 0)); - } - catch (...) - { - context.GetIndex().SetMaximumPatientCount(0); - } + try + { + uint64_t size = lock.GetConfiguration().GetUnsignedIntegerParameter("MaximumStorageSize", 0); + context.GetIndex().SetMaximumStorageSize(size * 1024 * 1024); + } + catch (...) + { + context.GetIndex().SetMaximumStorageSize(0); + } - try - { - uint64_t size = Configuration::GetGlobalUnsignedIntegerParameter("MaximumStorageSize", 0); - context.GetIndex().SetMaximumStorageSize(size * 1024 * 1024); - } - catch (...) - { - context.GetIndex().SetMaximumStorageSize(0); + context.GetJobsEngine().GetRegistry().SetMaxCompletedJobs + (lock.GetConfiguration().GetUnsignedIntegerParameter("JobsHistorySize", 10)); } - context.GetJobsEngine().GetRegistry().SetMaxCompletedJobs - (Configuration::GetGlobalUnsignedIntegerParameter("JobsHistorySize", 10)); - -#if ORTHANC_ENABLE_PLUGINS == 1 - if (plugins) { - plugins->SetServerContext(context); - context.SetPlugins(*plugins); - } -#endif - - bool restart = false; - ErrorCode error = ErrorCode_Success; - - try - { - restart = ConfigureHttpHandler(context, plugins, loadJobsFromDatabase); + ServerContextConfigurator configurator(context, plugins); + return ConfigureHttpHandler(context, plugins, loadJobsFromDatabase); } - catch (OrthancException& e) - { - error = e.GetErrorCode(); - } - -#if ORTHANC_ENABLE_PLUGINS == 1 - if (plugins) - { - plugins->ResetServerContext(); - context.ResetPlugins(); - } -#endif - - if (error != ErrorCode_Success) - { - throw OrthancException(error); - } - - return restart; } @@ -1105,7 +1189,7 @@ } else { - databasePtr.reset(Configuration::CreateDatabaseWrapper()); + databasePtr.reset(CreateDatabaseWrapper()); database = databasePtr.get(); } @@ -1116,7 +1200,7 @@ } else { - storage.reset(Configuration::CreateStorageArea()); + storage.reset(CreateStorageArea()); } assert(database != NULL); @@ -1127,8 +1211,8 @@ #elif ORTHANC_ENABLE_PLUGINS == 0 // The plugins are disabled - databasePtr.reset(Configuration::CreateDatabaseWrapper()); - storage.reset(Configuration::CreateStorageArea()); + databasePtr.reset(lock.GetConfiguration().CreateDatabaseWrapper()); + storage.reset(lock.GetConfiguration().CreateStorageArea()); return ConfigureDatabase(*databasePtr, *storage, NULL, upgradeDatabase, loadJobsFromDatabase); diff -r e9613a09cf9e -r 9f6716008066 Plugins/Engine/OrthancPlugins.cpp --- a/Plugins/Engine/OrthancPlugins.cpp Thu Nov 29 15:26:31 2018 +0100 +++ b/Plugins/Engine/OrthancPlugins.cpp Thu Nov 29 15:26:47 2018 +0100 @@ -48,7 +48,7 @@ #include "../../Core/Toolbox.h" #include "../../Core/DicomParsing/FromDcmtkBridge.h" #include "../../Core/DicomParsing/ToDcmtkBridge.h" -#include "../../OrthancServer/OrthancInitialization.h" +#include "../../OrthancServer/OrthancConfiguration.h" #include "../../OrthancServer/ServerContext.h" #include "../../OrthancServer/ServerToolbox.h" #include "../../OrthancServer/Search/HierarchicalMatcher.h" @@ -271,8 +271,10 @@ public: OrthancPeers() { + OrthancConfiguration::ReaderLock lock; + std::set peers; - Configuration::GetListOfOrthancPeers(peers); + lock.GetConfiguration().GetListOfOrthancPeers(peers); names_.reserve(peers.size()); parameters_.reserve(peers.size()); @@ -281,7 +283,7 @@ it = peers.begin(); it != peers.end(); ++it) { WebServiceParameters peer; - if (Configuration::GetOrthancPeer(peer, *it)) + if (lock.GetConfiguration().GetOrthancPeer(peer, *it)) { names_.push_back(*it); parameters_.push_back(peer); @@ -2105,19 +2107,23 @@ { const _OrthancPluginGetFontInfo& p = *reinterpret_cast(parameters); - const Font& font = Configuration::GetFontRegistry().GetFont(p.fontIndex); - - if (p.name != NULL) { - *(p.name) = font.GetName().c_str(); - } - else if (p.size != NULL) - { - *(p.size) = font.GetSize(); - } - else - { - throw OrthancException(ErrorCode_InternalError); + OrthancConfiguration::ReaderLock lock; + + const Font& font = lock.GetConfiguration().GetFontRegistry().GetFont(p.fontIndex); + + if (p.name != NULL) + { + *(p.name) = font.GetName().c_str(); + } + else if (p.size != NULL) + { + *(p.size) = font.GetSize(); + } + else + { + throw OrthancException(ErrorCode_InternalError); + } } } @@ -2127,9 +2133,12 @@ const _OrthancPluginDrawText& p = *reinterpret_cast(parameters); ImageAccessor& target = *reinterpret_cast(p.image); - const Font& font = Configuration::GetFontRegistry().GetFont(p.fontIndex); - - font.Draw(target, p.utf8Text, p.x, p.y, p.r, p.g, p.b); + + { + OrthancConfiguration::ReaderLock lock; + const Font& font = lock.GetConfiguration().GetFontRegistry().GetFont(p.fontIndex); + font.Draw(target, p.utf8Text, p.x, p.y, p.r, p.g, p.b); + } } @@ -2392,15 +2401,25 @@ case _OrthancPluginService_GetConfigurationPath: { - *reinterpret_cast(parameters)->result = - CopyString(Configuration::GetConfigurationAbsolutePath()); + std::string s; + + { + OrthancConfiguration::ReaderLock lock; + s = lock.GetConfiguration().GetConfigurationAbsolutePath(); + } + + *reinterpret_cast(parameters)->result = CopyString(s); return true; } case _OrthancPluginService_GetConfiguration: { std::string s; - Configuration::FormatConfiguration(s); + + { + OrthancConfiguration::ReaderLock lock; + lock.GetConfiguration().Format(s); + } *reinterpret_cast(parameters)->result = CopyString(s); return true; @@ -2665,7 +2684,12 @@ { const _OrthancPluginReturnSingleValue& p = *reinterpret_cast(parameters); - *(p.resultUint32) = Configuration::GetFontRegistry().GetSize(); + + { + OrthancConfiguration::ReaderLock lock; + *(p.resultUint32) = lock.GetConfiguration().GetFontRegistry().GetSize(); + } + return true; } diff -r e9613a09cf9e -r 9f6716008066 UnitTestsSources/ImageTests.cpp --- a/UnitTestsSources/ImageTests.cpp Thu Nov 29 15:26:31 2018 +0100 +++ b/UnitTestsSources/ImageTests.cpp Thu Nov 29 15:26:47 2018 +0100 @@ -43,9 +43,10 @@ #include "../Core/Images/PngWriter.h" #include "../Core/Images/PamReader.h" #include "../Core/Images/PamWriter.h" +#include "../Core/SystemToolbox.h" #include "../Core/Toolbox.h" #include "../Core/TemporaryFile.h" -#include "../OrthancServer/OrthancInitialization.h" // For the FontRegistry +#include "../OrthancServer/OrthancConfiguration.h" // For the FontRegistry #include @@ -264,8 +265,12 @@ Orthanc::Image s(Orthanc::PixelFormat_RGB24, 640, 480, false); memset(s.GetBuffer(), 0, s.GetPitch() * s.GetHeight()); - ASSERT_GE(1u, Orthanc::Configuration::GetFontRegistry().GetSize()); - Orthanc::Configuration::GetFontRegistry().GetFont(0).Draw(s, "Hello world É\n\rComment ça va ?\nq", 50, 60, 255, 0, 0); + { + Orthanc::OrthancConfiguration::ReaderLock lock; + ASSERT_GE(1u, lock.GetConfiguration().GetFontRegistry().GetSize()); + lock.GetConfiguration().GetFontRegistry().GetFont(0).Draw + (s, "Hello world É\n\rComment ça va ?\nq", 50, 60, 255, 0, 0); + } Orthanc::PngWriter w; w.WriteToFile("UnitTestsResults/font.png", s);