changeset 1135:a0a33e5ea5bb broker

IOracleCommand::Clone()
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 06 Nov 2019 17:34:58 +0100
parents 87fbeb823375
children 42581a6182c8
files Framework/Loaders/LoaderStateMachine.cpp Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.cpp Framework/Oracle/GenericOracleRunner.cpp Framework/Oracle/GenericOracleRunner.h Framework/Oracle/GetOrthancImageCommand.cpp Framework/Oracle/GetOrthancImageCommand.h Framework/Oracle/GetOrthancWebViewerJpegCommand.cpp Framework/Oracle/GetOrthancWebViewerJpegCommand.h Framework/Oracle/HttpCommand.cpp Framework/Oracle/HttpCommand.h Framework/Oracle/IOracleCommand.h Framework/Oracle/OracleCommandBase.h Framework/Oracle/OracleCommandExceptionMessage.h Framework/Oracle/OrthancRestApiCommand.h Framework/Oracle/ParseDicomFileCommand.cpp Framework/Oracle/ParseDicomFileCommand.h Framework/Oracle/ReadFileCommand.h Framework/Oracle/SleepOracleCommand.h Resources/CMake/OrthancStoneConfiguration.cmake
diffstat 19 files changed, 152 insertions(+), 94 deletions(-) [+]
line wrap: on
line diff
--- a/Framework/Loaders/LoaderStateMachine.cpp	Wed Nov 06 15:16:45 2019 +0100
+++ b/Framework/Loaders/LoaderStateMachine.cpp	Wed Nov 06 17:34:58 2019 +0100
@@ -144,7 +144,7 @@
       activeCommands_--;
       try
       {
-        dynamic_cast<State&>(message.GetCommand().GetPayload()).Handle(message);
+        dynamic_cast<State&>(message.GetOrigin().GetPayload()).Handle(message);
         Step();
       }
       catch (Orthanc::OrthancException& e)
--- a/Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.cpp	Wed Nov 06 15:16:45 2019 +0100
+++ b/Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.cpp	Wed Nov 06 17:34:58 2019 +0100
@@ -237,8 +237,9 @@
   }
 
 
-  static unsigned int GetSliceIndexPayload(const IOracleCommand& command)
+  static unsigned int GetSliceIndexPayload(const OracleCommandBase& command)
   {
+    assert(command.HasPayload());
     return dynamic_cast< const Orthanc::SingleValueObject<unsigned int>& >(command.GetPayload()).GetValue();
   }
 
@@ -394,7 +395,7 @@
 
   void OrthancSeriesVolumeProgressiveLoader::LoadBestQualitySliceContent(const GetOrthancImageCommand::SuccessMessage& message)
   {
-    SetSliceContent(GetSliceIndexPayload(message.GetCommand()), message.GetImage(), BEST_QUALITY);
+    SetSliceContent(GetSliceIndexPayload(message.GetOrigin()), message.GetImage(), BEST_QUALITY);
   }
 
 
@@ -402,7 +403,7 @@
   {
     unsigned int quality;
       
-    switch (dynamic_cast<const GetOrthancWebViewerJpegCommand&>(message.GetCommand()).GetQuality())
+    switch (dynamic_cast<const GetOrthancWebViewerJpegCommand&>(message.GetOrigin()).GetQuality())
     {
       case 50:
         quality = LOW_QUALITY;
@@ -416,7 +417,7 @@
         throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
     }
       
-    SetSliceContent(GetSliceIndexPayload(message.GetCommand()), message.GetImage(), quality);
+    SetSliceContent(GetSliceIndexPayload(message.GetOrigin()), message.GetImage(), quality);
   }
 
 
--- a/Framework/Oracle/GenericOracleRunner.cpp	Wed Nov 06 15:16:45 2019 +0100
+++ b/Framework/Oracle/GenericOracleRunner.cpp	Wed Nov 06 17:34:58 2019 +0100
@@ -102,7 +102,7 @@
 
   static void RunInternal(boost::weak_ptr<IObserver> receiver,
                           IMessageEmitter& emitter,
-                          HttpCommand& command)
+                          const HttpCommand& command)
   {
     Orthanc::HttpClient client;
     client.SetUrl(command.GetUrl());
@@ -136,7 +136,7 @@
   static void RunInternal(boost::weak_ptr<IObserver> receiver,
                           IMessageEmitter& emitter,
                           const Orthanc::WebServiceParameters& orthanc,
-                          OrthancRestApiCommand& command)
+                          const OrthancRestApiCommand& command)
   {
     Orthanc::HttpClient client(orthanc, command.GetUri());
     client.SetMethod(command.GetMethod());
@@ -164,7 +164,7 @@
   static void RunInternal(boost::weak_ptr<IObserver> receiver,
                           IMessageEmitter& emitter,
                           const Orthanc::WebServiceParameters& orthanc,
-                          GetOrthancImageCommand& command)
+                          const GetOrthancImageCommand& command)
   {
     Orthanc::HttpClient client(orthanc, command.GetUri());
     client.SetTimeout(command.GetTimeout());
@@ -184,7 +184,7 @@
   static void RunInternal(boost::weak_ptr<IObserver> receiver,
                           IMessageEmitter& emitter,
                           const Orthanc::WebServiceParameters& orthanc,
-                          GetOrthancWebViewerJpegCommand& command)
+                          const GetOrthancWebViewerJpegCommand& command)
   {
     Orthanc::HttpClient client(orthanc, command.GetUri());
     client.SetTimeout(command.GetTimeout());
@@ -225,7 +225,7 @@
   static void RunInternal(boost::weak_ptr<IObserver> receiver,
                           IMessageEmitter& emitter,
                           const std::string& root,
-                          ReadFileCommand& command)
+                          const ReadFileCommand& command)
   {
     std::string path = GetPath(root, command.GetPath());
 
@@ -241,7 +241,7 @@
   static void RunInternal(boost::weak_ptr<IObserver> receiver,
                           IMessageEmitter& emitter,
                           const std::string& root,
-                          ParseDicomFileCommand& command)
+                          const ParseDicomFileCommand& command)
   {
     std::string path = GetPath(root, command.GetPath());
 
@@ -303,7 +303,7 @@
                           IMessageEmitter& emitter,
                           boost::shared_ptr<ParsedDicomFileCache> cache,
                           const std::string& root,
-                          ParseDicomFileCommand& command)
+                          const ParseDicomFileCommand& command)
   {
 #if 0
     // The code to use the cache is buggy in multithreaded environments => TODO FIX
@@ -348,7 +348,7 @@
 
   void GenericOracleRunner::Run(boost::weak_ptr<IObserver> receiver,
                                 IMessageEmitter& emitter,
-                                IOracleCommand& command)
+                                const IOracleCommand& command)
   {
     Orthanc::ErrorCode error = Orthanc::ErrorCode_Success;
     
@@ -361,33 +361,33 @@
                                           "Sleep command cannot be executed by the runner");
 
         case IOracleCommand::Type_Http:
-          RunInternal(receiver, emitter, dynamic_cast<HttpCommand&>(command));
+          RunInternal(receiver, emitter, dynamic_cast<const HttpCommand&>(command));
           break;
 
         case IOracleCommand::Type_OrthancRestApi:
           RunInternal(receiver, emitter, orthanc_,
-                      dynamic_cast<OrthancRestApiCommand&>(command));
+                      dynamic_cast<const OrthancRestApiCommand&>(command));
           break;
 
         case IOracleCommand::Type_GetOrthancImage:
           RunInternal(receiver, emitter, orthanc_,
-                      dynamic_cast<GetOrthancImageCommand&>(command));
+                      dynamic_cast<const GetOrthancImageCommand&>(command));
           break;
 
         case IOracleCommand::Type_GetOrthancWebViewerJpeg:
           RunInternal(receiver, emitter, orthanc_,
-                      dynamic_cast<GetOrthancWebViewerJpegCommand&>(command));
+                      dynamic_cast<const GetOrthancWebViewerJpegCommand&>(command));
           break;
 
         case IOracleCommand::Type_ReadFile:
           RunInternal(receiver, emitter, rootDirectory_,
-                      dynamic_cast<ReadFileCommand&>(command));
+                      dynamic_cast<const ReadFileCommand&>(command));
           break;
 
         case IOracleCommand::Type_ParseDicomFile:
 #if ORTHANC_ENABLE_DCMTK == 1
           RunInternal(receiver, emitter, dicomCache_, rootDirectory_,
-                      dynamic_cast<ParseDicomFileCommand&>(command));
+                      dynamic_cast<const ParseDicomFileCommand&>(command));
           break;
 #else
           throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented,
@@ -409,7 +409,10 @@
       error = Orthanc::ErrorCode_InternalError;
     }
 
-    OracleCommandExceptionMessage message(command, error);
-    emitter.EmitMessage(receiver, message);
+    if (error != Orthanc::ErrorCode_Success)
+    {
+      OracleCommandExceptionMessage message(command, error);
+      emitter.EmitMessage(receiver, message);
+    }
   }
 }
--- a/Framework/Oracle/GenericOracleRunner.h	Wed Nov 06 15:16:45 2019 +0100
+++ b/Framework/Oracle/GenericOracleRunner.h	Wed Nov 06 17:34:58 2019 +0100
@@ -82,6 +82,6 @@
 
     void Run(boost::weak_ptr<IObserver> receiver,
              IMessageEmitter& emitter,
-             IOracleCommand& command);
+             const IOracleCommand& command);
   };
 }
--- a/Framework/Oracle/GetOrthancImageCommand.cpp	Wed Nov 06 15:16:45 2019 +0100
+++ b/Framework/Oracle/GetOrthancImageCommand.cpp	Wed Nov 06 17:34:58 2019 +0100
@@ -72,7 +72,7 @@
   void GetOrthancImageCommand::ProcessHttpAnswer(boost::weak_ptr<IObserver> receiver,
                                                  IMessageEmitter& emitter,
                                                  const std::string& answer,
-                                                 const HttpHeaders& answerHeaders)
+                                                 const HttpHeaders& answerHeaders) const
   {
     Orthanc::MimeType contentType = Orthanc::MimeType_Binary;
 
--- a/Framework/Oracle/GetOrthancImageCommand.h	Wed Nov 06 15:16:45 2019 +0100
+++ b/Framework/Oracle/GetOrthancImageCommand.h	Wed Nov 06 17:34:58 2019 +0100
@@ -35,7 +35,7 @@
   public:
     typedef std::map<std::string, std::string>  HttpHeaders;
 
-    class SuccessMessage : public OracleMessageBase
+    class SuccessMessage : public OriginMessage<GetOrthancImageCommand>
     {
       ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
       
@@ -44,10 +44,10 @@
       Orthanc::MimeType              mime_;
 
     public:
-      SuccessMessage(GetOrthancImageCommand& command,
+      SuccessMessage(const GetOrthancImageCommand& command,
                      const Orthanc::ImageAccessor& image,
                      Orthanc::MimeType mime) :
-        OracleMessageBase(command),
+        OriginMessage(command),
         image_(image),
         mime_(mime)
       {
@@ -72,6 +72,15 @@
     bool                  hasExpectedFormat_;
     Orthanc::PixelFormat  expectedFormat_;
 
+    GetOrthancImageCommand(const GetOrthancImageCommand& other) :
+      uri_(other.uri_),
+      headers_(other.headers_),
+      timeout_(other.timeout_),
+      hasExpectedFormat_(other.hasExpectedFormat_),
+      expectedFormat_(other.expectedFormat_)
+    {
+    }
+
   public:
     GetOrthancImageCommand();
 
@@ -80,6 +89,11 @@
       return Type_GetOrthancImage;
     }
 
+    virtual IOracleCommand* Clone() const
+    {
+      return new GetOrthancImageCommand(*this);
+    }
+
     void SetExpectedPixelFormat(Orthanc::PixelFormat format);
 
     void SetUri(const std::string& uri)
@@ -119,6 +133,6 @@
     void ProcessHttpAnswer(boost::weak_ptr<IObserver> receiver,
                            IMessageEmitter& emitter,
                            const std::string& answer,
-                           const HttpHeaders& answerHeaders);
+                           const HttpHeaders& answerHeaders) const;
   };
 }
--- a/Framework/Oracle/GetOrthancWebViewerJpegCommand.cpp	Wed Nov 06 15:16:45 2019 +0100
+++ b/Framework/Oracle/GetOrthancWebViewerJpegCommand.cpp	Wed Nov 06 17:34:58 2019 +0100
@@ -66,7 +66,7 @@
 
   void GetOrthancWebViewerJpegCommand::ProcessHttpAnswer(boost::weak_ptr<IObserver> receiver,
                                                          IMessageEmitter& emitter,
-                                                         const std::string& answer)
+                                                         const std::string& answer) const
   {
     // This code comes from older "OrthancSlicesLoader::ParseSliceImageJpeg()"
       
--- a/Framework/Oracle/GetOrthancWebViewerJpegCommand.h	Wed Nov 06 15:16:45 2019 +0100
+++ b/Framework/Oracle/GetOrthancWebViewerJpegCommand.h	Wed Nov 06 17:34:58 2019 +0100
@@ -35,7 +35,7 @@
   public:
     typedef std::map<std::string, std::string>  HttpHeaders;
 
-    class SuccessMessage : public OracleMessageBase
+    class SuccessMessage : public OriginMessage<GetOrthancWebViewerJpegCommand>
     {
       ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
       
@@ -43,9 +43,9 @@
       const Orthanc::ImageAccessor&  image_;
 
     public:
-      SuccessMessage(GetOrthancWebViewerJpegCommand& command,
+      SuccessMessage(const GetOrthancWebViewerJpegCommand& command,
                      const Orthanc::ImageAccessor& image) :
-        OracleMessageBase(command),
+        OriginMessage(command),
         image_(image)
       {
       }
@@ -64,6 +64,16 @@
     unsigned int          timeout_;
     Orthanc::PixelFormat  expectedFormat_;
 
+    GetOrthancWebViewerJpegCommand(const GetOrthancWebViewerJpegCommand& other) :
+      instanceId_(other.instanceId_),
+      frame_(other.frame_),
+      quality_(other.quality_),
+      headers_(other.headers_),
+      timeout_(other.timeout_),
+      expectedFormat_(other.expectedFormat_)
+    {
+    }
+    
   public:
     GetOrthancWebViewerJpegCommand();
 
@@ -72,6 +82,11 @@
       return Type_GetOrthancWebViewerJpeg;
     }
 
+    virtual IOracleCommand* Clone() const
+    {
+      return new GetOrthancWebViewerJpegCommand(*this);
+    }
+
     void SetExpectedPixelFormat(Orthanc::PixelFormat format)
     {
       expectedFormat_ = format;
@@ -134,6 +149,6 @@
 
     void ProcessHttpAnswer(boost::weak_ptr<IObserver> receiver,
                            IMessageEmitter& emitter,
-                           const std::string& answer);
+                           const std::string& answer) const;
   };
 }
--- a/Framework/Oracle/HttpCommand.cpp	Wed Nov 06 15:16:45 2019 +0100
+++ b/Framework/Oracle/HttpCommand.cpp	Wed Nov 06 17:34:58 2019 +0100
@@ -28,16 +28,6 @@
 
 namespace OrthancStone
 {
-  HttpCommand::SuccessMessage::SuccessMessage(HttpCommand& command,
-                                              const HttpHeaders& answerHeaders,
-                                              const std::string& answer) :
-    OracleMessageBase(command),
-    headers_(answerHeaders),
-    answer_(answer)
-  {
-  }
-
-
   void HttpCommand::SuccessMessage::ParseJsonBody(Json::Value& target) const
   {
     Json::Reader reader;
--- a/Framework/Oracle/HttpCommand.h	Wed Nov 06 15:16:45 2019 +0100
+++ b/Framework/Oracle/HttpCommand.h	Wed Nov 06 17:34:58 2019 +0100
@@ -36,7 +36,7 @@
   public:
     typedef std::map<std::string, std::string>  HttpHeaders;
 
-    class SuccessMessage : public OracleMessageBase
+    class SuccessMessage : public OriginMessage<HttpCommand>
     {
       ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
       
@@ -45,9 +45,14 @@
       const std::string&  answer_;
 
     public:
-      SuccessMessage(HttpCommand& command,
+      SuccessMessage(const HttpCommand& command,
                      const HttpHeaders& answerHeaders,
-                     const std::string& answer);
+                     const std::string& answer) :
+        OriginMessage(command),
+        headers_(answerHeaders),
+        answer_(answer)
+      {
+      }
 
       const std::string& GetAnswer() const
       {
@@ -72,6 +77,17 @@
     std::string          username_;
     std::string          password_;
 
+    HttpCommand(const HttpCommand& other) :
+      method_(other.method_),
+      url_(other.url_),
+      body_(other.body_),
+      headers_(other.headers_),
+      timeout_(other.timeout_),
+      username_(other.username_),
+      password_(other.password_)
+    {
+    }
+
   public:
     HttpCommand();
 
@@ -80,6 +96,11 @@
       return Type_Http;
     }
 
+    virtual IOracleCommand* Clone() const
+    {
+      return new HttpCommand(*this);
+    }
+
     void SetMethod(Orthanc::HttpMethod method)
     {
       method_ = method;
--- a/Framework/Oracle/IOracleCommand.h	Wed Nov 06 15:16:45 2019 +0100
+++ b/Framework/Oracle/IOracleCommand.h	Wed Nov 06 17:34:58 2019 +0100
@@ -21,39 +21,13 @@
 
 #pragma once
 
-#include "../Messages/IMessage.h"
-
 #include <Core/IDynamicObject.h>
 
-#include <boost/noncopyable.hpp>
-
 namespace OrthancStone
 {
   class IOracleCommand : public boost::noncopyable
   {
   public:
-    class OracleMessageBase : public IMessage
-    {
-    private:
-      IOracleCommand&  command_;
-
-    public:
-      OracleMessageBase(IOracleCommand& command) :
-        command_(command)
-      {
-      }
-
-      void AcquireCommandPayload(Orthanc::IDynamicObject* payload) const
-      {
-        command_.AcquirePayload(payload);
-      }
-
-      const IOracleCommand& GetCommand() const
-      {
-        return command_;
-      }
-    };
-
     enum Type
     {
       Type_GetOrthancImage,
@@ -71,10 +45,7 @@
 
     virtual Type GetType() const = 0;
 
-    virtual void AcquirePayload(Orthanc::IDynamicObject* payload) = 0;
-    
-    virtual bool HasPayload() const = 0;
-
-    virtual Orthanc::IDynamicObject& GetPayload() const = 0;
+    // This only clones the command, *not* its possibly associated payload
+    virtual IOracleCommand* Clone() const = 0;
   };
 }
--- a/Framework/Oracle/OracleCommandBase.h	Wed Nov 06 15:16:45 2019 +0100
+++ b/Framework/Oracle/OracleCommandBase.h	Wed Nov 06 17:34:58 2019 +0100
@@ -33,7 +33,7 @@
     std::auto_ptr<Orthanc::IDynamicObject>  payload_;
 
   public:
-    virtual void AcquirePayload(Orthanc::IDynamicObject* payload);
+    void AcquirePayload(Orthanc::IDynamicObject* payload);
 
     virtual bool HasPayload() const
     {
--- a/Framework/Oracle/OracleCommandExceptionMessage.h	Wed Nov 06 15:16:45 2019 +0100
+++ b/Framework/Oracle/OracleCommandExceptionMessage.h	Wed Nov 06 17:34:58 2019 +0100
@@ -28,7 +28,7 @@
 
 namespace OrthancStone
 {
-  class OracleCommandExceptionMessage : public IOracleCommand::OracleMessageBase
+  class OracleCommandExceptionMessage : public OriginMessage<IOracleCommand>
   {
     ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
 
@@ -36,13 +36,20 @@
     Orthanc::OrthancException  exception_;
 
   public:
-    OracleCommandExceptionMessage(IOracleCommand& command,
+    OracleCommandExceptionMessage(const IOracleCommand& command,
                                   const Orthanc::ErrorCode& error) :
-      OracleMessageBase(command),
+      OriginMessage(command),
       exception_(error)
     {
     }
 
+    OracleCommandExceptionMessage(const IOracleCommand& command,
+                                  const Orthanc::OrthancException& exception) :
+      OriginMessage(command),
+      exception_(exception)
+    {
+    }
+
     const Orthanc::OrthancException& GetException() const
     {
       return exception_;
--- a/Framework/Oracle/OrthancRestApiCommand.h	Wed Nov 06 15:16:45 2019 +0100
+++ b/Framework/Oracle/OrthancRestApiCommand.h	Wed Nov 06 17:34:58 2019 +0100
@@ -36,7 +36,7 @@
   public:
     typedef std::map<std::string, std::string>  HttpHeaders;
 
-    class SuccessMessage : public OracleMessageBase
+    class SuccessMessage : public OriginMessage<OrthancRestApiCommand>
     {
       ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
       
@@ -45,10 +45,10 @@
       const std::string&  answer_;
 
     public:
-      SuccessMessage(OrthancRestApiCommand& command,
+      SuccessMessage(const OrthancRestApiCommand& command,
                      const HttpHeaders& answerHeaders,
                      const std::string& answer) :
-        OracleMessageBase(command),
+        OriginMessage(command),
         headers_(answerHeaders),
         answer_(answer)
       {
@@ -76,6 +76,16 @@
     unsigned int         timeout_;
     bool                 applyPlugins_;  // Only makes sense for Stone as an Orthanc plugin
 
+    OrthancRestApiCommand(const OrthancRestApiCommand& other) :
+      method_(other.method_),
+      uri_(other.uri_),
+      body_(other.body_),
+      headers_(other.headers_),
+      timeout_(other.timeout_),
+      applyPlugins_(other.applyPlugins_)
+    {
+    }
+    
   public:
     OrthancRestApiCommand();
 
@@ -84,6 +94,11 @@
       return Type_OrthancRestApi;
     }
 
+    virtual IOracleCommand* Clone() const
+    {
+      return new OrthancRestApiCommand(*this);
+    }
+
     void SetMethod(Orthanc::HttpMethod method)
     {
       method_ = method;
--- a/Framework/Oracle/ParseDicomFileCommand.cpp	Wed Nov 06 15:16:45 2019 +0100
+++ b/Framework/Oracle/ParseDicomFileCommand.cpp	Wed Nov 06 17:34:58 2019 +0100
@@ -27,11 +27,11 @@
 
 namespace OrthancStone
 {
-  ParseDicomFileCommand::SuccessMessage::SuccessMessage(ParseDicomFileCommand& command,
+  ParseDicomFileCommand::SuccessMessage::SuccessMessage(const ParseDicomFileCommand& command,
                                                         Orthanc::ParsedDicomFile& dicom,
                                                         size_t fileSize,
                                                         bool hasPixelData) :
-    OracleMessageBase(command),
+    OriginMessage(command),
     dicom_(dicom),
     fileSize_(fileSize),
     hasPixelData_(hasPixelData)
--- a/Framework/Oracle/ParseDicomFileCommand.h	Wed Nov 06 15:16:45 2019 +0100
+++ b/Framework/Oracle/ParseDicomFileCommand.h	Wed Nov 06 17:34:58 2019 +0100
@@ -41,7 +41,7 @@
   class ParseDicomFileCommand : public OracleCommandBase
   {
   public:
-    class SuccessMessage : public OracleMessageBase
+    class SuccessMessage : public OriginMessage<ParseDicomFileCommand>
     {
       ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
 
@@ -52,7 +52,7 @@
       std::string                sopInstanceUid_;
 
     public:
-      SuccessMessage(ParseDicomFileCommand& command,
+      SuccessMessage(const ParseDicomFileCommand& command,
                      Orthanc::ParsedDicomFile& dicom,
                      size_t fileSize,
                      bool hasPixelData);
@@ -82,6 +82,12 @@
     std::string  path_;
     bool         pixelDataIncluded_;
 
+    ParseDicomFileCommand(const ParseDicomFileCommand& other) :
+      path_(other.path_),
+      pixelDataIncluded_(other.pixelDataIncluded_)
+    {
+    }
+
   public:
     ParseDicomFileCommand(const std::string& path) :
       path_(path),
@@ -104,6 +110,11 @@
       return Type_ParseDicomFile;
     }
 
+    virtual IOracleCommand* Clone() const
+    {
+      return new ParseDicomFileCommand(*this);
+    }
+
     const std::string& GetPath() const
     {
       return path_;
--- a/Framework/Oracle/ReadFileCommand.h	Wed Nov 06 15:16:45 2019 +0100
+++ b/Framework/Oracle/ReadFileCommand.h	Wed Nov 06 17:34:58 2019 +0100
@@ -29,19 +29,19 @@
   class ReadFileCommand : public OracleCommandBase
   {
   public:
-    class SuccessMessage : public OracleMessageBase
+    class SuccessMessage : public OriginMessage<ReadFileCommand>
     {
       ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
       
     private:
-      std::string content_;
+      const std::string& content_;
 
     public:
-      SuccessMessage(ReadFileCommand& command,
-                     std::string& content  /* will be swapped to avoid a memcpy() */) :
-        OracleMessageBase(command)
+      SuccessMessage(const ReadFileCommand& command,
+                     const std::string& content) :
+        OriginMessage(command),
+        content_(content)
       {
-        content_.swap(content);
       }
 
       const std::string& GetContent() const
@@ -65,6 +65,11 @@
       return Type_ReadFile;
     }
 
+    virtual IOracleCommand* Clone() const
+    {
+      return new ReadFileCommand(path_);
+    }
+
     const std::string& GetPath() const
     {
       return path_;
--- a/Framework/Oracle/SleepOracleCommand.h	Wed Nov 06 15:16:45 2019 +0100
+++ b/Framework/Oracle/SleepOracleCommand.h	Wed Nov 06 17:34:58 2019 +0100
@@ -44,6 +44,11 @@
       return Type_Sleep;
     }
 
+    virtual IOracleCommand* Clone() const
+    {
+      return new SleepOracleCommand(milliseconds_);
+    }
+
     unsigned int GetDelay() const
     {
       return milliseconds_;
--- a/Resources/CMake/OrthancStoneConfiguration.cmake	Wed Nov 06 15:16:45 2019 +0100
+++ b/Resources/CMake/OrthancStoneConfiguration.cmake	Wed Nov 06 17:34:58 2019 +0100
@@ -464,9 +464,9 @@
   ${ORTHANC_STONE_ROOT}/Framework/Messages/IObserver.h
   ${ORTHANC_STONE_ROOT}/Framework/Oracle/GetOrthancImageCommand.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Oracle/GetOrthancWebViewerJpegCommand.cpp
+  ${ORTHANC_STONE_ROOT}/Framework/Oracle/HttpCommand.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Oracle/OracleCommandBase.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Oracle/OrthancRestApiCommand.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Oracle/HttpCommand.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Scene2D/CairoCompositor.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Scene2D/ColorTextureSceneLayer.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Scene2D/FloatTextureSceneLayer.cpp