# HG changeset patch # User Sebastien Jodogne # Date 1435322650 -7200 # Node ID 7b7d597a190cd254978713c060752e59a43faca6 # Parent 52b2070fc8f1f28dda9bfbfa5d4dd649acea1270 The configuration can be splitted into several files stored inside the same folder diff -r 52b2070fc8f1 -r 7b7d597a190c NEWS --- a/NEWS Wed Jun 24 16:17:43 2015 +0200 +++ b/NEWS Fri Jun 26 14:44:10 2015 +0200 @@ -1,6 +1,7 @@ Pending changes in the mainline =============================== +* The configuration can be splitted into several files stored inside the same folder * Fix issue 35 (Characters in PatientID string are not protected for C-Find) * Fix issue 37 (Hyphens trigger range query even if datatype does not support ranges) diff -r 52b2070fc8f1 -r 7b7d597a190c OrthancServer/OrthancInitialization.cpp --- a/OrthancServer/OrthancInitialization.cpp Wed Jun 24 16:17:43 2015 +0200 +++ b/OrthancServer/OrthancInitialization.cpp Fri Jun 26 14:44:10 2015 +0200 @@ -62,23 +62,102 @@ namespace Orthanc { static boost::mutex globalMutex_; - static std::auto_ptr configuration_; + static Json::Value configuration_; static boost::filesystem::path defaultDirectory_; static std::string configurationAbsolutePath_; + static void AddFileToConfiguration(const boost::filesystem::path& path) + { + LOG(WARNING) << "Reading the configuration from: " << path; + + std::string content; + Toolbox::ReadFile(content, path.string()); + + Json::Value tmp; + Json::Reader reader; + if (!reader.parse(content, tmp) || + tmp.type() != Json::objectValue) + { + LOG(ERROR) << "Bad file format for this configuration file: " << path; + throw OrthancException(ErrorCode_BadFileFormat); + } + + if (configuration_.size() == 0) + { + configuration_ = tmp; + } + else + { + Json::Value::Members members = tmp.getMemberNames(); + for (Json::Value::ArrayIndex i = 0; i < members.size(); i++) + { + if (configuration_.isMember(members[i])) + { + LOG(ERROR) << "The configuration section \"" << members[i] << "\" is defined in 2 different configuration files"; + throw OrthancException(ErrorCode_BadFileFormat); + } + else + { + configuration_[members[i]] = tmp[members[i]]; + } + } + } + } + + + static void ScanFolderForConfiguration(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 (!is_directory(it->status())) + { + std::string extension = boost::filesystem::extension(it->path()); + Toolbox::ToLowerCase(extension); + + if (extension == ".json") + { + AddFileToConfiguration(it->path().string()); + } + } + } + } + + static void ReadGlobalConfiguration(const char* configurationFile) { - configuration_.reset(new Json::Value); - - std::string content; + // Prepare the default configuration + defaultDirectory_ = boost::filesystem::current_path(); + configuration_ = Json::objectValue; + configurationAbsolutePath_ = ""; if (configurationFile) { - Toolbox::ReadFile(content, configurationFile); - defaultDirectory_ = boost::filesystem::path(configurationFile).parent_path(); - LOG(WARNING) << "Using the configuration from: " << configurationFile; - configurationAbsolutePath_ = boost::filesystem::absolute(configurationFile).string(); + if (!boost::filesystem::exists(configurationFile)) + { + LOG(ERROR) << "Inexistent path to configuration: " << configurationFile; + return; + } + + if (boost::filesystem::is_directory(configurationFile)) + { + defaultDirectory_ = boost::filesystem::path(configurationFile); + configurationAbsolutePath_ = boost::filesystem::absolute(configurationFile).parent_path().string(); + ScanFolderForConfiguration(configurationFile); + } + else + { + defaultDirectory_ = boost::filesystem::path(configurationFile).parent_path(); + configurationAbsolutePath_ = boost::filesystem::absolute(configurationFile).string(); + AddFileToConfiguration(configurationFile); + } } else { @@ -91,39 +170,22 @@ // In a non-standalone build, we use the // "Resources/Configuration.json" from the Orthanc source code - try - { - boost::filesystem::path p = ORTHANC_PATH; - p /= "Resources"; - p /= "Configuration.json"; - Toolbox::ReadFile(content, p.string()); - LOG(WARNING) << "Using the configuration from: " << p.string(); - configurationAbsolutePath_ = boost::filesystem::absolute(p).string(); - } - catch (OrthancException&) - { - // No configuration file found, give up with empty configuration - LOG(WARNING) << "Using the default Orthanc configuration"; - return; - } + boost::filesystem::path p = ORTHANC_PATH; + p /= "Resources"; + p /= "Configuration.json"; + configurationAbsolutePath_ = boost::filesystem::absolute(p).string(); + + AddFileToConfiguration(p); #endif } - - - Json::Reader reader; - if (!reader.parse(content, *configuration_)) - { - LOG(ERROR) << "Unable to read the configuration file"; - throw OrthancException(ErrorCode_BadFileFormat); - } } static void RegisterUserMetadata() { - if (configuration_->isMember("UserMetadata")) + if (configuration_.isMember("UserMetadata")) { - const Json::Value& parameter = (*configuration_) ["UserMetadata"]; + const Json::Value& parameter = configuration_["UserMetadata"]; Json::Value::Members members = parameter.getMemberNames(); for (size_t i = 0; i < members.size(); i++) @@ -155,9 +217,9 @@ static void RegisterUserContentType() { - if (configuration_->isMember("UserContentType")) + if (configuration_.isMember("UserContentType")) { - const Json::Value& parameter = (*configuration_) ["UserContentType"]; + const Json::Value& parameter = configuration_["UserContentType"]; Json::Value::Members members = parameter.getMemberNames(); for (size_t i = 0; i < members.size(); i++) @@ -192,7 +254,8 @@ boost::mutex::scoped_lock lock(globalMutex_); InitializeServerEnumerations(); - defaultDirectory_ = boost::filesystem::current_path(); + + // Read the user-provided configuration ReadGlobalConfiguration(configurationFile); HttpClient::GlobalInitialize(); @@ -219,7 +282,6 @@ { boost::mutex::scoped_lock lock(globalMutex_); HttpClient::GlobalFinalize(); - configuration_.reset(NULL); #if ORTHANC_JPEG_LOSSLESS_ENABLED == 1 // Unregister JPEG-LS codecs @@ -239,10 +301,9 @@ { boost::mutex::scoped_lock lock(globalMutex_); - if (configuration_.get() != NULL && - configuration_->isMember(parameter)) + if (configuration_.isMember(parameter)) { - return (*configuration_) [parameter].asString(); + return configuration_[parameter].asString(); } else { @@ -256,10 +317,9 @@ { boost::mutex::scoped_lock lock(globalMutex_); - if (configuration_.get() != NULL && - configuration_->isMember(parameter)) + if (configuration_.isMember(parameter)) { - return (*configuration_) [parameter].asInt(); + return configuration_[parameter].asInt(); } else { @@ -273,10 +333,9 @@ { boost::mutex::scoped_lock lock(globalMutex_); - if (configuration_.get() != NULL && - configuration_->isMember(parameter)) + if (configuration_.isMember(parameter)) { - return (*configuration_) [parameter].asBool(); + return configuration_[parameter].asBool(); } else { @@ -290,19 +349,13 @@ { boost::mutex::scoped_lock lock(globalMutex_); - if (configuration_.get() == NULL) - { - LOG(ERROR) << "The configuration file was not properly loaded"; - throw OrthancException(ErrorCode_InexistentItem); - } - - if (!configuration_->isMember("DicomModalities")) + if (!configuration_.isMember("DicomModalities")) { LOG(ERROR) << "No modality with symbolic name: " << name; throw OrthancException(ErrorCode_InexistentItem); } - const Json::Value& modalities = (*configuration_) ["DicomModalities"]; + const Json::Value& modalities = configuration_["DicomModalities"]; if (modalities.type() != Json::objectValue || !modalities.isMember(name)) { @@ -329,13 +382,7 @@ { boost::mutex::scoped_lock lock(globalMutex_); - if (configuration_.get() == NULL) - { - LOG(ERROR) << "The configuration file was not properly loaded"; - throw OrthancException(ErrorCode_InexistentItem); - } - - if (!configuration_->isMember("OrthancPeers")) + if (!configuration_.isMember("OrthancPeers")) { LOG(ERROR) << "No peer with symbolic name: " << name; throw OrthancException(ErrorCode_InexistentItem); @@ -343,7 +390,7 @@ try { - const Json::Value& modalities = (*configuration_) ["OrthancPeers"]; + const Json::Value& modalities = configuration_["OrthancPeers"]; if (modalities.type() != Json::objectValue || !modalities.isMember(name)) { @@ -370,13 +417,12 @@ target.clear(); - if (configuration_.get() == NULL || - !configuration_->isMember(parameter)) + if (!configuration_.isMember(parameter)) { return true; } - const Json::Value& modalities = (*configuration_) [parameter]; + const Json::Value& modalities = configuration_[parameter]; if (modalities.type() != Json::objectValue) { LOG(ERROR) << "Bad format of the \"DicomModalities\" configuration section"; @@ -431,13 +477,12 @@ httpServer.ClearUsers(); - if (configuration_.get() == NULL || - !configuration_->isMember("RegisteredUsers")) + if (!configuration_.isMember("RegisteredUsers")) { return; } - const Json::Value& users = (*configuration_) ["RegisteredUsers"]; + const Json::Value& users = configuration_["RegisteredUsers"]; if (users.type() != Json::objectValue) { LOG(ERROR) << "Badly formatted list of users"; @@ -494,13 +539,12 @@ target.clear(); - if (configuration_.get() == NULL || - !configuration_->isMember(key)) + if (!configuration_.isMember(key)) { return; } - const Json::Value& lst = (*configuration_) [key]; + const Json::Value& lst = configuration_[key]; if (lst.type() != Json::arrayValue) { @@ -598,18 +642,12 @@ { boost::mutex::scoped_lock lock(globalMutex_); - if (configuration_.get() == NULL) + if (!configuration_.isMember("DicomModalities")) { - LOG(ERROR) << "The configuration file was not properly loaded"; - throw OrthancException(ErrorCode_InternalError); + configuration_["DicomModalities"] = Json::objectValue; } - if (!configuration_->isMember("DicomModalities")) - { - (*configuration_) ["DicomModalities"] = Json::objectValue; - } - - Json::Value& modalities = (*configuration_) ["DicomModalities"]; + Json::Value& modalities = configuration_["DicomModalities"]; if (modalities.type() != Json::objectValue) { LOG(ERROR) << "Bad file format for modality: " << symbolicName; @@ -628,19 +666,13 @@ { boost::mutex::scoped_lock lock(globalMutex_); - if (configuration_.get() == NULL) - { - LOG(ERROR) << "The configuration file was not properly loaded"; - throw OrthancException(ErrorCode_InternalError); - } - - if (!configuration_->isMember("DicomModalities")) + if (!configuration_.isMember("DicomModalities")) { LOG(ERROR) << "No modality with symbolic name: " << symbolicName; throw OrthancException(ErrorCode_BadFileFormat); } - Json::Value& modalities = (*configuration_) ["DicomModalities"]; + Json::Value& modalities = configuration_["DicomModalities"]; if (modalities.type() != Json::objectValue) { LOG(ERROR) << "Bad file format for the \"DicomModalities\" configuration section"; @@ -656,19 +688,13 @@ { boost::mutex::scoped_lock lock(globalMutex_); - if (configuration_.get() == NULL) + if (!configuration_.isMember("OrthancPeers")) { - LOG(ERROR) << "The configuration file was not properly loaded"; - throw OrthancException(ErrorCode_InternalError); + LOG(ERROR) << "No peer with symbolic name: " << symbolicName; + configuration_["OrthancPeers"] = Json::objectValue; } - if (!configuration_->isMember("OrthancPeers")) - { - LOG(ERROR) << "No peer with symbolic name: " << symbolicName; - (*configuration_) ["OrthancPeers"] = Json::objectValue; - } - - Json::Value& peers = (*configuration_) ["OrthancPeers"]; + Json::Value& peers = configuration_["OrthancPeers"]; if (peers.type() != Json::objectValue) { LOG(ERROR) << "Bad file format for the \"OrthancPeers\" configuration section"; @@ -687,19 +713,13 @@ { boost::mutex::scoped_lock lock(globalMutex_); - if (configuration_.get() == NULL) - { - LOG(ERROR) << "The configuration file was not properly loaded"; - throw OrthancException(ErrorCode_InternalError); - } - - if (!configuration_->isMember("OrthancPeers")) + if (!configuration_.isMember("OrthancPeers")) { LOG(ERROR) << "No peer with symbolic name: " << symbolicName; throw OrthancException(ErrorCode_BadFileFormat); } - Json::Value& peers = (*configuration_) ["OrthancPeers"]; + Json::Value& peers = configuration_["OrthancPeers"]; if (peers.type() != Json::objectValue) { LOG(ERROR) << "Bad file format for the \"OrthancPeers\" configuration section"; diff -r 52b2070fc8f1 -r 7b7d597a190c Plugins/Include/OrthancCPlugin.h --- a/Plugins/Include/OrthancCPlugin.h Wed Jun 24 16:17:43 2015 +0200 +++ b/Plugins/Include/OrthancCPlugin.h Fri Jun 26 14:44:10 2015 +0200 @@ -1799,10 +1799,12 @@ /** - * @brief Return the path to the configuration file. + * @brief Return the path to the configuration file(s). * - * This function returns the path to the configuration file that was - * specified when starting Orthanc. + * This function returns the path to the configuration file(s) that + * was specified when starting Orthanc. Since version 0.9.1, this + * path can refer to a folder that stores a set of configuration + * files. * * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). * @return NULL in the case of an error, or a newly allocated string