changeset 2943:9f6716008066

merge
author am@osimis.io
date Thu, 29 Nov 2018 15:26:47 +0100
parents e9613a09cf9e (current diff) e292798f9980 (diff)
children 3c636087d060
files
diffstat 19 files changed, 627 insertions(+), 1219 deletions(-) [+]
line wrap: on
line diff
--- 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<std::string> luaScripts;
-    Configuration::GetGlobalListOfStringsParameter(luaScripts, "LuaScripts");
+    configLock.GetConfiguration().GetListOfStringsParameter(luaScripts, "LuaScripts");
 
     LuaScripting::Lock lock(*this);
 
     for (std::list<std::string>::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);
--- 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<std::string>& target,
-    const std::string& key) const
+  void OrthancConfiguration::GetListOfStringsParameter(std::list<std::string>& 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;
+  }
 }
--- 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 <boost/filesystem.hpp>
 #include <boost/thread/shared_mutex.hpp>
+#include <boost/thread/lock_types.hpp>
 
 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<boost::shared_mutex>  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<boost::shared_mutex>  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<std::string>& target,
-                                         const std::string& key) const;
+    void GetListOfStringsParameter(std::list<std::string>& 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();
   };
 }
--- 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++)
     {
--- 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 <boost/lexical_cast.hpp>
-#include <boost/filesystem.hpp>
-#include <curl/curl.h>
-#include <boost/thread/recursive_mutex.hpp>
+#include "OrthancConfiguration.h"
 
 #include <dcmtk/dcmnet/dul.h>   // 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<std::string, std::string> 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<std::string> ids;
-
-    Configuration::GetListOfOrthancPeers(ids);
-    for (std::set<std::string>::const_iterator it = ids.begin(); it != ids.end(); ++it)
-    {
-      WebServiceParameters peer;
-      Configuration::GetOrthancPeer(peer, *it);
-      peer.CheckClientCertificate();
-    }
-
-    Configuration::GetListOfDicomModalities(ids);
-    for (std::set<std::string>::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<unsigned int>(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<std::string>& 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<std::string>& 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<std::string>& 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<std::string>& 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<std::string> modalities;
-    GetListOfDicomModalities(modalities);
-
-    for (std::set<std::string>::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;
-  }
 }
--- 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 <string>
-#include <set>
-#include <json/json.h>
-#include <stdint.h>
-
 #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<std::string>& target);
-
-    static void GetListOfOrthancPeers(std::set<std::string>& target);
-
-    static void SetupRegisteredUsers(MongooseServer& httpServer);
-
-    static std::string InterpretStringParameterAsPath(const std::string& parameter);
-
-    static void GetGlobalListOfStringsParameter(std::list<std::string>& 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();
 }
--- 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)
     {
--- 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<ParsedDicomFile> query(ParsedDicomFile::CreateFromJson(json, static_cast<DicomFromJsonFlags>(0)));
 
--- 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) 
--- 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);
   }
--- 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);
+    }
   }
 
 
--- 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 <dcmtk/dcmdata/dcfilefo.h>
 
@@ -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());
   }
 
 
--- 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
+  }
 }
--- 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<SharedArchive>  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();
   };
 }
--- 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;
--- 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";
   }
--- 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 <boost/algorithm/string/predicate.hpp>
 
-#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<OrthancFindRequestHandler> 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<std::string> path;
-  Configuration::GetGlobalListOfStringsParameter(path, "Plugins");
+
+  {
+    OrthancConfiguration::ReaderLock lock;
+    lock.GetConfiguration().GetListOfStringsParameter(path, "Plugins");
+  }
+
   for (std::list<std::string>::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);
--- 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<std::string> 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<const _OrthancPluginGetFontInfo*>(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<const _OrthancPluginDrawText*>(parameters);
 
     ImageAccessor& target = *reinterpret_cast<ImageAccessor*>(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<const _OrthancPluginRetrieveDynamicString*>(parameters)->result = 
-          CopyString(Configuration::GetConfigurationAbsolutePath());
+        std::string s;
+
+        {
+          OrthancConfiguration::ReaderLock lock;
+          s = lock.GetConfiguration().GetConfigurationAbsolutePath();
+        }
+
+        *reinterpret_cast<const _OrthancPluginRetrieveDynamicString*>(parameters)->result = CopyString(s);
         return true;
       }
 
       case _OrthancPluginService_GetConfiguration:
       {
         std::string s;
-        Configuration::FormatConfiguration(s);
+
+        {
+          OrthancConfiguration::ReaderLock lock;
+          lock.GetConfiguration().Format(s);
+        }
 
         *reinterpret_cast<const _OrthancPluginRetrieveDynamicString*>(parameters)->result = CopyString(s);
         return true;
@@ -2665,7 +2684,12 @@
       {
         const _OrthancPluginReturnSingleValue& p =
           *reinterpret_cast<const _OrthancPluginReturnSingleValue*>(parameters);
-        *(p.resultUint32) = Configuration::GetFontRegistry().GetSize();
+
+        {
+          OrthancConfiguration::ReaderLock lock;
+          *(p.resultUint32) = lock.GetConfiguration().GetFontRegistry().GetSize();
+        }
+
         return true;
       }
 
--- 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 <stdint.h>
 
@@ -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);