changeset 2664:a21b244efb37 jobs

serialization of DicomModalityStoreJob, OrthancPeerStoreJob and ResourceModificationJob
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 07 Jun 2018 21:37:40 +0200
parents 228e2783ce83
children 389d050a2e66
files OrthancServer/DicomInstanceOrigin.cpp OrthancServer/DicomInstanceOrigin.h OrthancServer/DicomInstanceToStore.h OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp OrthancServer/OrthancRestApi/OrthancRestApi.cpp OrthancServer/ServerIndex.cpp OrthancServer/ServerJobs/DicomModalityStoreJob.cpp OrthancServer/ServerJobs/DicomModalityStoreJob.h OrthancServer/ServerJobs/Operations/ModifyInstanceOperation.cpp OrthancServer/ServerJobs/OrthancJobUnserializer.cpp OrthancServer/ServerJobs/OrthancPeerStoreJob.cpp OrthancServer/ServerJobs/OrthancPeerStoreJob.h OrthancServer/ServerJobs/ResourceModificationJob.cpp OrthancServer/ServerJobs/ResourceModificationJob.h OrthancServer/main.cpp Plugins/Engine/OrthancPlugins.cpp UnitTestsSources/MultiThreadingTests.cpp
diffstat 17 files changed, 431 insertions(+), 65 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancServer/DicomInstanceOrigin.cpp	Thu Jun 07 18:18:02 2018 +0200
+++ b/OrthancServer/DicomInstanceOrigin.cpp	Thu Jun 07 21:37:40 2018 +0200
@@ -35,6 +35,7 @@
 #include "DicomInstanceOrigin.h"
 
 #include "../Core/OrthancException.h"
+#include "../Core/SerializationToolbox.h"
 
 
 namespace Orthanc
@@ -81,46 +82,40 @@
   }
 
 
-  void DicomInstanceOrigin::SetDicomProtocolOrigin(const char* remoteIp,
-                                                   const char* remoteAet,
-                                                   const char* calledAet)
+  DicomInstanceOrigin DicomInstanceOrigin::FromDicomProtocol(const char* remoteIp,
+                                                             const char* remoteAet,
+                                                             const char* calledAet)
   {
-    origin_ = RequestOrigin_DicomProtocol;
-    remoteIp_ = remoteIp;
-    dicomRemoteAet_ = remoteAet;
-    dicomCalledAet_ = calledAet;
+    DicomInstanceOrigin result(RequestOrigin_DicomProtocol);
+    result.remoteIp_ = remoteIp;
+    result.dicomRemoteAet_ = remoteAet;
+    result.dicomCalledAet_ = calledAet;
+    return result;
   }
 
-  void DicomInstanceOrigin::SetRestOrigin(const RestApiCall& call)
+  DicomInstanceOrigin DicomInstanceOrigin::FromRest(const RestApiCall& call)
   {
-    origin_ = call.GetRequestOrigin();
+    DicomInstanceOrigin result(call.GetRequestOrigin());
 
-    if (origin_ == RequestOrigin_RestApi)
+    if (result.origin_ == RequestOrigin_RestApi)
     {
-      remoteIp_ = call.GetRemoteIp();
-      httpUsername_ = call.GetUsername();
+      result.remoteIp_ = call.GetRemoteIp();
+      result.httpUsername_ = call.GetUsername();
     }
+
+    return result;
   }
 
-  void DicomInstanceOrigin::SetHttpOrigin(const char* remoteIp,
-                                          const char* username)
+  DicomInstanceOrigin DicomInstanceOrigin::FromHttp(const char* remoteIp,
+                                                    const char* username)
   {
-    origin_ = RequestOrigin_RestApi;
-    remoteIp_ = remoteIp;
-    httpUsername_ = username;
+    DicomInstanceOrigin result(RequestOrigin_RestApi);
+    result.remoteIp_ = remoteIp;
+    result.httpUsername_ = username;
+    return result;
   }
 
-  void DicomInstanceOrigin::SetLuaOrigin()
-  {
-    origin_ = RequestOrigin_Lua;
-  }
-
-  void DicomInstanceOrigin::SetPluginsOrigin()
-  {
-    origin_ = RequestOrigin_Plugins;
-  }
-
-  const char* DicomInstanceOrigin::GetRemoteAet() const
+  const char* DicomInstanceOrigin::GetRemoteAetC() const
   {
     if (origin_ == RequestOrigin_DicomProtocol)
     {
@@ -131,4 +126,70 @@
       return "";
     }
   }
+
+  const std::string& DicomInstanceOrigin::GetRemoteIp() const
+  {
+    if (origin_ == RequestOrigin_DicomProtocol ||
+        origin_ == RequestOrigin_RestApi)
+    {
+      return remoteIp_;
+    }
+    else
+    {
+      throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+  }
+
+  const std::string& DicomInstanceOrigin::GetCalledAet() const
+  {
+    if (origin_ == RequestOrigin_DicomProtocol)
+    {
+      return dicomCalledAet_;
+    }
+    else
+    {
+      throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+  }
+
+  const std::string& DicomInstanceOrigin::GetHttpUsername() const
+  {
+    if (origin_ == RequestOrigin_RestApi)
+    {
+      return httpUsername_;
+    }
+    else
+    {
+      throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+  }
+
+
+
+  static const char* ORIGIN = "Origin";
+  static const char* REMOTE_IP = "RemoteIP";
+  static const char* DICOM_REMOTE_AET = "RemoteAET";
+  static const char* DICOM_CALLED_AET = "CalledAET";
+  static const char* HTTP_USERNAME = "Username";
+  
+
+  DicomInstanceOrigin::DicomInstanceOrigin(const Json::Value& serialized)
+  {
+    origin_ = StringToRequestOrigin(SerializationToolbox::ReadString(serialized, ORIGIN));
+    remoteIp_ = SerializationToolbox::ReadString(serialized, REMOTE_IP);
+    dicomRemoteAet_ = SerializationToolbox::ReadString(serialized, DICOM_REMOTE_AET);
+    dicomCalledAet_ = SerializationToolbox::ReadString(serialized, DICOM_CALLED_AET);
+    httpUsername_ = SerializationToolbox::ReadString(serialized, HTTP_USERNAME);
+  }
+  
+  
+  void DicomInstanceOrigin::Serialize(Json::Value& result) const
+  {
+    result = Json::objectValue;
+    result[ORIGIN] = EnumerationToString(origin_);
+    result[REMOTE_IP] = remoteIp_;
+    result[DICOM_REMOTE_AET] = dicomRemoteAet_;
+    result[DICOM_CALLED_AET] = dicomCalledAet_;
+    result[HTTP_USERNAME] = httpUsername_;
+  }
 }
--- a/OrthancServer/DicomInstanceOrigin.h	Thu Jun 07 18:18:02 2018 +0200
+++ b/OrthancServer/DicomInstanceOrigin.h	Thu Jun 07 21:37:40 2018 +0200
@@ -46,32 +46,53 @@
     std::string   dicomCalledAet_;
     std::string   httpUsername_;
 
+    DicomInstanceOrigin(RequestOrigin origin) :
+      origin_(origin)
+    {
+    }
+
   public:
     DicomInstanceOrigin() :
       origin_(RequestOrigin_Unknown)
     {
     }
 
-    void SetDicomProtocolOrigin(const char* remoteIp,
-                                const char* remoteAet,
-                                const char* calledAet);
+    DicomInstanceOrigin(const Json::Value& serialized);
 
-    void SetRestOrigin(const RestApiCall& call);
+    static DicomInstanceOrigin FromDicomProtocol(const char* remoteIp,
+                                                 const char* remoteAet,
+                                                 const char* calledAet);
+
+    static DicomInstanceOrigin FromRest(const RestApiCall& call);
 
-    void SetHttpOrigin(const char* remoteIp,
-                       const char* username);
+    static DicomInstanceOrigin FromHttp(const char* remoteIp,
+                                        const char* username);
 
-    void SetLuaOrigin();
+    static DicomInstanceOrigin FromLua()
+    {
+      return DicomInstanceOrigin(RequestOrigin_Lua);
+    }
 
-    void SetPluginsOrigin();
+    static DicomInstanceOrigin FromPlugins()
+    {
+      return DicomInstanceOrigin(RequestOrigin_Plugins);
+    }
 
     RequestOrigin GetRequestOrigin() const
     {
       return origin_;
     }
 
-    const char* GetRemoteAet() const; 
+    const char* GetRemoteAetC() const; 
+
+    const std::string& GetRemoteIp() const;
+    
+    const std::string& GetCalledAet() const; 
+
+    const std::string& GetHttpUsername() const; 
 
     void Format(Json::Value& result) const;
+
+    void Serialize(Json::Value& result) const;
   };
 }
--- a/OrthancServer/DicomInstanceToStore.h	Thu Jun 07 18:18:02 2018 +0200
+++ b/OrthancServer/DicomInstanceToStore.h	Thu Jun 07 21:37:40 2018 +0200
@@ -154,11 +154,6 @@
       origin_ = origin;
     }
     
-    DicomInstanceOrigin& GetOrigin()
-    {
-      return origin_;
-    }
-    
     const DicomInstanceOrigin& GetOrigin() const
     {
       return origin_;
--- a/OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp	Thu Jun 07 18:18:02 2018 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp	Thu Jun 07 21:37:40 2018 +0200
@@ -243,7 +243,7 @@
                                    ParsedDicomFile& dicom)
   {
     DicomInstanceToStore toStore;
-    toStore.GetOrigin().SetRestOrigin(call);
+    toStore.SetOrigin(DicomInstanceOrigin::FromRest(call));
     toStore.SetParsedDicomFile(dicom);
 
     ServerContext& context = OrthancRestApi::GetContext(call);
--- a/OrthancServer/OrthancRestApi/OrthancRestApi.cpp	Thu Jun 07 18:18:02 2018 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestApi.cpp	Thu Jun 07 21:37:40 2018 +0200
@@ -93,7 +93,7 @@
     std::string postData(call.GetBodyData(), call.GetBodySize());
 
     DicomInstanceToStore toStore;
-    toStore.GetOrigin().SetRestOrigin(call);
+    toStore.SetOrigin(DicomInstanceOrigin::FromRest(call));
     toStore.SetBuffer(postData);
 
     std::string publicId;
--- a/OrthancServer/ServerIndex.cpp	Thu Jun 07 18:18:02 2018 +0200
+++ b/OrthancServer/ServerIndex.cpp	Thu Jun 07 21:37:40 2018 +0200
@@ -779,7 +779,7 @@
       // reflecting these additions into the input metadata map
       SetInstanceMetadata(instanceMetadata, instance, MetadataType_Instance_ReceptionDate, now);
       SetInstanceMetadata(instanceMetadata, instance, MetadataType_Instance_RemoteAet,
-                          instanceToStore.GetOrigin().GetRemoteAet());
+                          instanceToStore.GetOrigin().GetRemoteAetC());
       SetInstanceMetadata(instanceMetadata, instance, MetadataType_Instance_Origin, 
                           EnumerationToString(instanceToStore.GetOrigin().GetRequestOrigin()));
         
--- a/OrthancServer/ServerJobs/DicomModalityStoreJob.cpp	Thu Jun 07 18:18:02 2018 +0200
+++ b/OrthancServer/ServerJobs/DicomModalityStoreJob.cpp	Thu Jun 07 21:37:40 2018 +0200
@@ -35,6 +35,7 @@
 #include "DicomModalityStoreJob.h"
 
 #include "../../Core/Logging.h"
+#include "../../Core/SerializationToolbox.h"
 
 namespace Orthanc
 {
@@ -182,4 +183,40 @@
       value["MoveOriginatorID"] = GetMoveOriginatorId();
     }
   }
+
+
+  static const char* LOCAL_AET = "LocalAet";
+  static const char* REMOTE = "Remote";
+  static const char* MOVE_ORIGINATOR_AET = "MoveOriginatorAet";
+  static const char* MOVE_ORIGINATOR_ID = "MoveOriginatorId";
+  
+
+  DicomModalityStoreJob::DicomModalityStoreJob(ServerContext& context,
+                                               const Json::Value& serialized) :
+    SetOfInstancesJob(serialized),
+    context_(context)
+  {
+    localAet_ = SerializationToolbox::ReadString(serialized, LOCAL_AET);
+    remote_ = RemoteModalityParameters(serialized[REMOTE]);
+    moveOriginatorAet_ = SerializationToolbox::ReadString(serialized, MOVE_ORIGINATOR_AET);
+    moveOriginatorId_ = static_cast<uint16_t>
+      (SerializationToolbox::ReadUnsignedInteger(serialized, MOVE_ORIGINATOR_ID));
+  }
+
+
+  bool DicomModalityStoreJob::Serialize(Json::Value& target)
+  {
+    if (!SetOfInstancesJob::Serialize(target))
+    {
+      return false;
+    }
+    else
+    {
+      target[LOCAL_AET] = localAet_;
+      remote_.Serialize(target[REMOTE]);
+      target[MOVE_ORIGINATOR_AET] = moveOriginatorAet_;
+      target[MOVE_ORIGINATOR_ID] = moveOriginatorId_;
+      return true;
+    }
+  }  
 }
--- a/OrthancServer/ServerJobs/DicomModalityStoreJob.h	Thu Jun 07 18:18:02 2018 +0200
+++ b/OrthancServer/ServerJobs/DicomModalityStoreJob.h	Thu Jun 07 21:37:40 2018 +0200
@@ -58,6 +58,9 @@
   public:
     DicomModalityStoreJob(ServerContext& context);
 
+    DicomModalityStoreJob(ServerContext& context,
+                          const Json::Value& serialized);
+
     const std::string& GetLocalAet() const
     {
       return localAet_;
@@ -92,5 +95,7 @@
     }
 
     virtual void GetPublicContent(Json::Value& value);
+
+    virtual bool Serialize(Json::Value& target);
   };
 }
--- a/OrthancServer/ServerJobs/Operations/ModifyInstanceOperation.cpp	Thu Jun 07 18:18:02 2018 +0200
+++ b/OrthancServer/ServerJobs/Operations/ModifyInstanceOperation.cpp	Thu Jun 07 21:37:40 2018 +0200
@@ -106,7 +106,7 @@
 
       DicomInstanceToStore toStore;
       assert(origin_ == RequestOrigin_Lua);
-      toStore.GetOrigin().SetLuaOrigin();
+      toStore.SetOrigin(DicomInstanceOrigin::FromLua());
       toStore.SetParsedDicomFile(*modified);
 
       // TODO other metadata
--- a/OrthancServer/ServerJobs/OrthancJobUnserializer.cpp	Thu Jun 07 18:18:02 2018 +0200
+++ b/OrthancServer/ServerJobs/OrthancJobUnserializer.cpp	Thu Jun 07 21:37:40 2018 +0200
@@ -45,11 +45,32 @@
 #include "Operations/StoreScuOperation.h"
 #include "Operations/SystemCallOperation.h"
 
+#include "DicomModalityStoreJob.h"
+#include "OrthancPeerStoreJob.h"
+#include "ResourceModificationJob.h"
+
 namespace Orthanc
 {
   IJob* OrthancJobUnserializer::UnserializeJob(const Json::Value& source)
   {
-    return GenericJobUnserializer::UnserializeJob(source);
+    const std::string type = SerializationToolbox::ReadString(source, "Type");
+
+    if (type == "DicomModalityStore")
+    {
+      return new DicomModalityStoreJob(context_, source);
+    }
+    else if (type == "OrthancPeerStore")
+    {
+      return new OrthancPeerStoreJob(context_, source);
+    }
+    else if (type == "ResourceModification")
+    {
+      return new ResourceModificationJob(context_, source);
+    }
+    else
+    {
+      return GenericJobUnserializer::UnserializeJob(source);
+    }
   }
 
 
--- a/OrthancServer/ServerJobs/OrthancPeerStoreJob.cpp	Thu Jun 07 18:18:02 2018 +0200
+++ b/OrthancServer/ServerJobs/OrthancPeerStoreJob.cpp	Thu Jun 07 21:37:40 2018 +0200
@@ -101,4 +101,29 @@
     peer_.ToJson(v);
     value["Peer"] = v;
   }
+
+
+  static const char* PEER = "Peer";
+
+  OrthancPeerStoreJob::OrthancPeerStoreJob(ServerContext& context,
+                                           const Json::Value& serialized) :
+    SetOfInstancesJob(serialized),
+    context_(context)
+  {
+    peer_ = WebServiceParameters(serialized[PEER]);
+  }
+
+
+  bool OrthancPeerStoreJob::Serialize(Json::Value& target)
+  {
+    if (!SetOfInstancesJob::Serialize(target))
+    {
+      return false;
+    }
+    else
+    {
+      peer_.Serialize(target[PEER]);
+      return true;
+    }
+  }  
 }
--- a/OrthancServer/ServerJobs/OrthancPeerStoreJob.h	Thu Jun 07 18:18:02 2018 +0200
+++ b/OrthancServer/ServerJobs/OrthancPeerStoreJob.h	Thu Jun 07 21:37:40 2018 +0200
@@ -57,6 +57,9 @@
     {
     }
 
+    OrthancPeerStoreJob(ServerContext& context,
+                        const Json::Value& serialize);
+
     void SetPeer(const WebServiceParameters& peer);
 
     const WebServiceParameters& GetPeer() const
@@ -72,5 +75,7 @@
     }
 
     virtual void GetPublicContent(Json::Value& value);
+
+    virtual bool Serialize(Json::Value& target);
   };
 }
--- a/OrthancServer/ServerJobs/ResourceModificationJob.cpp	Thu Jun 07 18:18:02 2018 +0200
+++ b/OrthancServer/ServerJobs/ResourceModificationJob.cpp	Thu Jun 07 21:37:40 2018 +0200
@@ -35,6 +35,7 @@
 #include "ResourceModificationJob.h"
 
 #include "../../Core/Logging.h"
+#include "../../Core/SerializationToolbox.h"
 
 namespace Orthanc
 {
@@ -259,9 +260,20 @@
   
   void ResourceModificationJob::SetOrigin(const RestApiCall& call)
   {
-    DicomInstanceOrigin tmp;
-    tmp.SetRestOrigin(call);      
-    SetOrigin(tmp);
+    SetOrigin(DicomInstanceOrigin::FromRest(call));
+  }
+
+
+  const DicomModification& ResourceModificationJob::GetModification() const
+  {
+    if (modification_.get() == NULL)
+    {
+      throw OrthancException(ErrorCode_BadSequenceOfCalls);
+    }
+    else
+    {
+      return *modification_;
+    }
   }
 
 
@@ -272,15 +284,38 @@
     value["IsAnonymization"] = isAnonymization_;
   }
 
+
+  static const char* MODIFICATION = "Modification";
+  static const char* ORIGIN = "Origin";
+  static const char* IS_ANONYMIZATION = "IsAnonymization";
+  
+
+  ResourceModificationJob::ResourceModificationJob(ServerContext& context,
+                                                   const Json::Value& serialized) :
+    SetOfInstancesJob(serialized),
+    context_(context)
+  {
+    isAnonymization_ = SerializationToolbox::ReadBoolean(serialized, IS_ANONYMIZATION);
+    origin_ = DicomInstanceOrigin(serialized[ORIGIN]);
+    modification_.reset(new DicomModification(serialized[MODIFICATION]));
+  }
   
   bool ResourceModificationJob::Serialize(Json::Value& value)
   {
-    SetOfInstancesJob::Serialize(value);
+    if (!SetOfInstancesJob::Serialize(value))
+    {
+      return false;
+    }
+    else
+    {
+      value[IS_ANONYMIZATION] = isAnonymization_;
+      origin_.Serialize(value[ORIGIN]);
+      
+      Json::Value tmp;
+      modification_->Serialize(tmp);
+      value[MODIFICATION] = tmp;
 
-    Json::Value tmp;
-    modification_->Serialize(tmp);
-    value["Modification"] = tmp;
-
-    return true;
+      return true;
+    }
   }
 }
--- a/OrthancServer/ServerJobs/ResourceModificationJob.h	Thu Jun 07 18:18:02 2018 +0200
+++ b/OrthancServer/ServerJobs/ResourceModificationJob.h	Thu Jun 07 21:37:40 2018 +0200
@@ -70,8 +70,6 @@
     std::auto_ptr<DicomModification>  modification_;
     boost::shared_ptr<Output>         output_;
     bool                              isAnonymization_;
-    MetadataType                      metadataType_;
-    std::string                       description_;
     DicomInstanceOrigin               origin_;
 
   protected:
@@ -84,6 +82,9 @@
     {
     }
 
+    ResourceModificationJob(ServerContext& context,
+                            const Json::Value& serialized);
+
     void SetModification(DicomModification* modification,   // Takes ownership
                          bool isAnonymization);
 
@@ -93,6 +94,18 @@
 
     void SetOrigin(const RestApiCall& call);
 
+    const DicomModification& GetModification() const;
+
+    bool IsAnonymization() const
+    {
+      return isAnonymization_;
+    }
+
+    const DicomInstanceOrigin& GetOrigin() const
+    {
+      return origin_;
+    }
+
     virtual void ReleaseResources()
     {
     }
--- a/OrthancServer/main.cpp	Thu Jun 07 18:18:02 2018 +0200
+++ b/OrthancServer/main.cpp	Thu Jun 07 21:37:40 2018 +0200
@@ -75,7 +75,8 @@
     if (dicomFile.size() > 0)
     {
       DicomInstanceToStore toStore;
-      toStore.GetOrigin().SetDicomProtocolOrigin(remoteIp.c_str(), remoteAet.c_str(), calledAet.c_str());
+      toStore.SetOrigin(DicomInstanceOrigin::FromDicomProtocol
+                        (remoteIp.c_str(), remoteAet.c_str(), calledAet.c_str()));
       toStore.SetBuffer(dicomFile);
       toStore.SetSummary(dicomSummary);
       toStore.SetJson(dicomJson);
--- a/Plugins/Engine/OrthancPlugins.cpp	Thu Jun 07 18:18:02 2018 +0200
+++ b/Plugins/Engine/OrthancPlugins.cpp	Thu Jun 07 21:37:40 2018 +0200
@@ -1544,7 +1544,7 @@
     switch (service)
     {
       case _OrthancPluginService_GetInstanceRemoteAet:
-        *p.resultString = instance.GetOrigin().GetRemoteAet();
+        *p.resultString = instance.GetOrigin().GetRemoteAetC();
         return;
 
       case _OrthancPluginService_GetInstanceSize:
--- a/UnitTestsSources/MultiThreadingTests.cpp	Thu Jun 07 18:18:02 2018 +0200
+++ b/UnitTestsSources/MultiThreadingTests.cpp	Thu Jun 07 21:37:40 2018 +0200
@@ -59,7 +59,9 @@
 #include "../OrthancServer/ServerJobs/Operations/SystemCallOperation.h"
 
 #include "../OrthancServer/ServerJobs/ArchiveJob.h"
-
+#include "../OrthancServer/ServerJobs/DicomModalityStoreJob.h"
+#include "../OrthancServer/ServerJobs/OrthancPeerStoreJob.h"
+#include "../OrthancServer/ServerJobs/ResourceModificationJob.h"
 
 
 using namespace Orthanc;
@@ -951,6 +953,74 @@
 }
 
 
+TEST(JobsSerialization, DicomInstanceOrigin)
+{   
+  Json::Value s;
+
+  {
+    DicomInstanceOrigin origin;
+    origin.Serialize(s);
+  }
+
+  {
+    DicomInstanceOrigin origin(s);
+    ASSERT_EQ(RequestOrigin_Unknown, origin.GetRequestOrigin());
+    ASSERT_THROW(origin.GetRemoteIp(), OrthancException);
+    ASSERT_EQ("", std::string(origin.GetRemoteAetC()));
+    ASSERT_THROW(origin.GetCalledAet(), OrthancException);
+    ASSERT_THROW(origin.GetHttpUsername(), OrthancException);
+  }
+
+  {
+    DicomInstanceOrigin origin(DicomInstanceOrigin::FromDicomProtocol("host", "aet", "called"));
+    origin.Serialize(s);
+  }
+
+  {
+    DicomInstanceOrigin origin(s);
+    ASSERT_EQ(RequestOrigin_DicomProtocol, origin.GetRequestOrigin());
+    ASSERT_EQ("host", origin.GetRemoteIp());
+    ASSERT_EQ("aet", std::string(origin.GetRemoteAetC()));
+    ASSERT_EQ("called", origin.GetCalledAet());
+    ASSERT_THROW(origin.GetHttpUsername(), OrthancException);
+  }
+
+  {
+    DicomInstanceOrigin origin(DicomInstanceOrigin::FromHttp("host", "username"));
+    origin.Serialize(s);
+  }
+
+  {
+    DicomInstanceOrigin origin(s);
+    ASSERT_EQ(RequestOrigin_RestApi, origin.GetRequestOrigin());
+    ASSERT_EQ("host", origin.GetRemoteIp());
+    ASSERT_EQ("", std::string(origin.GetRemoteAetC()));
+    ASSERT_THROW(origin.GetCalledAet(), OrthancException);
+    ASSERT_EQ("username", origin.GetHttpUsername());
+  }
+
+  {
+    DicomInstanceOrigin origin(DicomInstanceOrigin::FromLua());
+    origin.Serialize(s);
+  }
+
+  {
+    DicomInstanceOrigin origin(s);
+    ASSERT_EQ(RequestOrigin_Lua, origin.GetRequestOrigin());
+  }
+
+  {
+    DicomInstanceOrigin origin(DicomInstanceOrigin::FromPlugins());
+    origin.Serialize(s);
+  }
+
+  {
+    DicomInstanceOrigin origin(s);
+    ASSERT_EQ(RequestOrigin_Plugins, origin.GetRequestOrigin());
+  }
+}
+
+
 TEST(JobsSerialization, Registry)
 {   
   // TODO : Test serialization of JobsRegistry
@@ -1154,12 +1224,89 @@
   {
     boost::shared_ptr<TemporaryFile> tmp(new TemporaryFile);
     ArchiveJob job(tmp, GetContext(), false, false);
-    ASSERT_FALSE(job.Serialize(s));
+    ASSERT_FALSE(job.Serialize(s));  // Cannot serialize this
+  }
+
+  // DicomModalityStoreJob
+
+  {
+    RemoteModalityParameters modality;
+    modality.SetApplicationEntityTitle("REMOTE");
+    modality.SetHost("192.168.1.1");
+    modality.SetPort(1000);
+    modality.SetManufacturer(ModalityManufacturer_StoreScp);
+
+    DicomModalityStoreJob job(GetContext());
+    job.SetLocalAet("LOCAL");
+    job.SetRemoteModality(modality);
+    job.SetMoveOriginator("MOVESCU", 42);
+    
+    ASSERT_TRUE(job.Serialize(s));
+  }
+
+  OrthancJobUnserializer unserializer(GetContext()); 
+
+  {
+    std::auto_ptr<IJob> job;
+    job.reset(unserializer.UnserializeJob(s));
+
+    DicomModalityStoreJob& tmp = dynamic_cast<DicomModalityStoreJob&>(*job);
+    ASSERT_EQ("LOCAL", tmp.GetLocalAet());
+    ASSERT_EQ("REMOTE", tmp.GetRemoteModality().GetApplicationEntityTitle());
+    ASSERT_EQ("192.168.1.1", tmp.GetRemoteModality().GetHost());
+    ASSERT_EQ(1000, tmp.GetRemoteModality().GetPort());
+    ASSERT_EQ(ModalityManufacturer_StoreScp, tmp.GetRemoteModality().GetManufacturer());
+    ASSERT_TRUE(tmp.HasMoveOriginator());
+    ASSERT_EQ("MOVESCU", tmp.GetMoveOriginatorAet());
+    ASSERT_EQ(42, tmp.GetMoveOriginatorId());
   }
 
-  // TODO : DicomModalityStoreJob
+  // OrthancPeerStoreJob
+
+  {
+    WebServiceParameters peer;
+    peer.SetUrl("http://localhost/");
+    peer.SetUsername("username");
+    peer.SetPassword("password");
+    peer.SetPkcs11Enabled(true);
+
+    OrthancPeerStoreJob job(GetContext());
+    job.SetPeer(peer);
+    
+    ASSERT_TRUE(job.Serialize(s));
+  }
+
+  {
+    std::auto_ptr<IJob> job;
+    job.reset(unserializer.UnserializeJob(s));
 
-  // TODO : OrthancPeerStoreJob
+    OrthancPeerStoreJob& tmp = dynamic_cast<OrthancPeerStoreJob&>(*job);
+    ASSERT_EQ("http://localhost/", tmp.GetPeer().GetUrl());
+    ASSERT_EQ("username", tmp.GetPeer().GetUsername());
+    ASSERT_EQ("password", tmp.GetPeer().GetPassword());
+    ASSERT_TRUE(tmp.GetPeer().IsPkcs11Enabled());
+  }
+
+  // ResourceModificationJob
+
+  {
+    std::auto_ptr<DicomModification> modification(new DicomModification);
+    modification->SetupAnonymization(DicomVersion_2008);    
 
-  // TODO : ResourceModificationJob
+    ResourceModificationJob job(GetContext());
+    job.SetModification(modification.release(), true);
+    job.SetOrigin(DicomInstanceOrigin::FromLua());
+    
+    ASSERT_TRUE(job.Serialize(s));
+  }
+
+  {
+    std::auto_ptr<IJob> job;
+    job.reset(unserializer.UnserializeJob(s));
+
+    ResourceModificationJob& tmp = dynamic_cast<ResourceModificationJob&>(*job);
+    ASSERT_TRUE(tmp.IsAnonymization());
+    ASSERT_EQ(RequestOrigin_Lua, tmp.GetOrigin().GetRequestOrigin());
+    ASSERT_TRUE(tmp.GetModification().IsRemoved(DICOM_TAG_STUDY_DESCRIPTION));
+  }
 }