changeset 127:1a21c9da983a dev

huge refactoring
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 23 Jun 2016 17:31:40 +0200
parents 93b15955460c
children ffe7bfbe370e
files CMakeLists.txt Orthanc/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp Orthanc/Plugins/Samples/Common/OrthancPluginCppWrapper.h Plugin/Configuration.cpp Plugin/Configuration.h Plugin/Dicom.cpp Plugin/DicomResults.cpp Plugin/DicomWebServers.cpp Plugin/DicomWebServers.h Plugin/Plugin.cpp Plugin/Plugin.h Plugin/QidoRs.cpp Plugin/StowRs.cpp Plugin/StowRsClient.cpp Plugin/WadoRs.cpp Plugin/WadoRsRetrieveFrames.cpp Plugin/WadoUri.cpp UnitTestsSources/UnitTestsMain.cpp
diffstat 18 files changed, 1230 insertions(+), 441 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Thu Jun 23 10:09:16 2016 +0200
+++ b/CMakeLists.txt	Thu Jun 23 17:31:40 2016 +0200
@@ -102,8 +102,11 @@
   -DORTHANC_ENABLE_MD5=0
   -DORTHANC_ENABLE_BASE64=0
   -DORTHANC_ENABLE_LOGGING=0
+  -DHAS_ORTHANC_EXCEPTION=1
   )
 
+include_directories(${ORTHANC_ROOT}/Core)  # To access "OrthancException.h"
+
 set(CORE_SOURCES
   ${BOOST_SOURCES}
   ${JSONCPP_SOURCES}
@@ -114,6 +117,7 @@
   ${ORTHANC_ROOT}/Core/Enumerations.cpp
   ${ORTHANC_ROOT}/Core/Toolbox.cpp
   ${ORTHANC_ROOT}/Core/WebServiceParameters.cpp
+  ${ORTHANC_ROOT}/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp
 
   Plugin/ChunkedBuffer.cpp
   Plugin/Configuration.cpp
@@ -153,6 +157,7 @@
 add_executable(UnitTests
   ${CORE_SOURCES}
   ${GTEST_SOURCES}
+  ${CMAKE_SOURCE_DIR}/Plugin/DicomWebServers.cpp
   UnitTestsSources/UnitTestsMain.cpp
   )
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Orthanc/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp	Thu Jun 23 17:31:40 2016 +0200
@@ -0,0 +1,577 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "OrthancPluginCppWrapper.h"
+
+#include <json/reader.h>
+
+
+namespace OrthancPlugins
+{
+  const char* PluginException::GetErrorDescription(OrthancPluginContext* context) const
+  {
+    const char* description = OrthancPluginGetErrorDescription(context, code_);
+    if (description)
+    {
+      return description;
+    }
+    else
+    {
+      return "No description available";
+    }
+  }
+
+
+  MemoryBuffer::MemoryBuffer(OrthancPluginContext* context) : 
+    context_(context)
+  {
+    buffer_.data = NULL;
+    buffer_.size = 0;
+  }
+
+
+  void MemoryBuffer::Clear()
+  {
+    if (buffer_.data != NULL)
+    {
+      OrthancPluginFreeMemoryBuffer(context_, &buffer_);
+      buffer_.data = NULL;
+      buffer_.size = 0;
+    }
+  }
+
+
+  void MemoryBuffer::ToString(std::string& target) const
+  {
+    if (buffer_.size == 0)
+    {
+      target.clear();
+    }
+    else
+    {
+      target.assign(reinterpret_cast<const char*>(buffer_.data), buffer_.size);
+    }
+  }
+
+
+  void MemoryBuffer::ToJson(Json::Value& target) const
+  {
+    if (buffer_.data == NULL ||
+        buffer_.size == 0)
+    {
+      OrthancPluginLogError(context_, "Cannot convert an empty memory buffer to JSON");
+      throw PluginException(OrthancPluginErrorCode_InternalError);
+    }
+
+    const char* tmp = reinterpret_cast<const char*>(buffer_.data);
+
+    Json::Reader reader;
+    if (!reader.parse(tmp, tmp + buffer_.size, target))
+    {
+      OrthancPluginLogError(context_, "Cannot convert some memory buffer to JSON");
+      throw PluginException(OrthancPluginErrorCode_BadFileFormat);
+    }
+  }
+
+
+  bool MemoryBuffer::RestApiGet(const std::string& uri,
+                                bool applyPlugins)
+  {
+    Clear();
+
+    OrthancPluginErrorCode error;
+
+    if (applyPlugins)
+    {
+      error = OrthancPluginRestApiGetAfterPlugins(context_, &buffer_, uri.c_str());
+    }
+    else
+    {
+      error = OrthancPluginRestApiGet(context_, &buffer_, uri.c_str());
+    }
+
+    if (error == OrthancPluginErrorCode_Success)
+    {
+      return true;
+    }
+    else if (error == OrthancPluginErrorCode_UnknownResource)
+    {
+      return false;
+    }
+    else
+    {
+      throw PluginException(error);
+    }
+  }
+
+  
+  bool MemoryBuffer::RestApiPost(const std::string& uri,
+                                 const std::string& body,
+                                 bool applyPlugins)
+  {
+    Clear();
+
+    OrthancPluginErrorCode error;
+
+    if (applyPlugins)
+    {
+      error = OrthancPluginRestApiPostAfterPlugins(context_, &buffer_, uri.c_str(), body.c_str(), body.size());
+    }
+    else
+    {
+      error = OrthancPluginRestApiPost(context_, &buffer_, uri.c_str(), body.c_str(), body.size());
+    }
+
+    if (error == OrthancPluginErrorCode_Success)
+    {
+      return true;
+    }
+    else if (error == OrthancPluginErrorCode_UnknownResource)
+    {
+      return false;
+    }
+    else
+    {
+      throw PluginException(error);
+    }
+  }
+
+
+  bool MemoryBuffer::RestApiPut(const std::string& uri,
+                                const std::string& body,
+                                bool applyPlugins)
+  {
+    Clear();
+
+    OrthancPluginErrorCode error;
+
+    if (applyPlugins)
+    {
+      error = OrthancPluginRestApiPutAfterPlugins(context_, &buffer_, uri.c_str(), body.c_str(), body.size());
+    }
+    else
+    {
+      error = OrthancPluginRestApiPut(context_, &buffer_, uri.c_str(), body.c_str(), body.size());
+    }
+
+    if (error == OrthancPluginErrorCode_Success)
+    {
+      return true;
+    }
+    else if (error == OrthancPluginErrorCode_UnknownResource)
+    {
+      return false;
+    }
+    else
+    {
+      throw PluginException(error);
+    }
+  }
+
+
+  OrthancString::OrthancString(OrthancPluginContext* context,
+                               char* str) :
+    context_(context),
+    str_(str)
+  {
+  }
+
+
+  void OrthancString::Clear()
+  {
+    if (str_ != NULL)
+    {
+      OrthancPluginFreeString(context_, str_);
+      str_ = NULL;
+    }
+  }
+
+
+  void OrthancString::ToString(std::string& target) const
+  {
+    if (str_ == NULL)
+    {
+      target.clear();
+    }
+    else
+    {
+      target.assign(str_);
+    }
+  }
+
+
+  void OrthancString::ToJson(Json::Value& target) const
+  {
+    if (str_ == NULL)
+    {
+      OrthancPluginLogError(context_, "Cannot convert an empty memory buffer to JSON");
+      throw PluginException(OrthancPluginErrorCode_InternalError);
+    }
+
+    Json::Reader reader;
+    if (!reader.parse(str_, target))
+    {
+      OrthancPluginLogError(context_, "Cannot convert some memory buffer to JSON");
+      throw PluginException(OrthancPluginErrorCode_BadFileFormat);
+    }
+  }
+  
+
+  OrthancConfiguration::OrthancConfiguration(OrthancPluginContext* context) : 
+    context_(context)
+  {
+    OrthancString str(context, OrthancPluginGetConfiguration(context));
+
+    if (str.GetContent() == NULL)
+    {
+      OrthancPluginLogError(context, "Cannot access the Orthanc configuration");
+      throw PluginException(OrthancPluginErrorCode_InternalError);
+    }
+
+    str.ToJson(configuration_);
+
+    if (configuration_.type() != Json::objectValue)
+    {
+      OrthancPluginLogError(context, "Unable to read the Orthanc configuration");
+      throw PluginException(OrthancPluginErrorCode_InternalError);
+    }
+  }
+
+
+  OrthancPluginContext* OrthancConfiguration::GetContext() const
+  {
+    if (context_ == NULL)
+    {
+      throw PluginException(OrthancPluginErrorCode_Plugin);
+    }
+    else
+    {
+      return context_;
+    }
+  }
+
+
+  std::string OrthancConfiguration::GetPath(const std::string& key) const
+  {
+    if (path_.empty())
+    {
+      return key;
+    }
+    else
+    {
+      return path_ + "." + key;
+    }
+  }
+
+
+  void OrthancConfiguration::GetSection(OrthancConfiguration& target,
+                                        const std::string& key) const
+  {
+    assert(configuration_.type() == Json::objectValue);
+
+    target.context_ = context_;
+    target.path_ = GetPath(key);
+
+    if (!configuration_.isMember(key))
+    {
+      target.configuration_ = Json::objectValue;
+    }
+    else
+    {
+      if (configuration_[key].type() != Json::objectValue)
+      {
+        if (context_ != NULL)
+        {
+          std::string s = "The configuration section \"" + target.path_ + "\" is not an associative array as expected";
+          OrthancPluginLogError(context_, s.c_str());
+        }
+
+        throw PluginException(OrthancPluginErrorCode_BadFileFormat);
+      }
+
+      target.configuration_ = configuration_[key];
+    }
+  }
+
+
+  bool OrthancConfiguration::LookupStringValue(std::string& target,
+                                               const std::string& key) const
+  {
+    assert(configuration_.type() == Json::objectValue);
+
+    if (!configuration_.isMember(key))
+    {
+      return false;
+    }
+
+    if (configuration_[key].type() != Json::stringValue)
+    {
+      if (context_ != NULL)
+      {
+        std::string s = "The configuration option \"" + GetPath(key) + "\" is not a string as expected";
+        OrthancPluginLogError(context_, s.c_str());
+      }
+
+      throw PluginException(OrthancPluginErrorCode_BadFileFormat);
+    }
+
+    target = configuration_[key].asString();
+    return true;
+  }
+
+
+  bool OrthancConfiguration::LookupIntegerValue(int& target,
+                                                const std::string& key) const
+  {
+    assert(configuration_.type() == Json::objectValue);
+
+    if (!configuration_.isMember(key))
+    {
+      return false;
+    }
+
+    switch (configuration_[key].type())
+    {
+      case Json::intValue:
+        target = configuration_[key].asInt();
+        return true;
+        
+      case Json::uintValue:
+        target = configuration_[key].asUInt();
+        return true;
+        
+      default:
+        if (context_ != NULL)
+        {
+          std::string s = "The configuration option \"" + GetPath(key) + "\" is not an integer as expected";
+          OrthancPluginLogError(context_, s.c_str());
+        }
+
+        throw PluginException(OrthancPluginErrorCode_BadFileFormat);
+    }
+  }
+
+
+  bool OrthancConfiguration::LookupUnsignedIntegerValue(unsigned int& target,
+                                                        const std::string& key) const
+  {
+    int tmp;
+    if (!LookupIntegerValue(tmp, key))
+    {
+      return false;
+    }
+
+    if (tmp < 0)
+    {
+      if (context_ != NULL)
+      {
+        std::string s = "The configuration option \"" + GetPath(key) + "\" is not a positive integer as expected";
+        OrthancPluginLogError(context_, s.c_str());
+      }
+
+      throw PluginException(OrthancPluginErrorCode_BadFileFormat);
+    }
+    else
+    {
+      target = static_cast<unsigned int>(tmp);
+      return true;
+    }
+  }
+
+
+  bool OrthancConfiguration::LookupBooleanValue(bool& target,
+                                                const std::string& key) const
+  {
+    assert(configuration_.type() == Json::objectValue);
+
+    if (!configuration_.isMember(key))
+    {
+      return false;
+    }
+
+    if (configuration_[key].type() != Json::booleanValue)
+    {
+      if (context_ != NULL)
+      {
+        std::string s = "The configuration option \"" + GetPath(key) + "\" is not a Boolean as expected";
+        OrthancPluginLogError(context_, s.c_str());
+      }
+
+      throw PluginException(OrthancPluginErrorCode_BadFileFormat);
+    }
+
+    target = configuration_[key].asBool();
+    return true;
+  }
+
+
+  bool OrthancConfiguration::LookupFloatValue(float& target,
+                                              const std::string& key) const
+  {
+    assert(configuration_.type() == Json::objectValue);
+
+    if (!configuration_.isMember(key))
+    {
+      return false;
+    }
+
+    switch (configuration_[key].type())
+    {
+      case Json::realValue:
+        target = configuration_[key].asFloat();
+        return true;
+        
+      case Json::intValue:
+        target = configuration_[key].asInt();
+        return true;
+        
+      case Json::uintValue:
+        target = configuration_[key].asUInt();
+        return true;
+        
+      default:
+        if (context_ != NULL)
+        {
+          std::string s = "The configuration option \"" + GetPath(key) + "\" is not an integer as expected";
+          OrthancPluginLogError(context_, s.c_str());
+        }
+
+        throw PluginException(OrthancPluginErrorCode_BadFileFormat);
+    }
+  }
+
+  
+  std::string OrthancConfiguration::GetStringValue(const std::string& key,
+                                                   const std::string& defaultValue) const
+  {
+    std::string tmp;
+    if (LookupStringValue(tmp, key))
+    {
+      return tmp;
+    }
+    else
+    {
+      return defaultValue;
+    }
+  }
+
+
+  int OrthancConfiguration::GetIntegerValue(const std::string& key,
+                                            int defaultValue) const
+  {
+    int tmp;
+    if (LookupIntegerValue(tmp, key))
+    {
+      return tmp;
+    }
+    else
+    {
+      return defaultValue;
+    }
+  }
+
+
+  unsigned int OrthancConfiguration::GetUnsignedIntegerValue(const std::string& key,
+                                                             unsigned int defaultValue) const
+  {
+    unsigned int tmp;
+    if (LookupUnsignedIntegerValue(tmp, key))
+    {
+      return tmp;
+    }
+    else
+    {
+      return defaultValue;
+    }
+  }
+
+
+  bool OrthancConfiguration::GetBooleanValue(const std::string& key,
+                                             bool defaultValue) const
+  {
+    bool tmp;
+    if (LookupBooleanValue(tmp, key))
+    {
+      return tmp;
+    }
+    else
+    {
+      return defaultValue;
+    }
+  }
+
+
+  float OrthancConfiguration::GetFloatValue(const std::string& key,
+                                            float defaultValue) const
+  {
+    float tmp;
+    if (LookupFloatValue(tmp, key))
+    {
+      return tmp;
+    }
+    else
+    {
+      return defaultValue;
+    }
+  }
+
+
+  bool RestApiDelete(OrthancPluginContext* context,
+                     const std::string& uri,
+                     bool applyPlugins)
+  {
+    OrthancPluginErrorCode error;
+
+    if (applyPlugins)
+    {
+      error = OrthancPluginRestApiDeleteAfterPlugins(context, uri.c_str());
+    }
+    else
+    {
+      error = OrthancPluginRestApiDelete(context, uri.c_str());
+    }
+
+    if (error == OrthancPluginErrorCode_Success)
+    {
+      return true;
+    }
+    else if (error == OrthancPluginErrorCode_UnknownResource)
+    {
+      return false;
+    }
+    else
+    {
+      throw PluginException(error);
+    }
+  }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Orthanc/Plugins/Samples/Common/OrthancPluginCppWrapper.h	Thu Jun 23 17:31:40 2016 +0200
@@ -0,0 +1,261 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include <orthanc/OrthancCPlugin.h>
+#include <boost/noncopyable.hpp>
+#include <boost/lexical_cast.hpp>
+#include <json/value.h>
+
+#if HAS_ORTHANC_EXCEPTION == 1
+#  include <OrthancException.h>
+#endif
+
+
+namespace OrthancPlugins
+{
+  typedef void (*RestCallback) (OrthancPluginRestOutput* output,
+                                const char* url,
+                                const OrthancPluginHttpRequest* request);
+
+
+  class PluginException
+  {
+  private:
+    OrthancPluginErrorCode  code_;
+
+  public:
+    PluginException(OrthancPluginErrorCode code) : code_(code)
+    {
+    }
+
+    OrthancPluginErrorCode GetErrorCode() const
+    {
+      return code_;
+    }
+
+    const char* GetErrorDescription(OrthancPluginContext* context) const;
+  };
+
+
+  class MemoryBuffer : public boost::noncopyable
+  {
+  private:
+    OrthancPluginContext*      context_;
+    OrthancPluginMemoryBuffer  buffer_;
+
+  public:
+    MemoryBuffer(OrthancPluginContext* context);
+
+    ~MemoryBuffer()
+    {
+      Clear();
+    }
+
+    OrthancPluginMemoryBuffer* operator*()
+    {
+      return &buffer_;
+    }
+
+    const void* GetData() const
+    {
+      return buffer_.data;
+    }
+
+    size_t GetSize() const
+    {
+      return buffer_.size;
+    }
+
+    void Clear();
+
+    void ToString(std::string& target) const;
+
+    void ToJson(Json::Value& target) const;
+
+    bool RestApiGet(const std::string& uri,
+                    bool applyPlugins);
+
+    bool RestApiPost(const std::string& uri,
+                     const std::string& body,
+                     bool applyPlugins);
+
+    bool RestApiPut(const std::string& uri,
+                    const std::string& body,
+                    bool applyPlugins);
+  };
+
+
+  class OrthancString : public boost::noncopyable
+  {
+  private:
+    OrthancPluginContext*  context_;
+    char*                  str_;
+
+  public:
+    OrthancString(OrthancPluginContext* context,
+                  char* str);
+
+    ~OrthancString()
+    {
+      Clear();
+    }
+
+    void Clear();
+
+    const char* GetContent() const
+    {
+      return str_;
+    }
+
+    void ToString(std::string& target) const;
+
+    void ToJson(Json::Value& target) const;
+  };
+
+
+  class OrthancConfiguration : public boost::noncopyable
+  {
+  private:
+    OrthancPluginContext*  context_;
+    Json::Value            configuration_;
+    std::string            path_;
+
+    std::string GetPath(const std::string& key) const;
+
+  public:
+    OrthancConfiguration() : context_(NULL)
+    {
+    }
+
+    OrthancConfiguration(OrthancPluginContext* context);
+
+    OrthancPluginContext* GetContext() const;
+
+    const Json::Value& GetJson() const
+    {
+      return configuration_;
+    }
+
+    void GetSection(OrthancConfiguration& target,
+                    const std::string& key) const;
+
+    bool LookupStringValue(std::string& target,
+                           const std::string& key) const;
+    
+    bool LookupIntegerValue(int& target,
+                            const std::string& key) const;
+
+    bool LookupUnsignedIntegerValue(unsigned int& target,
+                                    const std::string& key) const;
+
+    bool LookupBooleanValue(bool& target,
+                            const std::string& key) const;
+
+    bool LookupFloatValue(float& target,
+                          const std::string& key) const;
+
+    std::string GetStringValue(const std::string& key,
+                               const std::string& defaultValue) const;
+
+    int GetIntegerValue(const std::string& key,
+                        int defaultValue) const;
+
+    unsigned int GetUnsignedIntegerValue(const std::string& key,
+                                         unsigned int defaultValue) const;
+
+    bool GetBooleanValue(const std::string& key,
+                         bool defaultValue) const;
+
+    float GetFloatValue(const std::string& key,
+                        float defaultValue) const;
+  };
+
+
+
+
+  bool RestApiDelete(OrthancPluginContext* context,
+                     const std::string& uri,
+                     bool applyPlugins);
+
+
+
+  namespace Internals
+  {
+    template <RestCallback Callback>
+    OrthancPluginErrorCode Protect(OrthancPluginRestOutput* output,
+                                   const char* url,
+                                   const OrthancPluginHttpRequest* request)
+    {
+      try
+      {
+        Callback(output, url, request);
+        return OrthancPluginErrorCode_Success;
+      }
+      catch (OrthancPlugins::PluginException& e)
+      {
+        return e.GetErrorCode();
+      }
+#if HAS_ORTHANC_EXCEPTION == 1
+      catch (Orthanc::OrthancException& e)
+      {
+        return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
+      }
+#endif
+      catch (boost::bad_lexical_cast& e)
+      {
+        return OrthancPluginErrorCode_BadFileFormat;
+      }
+      catch (...)
+      {
+        return OrthancPluginErrorCode_Plugin;
+      }
+    }
+  }
+
+  
+  template <RestCallback Callback>
+  void RegisterRestCallback(OrthancPluginContext* context,
+                            const std::string& uri,
+                            bool isThreadSafe)
+  {
+    if (isThreadSafe)
+    {
+      OrthancPluginRegisterRestCallbackNoLock(context, uri.c_str(), Internals::Protect<Callback>);
+    }
+    else
+    {
+      OrthancPluginRegisterRestCallback(context, uri.c_str(), Internals::Protect<Callback>);
+    }
+  }
+}
--- a/Plugin/Configuration.cpp	Thu Jun 23 10:09:16 2016 +0200
+++ b/Plugin/Configuration.cpp	Thu Jun 23 17:31:40 2016 +0200
@@ -25,6 +25,8 @@
 #include <boost/regex.hpp>
 #include <boost/lexical_cast.hpp>
 
+#include "Plugin.h"
+#include "DicomWebServers.h"
 #include "../Orthanc/Core/Toolbox.h"
 
 namespace OrthancPlugins
@@ -309,73 +311,43 @@
 
   namespace Configuration
   {
-    bool Read(Json::Value& configuration,
-              OrthancPluginContext* context)
-    {
-      std::string s;
+    static OrthancConfiguration configuration_;
 
-      {
-        char* tmp = OrthancPluginGetConfiguration(context);
-        if (tmp == NULL)
-        {
-          OrthancPluginLogError(context, "Error while retrieving the configuration from Orthanc");
-          return false;
-        }
 
-        s.assign(tmp);
-        OrthancPluginFreeString(context, tmp);      
-      }
+    void Initialize(OrthancPluginContext* context)
+    {      
+      OrthancPlugins::OrthancConfiguration global(context);
+      global.GetSection(configuration_, "DicomWeb");
 
-      Json::Reader reader;
-      if (reader.parse(s, configuration))
-      {
-        return true;
-      }
-      else
-      {
-        OrthancPluginLogError(context, "Unable to parse the configuration");
-        return false;
-      }
+      OrthancPlugins::OrthancConfiguration servers;
+      configuration_.GetSection(servers, "Servers");
+      OrthancPlugins::DicomWebServers::GetInstance().Load(servers.GetJson());
     }
 
 
-    std::string GetStringValue(const Json::Value& configuration,
-                               const std::string& key,
-                               const std::string& defaultValue)
+    OrthancPluginContext* GetContext()
     {
-      if (configuration.type() != Json::objectValue ||
-          !configuration.isMember(key) ||
-          configuration[key].type() != Json::stringValue)
-      {
-        return defaultValue;
-      }
-      else
-      {
-        return configuration[key].asString();
-      }
+      return configuration_.GetContext();
     }
 
 
-    bool GetBoolValue(const Json::Value& configuration,
-                      const std::string& key,
-                      bool defaultValue)
+    std::string GetStringValue(const std::string& key,
+                               const std::string& defaultValue)
     {
-      if (configuration.type() != Json::objectValue ||
-          !configuration.isMember(key) ||
-          configuration[key].type() != Json::booleanValue)
-      {
-        return defaultValue;
-      }
-      else
-      {
-        return configuration[key].asBool();
-      }
+      return configuration_.GetStringValue(key, defaultValue);
     }
 
 
-    std::string GetRoot(const Json::Value& configuration)
+    bool GetBooleanValue(const std::string& key,
+                         bool defaultValue)
     {
-      std::string root = GetStringValue(configuration, "Root", "/dicom-web/");
+      return configuration_.GetBooleanValue(key, defaultValue);
+    }
+
+
+    std::string GetRoot()
+    {
+      std::string root = configuration_.GetStringValue("Root", "/dicom-web/");
 
       // Make sure the root URI starts and ends with a slash
       if (root.size() == 0 ||
@@ -393,9 +365,9 @@
     }
 
 
-    std::string GetWadoRoot(const Json::Value& configuration)
+    std::string GetWadoRoot()
     {
-      std::string root = GetStringValue(configuration, "WadoRoot", "/wado/");
+      std::string root = configuration_.GetStringValue("WadoRoot", "/wado/");
 
       // Make sure the root URI starts with a slash
       if (root.size() == 0 ||
@@ -414,11 +386,10 @@
     }
 
 
-    std::string  GetBaseUrl(const Json::Value& configuration,
-                            const OrthancPluginHttpRequest* request)
+    std::string  GetBaseUrl(const OrthancPluginHttpRequest* request)
     {
-      std::string host = GetStringValue(configuration, "Host", "");
-      bool ssl = GetBoolValue(configuration, "Ssl", false);
+      std::string host = configuration_.GetStringValue("Host", "");
+      bool ssl = configuration_.GetBooleanValue("Ssl", false);
 
       if (host.empty() &&
           !LookupHttpHeader(host, request, "host"))
@@ -428,11 +399,10 @@
         host = "localhost:8042";
       }
 
-      return (ssl ? "https://" : "http://") + host + GetRoot(configuration);
+      return (ssl ? "https://" : "http://") + host + GetRoot();
     }
 
 
-
     std::string GetWadoUrl(const std::string& wadoBase,
                            const std::string& studyInstanceUid,
                            const std::string& seriesInstanceUid,
@@ -452,5 +422,23 @@
                 "/instances/" + sopInstanceUid + "/");
       }
     }
+
+
+    void LogError(const std::string& message)
+    {
+      OrthancPluginLogError(GetContext(), message.c_str());
+    }
+
+
+    void LogWarning(const std::string& message)
+    {
+      OrthancPluginLogWarning(GetContext(), message.c_str());
+    }
+
+
+    void LogInfo(const std::string& message)
+    {
+      OrthancPluginLogInfo(GetContext(), message.c_str());
+    }
   }
 }
--- a/Plugin/Configuration.h	Thu Jun 23 10:09:16 2016 +0200
+++ b/Plugin/Configuration.h	Thu Jun 23 17:31:40 2016 +0200
@@ -76,27 +76,31 @@
 
   namespace Configuration
   {
-    bool Read(Json::Value& configuration,
-              OrthancPluginContext* context);
+    void Initialize(OrthancPluginContext* context);
 
-    std::string GetStringValue(const Json::Value& configuration,
-                               const std::string& key,
-                               const std::string& defaultValue);
+    OrthancPluginContext* GetContext();
     
-    bool GetBoolValue(const Json::Value& configuration,
-                      const std::string& key,
-                      bool defaultValue);
+    std::string GetStringValue(const std::string& key,
+                               const std::string& defaultValue);
+
+    bool GetBooleanValue(const std::string& key,
+                         bool defaultValue);
 
-    std::string GetRoot(const Json::Value& configuration);
+    std::string GetRoot();
 
-    std::string GetWadoRoot(const Json::Value& configuration);
+    std::string GetWadoRoot();
       
-    std::string GetBaseUrl(const Json::Value& configuration,
-                           const OrthancPluginHttpRequest* request);
+    std::string GetBaseUrl(const OrthancPluginHttpRequest* request);
 
     std::string GetWadoUrl(const std::string& wadoBase,
                            const std::string& studyInstanceUid,
                            const std::string& seriesInstanceUid,
                            const std::string& sopInstanceUid);
+
+    void LogError(const std::string& message);
+
+    void LogWarning(const std::string& message);
+
+    void LogInfo(const std::string& message);
   }
 }
--- a/Plugin/Dicom.cpp	Thu Jun 23 10:09:16 2016 +0200
+++ b/Plugin/Dicom.cpp	Thu Jun 23 17:31:40 2016 +0200
@@ -662,7 +662,7 @@
 
   std::string ParsedDicomFile::GetWadoUrl(const OrthancPluginHttpRequest* request) const
   {
-    const std::string base = OrthancPlugins::Configuration::GetBaseUrl(configuration_, request);
+    const std::string base = OrthancPlugins::Configuration::GetBaseUrl(request);
     return OrthancPlugins::GetWadoUrl(base, GetDataSet());
   }
 
@@ -693,8 +693,7 @@
   {
     if (key.find('.') != std::string::npos)
     {
-      std::string s = "This DICOMweb plugin does not support hierarchical queries: " + key;
-      OrthancPluginLogError(context_, s.c_str());
+      OrthancPlugins::Configuration::LogError("This DICOMweb plugin does not support hierarchical queries: " + key);
       throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
     }
 
@@ -734,14 +733,12 @@
       {
         if (key.find('.') != std::string::npos)
         {
-          std::string s = "This QIDO-RS implementation does not support search over sequences: " + key;
-          OrthancPluginLogError(context_, s.c_str());
+          OrthancPlugins::Configuration::LogError("This QIDO-RS implementation does not support search over sequences: " + key);
           throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
         }
         else
         {
-          std::string s = "Illegal tag name in QIDO-RS: " + key;
-          OrthancPluginLogError(context_, s.c_str());
+          OrthancPlugins::Configuration::LogError("Illegal tag name in QIDO-RS: " + key);
           throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownDicomTag);
         }
       }
--- a/Plugin/DicomResults.cpp	Thu Jun 23 10:09:16 2016 +0200
+++ b/Plugin/DicomResults.cpp	Thu Jun 23 17:31:40 2016 +0200
@@ -46,7 +46,7 @@
     if (isXml_ &&
         OrthancPluginStartMultipartAnswer(context_, output_, "related", "application/dicom+xml") != 0)
     {
-      OrthancPluginLogError(context_, "Unable to create a multipart stream of DICOM+XML answers");
+      OrthancPlugins::Configuration::LogError("Unable to create a multipart stream of DICOM+XML answers");
       throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
     }
 
@@ -60,7 +60,7 @@
     {
       if (OrthancPluginSendMultipartItem(context_, output_, item.c_str(), item.size()) != 0)
       {
-        OrthancPluginLogError(context_, "Unable to create a multipart stream of DICOM+XML answers");
+        OrthancPlugins::Configuration::LogError("Unable to create a multipart stream of DICOM+XML answers");
         throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
       }
     }
--- a/Plugin/DicomWebServers.cpp	Thu Jun 23 10:09:16 2016 +0200
+++ b/Plugin/DicomWebServers.cpp	Thu Jun 23 17:31:40 2016 +0200
@@ -20,7 +20,7 @@
 
 #include "DicomWebServers.h"
 
-#include "Plugin.h"
+#include "Configuration.h"
 #include "../Orthanc/Core/OrthancException.h"
 
 namespace OrthancPlugins
@@ -34,33 +34,28 @@
   }
 
 
-  void DicomWebServers::Load(const Json::Value& configuration)
+  void DicomWebServers::Load(const Json::Value& servers)
   {
     boost::mutex::scoped_lock lock(mutex_);
 
     Clear();
 
-    if (!configuration.isMember("Servers"))
-    {
-      return;
-    }
-
     bool ok = true;
 
     try
     {
-      if (configuration["Servers"].type() != Json::objectValue)
+      if (servers.type() != Json::objectValue)
       {
         ok = false;
       }
       else
       {
-        Json::Value::Members members = configuration["Servers"].getMemberNames();
+        Json::Value::Members members = servers.getMemberNames();
 
         for (size_t i = 0; i < members.size(); i++)
         {
           std::auto_ptr<Orthanc::WebServiceParameters> parameters(new Orthanc::WebServiceParameters);
-          parameters->FromJson(configuration["Servers"][members[i]]);
+          parameters->FromJson(servers[members[i]]);
 
           servers_[members[i]] = parameters.release();
         }
@@ -68,15 +63,14 @@
     }
     catch (Orthanc::OrthancException& e)
     {
-      std::string s = ("Exception while parsing the \"DicomWeb.Servers\" section "
-                       "of the configuration file: " + std::string(e.What()));
-      OrthancPluginLogError(context_, s.c_str());
+      OrthancPlugins::Configuration::LogError("Exception while parsing the \"DicomWeb.Servers\" section "
+                                              "of the configuration file: " + std::string(e.What()));
       throw;
     }
 
     if (!ok)
     {
-      OrthancPluginLogError(context_, "Cannot parse the \"DicomWeb.Servers\" section of the configuration file");
+      OrthancPlugins::Configuration::LogError("Cannot parse the \"DicomWeb.Servers\" section of the configuration file");
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
     }
   }
@@ -97,8 +91,7 @@
     if (server == servers_.end() ||
         server->second == NULL)
     {
-      std::string s = "Inexistent server: " + name;
-      OrthancPluginLogError(context_, s.c_str());
+      OrthancPlugins::Configuration::LogError("Inexistent server: " + name);
       throw Orthanc::OrthancException(Orthanc::ErrorCode_InexistentItem);
     }
     else
--- a/Plugin/DicomWebServers.h	Thu Jun 23 10:09:16 2016 +0200
+++ b/Plugin/DicomWebServers.h	Thu Jun 23 17:31:40 2016 +0200
@@ -20,6 +20,7 @@
 #pragma once
 
 #include "../Orthanc/Core/WebServiceParameters.h"
+#include "../Orthanc/Plugins/Samples/Common/OrthancPluginCppWrapper.h"
 
 #include <list>
 #include <string>
@@ -56,4 +57,10 @@
 
     void ListServers(std::list<std::string>& servers);
   };
+
+  void QueryServer(std::string& result,
+                   const Orthanc::WebServiceParameters& server,
+                   const std::map<std::string, std::string>& httpHeaders,
+                   const std::string& uri,
+                   const std::string& body);
 }
--- a/Plugin/Plugin.cpp	Thu Jun 23 10:09:16 2016 +0200
+++ b/Plugin/Plugin.cpp	Thu Jun 23 17:31:40 2016 +0200
@@ -28,6 +28,8 @@
 #include "Configuration.h"
 #include "DicomWebServers.h"
 
+#include "../Orthanc/Plugins/Samples/Common/OrthancPluginCppWrapper.h"
+
 #include <gdcmDictEntry.h>
 #include <gdcmDict.h>
 #include <gdcmDicts.h>
@@ -35,48 +37,13 @@
 
 
 // Global state
-OrthancPluginContext* context_ = NULL;
-Json::Value configuration_;
 const gdcm::Dict* dictionary_ = NULL;
 
+
 #include "../Orthanc/Core/OrthancException.h"
 #include <boost/lexical_cast.hpp>
 
 
-typedef void (*RestCallback) (OrthancPluginRestOutput* output,
-                              const char* url,
-                              const OrthancPluginHttpRequest* request);
-
-
-template <RestCallback Callback>
-OrthancPluginErrorCode Protect(OrthancPluginRestOutput* output,
-                               const char* url,
-                               const OrthancPluginHttpRequest* request)
-{
-  try
-  {
-    Callback(output, url, request);
-    return OrthancPluginErrorCode_Success;
-  }
-  catch (Orthanc::OrthancException& e)
-  {
-    OrthancPluginLogError(context_, e.What());
-    return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
-  }
-  catch (boost::bad_lexical_cast& e)
-  {
-    OrthancPluginLogError(context_, e.what());
-    return OrthancPluginErrorCode_Plugin;
-  }
-  catch (std::runtime_error& e)
-  {
-    OrthancPluginLogError(context_, e.what());
-    return OrthancPluginErrorCode_Plugin;
-  }
-}
-
-
-
 void SwitchStudies(OrthancPluginRestOutput* output,
                    const char* url,
                    const OrthancPluginHttpRequest* request)
@@ -94,7 +61,7 @@
       break;
 
     default:
-      OrthancPluginSendMethodNotAllowed(context_, output, "GET,POST");
+      OrthancPluginSendMethodNotAllowed(OrthancPlugins::Configuration::GetContext(), output, "GET,POST");
       break;
   }
 }
@@ -117,30 +84,19 @@
       break;
 
     default:
-      OrthancPluginSendMethodNotAllowed(context_, output, "GET,POST");
+      OrthancPluginSendMethodNotAllowed(OrthancPlugins::Configuration::GetContext(), output, "GET,POST");
       break;
   }
 }
 
 
-static void Register(const std::string& root,
-                     const std::string& uri,
-                     OrthancPluginRestCallback callback)
-{
-  assert(!uri.empty() && uri[0] != '/');
-  std::string s = root + uri;
-  OrthancPluginRegisterRestCallback(context_, s.c_str(), callback);
-}
-
-
-
 void ListServers(OrthancPluginRestOutput* output,
                  const char* url,
                  const OrthancPluginHttpRequest* request)
 {
   if (request->method != OrthancPluginHttpMethod_Get)
   {
-    OrthancPluginSendMethodNotAllowed(context_, output, "GET");
+    OrthancPluginSendMethodNotAllowed(OrthancPlugins::Configuration::GetContext(), output, "GET");
   }
   else
   {
@@ -154,7 +110,7 @@
     }
 
     std::string answer = json.toStyledString(); 
-    OrthancPluginAnswerBuffer(context_, output, answer.c_str(), answer.size(), "application/json");
+    OrthancPluginAnswerBuffer(OrthancPlugins::Configuration::GetContext(), output, answer.c_str(), answer.size(), "application/json");
   }
 }
 
@@ -165,7 +121,7 @@
 {
   if (request->method != OrthancPluginHttpMethod_Get)
   {
-    OrthancPluginSendMethodNotAllowed(context_, output, "GET");
+    OrthancPluginSendMethodNotAllowed(OrthancPlugins::Configuration::GetContext(), output, "GET");
   }
   else
   {
@@ -173,11 +129,48 @@
     OrthancPlugins::DicomWebServers::GetInstance().GetServer(request->groups[0]);
 
     Json::Value json = Json::arrayValue;
+    json.append("get");
     json.append("stow");
 
     std::string answer = json.toStyledString(); 
-    OrthancPluginAnswerBuffer(context_, output, answer.c_str(), answer.size(), "application/json");
+    OrthancPluginAnswerBuffer(OrthancPlugins::Configuration::GetContext(), output, answer.c_str(), answer.size(), "application/json");
+  }
+}
+
+
+
+void GetFromServer(OrthancPluginRestOutput* output,
+                   const char* url,
+                   const OrthancPluginHttpRequest* request)
+{
+  if (request->method != OrthancPluginHttpMethod_Post)
+  {
+    OrthancPluginSendMethodNotAllowed(OrthancPlugins::Configuration::GetContext(), output, "POST");
+    return;
   }
+
+  Orthanc::WebServiceParameters server(OrthancPlugins::DicomWebServers::GetInstance().GetServer(request->groups[0]));
+
+#if 0
+  static const char* URI = "Uri";
+  static const char* HTTP_HEADERS = "HttpHeaders";
+
+  Json::Value body;
+  Json::Reader reader;
+  if (!reader.parse(request->body, request->body + request->bodySize, body) ||
+      body.type() != Json::objectValue ||
+      !body.isMember(URI) ||
+      body[URI].type() != Json::stringValue)
+  {
+    std::string s = ("A request to the DICOMweb STOW-RS client must provide a JSON object "
+                     "with the field \"Uri\" containing the URI of interest");
+    OrthancPluginLogError(OrthancPlugins::Configuration::GetContext(), s.c_str());
+    throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+  }
+
+  std::map<std::string, std::string>
+  Json
+#endif
 }
 
 
@@ -186,93 +179,86 @@
 {
   ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext* context)
   {
-    context_ = context;
-
     /* Check the version of the Orthanc core */
-    if (OrthancPluginCheckVersion(context_) == 0)
+    if (OrthancPluginCheckVersion(context) == 0)
     {
       char info[1024];
       sprintf(info, "Your version of Orthanc (%s) must be above %d.%d.%d to run this plugin",
-              context_->orthancVersion,
+              context->orthancVersion,
               ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER,
               ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER,
               ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER);
-      OrthancPluginLogError(context_, info);
+      OrthancPluginLogError(context, info);
       return -1;
     }
 
-    OrthancPluginSetDescription(context_, "Implementation of DICOMweb (QIDO-RS, STOW-RS and WADO-RS) and WADO-URI.");
+    OrthancPluginSetDescription(context, "Implementation of DICOMweb (QIDO-RS, STOW-RS and WADO-RS) and WADO-URI.");
+
+    try
+    {
+      // Read the configuration
+      OrthancPlugins::Configuration::Initialize(context);
 
-    // Read the configuration
-    dictionary_ = &gdcm::Global::GetInstance().GetDicts().GetPublicDict();
+      // Initialize GDCM
+      dictionary_ = &gdcm::Global::GetInstance().GetDicts().GetPublicDict();
 
-    configuration_ = Json::objectValue;
+      // Configure the DICOMweb callbacks
+      if (OrthancPlugins::Configuration::GetBooleanValue("Enable", true))
+      {
+        std::string root = OrthancPlugins::Configuration::GetRoot();
+        assert(!root.empty() && root[root.size() - 1] == '/');
+
+        OrthancPlugins::Configuration::LogWarning("URI to the DICOMweb REST API: " + root);
 
-    {
-      Json::Value tmp;
-      if (!OrthancPlugins::Configuration::Read(tmp, context) ||
-          tmp.type() != Json::objectValue)
+        OrthancPlugins::RegisterRestCallback<SearchForInstances>(context, root + "instances", true);
+        OrthancPlugins::RegisterRestCallback<SearchForSeries>(context, root + "series", true);    
+        OrthancPlugins::RegisterRestCallback<SwitchStudies>(context, root + "studies", true);
+        OrthancPlugins::RegisterRestCallback<SwitchStudy>(context, root + "studies/([^/]*)", true);
+        OrthancPlugins::RegisterRestCallback<SearchForInstances>(context, root + "studies/([^/]*)/instances", true);    
+        OrthancPlugins::RegisterRestCallback<RetrieveStudyMetadata>(context, root + "studies/([^/]*)/metadata", true);
+        OrthancPlugins::RegisterRestCallback<SearchForSeries>(context, root + "studies/([^/]*)/series", true);    
+        OrthancPlugins::RegisterRestCallback<RetrieveDicomSeries>(context, root + "studies/([^/]*)/series/([^/]*)", true);
+        OrthancPlugins::RegisterRestCallback<SearchForInstances>(context, root + "studies/([^/]*)/series/([^/]*)/instances", true);    
+        OrthancPlugins::RegisterRestCallback<RetrieveDicomInstance>(context, root + "studies/([^/]*)/series/([^/]*)/instances/([^/]*)", true);
+        OrthancPlugins::RegisterRestCallback<RetrieveBulkData>(context, root + "studies/([^/]*)/series/([^/]*)/instances/([^/]*)/bulk/(.*)", true);
+        OrthancPlugins::RegisterRestCallback<RetrieveInstanceMetadata>(context, root + "studies/([^/]*)/series/([^/]*)/instances/([^/]*)/metadata", true);
+        OrthancPlugins::RegisterRestCallback<RetrieveSeriesMetadata>(context, root + "studies/([^/]*)/series/([^/]*)/metadata", true);
+        OrthancPlugins::RegisterRestCallback<RetrieveFrames>(context, root + "studies/([^/]*)/series/([^/]*)/instances/([^/]*)/frames", true);
+        OrthancPlugins::RegisterRestCallback<RetrieveFrames>(context, root + "studies/([^/]*)/series/([^/]*)/instances/([^/]*)/frames/([^/]*)", true);
+
+        OrthancPlugins::RegisterRestCallback<ListServers>(context, root + "servers", true);
+        OrthancPlugins::RegisterRestCallback<ListServerOperations>(context, root + "servers/([^/]*)", true);
+        OrthancPlugins::RegisterRestCallback<StowClient>(context, root + "servers/([^/]*)/stow", true);
+        OrthancPlugins::RegisterRestCallback<GetFromServer>(context, root + "servers/([^/]*)/get", true);
+      }
+      else
       {
-        OrthancPluginLogError(context_, "Unable to read the configuration file");
-        return -1;
+        OrthancPlugins::Configuration::LogWarning("DICOMweb support is disabled");
       }
 
-      if (tmp.isMember("DicomWeb") &&
-          tmp["DicomWeb"].type() == Json::objectValue)
+      // Configure the WADO callback
+      if (OrthancPlugins::Configuration::GetBooleanValue("EnableWado", true))
       {
-        configuration_ = tmp["DicomWeb"];
+        std::string wado = OrthancPlugins::Configuration::GetWadoRoot();
+        OrthancPlugins::Configuration::LogWarning("URI to the WADO-URI API: " + wado);
+
+        OrthancPlugins::RegisterRestCallback<WadoUriCallback>(context, wado, true);
+      }
+      else
+      {
+        OrthancPlugins::Configuration::LogWarning("WADO-URI support is disabled");
       }
     }
-
-    OrthancPlugins::DicomWebServers::GetInstance().Load(configuration_);
-
-
-    // Configure the DICOMweb callbacks
-    if (OrthancPlugins::Configuration::GetBoolValue(configuration_, "Enable", true))
+    catch (OrthancPlugins::PluginException& e)
     {
-      std::string root = OrthancPlugins::Configuration::GetRoot(configuration_);
-
-      std::string message = "URI to the DICOMweb REST API: " + root;
-      OrthancPluginLogWarning(context_, message.c_str());
-
-      Register(root, "instances", Protect<SearchForInstances>);
-      Register(root, "series", Protect<SearchForSeries>);    
-      Register(root, "studies", Protect<SwitchStudies>);
-      Register(root, "studies/([^/]*)", Protect<SwitchStudy>);
-      Register(root, "studies/([^/]*)/instances", Protect<SearchForInstances>);    
-      Register(root, "studies/([^/]*)/metadata", Protect<RetrieveStudyMetadata>);
-      Register(root, "studies/([^/]*)/series", Protect<SearchForSeries>);    
-      Register(root, "studies/([^/]*)/series/([^/]*)", Protect<RetrieveDicomSeries>);
-      Register(root, "studies/([^/]*)/series/([^/]*)/instances", Protect<SearchForInstances>);    
-      Register(root, "studies/([^/]*)/series/([^/]*)/instances/([^/]*)", Protect<RetrieveDicomInstance>);
-      Register(root, "studies/([^/]*)/series/([^/]*)/instances/([^/]*)/bulk/(.*)", Protect<RetrieveBulkData>);
-      Register(root, "studies/([^/]*)/series/([^/]*)/instances/([^/]*)/metadata", Protect<RetrieveInstanceMetadata>);
-      Register(root, "studies/([^/]*)/series/([^/]*)/metadata", Protect<RetrieveSeriesMetadata>);
-      Register(root, "studies/([^/]*)/series/([^/]*)/instances/([^/]*)/frames", Protect<RetrieveFrames>);
-      Register(root, "studies/([^/]*)/series/([^/]*)/instances/([^/]*)/frames/([^/]*)", Protect<RetrieveFrames>);
-
-      Register(root, "servers", Protect<ListServers>);
-      Register(root, "servers/([^/]*)", Protect<ListServerOperations>);
-      Register(root, "servers/([^/]*)/stow", Protect<StowClient>);
+      OrthancPlugins::Configuration::LogError("Exception while initializing the DICOMweb plugin: " + 
+                                              std::string(e.GetErrorDescription(context)));
+      return -1;
     }
-    else
-    {
-      OrthancPluginLogWarning(context_, "DICOMweb support is disabled");
-    }
-
-    // Configure the WADO callback
-    if (OrthancPlugins::Configuration::GetBoolValue(configuration_, "EnableWado", true))
+    catch (...)
     {
-      std::string wado = OrthancPlugins::Configuration::GetWadoRoot(configuration_);
-
-      std::string message = "URI to the WADO-URI API: " + wado;
-      OrthancPluginLogWarning(context_, message.c_str());
-
-      OrthancPluginRegisterRestCallback(context_, wado.c_str(), Protect<WadoUriCallback>);
-    }
-    else
-    {
-      OrthancPluginLogWarning(context_, "WADO-URI support is disabled");
+      OrthancPlugins::Configuration::LogError("Exception while initializing the DICOMweb plugin");
+      return -1;
     }
 
     return 0;
--- a/Plugin/Plugin.h	Thu Jun 23 10:09:16 2016 +0200
+++ b/Plugin/Plugin.h	Thu Jun 23 17:31:40 2016 +0200
@@ -20,11 +20,9 @@
 
 #pragma once
 
-#include <orthanc/OrthancCPlugin.h>
-#include <json/value.h>
+#include "../Orthanc/Plugins/Samples/Common/OrthancPluginCppWrapper.h"
+
 #include <gdcmDict.h>
 
 // Global state
-extern OrthancPluginContext* context_;
-extern Json::Value configuration_;
 extern const gdcm::Dict* dictionary_;
--- a/Plugin/QidoRs.cpp	Thu Jun 23 10:09:16 2016 +0200
+++ b/Plugin/QidoRs.cpp	Thu Jun 23 17:31:40 2016 +0200
@@ -188,8 +188,7 @@
           }
           else
           {
-            std::string s = "Not a proper value for fuzzy matching (true or false): " + value;
-            OrthancPluginLogError(context_, s.c_str());
+            OrthancPlugins::Configuration::LogError("Not a proper value for fuzzy matching (true or false): " + value);
             throw Orthanc::OrthancException(Orthanc::ErrorCode_BadRequest);
           }
         }
@@ -288,8 +287,8 @@
         case QueryLevel_Study:
         {
           Json::Value series, instances;
-          if (OrthancPlugins::RestApiGetJson(series, context_, "/studies/" + resource + "/series?expand") &&
-              OrthancPlugins::RestApiGetJson(instances, context_, "/studies/" + resource + "/instances"))
+          if (OrthancPlugins::RestApiGetJson(series, OrthancPlugins::Configuration::GetContext(), "/studies/" + resource + "/series?expand") &&
+              OrthancPlugins::RestApiGetJson(instances, OrthancPlugins::Configuration::GetContext(), "/studies/" + resource + "/instances"))
           {
             // Number of Study Related Series
             target[gdcm::Tag(0x0020, 0x1206)] = boost::lexical_cast<std::string>(series.size());
@@ -335,7 +334,7 @@
         case QueryLevel_Series:
         {
           Json::Value instances;
-          if (OrthancPlugins::RestApiGetJson(instances, context_, "/series/" + resource + "/instances"))
+          if (OrthancPlugins::RestApiGetJson(instances, OrthancPlugins::Configuration::GetContext(), "/series/" + resource + "/instances"))
           {
             // Number of Series Related Instances
             target[gdcm::Tag(0x0020, 0x1209)] = boost::lexical_cast<std::string>(instances.size());
@@ -505,7 +504,7 @@
   std::string body = writer.write(find);
   
   Json::Value resources;
-  if (!OrthancPlugins::RestApiPostJson(resources, context_, "/tools/find", body) ||
+  if (!OrthancPlugins::RestApiPostJson(resources, OrthancPlugins::Configuration::GetContext(), "/tools/find", body) ||
       resources.type() != Json::arrayValue)
   {
     throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
@@ -525,7 +524,7 @@
     {
       // Find one child instance of this resource
       Json::Value tmp;
-      if (OrthancPlugins::RestApiGetJson(tmp, context_, root + resource + "/instances") &&
+      if (OrthancPlugins::RestApiGetJson(tmp, OrthancPlugins::Configuration::GetContext(), root + resource + "/instances") &&
           tmp.type() == Json::arrayValue &&
           tmp.size() > 0)
       {
@@ -538,9 +537,9 @@
     }
   }
   
-  std::string wadoBase = OrthancPlugins::Configuration::GetBaseUrl(configuration_, request);
+  std::string wadoBase = OrthancPlugins::Configuration::GetBaseUrl(request);
 
-  OrthancPlugins::DicomResults results(context_, output, wadoBase, *dictionary_, IsXmlExpected(request), true);
+  OrthancPlugins::DicomResults results(OrthancPlugins::Configuration::GetContext(), output, wadoBase, *dictionary_, IsXmlExpected(request), true);
 
 #if 0
   // Implementation up to version 0.2 of the plugin. Each instance is
@@ -554,7 +553,7 @@
     matcher.ComputeDerivedTags(derivedTags, level, it->first);
 
     std::string file;
-    if (OrthancPlugins::RestApiGetString(file, context_, "/instances/" + it->second + "/file"))
+    if (OrthancPlugins::RestApiGetString(file, OrthancPlugins::Configuration::GetContext(), "/instances/" + it->second + "/file"))
     {
       OrthancPlugins::ParsedDicomFile dicom(file);
 
@@ -583,7 +582,7 @@
          it = resourcesAndInstances.begin(); it != resourcesAndInstances.end(); ++it)
   {
     Json::Value tags;
-    if (OrthancPlugins::RestApiGetJson(tags, context_, "/instances/" + it->second + "/tags"))
+    if (OrthancPlugins::RestApiGetJson(tags, OrthancPlugins::Configuration::GetContext(), "/instances/" + it->second + "/tags"))
     {
       std::string wadoUrl = OrthancPlugins::Configuration::GetWadoUrl(
         wadoBase, 
@@ -624,7 +623,7 @@
 {
   if (request->method != OrthancPluginHttpMethod_Get)
   {
-    OrthancPluginSendMethodNotAllowed(context_, output, "GET");
+    OrthancPluginSendMethodNotAllowed(OrthancPlugins::Configuration::GetContext(), output, "GET");
   }
   else
   {
@@ -640,7 +639,7 @@
 {
   if (request->method != OrthancPluginHttpMethod_Get)
   {
-    OrthancPluginSendMethodNotAllowed(context_, output, "GET");
+    OrthancPluginSendMethodNotAllowed(OrthancPlugins::Configuration::GetContext(), output, "GET");
   }
   else
   {
@@ -663,7 +662,7 @@
 {
   if (request->method != OrthancPluginHttpMethod_Get)
   {
-    OrthancPluginSendMethodNotAllowed(context_, output, "GET");
+    OrthancPluginSendMethodNotAllowed(OrthancPlugins::Configuration::GetContext(), output, "GET");
   }
   else
   {
--- a/Plugin/StowRs.cpp	Thu Jun 23 10:09:16 2016 +0200
+++ b/Plugin/StowRs.cpp	Thu Jun 23 17:31:40 2016 +0200
@@ -75,8 +75,7 @@
       accept != "text/xml" &&
       accept != "*/*")
   {
-    std::string s = "Unsupported return MIME type: " + accept + ", will return XML";
-    OrthancPluginLogError(context_, s.c_str());
+    OrthancPlugins::Configuration::LogError("Unsupported return MIME type: " + accept + ", will return XML");
   }
 
   return true;
@@ -88,11 +87,11 @@
                   const char* url,
                   const OrthancPluginHttpRequest* request)
 {
-  const std::string wadoBase = OrthancPlugins::Configuration::GetBaseUrl(configuration_, request);
+  const std::string wadoBase = OrthancPlugins::Configuration::GetBaseUrl(request);
 
   if (request->method != OrthancPluginHttpMethod_Post)
   {
-    OrthancPluginSendMethodNotAllowed(context_, output, "POST");
+    OrthancPluginSendMethodNotAllowed(OrthancPlugins::Configuration::GetContext(), output, "POST");
     return;
   }
 
@@ -104,12 +103,11 @@
 
   if (expectedStudy.empty())
   {
-    OrthancPluginLogInfo(context_, "STOW-RS request without study");
+    OrthancPlugins::Configuration::LogInfo("STOW-RS request without study");
   }
   else
   {
-    std::string s = "STOW-RS request restricted to study UID " + expectedStudy;
-    OrthancPluginLogInfo(context_, s.c_str());
+    OrthancPlugins::Configuration::LogInfo("STOW-RS request restricted to study UID " + expectedStudy);
   }
 
   bool isXml = IsXmlExpected(request);
@@ -117,8 +115,8 @@
   std::string header;
   if (!OrthancPlugins::LookupHttpHeader(header, request, "content-type"))
   {
-    OrthancPluginLogError(context_, "No content type in the HTTP header of a STOW-RS request");
-    OrthancPluginSendHttpStatusCode(context_, output, 400 /* Bad request */);
+    OrthancPlugins::Configuration::LogError("No content type in the HTTP header of a STOW-RS request");
+    OrthancPluginSendHttpStatusCode(OrthancPlugins::Configuration::GetContext(), output, 400 /* Bad request */);
     return;
   }
 
@@ -130,9 +128,8 @@
       attributes.find("type") == attributes.end() ||
       attributes.find("boundary") == attributes.end())
   {
-    std::string s = "Unable to parse the content type of a STOW-RS request (" + application + ")";
-    OrthancPluginLogError(context_, s.c_str());
-    OrthancPluginSendHttpStatusCode(context_, output, 400 /* Bad request */);
+    OrthancPlugins::Configuration::LogError("Unable to parse the content type of a STOW-RS request (" + application + ")");
+    OrthancPluginSendHttpStatusCode(OrthancPlugins::Configuration::GetContext(), output, 400 /* Bad request */);
     return;
   }
 
@@ -141,8 +138,8 @@
 
   if (attributes["type"] != "application/dicom")
   {
-    OrthancPluginLogError(context_, "The STOW-RS plugin currently only supports application/dicom");
-    OrthancPluginSendHttpStatusCode(context_, output, 415 /* Unsupported media type */);
+    OrthancPlugins::Configuration::LogError("The STOW-RS plugin currently only supports application/dicom");
+    OrthancPluginSendHttpStatusCode(OrthancPlugins::Configuration::GetContext(), output, 415 /* Unsupported media type */);
     return;
   }
 
@@ -153,15 +150,17 @@
   gdcm::SmartPointer<gdcm::SequenceOfItems> failed = new gdcm::SequenceOfItems();
   
   std::vector<OrthancPlugins::MultipartItem> items;
-  OrthancPlugins::ParseMultipartBody(items, context_, request->body, request->bodySize, boundary);
+  OrthancPlugins::ParseMultipartBody(items, OrthancPlugins::Configuration::GetContext(), request->body, request->bodySize, boundary);
+
+
   for (size_t i = 0; i < items.size(); i++)
   {
     if (!items[i].contentType_.empty() &&
         items[i].contentType_ != "application/dicom")
     {
-      std::string s = "The STOW-RS request contains a part that is not application/dicom (it is: \"" + items[i].contentType_ + "\")";
-      OrthancPluginLogError(context_, s.c_str());
-      OrthancPluginSendHttpStatusCode(context_, output, 415 /* Unsupported media type */);
+      OrthancPlugins::Configuration::LogError("The STOW-RS request contains a part that is not "
+                                              "\"application/dicom\" (it is: \"" + items[i].contentType_ + "\")");
+      OrthancPluginSendHttpStatusCode(OrthancPlugins::Configuration::GetContext(), output, 415 /* Unsupported media type */);
       return;
     }
 
@@ -181,9 +180,8 @@
     if (!expectedStudy.empty() &&
         studyInstanceUid != expectedStudy)
     {
-      std::string s = ("STOW-RS request restricted to study [" + expectedStudy + 
-                       "]: Ignoring instance from study [" + studyInstanceUid + "]");
-      OrthancPluginLogInfo(context_, s.c_str());
+      OrthancPlugins::Configuration::LogInfo("STOW-RS request restricted to study [" + expectedStudy + 
+                                             "]: Ignoring instance from study [" + studyInstanceUid + "]");
 
       SetTag(status, OrthancPlugins::DICOM_TAG_WARNING_REASON, gdcm::VR::US, "B006");  // Elements discarded
       success->AddItem(item);      
@@ -198,8 +196,8 @@
       }
 
       OrthancPluginMemoryBuffer result;
-      bool ok = OrthancPluginRestApiPost(context_, &result, "/instances", items[i].data_, items[i].size_) == 0;
-      OrthancPluginFreeMemoryBuffer(context_, &result);
+      bool ok = OrthancPluginRestApiPost(OrthancPlugins::Configuration::GetContext(), &result, "/instances", items[i].data_, items[i].size_) == 0;
+      OrthancPluginFreeMemoryBuffer(OrthancPlugins::Configuration::GetContext(), &result);
 
       if (ok)
       {
@@ -213,7 +211,7 @@
       }
       else
       {
-        OrthancPluginLogError(context_, "Orthanc was unable to store instance through STOW-RS request");
+        OrthancPlugins::Configuration::LogError("Orthanc was unable to store instance through STOW-RS request");
         SetTag(status, OrthancPlugins::DICOM_TAG_FAILURE_REASON, gdcm::VR::US, "0110");  // Processing failure
         failed->AddItem(item);
       }
@@ -223,5 +221,5 @@
   SetSequenceTag(result, OrthancPlugins::DICOM_TAG_FAILED_SOP_SEQUENCE, failed);
   SetSequenceTag(result, OrthancPlugins::DICOM_TAG_REFERENCED_SOP_SEQUENCE, success);
 
-  OrthancPlugins::AnswerDicom(context_, output, wadoBase, *dictionary_, result, isXml, false);
+  OrthancPlugins::AnswerDicom(OrthancPlugins::Configuration::GetContext(), output, wadoBase, *dictionary_, result, isXml, false);
 }
--- a/Plugin/StowRsClient.cpp	Thu Jun 23 10:09:16 2016 +0200
+++ b/Plugin/StowRsClient.cpp	Thu Jun 23 17:31:40 2016 +0200
@@ -70,9 +70,8 @@
   }
   else if (isMandatory)
   {
-    std::string s = ("The STOW-RS JSON response from DICOMweb server " + server + 
-                     " does not contain the mandatory tag " + upper);
-    OrthancPluginLogError(context_, s.c_str());
+    OrthancPlugins::Configuration::LogError("The STOW-RS JSON response from DICOMweb server " + server + 
+                                            " does not contain the mandatory tag " + upper);
     throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
   }
   else
@@ -84,8 +83,7 @@
       !value->isMember("Value") ||
       (*value) ["Value"].type() != Json::arrayValue)
   {
-    std::string s = "Unable to parse STOW-RS JSON response from DICOMweb server " + server;
-    OrthancPluginLogError(context_, s.c_str());
+    OrthancPlugins::Configuration::LogError("Unable to parse STOW-RS JSON response from DICOMweb server " + server);
     throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
   }
 
@@ -133,7 +131,7 @@
   uint16_t status = 0;
   OrthancPluginMemoryBuffer answerBody;
   OrthancPluginErrorCode code = OrthancPluginHttpClient(
-    context_, &answerBody, 
+    OrthancPlugins::Configuration::GetContext(), &answerBody, 
     NULL,                                   /* No interest in the HTTP headers of the answer */
     &status, 
     OrthancPluginHttpMethod_Post,
@@ -154,9 +152,8 @@
   if (code != OrthancPluginErrorCode_Success ||
       (status != 200 && status != 202))
   {
-    std::string s = ("Cannot send DICOM images through STOW-RS to DICOMweb server " + server.GetUrl() + 
-                     " (HTTP status: " + boost::lexical_cast<std::string>(status) + ")");
-    OrthancPluginLogError(context_, s.c_str());
+    OrthancPlugins::Configuration::LogError("Cannot send DICOM images through STOW-RS to DICOMweb server " + server.GetUrl() + 
+                                            " (HTTP status: " + boost::lexical_cast<std::string>(status) + ")");
     throw Orthanc::OrthancException(static_cast<Orthanc::ErrorCode>(code));
   }
 
@@ -164,14 +161,13 @@
   Json::Reader reader;
   bool success = reader.parse(reinterpret_cast<const char*>(answerBody.data),
                               reinterpret_cast<const char*>(answerBody.data) + answerBody.size, response);
-  OrthancPluginFreeMemoryBuffer(context_, &answerBody);
+  OrthancPluginFreeMemoryBuffer(OrthancPlugins::Configuration::GetContext(), &answerBody);
 
   if (!success ||
       response.type() != Json::objectValue ||
       !response.isMember("00081199"))
   {
-    std::string s = "Unable to parse STOW-RS JSON response from DICOMweb server " + server.GetUrl();
-    OrthancPluginLogError(context_, s.c_str());
+    OrthancPlugins::Configuration::LogError("Unable to parse STOW-RS JSON response from DICOMweb server " + server.GetUrl());
     throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
   }
 
@@ -179,30 +175,27 @@
   if (!GetSequenceSize(size, response, "00081199", true, server.GetUrl()) ||
       size != countInstances)
   {
-    std::string s = ("The STOW-RS server was only able to receive " + 
-                     boost::lexical_cast<std::string>(size) + " instances out of " +
-                     boost::lexical_cast<std::string>(countInstances));
-    OrthancPluginLogError(context_, s.c_str());
+    OrthancPlugins::Configuration::LogError("The STOW-RS server was only able to receive " + 
+                                            boost::lexical_cast<std::string>(size) + " instances out of " +
+                                            boost::lexical_cast<std::string>(countInstances));
     throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
   }
 
   if (GetSequenceSize(size, response, "00081198", false, server.GetUrl()) &&
       size != 0)
   {
-    std::string s = ("The response from the STOW-RS server contains " + 
-                     boost::lexical_cast<std::string>(size) + 
-                     " items in its Failed SOP Sequence (0008,1198) tag");
-    OrthancPluginLogError(context_, s.c_str());
+    OrthancPlugins::Configuration::LogError("The response from the STOW-RS server contains " + 
+                                            boost::lexical_cast<std::string>(size) + 
+                                            " items in its Failed SOP Sequence (0008,1198) tag");
     throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);    
   }
 
   if (GetSequenceSize(size, response, "0008119A", false, server.GetUrl()) &&
       size != 0)
   {
-    std::string s = ("The response from the STOW-RS server contains " + 
-                     boost::lexical_cast<std::string>(size) + 
-                     " items in its Other Failures Sequence (0008,119A) tag");
-    OrthancPluginLogError(context_, s.c_str());
+    OrthancPlugins::Configuration::LogError("The response from the STOW-RS server contains " + 
+                                            boost::lexical_cast<std::string>(size) + 
+                                            " items in its Other Failures Sequence (0008,119A) tag");
     throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);    
   }
 }
@@ -222,9 +215,9 @@
       !body.isMember(RESOURCES) ||
       body[RESOURCES].type() != Json::arrayValue)
   {
-    std::string s = ("A request to the DICOMweb STOW-RS client must provide a JSON object "
-                     "with the field \"Resources\" containing an array of resources to be sent");
-    OrthancPluginLogError(context_, s.c_str());
+    OrthancPlugins::Configuration::LogError("A request to the DICOMweb STOW-RS client must provide a JSON object "
+                                            "with the field \"" + std::string(RESOURCES) + 
+                                            "\" containing an array of resources to be sent");
     throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
   }
 
@@ -236,8 +229,8 @@
 
     if (tmp.type() != Json::objectValue)
     {
-      std::string s = "The HTTP headers of a DICOMweb STOW-RS client request must be given as a JSON associative array";
-      OrthancPluginLogError(context_, s.c_str());
+      OrthancPlugins::Configuration::LogError("The HTTP headers of a DICOMweb STOW-RS client request "
+                                              "must be given as a JSON associative array");
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
     }
     else
@@ -247,8 +240,8 @@
       {
         if (tmp[names[i]].type() != Json::stringValue)
         {
-          std::string s = "The HTTP header \"" + names[i] + "\" is not a string in some DICOMweb STOW-RS client request";
-          OrthancPluginLogError(context_, s.c_str());
+          OrthancPlugins::Configuration::LogError("The HTTP header \"" + names[i] + 
+                                                  "\" is not a string in some DICOMweb STOW-RS client request");
           throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
         }
         else
@@ -274,16 +267,16 @@
     }
 
     Json::Value tmp;
-    if (OrthancPlugins::RestApiGetJson(tmp, context_, "/instances/" + resource, false))
+    if (OrthancPlugins::RestApiGetJson(tmp, OrthancPlugins::Configuration::GetContext(), "/instances/" + resource, false))
     {
       AddInstance(instances, tmp);
     }
-    else if ((OrthancPlugins::RestApiGetJson(tmp, context_, "/series/" + resource, false) &&
-              OrthancPlugins::RestApiGetJson(tmp, context_, "/series/" + resource + "/instances", false)) ||
-             (OrthancPlugins::RestApiGetJson(tmp, context_, "/studies/" + resource, false) &&
-              OrthancPlugins::RestApiGetJson(tmp, context_, "/studies/" + resource + "/instances", false)) ||
-             (OrthancPlugins::RestApiGetJson(tmp, context_, "/patients/" + resource, false) &&
-              OrthancPlugins::RestApiGetJson(tmp, context_, "/patients/" + resource + "/instances", false)))
+    else if ((OrthancPlugins::RestApiGetJson(tmp, OrthancPlugins::Configuration::GetContext(), "/series/" + resource, false) &&
+              OrthancPlugins::RestApiGetJson(tmp, OrthancPlugins::Configuration::GetContext(), "/series/" + resource + "/instances", false)) ||
+             (OrthancPlugins::RestApiGetJson(tmp, OrthancPlugins::Configuration::GetContext(), "/studies/" + resource, false) &&
+              OrthancPlugins::RestApiGetJson(tmp, OrthancPlugins::Configuration::GetContext(), "/studies/" + resource + "/instances", false)) ||
+             (OrthancPlugins::RestApiGetJson(tmp, OrthancPlugins::Configuration::GetContext(), "/patients/" + resource, false) &&
+              OrthancPlugins::RestApiGetJson(tmp, OrthancPlugins::Configuration::GetContext(), "/patients/" + resource + "/instances", false)))
     {
       if (tmp.type() != Json::arrayValue)
       {
@@ -336,7 +329,7 @@
 
   if (request->method != OrthancPluginHttpMethod_Post)
   {
-    OrthancPluginSendMethodNotAllowed(context_, output, "POST");
+    OrthancPluginSendMethodNotAllowed(OrthancPlugins::Configuration::GetContext(), output, "POST");
     return;
   }
 
@@ -345,18 +338,18 @@
   std::string boundary;
 
   {
-    char* uuid = OrthancPluginGenerateUuid(context_);
+    char* uuid = OrthancPluginGenerateUuid(OrthancPlugins::Configuration::GetContext());
     try
     {
       boundary.assign(uuid);
     }
     catch (...)
     {
-      OrthancPluginFreeString(context_, uuid);
+      OrthancPluginFreeString(OrthancPlugins::Configuration::GetContext(), uuid);
       throw Orthanc::OrthancException(Orthanc::ErrorCode_NotEnoughMemory);
     }
 
-    OrthancPluginFreeString(context_, uuid);
+    OrthancPluginFreeString(OrthancPlugins::Configuration::GetContext(), uuid);
   }
 
   std::string mime = "multipart/related; type=application/dicom; boundary=" + boundary;
@@ -370,9 +363,8 @@
   ParseRestRequest(instances, httpHeaders, request);
 
   {
-    std::string s = ("Sending " + boost::lexical_cast<std::string>(instances.size()) + 
-                     " instances using STOW-RS to DICOMweb server: " + server.GetUrl());
-    OrthancPluginLogInfo(context_, s.c_str());
+    OrthancPlugins::Configuration::LogInfo("Sending " + boost::lexical_cast<std::string>(instances.size()) + 
+                                           " instances using STOW-RS to DICOMweb server: " + server.GetUrl());
   }
 
   Orthanc::ChunkedBuffer chunks;
@@ -381,7 +373,7 @@
   for (std::list<std::string>::const_iterator it = instances.begin(); it != instances.end(); it++)
   {
     std::string dicom;
-    if (OrthancPlugins::RestApiGetString(dicom, context_, "/instances/" + *it + "/file"))
+    if (OrthancPlugins::RestApiGetString(dicom, OrthancPlugins::Configuration::GetContext(), "/instances/" + *it + "/file"))
     {
       chunks.AddChunk("\r\n--" + boundary + "\r\n" +
                       "Content-Type: application/dicom\r\n" +
@@ -397,5 +389,5 @@
   SendStowChunks(server, httpHeaders, boundary, chunks, countInstances, true);
 
   std::string answer = "{}\n";
-  OrthancPluginAnswerBuffer(context_, output, answer.c_str(), answer.size(), "application/json");
+  OrthancPluginAnswerBuffer(OrthancPlugins::Configuration::GetContext(), output, answer.c_str(), answer.size(), "application/json");
 }
--- a/Plugin/WadoRs.cpp	Thu Jun 23 10:09:16 2016 +0200
+++ b/Plugin/WadoRs.cpp	Thu Jun 23 17:31:40 2016 +0200
@@ -45,8 +45,7 @@
   if (application != "multipart/related" &&
       application != "*/*")
   {
-    std::string s = "This WADO-RS plugin cannot generate the following content type: " + accept;
-    OrthancPluginLogError(context_, s.c_str());
+    OrthancPlugins::Configuration::LogError("This WADO-RS plugin cannot generate the following content type: " + accept);
     return false;
   }
 
@@ -56,16 +55,16 @@
     Orthanc::Toolbox::ToLowerCase(s);
     if (s != "application/dicom")
     {
-      std::string s = "This WADO-RS plugin only supports application/dicom return type for DICOM retrieval (" + accept + ")";
-      OrthancPluginLogError(context_, s.c_str());
+      OrthancPlugins::Configuration::LogError("This WADO-RS plugin only supports application/dicom "
+                                              "return type for DICOM retrieval (" + accept + ")");
       return false;
     }
   }
 
   if (attributes.find("transfer-syntax") != attributes.end())
   {
-    std::string s = "This WADO-RS plugin cannot change the transfer syntax to " + attributes["transfer-syntax"];
-    OrthancPluginLogError(context_, s.c_str());
+    OrthancPlugins::Configuration::LogError("This WADO-RS plugin cannot change the transfer syntax to " + 
+                                            attributes["transfer-syntax"]);
     return false;
   }
 
@@ -100,8 +99,7 @@
   if (application != "multipart/related" &&
       application != "*/*")
   {
-    std::string s = "This WADO-RS plugin cannot generate the following content type: " + accept;
-    OrthancPluginLogError(context_, s.c_str());
+    OrthancPlugins::Configuration::LogError("This WADO-RS plugin cannot generate the following content type: " + accept);
     return false;
   }
 
@@ -111,16 +109,16 @@
     Orthanc::Toolbox::ToLowerCase(s);
     if (s != "application/dicom+xml")
     {
-      std::string s = "This WADO-RS plugin only supports application/json or application/dicom+xml return types for metadata (" + accept + ")";
-      OrthancPluginLogError(context_, s.c_str());
+      OrthancPlugins::Configuration::LogError("This WADO-RS plugin only supports application/json or "
+                                              "application/dicom+xml return types for metadata (" + accept + ")");
       return false;
     }
   }
 
   if (attributes.find("transfer-syntax") != attributes.end())
   {
-    std::string s = "This WADO-RS plugin cannot change the transfer syntax to " + attributes["transfer-syntax"];
-    OrthancPluginLogError(context_, s.c_str());
+    OrthancPlugins::Configuration::LogError("This WADO-RS plugin cannot change the transfer syntax to " + 
+                                            attributes["transfer-syntax"]);
     return false;
   }
 
@@ -145,8 +143,7 @@
   if (application != "multipart/related" &&
       application != "*/*")
   {
-    std::string s = "This WADO-RS plugin cannot generate the following bulk data type: " + accept;
-    OrthancPluginLogError(context_, s.c_str());
+    OrthancPlugins::Configuration::LogError("This WADO-RS plugin cannot generate the following bulk data type: " + accept);
     return false;
   }
 
@@ -156,16 +153,16 @@
     Orthanc::Toolbox::ToLowerCase(s);
     if (s != "application/octet-stream")
     {
-      std::string s = "This WADO-RS plugin only supports application/octet-stream return type for bulk data retrieval (" + accept + ")";
-      OrthancPluginLogError(context_, s.c_str());
+      OrthancPlugins::Configuration::LogError("This WADO-RS plugin only supports application/octet-stream "
+                                              "return type for bulk data retrieval (" + accept + ")");
       return false;
     }
   }
 
   if (attributes.find("ra,ge") != attributes.end())
   {
-    std::string s = "This WADO-RS plugin does not support Range retrieval, it can only return entire bulk data object";
-    OrthancPluginLogError(context_, s.c_str());
+    OrthancPlugins::Configuration::LogError("This WADO-RS plugin does not support Range retrieval, "
+                                            "it can only return entire bulk data object");
     return false;
   }
 
@@ -177,14 +174,14 @@
                                        const std::string& resource)
 {
   Json::Value instances;
-  if (!OrthancPlugins::RestApiGetJson(instances, context_, resource + "/instances"))
+  if (!OrthancPlugins::RestApiGetJson(instances, OrthancPlugins::Configuration::GetContext(), resource + "/instances"))
   {
     // Internal error
-    OrthancPluginSendHttpStatusCode(context_, output, 400);
+    OrthancPluginSendHttpStatusCode(OrthancPlugins::Configuration::GetContext(), output, 400);
     return;
   }
 
-  if (OrthancPluginStartMultipartAnswer(context_, output, "related", "application/dicom"))
+  if (OrthancPluginStartMultipartAnswer(OrthancPlugins::Configuration::GetContext(), output, "related", "application/dicom"))
   {
     throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
   }
@@ -193,8 +190,8 @@
   {
     std::string uri = "/instances/" + instances[i]["ID"].asString() + "/file";
     std::string dicom;
-    if (OrthancPlugins::RestApiGetString(dicom, context_, uri) &&
-        OrthancPluginSendMultipartItem(context_, output, dicom.c_str(), dicom.size()) != 0)
+    if (OrthancPlugins::RestApiGetString(dicom, OrthancPlugins::Configuration::GetContext(), uri) &&
+        OrthancPluginSendMultipartItem(OrthancPlugins::Configuration::GetContext(), output, dicom.c_str(), dicom.size()) != 0)
     {
       throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
     }
@@ -217,10 +214,10 @@
   else
   {
     Json::Value instances;
-    if (!OrthancPlugins::RestApiGetJson(instances, context_, resource + "/instances"))
+    if (!OrthancPlugins::RestApiGetJson(instances, OrthancPlugins::Configuration::GetContext(), resource + "/instances"))
     {
       // Internal error
-      OrthancPluginSendHttpStatusCode(context_, output, 400);
+      OrthancPluginSendHttpStatusCode(OrthancPlugins::Configuration::GetContext(), output, 400);
       return;
     }
 
@@ -230,14 +227,14 @@
     }
   }
 
-  const std::string wadoBase = OrthancPlugins::Configuration::GetBaseUrl(configuration_, request);
-  OrthancPlugins::DicomResults results(context_, output, wadoBase, *dictionary_, isXml, true);
+  const std::string wadoBase = OrthancPlugins::Configuration::GetBaseUrl(request);
+  OrthancPlugins::DicomResults results(OrthancPlugins::Configuration::GetContext(), output, wadoBase, *dictionary_, isXml, true);
   
   for (std::list<std::string>::const_iterator
          it = files.begin(); it != files.end(); ++it)
   {
     std::string content; 
-    if (OrthancPlugins::RestApiGetString(content, context_, *it))
+    if (OrthancPlugins::RestApiGetString(content, OrthancPlugins::Configuration::GetContext(), *it))
     {
       OrthancPlugins::ParsedDicomFile dicom(content);
       results.Add(dicom.GetFile());
@@ -256,24 +253,23 @@
 {
   if (request->method != OrthancPluginHttpMethod_Get)
   {
-    OrthancPluginSendMethodNotAllowed(context_, output, "GET");
+    OrthancPluginSendMethodNotAllowed(OrthancPlugins::Configuration::GetContext(), output, "GET");
     return false;
   }
 
   std::string id;
 
   {
-    char* tmp = OrthancPluginLookupStudy(context_, request->groups[0]);
+    char* tmp = OrthancPluginLookupStudy(OrthancPlugins::Configuration::GetContext(), request->groups[0]);
     if (tmp == NULL)
     {
-      std::string s = "Accessing an inexistent study with WADO-RS: " + std::string(request->groups[0]);
-      OrthancPluginLogError(context_, s.c_str());
-      OrthancPluginSendHttpStatusCode(context_, output, 404);
+      OrthancPlugins::Configuration::LogError("Accessing an inexistent study with WADO-RS: " + std::string(request->groups[0]));
+      OrthancPluginSendHttpStatusCode(OrthancPlugins::Configuration::GetContext(), output, 404);
       return false;
     }
 
     id.assign(tmp);
-    OrthancPluginFreeString(context_, tmp);
+    OrthancPluginFreeString(OrthancPlugins::Configuration::GetContext(), tmp);
   }
   
   uri = "/studies/" + id;
@@ -287,38 +283,37 @@
 {
   if (request->method != OrthancPluginHttpMethod_Get)
   {
-    OrthancPluginSendMethodNotAllowed(context_, output, "GET");
+    OrthancPluginSendMethodNotAllowed(OrthancPlugins::Configuration::GetContext(), output, "GET");
     return false;
   }
 
   std::string id;
 
   {
-    char* tmp = OrthancPluginLookupSeries(context_, request->groups[1]);
+    char* tmp = OrthancPluginLookupSeries(OrthancPlugins::Configuration::GetContext(), request->groups[1]);
     if (tmp == NULL)
     {
-      std::string s = "Accessing an inexistent series with WADO-RS: " + std::string(request->groups[1]);
-      OrthancPluginLogError(context_, s.c_str());
-      OrthancPluginSendHttpStatusCode(context_, output, 404);
+      OrthancPlugins::Configuration::LogError("Accessing an inexistent series with WADO-RS: " + std::string(request->groups[1]));
+      OrthancPluginSendHttpStatusCode(OrthancPlugins::Configuration::GetContext(), output, 404);
       return false;
     }
 
     id.assign(tmp);
-    OrthancPluginFreeString(context_, tmp);
+    OrthancPluginFreeString(OrthancPlugins::Configuration::GetContext(), tmp);
   }
   
   Json::Value study;
-  if (!OrthancPlugins::RestApiGetJson(study, context_, "/series/" + id + "/study"))
+  if (!OrthancPlugins::RestApiGetJson(study, OrthancPlugins::Configuration::GetContext(), "/series/" + id + "/study"))
   {
-    OrthancPluginSendHttpStatusCode(context_, output, 404);
+    OrthancPluginSendHttpStatusCode(OrthancPlugins::Configuration::GetContext(), output, 404);
     return false;
   }
 
   if (study["MainDicomTags"]["StudyInstanceUID"].asString() != std::string(request->groups[0]))
   {
-    std::string s = "No series " + std::string(request->groups[1]) + " in study " + std::string(request->groups[0]);
-    OrthancPluginLogError(context_, s.c_str());
-    OrthancPluginSendHttpStatusCode(context_, output, 404);
+    OrthancPlugins::Configuration::LogError("No series " + std::string(request->groups[1]) + 
+                                            " in study " + std::string(request->groups[0]));
+    OrthancPluginSendHttpStatusCode(OrthancPlugins::Configuration::GetContext(), output, 404);
     return false;
   }
   
@@ -333,42 +328,41 @@
 {
   if (request->method != OrthancPluginHttpMethod_Get)
   {
-    OrthancPluginSendMethodNotAllowed(context_, output, "GET");
+    OrthancPluginSendMethodNotAllowed(OrthancPlugins::Configuration::GetContext(), output, "GET");
     return false;
   }
 
   std::string id;
 
   {
-    char* tmp = OrthancPluginLookupInstance(context_, request->groups[2]);
+    char* tmp = OrthancPluginLookupInstance(OrthancPlugins::Configuration::GetContext(), request->groups[2]);
     if (tmp == NULL)
     {
-      std::string s = "Accessing an inexistent instance with WADO-RS: " + std::string(request->groups[2]);
-      OrthancPluginLogError(context_, s.c_str());
-      OrthancPluginSendHttpStatusCode(context_, output, 404);
+      OrthancPlugins::Configuration::LogError("Accessing an inexistent instance with WADO-RS: " + 
+                                              std::string(request->groups[2]));
+      OrthancPluginSendHttpStatusCode(OrthancPlugins::Configuration::GetContext(), output, 404);
       return false;
     }
 
     id.assign(tmp);
-    OrthancPluginFreeString(context_, tmp);
+    OrthancPluginFreeString(OrthancPlugins::Configuration::GetContext(), tmp);
   }
   
   Json::Value study, series;
-  if (!OrthancPlugins::RestApiGetJson(series, context_, "/instances/" + id + "/series") ||
-      !OrthancPlugins::RestApiGetJson(study, context_, "/instances/" + id + "/study"))
+  if (!OrthancPlugins::RestApiGetJson(series, OrthancPlugins::Configuration::GetContext(), "/instances/" + id + "/series") ||
+      !OrthancPlugins::RestApiGetJson(study, OrthancPlugins::Configuration::GetContext(), "/instances/" + id + "/study"))
   {
-    OrthancPluginSendHttpStatusCode(context_, output, 404);
+    OrthancPluginSendHttpStatusCode(OrthancPlugins::Configuration::GetContext(), output, 404);
     return false;
   }
 
   if (study["MainDicomTags"]["StudyInstanceUID"].asString() != std::string(request->groups[0]) ||
       series["MainDicomTags"]["SeriesInstanceUID"].asString() != std::string(request->groups[1]))
   {
-    std::string s = ("No instance " + std::string(request->groups[2]) + 
-                     " in study " + std::string(request->groups[0]) + " or " +
-                     " in series " + std::string(request->groups[1]));
-    OrthancPluginLogError(context_, s.c_str());
-    OrthancPluginSendHttpStatusCode(context_, output, 404);
+    OrthancPlugins::Configuration::LogError("No instance " + std::string(request->groups[2]) + 
+                                            " in study " + std::string(request->groups[0]) + " or " +
+                                            " in series " + std::string(request->groups[1]));
+    OrthancPluginSendHttpStatusCode(OrthancPlugins::Configuration::GetContext(), output, 404);
     return false;
   }
 
@@ -383,7 +377,7 @@
 {
   if (!AcceptMultipartDicom(request))
   {
-    OrthancPluginSendHttpStatusCode(context_, output, 400 /* Bad request */);
+    OrthancPluginSendHttpStatusCode(OrthancPlugins::Configuration::GetContext(), output, 400 /* Bad request */);
   }
   else
   {
@@ -402,7 +396,7 @@
 {
   if (!AcceptMultipartDicom(request))
   {
-    OrthancPluginSendHttpStatusCode(context_, output, 400 /* Bad request */);
+    OrthancPluginSendHttpStatusCode(OrthancPlugins::Configuration::GetContext(), output, 400 /* Bad request */);
   }
   else
   {
@@ -422,21 +416,21 @@
 {
   if (!AcceptMultipartDicom(request))
   {
-    OrthancPluginSendHttpStatusCode(context_, output, 400 /* Bad request */);
+    OrthancPluginSendHttpStatusCode(OrthancPlugins::Configuration::GetContext(), output, 400 /* Bad request */);
   }
   else
   {
     std::string uri;
     if (LocateInstance(output, uri, request))
     {
-      if (OrthancPluginStartMultipartAnswer(context_, output, "related", "application/dicom"))
+      if (OrthancPluginStartMultipartAnswer(OrthancPlugins::Configuration::GetContext(), output, "related", "application/dicom"))
       {
         throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
       }
   
       std::string dicom;
-      if (OrthancPlugins::RestApiGetString(dicom, context_, uri + "/file") &&
-          OrthancPluginSendMultipartItem(context_, output, dicom.c_str(), dicom.size()) != 0)
+      if (OrthancPlugins::RestApiGetString(dicom, OrthancPlugins::Configuration::GetContext(), uri + "/file") &&
+          OrthancPluginSendMultipartItem(OrthancPlugins::Configuration::GetContext(), output, dicom.c_str(), dicom.size()) != 0)
       {
         throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
       }
@@ -453,7 +447,7 @@
   bool isXml;
   if (!AcceptMetadata(request, isXml))
   {
-    OrthancPluginSendHttpStatusCode(context_, output, 400 /* Bad request */);
+    OrthancPluginSendHttpStatusCode(OrthancPlugins::Configuration::GetContext(), output, 400 /* Bad request */);
   }
   else
   {
@@ -473,7 +467,7 @@
   bool isXml;
   if (!AcceptMetadata(request, isXml))
   {
-    OrthancPluginSendHttpStatusCode(context_, output, 400 /* Bad request */);
+    OrthancPluginSendHttpStatusCode(OrthancPlugins::Configuration::GetContext(), output, 400 /* Bad request */);
   }
   else
   {
@@ -493,7 +487,7 @@
   bool isXml;
   if (!AcceptMetadata(request, isXml))
   {
-    OrthancPluginSendHttpStatusCode(context_, output, 400 /* Bad request */);
+    OrthancPluginSendHttpStatusCode(OrthancPlugins::Configuration::GetContext(), output, 400 /* Bad request */);
   }
   else
   {
@@ -584,13 +578,13 @@
 {
   if (!AcceptBulkData(request))
   {
-    OrthancPluginSendHttpStatusCode(context_, output, 400 /* Bad request */);
+    OrthancPluginSendHttpStatusCode(OrthancPlugins::Configuration::GetContext(), output, 400 /* Bad request */);
     return;
   }
 
   std::string uri, content;
   if (LocateInstance(output, uri, request) &&
-      OrthancPlugins::RestApiGetString(content, context_, uri + "/file"))
+      OrthancPlugins::RestApiGetString(content, OrthancPlugins::Configuration::GetContext(), uri + "/file"))
   {
     OrthancPlugins::ParsedDicomFile dicom(content);
 
@@ -601,15 +595,15 @@
     if (path.size() % 2 == 1 &&
         ExploreBulkData(result, path, 0, dicom.GetDataSet()))
     {
-      if (OrthancPluginStartMultipartAnswer(context_, output, "related", "application/octet-stream") != 0 ||
-          OrthancPluginSendMultipartItem(context_, output, result.c_str(), result.size()) != 0)
+      if (OrthancPluginStartMultipartAnswer(OrthancPlugins::Configuration::GetContext(), output, "related", "application/octet-stream") != 0 ||
+          OrthancPluginSendMultipartItem(OrthancPlugins::Configuration::GetContext(), output, result.c_str(), result.size()) != 0)
       {
         throw Orthanc::OrthancException(Orthanc::ErrorCode_Plugin);
       }
     }
     else
     {
-      OrthancPluginSendHttpStatusCode(context_, output, 400 /* Bad request */);
+      OrthancPluginSendHttpStatusCode(OrthancPlugins::Configuration::GetContext(), output, 400 /* Bad request */);
     }      
   }
 }
--- a/Plugin/WadoRsRetrieveFrames.cpp	Thu Jun 23 10:09:16 2016 +0200
+++ b/Plugin/WadoRsRetrieveFrames.cpp	Thu Jun 23 17:31:40 2016 +0200
@@ -104,9 +104,8 @@
         }
         else
         {
-          std::string s = ("DICOMweb RetrieveFrames: Cannot specify a transfer syntax (" + 
-                           transferSyntax + ") for default Little Endian uncompressed pixel data");
-          OrthancPluginLogError(context_, s.c_str());
+          OrthancPlugins::Configuration::LogError("DICOMweb RetrieveFrames: Cannot specify a transfer syntax (" + 
+                                                  transferSyntax + ") for default Little Endian uncompressed pixel data");
           throw Orthanc::OrthancException(Orthanc::ErrorCode_BadRequest);
         }
       }
@@ -164,9 +163,8 @@
         }
         else
         {
-          std::string s = ("DICOMweb RetrieveFrames: Transfer syntax \"" + 
-                           transferSyntax + "\" is incompatible with media type \"" + type + "\"");
-          OrthancPluginLogError(context_, s.c_str());
+          OrthancPlugins::Configuration::LogError("DICOMweb RetrieveFrames: Transfer syntax \"" + 
+                                                  transferSyntax + "\" is incompatible with media type \"" + type + "\"");
           throw Orthanc::OrthancException(Orthanc::ErrorCode_BadRequest);
         }
       }
@@ -201,8 +199,7 @@
     int frame = boost::lexical_cast<int>(tokens[i]);
     if (frame <= 0)
     {
-      std::string s = "Invalid frame number (must be > 0): " + tokens[i];
-      OrthancPluginLogError(context_, s.c_str());
+      OrthancPlugins::Configuration::LogError("Invalid frame number (must be > 0): " + tokens[i]);
       throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
     }
 
@@ -272,9 +269,9 @@
   std::string location = dicom.GetWadoUrl(request) + "frames/" + boost::lexical_cast<std::string>(frameIndex + 1);
   const char *keys[] = { "Content-Location" };
   const char *values[] = { location.c_str() };
-  error = OrthancPluginSendMultipartItem2(context_, output, frame, size, 1, keys, values);
+  error = OrthancPluginSendMultipartItem2(OrthancPlugins::Configuration::GetContext(), output, frame, size, 1, keys, values);
 #else
-  error = OrthancPluginSendMultipartItem(context_, output, frame, size);
+  error = OrthancPluginSendMultipartItem(OrthancPlugins::Configuration::GetContext(), output, frame, size);
 #endif
 
   if (error != OrthancPluginErrorCode_Success)
@@ -299,7 +296,7 @@
   const gdcm::DataElement& pixelData = dicom.GetDataSet().GetDataElement(OrthancPlugins::DICOM_TAG_PIXEL_DATA);
   const gdcm::SequenceOfFragments* fragments = pixelData.GetSequenceOfFragments();
 
-  if (OrthancPluginStartMultipartAnswer(context_, output, "related", GetMimeType(syntax)) != OrthancPluginErrorCode_Success)
+  if (OrthancPluginStartMultipartAnswer(OrthancPlugins::Configuration::GetContext(), output, "related", GetMimeType(syntax)) != OrthancPluginErrorCode_Success)
   {
     return false;
   }
@@ -310,7 +307,7 @@
 
     if (pixelData.GetByteValue() == NULL)
     {
-      OrthancPluginLogError(context_, "Image was not properly decoded");
+      OrthancPlugins::Configuration::LogError("Image was not properly decoded");
       throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);      
     }
 
@@ -349,9 +346,8 @@
     {
       if (*frame >= framesCount)
       {
-        std::string s = ("Trying to access frame number " + boost::lexical_cast<std::string>(*frame + 1) + 
-                         " of an image with " + boost::lexical_cast<std::string>(framesCount) + " frames");
-        OrthancPluginLogError(context_, s.c_str());
+        OrthancPlugins::Configuration::LogError("Trying to access frame number " + boost::lexical_cast<std::string>(*frame + 1) + 
+                                                " of an image with " + boost::lexical_cast<std::string>(framesCount) + " frames");
         throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
       }
       else
@@ -379,9 +375,12 @@
     {
       if (*frame >= fragments->GetNumberOfFragments())
       {
-        std::string s = ("Trying to access frame number " + boost::lexical_cast<std::string>(*frame + 1) + 
-                         " of an image with " + boost::lexical_cast<std::string>(fragments->GetNumberOfFragments()) + " frames");
-        OrthancPluginLogError(context_, s.c_str());
+        // TODO A frame is not a fragment, looks like a bug
+        OrthancPlugins::Configuration::LogError("Trying to access frame number " + 
+                                                boost::lexical_cast<std::string>(*frame + 1) + 
+                                                " of an image with " + 
+                                                boost::lexical_cast<std::string>(fragments->GetNumberOfFragments()) + 
+                                                " frames");
         throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
       }
       else
@@ -410,8 +409,8 @@
   Json::Value header;
   std::string uri, content;
   if (LocateInstance(output, uri, request) &&
-      OrthancPlugins::RestApiGetString(content, context_, uri + "/file") &&
-      OrthancPlugins::RestApiGetJson(header, context_, uri + "/header?simplify"))
+      OrthancPlugins::RestApiGetString(content, OrthancPlugins::Configuration::GetContext(), uri + "/file") &&
+      OrthancPlugins::RestApiGetJson(header, OrthancPlugins::Configuration::GetContext(), uri + "/header?simplify"))
   {
     {
       std::string s = "DICOMweb RetrieveFrames on " + uri + ", frames: ";
@@ -420,7 +419,8 @@
       {
         s += boost::lexical_cast<std::string>(*frame + 1) + " ";
       }
-      OrthancPluginLogInfo(context_, s.c_str());
+
+      OrthancPlugins::Configuration::LogInfo(s);
     }
 
     std::auto_ptr<OrthancPlugins::ParsedDicomFile> source;
@@ -456,9 +456,9 @@
       // Need to convert the transfer syntax
 
       {
-        std::string s = ("DICOMweb RetrieveFrames: Transcoding " + uri + " from transfer syntax " + 
-                         std::string(sourceSyntax.GetString()) + " to " + std::string(targetSyntax.GetString()));
-        OrthancPluginLogInfo(context_, s.c_str());
+        OrthancPlugins::Configuration::LogInfo("DICOMweb RetrieveFrames: Transcoding " + uri + 
+                                               " from transfer syntax " + std::string(sourceSyntax.GetString()) + 
+                                               " to " + std::string(targetSyntax.GetString()));
       }
 
       gdcm::ImageChangeTransferSyntax change;
@@ -470,14 +470,14 @@
       reader.SetStream(stream);
       if (!reader.Read())
       {
-        OrthancPluginLogError(context_, "Cannot decode the image");
+        OrthancPlugins::Configuration::LogError("Cannot decode the image");
         throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
       }
 
       change.SetInput(reader.GetImage());
       if (!change.Change())
       {
-        OrthancPluginLogError(context_, "Cannot change the transfer syntax of the image");
+        OrthancPlugins::Configuration::LogError("Cannot change the transfer syntax of the image");
         throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
       }
 
--- a/Plugin/WadoUri.cpp	Thu Jun 23 10:09:16 2016 +0200
+++ b/Plugin/WadoUri.cpp	Thu Jun 23 17:31:40 2016 +0200
@@ -30,12 +30,12 @@
                                        char* (*func) (OrthancPluginContext*, const char*),
                                        const std::string& dicom)
 {
-  char* tmp = func(context_, dicom.c_str());
+  char* tmp = func(OrthancPlugins::Configuration::GetContext(), dicom.c_str());
 
   if (tmp)
   {
     orthanc = tmp;
-    OrthancPluginFreeString(context_, tmp);
+    OrthancPluginFreeString(OrthancPlugins::Configuration::GetContext(), tmp);
     return true;
   }
   else
@@ -80,21 +80,19 @@
 
   if (requestType != "WADO")
   {
-    std::string msg = "WADO-URI: Invalid requestType: \"" + requestType + "\"";
-    OrthancPluginLogError(context_, msg.c_str());
+    OrthancPlugins::Configuration::LogError("WADO-URI: Invalid requestType: \"" + requestType + "\"");
     return false;
   }
 
   if (objectUid.empty())
   {
-    OrthancPluginLogError(context_, "WADO-URI: No SOPInstanceUID provided");
+    OrthancPlugins::Configuration::LogError("WADO-URI: No SOPInstanceUID provided");
     return false;
   }
 
   if (!MapWadoToOrthancIdentifier(instance, OrthancPluginLookupInstance, objectUid))
   {
-    std::string msg = "WADO-URI: No such SOPInstanceUID in Orthanc: \"" + objectUid + "\"";
-    OrthancPluginLogError(context_, msg.c_str());
+    OrthancPlugins::Configuration::LogError("WADO-URI: No such SOPInstanceUID in Orthanc: \"" + objectUid + "\"");
     return false;
   }
 
@@ -108,18 +106,16 @@
     std::string series;
     if (!MapWadoToOrthancIdentifier(series, OrthancPluginLookupSeries, seriesUid))
     {
-      std::string msg = "WADO-URI: No such SeriesInstanceUID in Orthanc: \"" + seriesUid + "\"";
-      OrthancPluginLogError(context_, msg.c_str());
+      OrthancPlugins::Configuration::LogError("WADO-URI: No such SeriesInstanceUID in Orthanc: \"" + seriesUid + "\"");
       return false;
     }
     else
     {
       Json::Value info;
-      if (!OrthancPlugins::RestApiGetJson(info, context_, "/instances/" + instance + "/series") ||
+      if (!OrthancPlugins::RestApiGetJson(info, OrthancPlugins::Configuration::GetContext(), "/instances/" + instance + "/series") ||
           info["MainDicomTags"]["SeriesInstanceUID"] != seriesUid)
       {
-        std::string msg = "WADO-URI: Instance " + objectUid + " does not belong to series " + seriesUid;
-        OrthancPluginLogError(context_, msg.c_str());
+        OrthancPlugins::Configuration::LogError("WADO-URI: Instance " + objectUid + " does not belong to series " + seriesUid);
         return false;
       }
     }
@@ -130,18 +126,16 @@
     std::string study;
     if (!MapWadoToOrthancIdentifier(study, OrthancPluginLookupStudy, studyUid))
     {
-      std::string msg = "WADO-URI: No such StudyInstanceUID in Orthanc: \"" + studyUid + "\"";
-      OrthancPluginLogError(context_, msg.c_str());
+      OrthancPlugins::Configuration::LogError("WADO-URI: No such StudyInstanceUID in Orthanc: \"" + studyUid + "\"");
       return false;
     }
     else
     {
       Json::Value info;
-      if (!OrthancPlugins::RestApiGetJson(info, context_, "/instances/" + instance + "/study") ||
+      if (!OrthancPlugins::RestApiGetJson(info, OrthancPlugins::Configuration::GetContext(), "/instances/" + instance + "/study") ||
           info["MainDicomTags"]["StudyInstanceUID"] != studyUid)
       {
-        std::string msg = "WADO-URI: Instance " + objectUid + " does not belong to study " + studyUid;
-        OrthancPluginLogError(context_, msg.c_str());
+        OrthancPlugins::Configuration::LogError("WADO-URI: Instance " + objectUid + " does not belong to study " + studyUid);
         return false;
       }
     }
@@ -157,14 +151,13 @@
   std::string uri = "/instances/" + instance + "/file";
 
   std::string dicom;
-  if (OrthancPlugins::RestApiGetString(dicom, context_, uri))
+  if (OrthancPlugins::RestApiGetString(dicom, OrthancPlugins::Configuration::GetContext(), uri))
   {
-    OrthancPluginAnswerBuffer(context_, output, dicom.c_str(), dicom.size(), "application/dicom");
+    OrthancPluginAnswerBuffer(OrthancPlugins::Configuration::GetContext(), output, dicom.c_str(), dicom.size(), "application/dicom");
   }
   else
   {
-    std::string msg = "WADO-URI: Unable to retrieve DICOM file from " + uri;
-    OrthancPluginLogError(context_, msg.c_str());
+    OrthancPlugins::Configuration::LogError("WADO-URI: Unable to retrieve DICOM file from " + uri);
     throw Orthanc::OrthancException(Orthanc::ErrorCode_Plugin);
   }
 }
@@ -175,14 +168,13 @@
 {
   std::string uri = "/instances/" + instance + "/preview";
 
-  if (OrthancPlugins::RestApiGetString(png, context_, uri, true))
+  if (OrthancPlugins::RestApiGetString(png, OrthancPlugins::Configuration::GetContext(), uri, true))
   {
     return true;
   }
   else
   {
-    std::string msg = "WADO-URI: Unable to generate a preview image for " + uri;
-    OrthancPluginLogError(context_, msg.c_str());
+    OrthancPlugins::Configuration::LogError("WADO-URI: Unable to generate a preview image for " + uri);
     return false;
   }
 }
@@ -194,7 +186,7 @@
   std::string png;
   if (RetrievePngPreview(png, instance))
   {
-    OrthancPluginAnswerBuffer(context_, output, png.c_str(), png.size(), "image/png");
+    OrthancPluginAnswerBuffer(OrthancPlugins::Configuration::GetContext(), output, png.c_str(), png.size(), "image/png");
   }
   else
   {
@@ -215,19 +207,19 @@
 
   // Decode the PNG file
   OrthancPluginImage* image = OrthancPluginUncompressImage(
-    context_, png.c_str(), png.size(), OrthancPluginImageFormat_Png);
+    OrthancPlugins::Configuration::GetContext(), png.c_str(), png.size(), OrthancPluginImageFormat_Png);
 
   // Convert to JPEG
   OrthancPluginCompressAndAnswerJpegImage(
-    context_, output, 
-    OrthancPluginGetImagePixelFormat(context_, image),
-    OrthancPluginGetImageWidth(context_, image),
-    OrthancPluginGetImageHeight(context_, image),
-    OrthancPluginGetImagePitch(context_, image),
-    OrthancPluginGetImageBuffer(context_, image), 
+    OrthancPlugins::Configuration::GetContext(), output, 
+    OrthancPluginGetImagePixelFormat(OrthancPlugins::Configuration::GetContext(), image),
+    OrthancPluginGetImageWidth(OrthancPlugins::Configuration::GetContext(), image),
+    OrthancPluginGetImageHeight(OrthancPlugins::Configuration::GetContext(), image),
+    OrthancPluginGetImagePitch(OrthancPlugins::Configuration::GetContext(), image),
+    OrthancPluginGetImageBuffer(OrthancPlugins::Configuration::GetContext(), image), 
     90 /*quality*/);
 
-  OrthancPluginFreeImage(context_, image);
+  OrthancPluginFreeImage(OrthancPlugins::Configuration::GetContext(), image);
 }
 
 
@@ -237,7 +229,7 @@
 {
   if (request->method != OrthancPluginHttpMethod_Get)
   {
-    OrthancPluginSendMethodNotAllowed(context_, output, "GET");
+    OrthancPluginSendMethodNotAllowed(OrthancPlugins::Configuration::GetContext(), output, "GET");
     return;
   }
 
@@ -263,8 +255,7 @@
   }
   else
   {
-    std::string msg = "WADO-URI: Unsupported content type: \"" + contentType + "\"";
-    OrthancPluginLogError(context_, msg.c_str());
+    OrthancPlugins::Configuration::LogError("WADO-URI: Unsupported content type: \"" + contentType + "\"");
     throw Orthanc::OrthancException(Orthanc::ErrorCode_BadRequest);
   }
 }
--- a/UnitTestsSources/UnitTestsMain.cpp	Thu Jun 23 10:09:16 2016 +0200
+++ b/UnitTestsSources/UnitTestsMain.cpp	Thu Jun 23 17:31:40 2016 +0200
@@ -26,7 +26,6 @@
 
 using namespace OrthancPlugins;
 
-Json::Value configuration_ = Json::objectValue;
 OrthancPluginContext* context_ = NULL;