changeset 2652:a3f0f61a14ca jobs

fix
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 04 Jun 2018 12:36:24 +0200
parents 1da5a052c777
children d7815540bd81
files Core/JobsEngine/GenericJobUnserializer.cpp Core/JobsEngine/GenericJobUnserializer.h Core/JobsEngine/IJobUnserializer.cpp Core/JobsEngine/IJobUnserializer.h Core/JobsEngine/JobsRegistry.cpp Core/JobsEngine/SetOfInstancesJob.cpp Core/JobsEngine/SetOfInstancesJob.h OrthancServer/ServerJobs/OrthancJobUnserializer.cpp OrthancServer/ServerJobs/OrthancJobUnserializer.h UnitTestsSources/MultiThreadingTests.cpp
diffstat 10 files changed, 351 insertions(+), 168 deletions(-) [+]
line wrap: on
line diff
--- a/Core/JobsEngine/GenericJobUnserializer.cpp	Mon Jun 04 11:53:12 2018 +0200
+++ b/Core/JobsEngine/GenericJobUnserializer.cpp	Mon Jun 04 12:36:24 2018 +0200
@@ -43,9 +43,9 @@
 
 namespace Orthanc
 {
-  IJob* GenericJobUnserializer::UnserializeJob(const std::string& type,
-                                               const Json::Value& source)
+  IJob* GenericJobUnserializer::UnserializeJob(const Json::Value& source)
   {
+    const std::string type = GetString(source, "Type");
 
     LOG(ERROR) << "Cannot unserialize job of type: " << type;
     throw OrthancException(ErrorCode_BadFileFormat);
--- a/Core/JobsEngine/GenericJobUnserializer.h	Mon Jun 04 11:53:12 2018 +0200
+++ b/Core/JobsEngine/GenericJobUnserializer.h	Mon Jun 04 12:36:24 2018 +0200
@@ -40,8 +40,7 @@
   class GenericJobUnserializer : public IJobUnserializer
   {
   public:
-    virtual IJob* UnserializeJob(const std::string& type,
-                                 const Json::Value& value);
+    virtual IJob* UnserializeJob(const Json::Value& value);
 
     virtual IJobOperation* UnserializeOperation(const Json::Value& value);
 
--- a/Core/JobsEngine/IJobUnserializer.cpp	Mon Jun 04 11:53:12 2018 +0200
+++ b/Core/JobsEngine/IJobUnserializer.cpp	Mon Jun 04 12:36:24 2018 +0200
@@ -85,4 +85,80 @@
       return static_cast<unsigned int>(tmp);
     }
   }
+
+
+  bool IJobUnserializer::GetBoolean(const Json::Value& value,
+                                    const std::string& name)
+  {
+    if (value.type() != Json::objectValue ||
+        !value.isMember(name.c_str()) ||
+        value[name.c_str()].type() != Json::booleanValue)
+    {
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+    else
+    {
+      return value[name.c_str()].asBool();
+    }   
+  }
+
+  
+  void IJobUnserializer::GetArrayOfStrings(std::vector<std::string>& target,
+                                           const Json::Value& value,
+                                           const std::string& name)
+  {
+    if (value.type() != Json::objectValue ||
+        !value.isMember(name.c_str()) ||
+        value[name.c_str()].type() != Json::arrayValue)
+    {
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+
+    target.clear();
+    target.resize(value.size());
+
+    const Json::Value arr = value[name.c_str()];
+    
+    for (Json::Value::ArrayIndex i = 0; i < arr.size(); i++)
+    {
+      if (arr[i].type() != Json::stringValue)
+      {
+        throw OrthancException(ErrorCode_BadFileFormat);        
+      }
+      else
+      {
+        target[i] = arr[i].asString();
+      }
+    }
+  }
+
+
+  void IJobUnserializer::GetListOfStrings(std::list<std::string>& target,
+                                          const Json::Value& value,
+                                          const std::string& name)
+  {
+    std::vector<std::string> tmp;
+    GetArrayOfStrings(tmp, value, name);
+
+    target.clear();
+    for (size_t i = 0; i < tmp.size(); i++)
+    {
+      target.push_back(tmp[i]);
+    }
+  }
+  
+
+  void IJobUnserializer::GetSetOfStrings(std::set<std::string>& target,
+                                         const Json::Value& value,
+                                         const std::string& name)
+  {
+    std::vector<std::string> tmp;
+    GetArrayOfStrings(tmp, value, name);
+
+    target.clear();
+    for (size_t i = 0; i < tmp.size(); i++)
+    {
+      target.insert(tmp[i]);
+    }
+  }
 }
--- a/Core/JobsEngine/IJobUnserializer.h	Mon Jun 04 11:53:12 2018 +0200
+++ b/Core/JobsEngine/IJobUnserializer.h	Mon Jun 04 12:36:24 2018 +0200
@@ -37,6 +37,8 @@
 #include "Operations/JobOperationValue.h"
 #include "Operations/IJobOperation.h"
 
+#include <vector>
+
 namespace Orthanc
 {
   class IJobUnserializer : public boost::noncopyable
@@ -46,8 +48,7 @@
     {
     }
 
-    virtual IJob* UnserializeJob(const std::string& jobType,
-                                 const Json::Value& value) = 0;
+    virtual IJob* UnserializeJob(const Json::Value& value) = 0;
 
     virtual IJobOperation* UnserializeOperation(const Json::Value& value) = 0;
 
@@ -61,5 +62,20 @@
 
     static unsigned int GetUnsignedInteger(const Json::Value& value,
                                            const std::string& name);
+
+    static bool GetBoolean(const Json::Value& value,
+                           const std::string& name);
+
+    static void GetArrayOfStrings(std::vector<std::string>& target,
+                                  const Json::Value& value,
+                                  const std::string& name);
+
+    static void GetListOfStrings(std::list<std::string>& target,
+                                 const Json::Value& value,
+                                 const std::string& name);
+
+    static void GetSetOfStrings(std::set<std::string>& target,
+                                const Json::Value& value,
+                                const std::string& name);
   };
 }
--- a/Core/JobsEngine/JobsRegistry.cpp	Mon Jun 04 11:53:12 2018 +0200
+++ b/Core/JobsEngine/JobsRegistry.cpp	Mon Jun 04 12:36:24 2018 +0200
@@ -266,7 +266,6 @@
       cancelScheduled_(false)
     {
       state_ = StringToJobState(IJobUnserializer::GetString(serialized, "State"));
-      jobType_ = IJobUnserializer::GetString(serialized, "Type");
       priority_ = IJobUnserializer::GetInteger(serialized, "Priority");
       creationTime_ = boost::posix_time::from_iso_string
         (IJobUnserializer::GetString(serialized, "CreationTime"));
@@ -280,16 +279,10 @@
         state_ = JobState_Pending;
       }
 
-      job_.reset(unserializer.UnserializeJob(jobType_, serialized["Job"]));
+      job_.reset(unserializer.UnserializeJob(serialized["Job"]));
+      job_->GetJobType(jobType_);
+      job_->Start();
 
-      std::string s;
-      job_->GetJobType(s);
-      if (s != jobType_)
-      {
-        throw OrthancException(ErrorCode_InternalError);
-      }
-
-      job_->Start();
       lastStatus_ = JobStatus(ErrorCode_Success, *job_);
     }
   };
--- a/Core/JobsEngine/SetOfInstancesJob.cpp	Mon Jun 04 11:53:12 2018 +0200
+++ b/Core/JobsEngine/SetOfInstancesJob.cpp	Mon Jun 04 12:36:24 2018 +0200
@@ -35,6 +35,7 @@
 #include "SetOfInstancesJob.h"
 
 #include "../OrthancException.h"
+#include "IJobUnserializer.h"
 
 namespace Orthanc
 {
@@ -188,6 +189,14 @@
 
   void SetOfInstancesJob::Serialize(Json::Value& value)
   {
+    std::string type;
+    GetJobType(type);
+    value["Type"] = type;
+
+    value["Permissive"] = permissive_;
+    value["Position"] = static_cast<unsigned int>(position_);
+    value["Description"] = description_;
+    
     Json::Value v = Json::arrayValue;
       
     for (size_t i = 0; i < instances_.size(); i++)
@@ -207,4 +216,21 @@
       
     value["FailedInstances"] = v;
   }
+
+
+  SetOfInstancesJob::SetOfInstancesJob(const Json::Value& value) :
+    started_(false),
+    permissive_(IJobUnserializer::GetBoolean(value, "Permissive")),
+    position_(IJobUnserializer::GetUnsignedInteger(value, "Position")),
+    description_(IJobUnserializer::GetString(value, "Description"))
+  {
+    IJobUnserializer::GetArrayOfStrings(instances_, value, "Instances");
+    IJobUnserializer::GetSetOfStrings(failedInstances_, value, "FailedInstances");
+
+    if (position_ > instances_.size())
+    {
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+  }
+
 }
--- a/Core/JobsEngine/SetOfInstancesJob.h	Mon Jun 04 11:53:12 2018 +0200
+++ b/Core/JobsEngine/SetOfInstancesJob.h	Mon Jun 04 12:36:24 2018 +0200
@@ -55,6 +55,8 @@
   public:
     SetOfInstancesJob();
 
+    SetOfInstancesJob(const Json::Value& s);  // Unserialization
+
     void SetDescription(const std::string& description)
     {
       description_ = description;
--- a/OrthancServer/ServerJobs/OrthancJobUnserializer.cpp	Mon Jun 04 11:53:12 2018 +0200
+++ b/OrthancServer/ServerJobs/OrthancJobUnserializer.cpp	Mon Jun 04 12:36:24 2018 +0200
@@ -41,10 +41,9 @@
 
 namespace Orthanc
 {
-  IJob* OrthancJobUnserializer::UnserializeJob(const std::string& type,
-                                               const Json::Value& source)
+  IJob* OrthancJobUnserializer::UnserializeJob(const Json::Value& source)
   {
-    return GenericJobUnserializer::UnserializeJob(type, source);
+    return GenericJobUnserializer::UnserializeJob(source);
   }
 
 
--- a/OrthancServer/ServerJobs/OrthancJobUnserializer.h	Mon Jun 04 11:53:12 2018 +0200
+++ b/OrthancServer/ServerJobs/OrthancJobUnserializer.h	Mon Jun 04 12:36:24 2018 +0200
@@ -49,8 +49,7 @@
     {
     }
 
-    virtual IJob* UnserializeJob(const std::string& type,
-                                 const Json::Value& value);
+    virtual IJob* UnserializeJob(const Json::Value& value);
 
     virtual IJobOperation* UnserializeOperation(const Json::Value& value);
 
--- a/UnitTestsSources/MultiThreadingTests.cpp	Mon Jun 04 11:53:12 2018 +0200
+++ b/UnitTestsSources/MultiThreadingTests.cpp	Mon Jun 04 12:36:24 2018 +0200
@@ -53,6 +53,124 @@
 
 namespace
 {
+  class DummyJob : public IJob
+  {
+  private:
+    bool         fails_;
+    unsigned int count_;
+    unsigned int steps_;
+
+  public:
+    DummyJob() :
+      fails_(false),
+      count_(0),
+      steps_(4)
+    {
+    }
+
+    explicit DummyJob(bool fails) :
+      fails_(fails),
+      count_(0),
+      steps_(4)
+    {
+    }
+
+    virtual void Start()
+    {
+    }
+
+    virtual void SignalResubmit()
+    {
+    }
+    
+    virtual JobStepResult ExecuteStep()
+    {
+      if (fails_)
+      {
+        return JobStepResult::Failure(ErrorCode_ParameterOutOfRange);
+      }
+      else if (count_ == steps_ - 1)
+      {
+        return JobStepResult::Success();
+      }
+      else
+      {
+        count_++;
+        return JobStepResult::Continue();
+      }
+    }
+
+    virtual void ReleaseResources()
+    {
+    }
+
+    virtual float GetProgress()
+    {
+      return static_cast<float>(count_) / static_cast<float>(steps_ - 1);
+    }
+
+    virtual void GetJobType(std::string& type)
+    {
+      type = "DummyJob";
+    }
+
+    virtual void Serialize(Json::Value& value)
+    {
+    }
+
+    virtual void GetPublicContent(Json::Value& value)
+    {
+      value["hello"] = "world";
+    }
+  };
+
+
+  class DummyInstancesJob : public SetOfInstancesJob
+  {
+  protected:
+    virtual bool HandleInstance(const std::string& instance)
+    {
+      return (instance != "nope");
+    }
+
+  public:
+    DummyInstancesJob()
+    {
+    }
+    
+    DummyInstancesJob(const Json::Value& value) :
+      SetOfInstancesJob(value)
+    {
+    }
+    
+    virtual void ReleaseResources()
+    {
+    }
+
+    virtual void GetJobType(std::string& s)
+    {
+      s = "DummyInstancesJob";
+    }
+  };
+
+
+  class DummyUnserializer : public GenericJobUnserializer
+  {
+  public:
+    virtual IJob* UnserializeJob(const Json::Value& value)
+    {
+      if (GetString(value, "Type") == "DummyInstancesJob")
+      {
+        return new DummyInstancesJob(value);
+      }
+      else
+      {
+        return GenericJobUnserializer::UnserializeJob(value);
+      }
+    }
+  };
+
+    
   class DynamicInteger : public IDynamicObject
   {
   private:
@@ -115,83 +233,11 @@
 
 
 
-class DummyJob : public Orthanc::IJob
+static bool CheckState(JobsRegistry& registry,
+                       const std::string& id,
+                       JobState state)
 {
-private:
-  bool         fails_;
-  unsigned int count_;
-  unsigned int steps_;
-
-public:
-  DummyJob() :
-    fails_(false),
-    count_(0),
-    steps_(4)
-  {
-  }
-
-  explicit DummyJob(bool fails) :
-  fails_(fails),
-    count_(0),
-    steps_(4)
-  {
-  }
-
-  virtual void Start()
-  {
-  }
-
-  virtual void SignalResubmit()
-  {
-  }
-    
-  virtual JobStepResult ExecuteStep()
-  {
-    if (fails_)
-    {
-      return JobStepResult::Failure(ErrorCode_ParameterOutOfRange);
-    }
-    else if (count_ == steps_ - 1)
-    {
-      return JobStepResult::Success();
-    }
-    else
-    {
-      count_++;
-      return JobStepResult::Continue();
-    }
-  }
-
-  virtual void ReleaseResources()
-  {
-  }
-
-  virtual float GetProgress()
-  {
-    return static_cast<float>(count_) / static_cast<float>(steps_ - 1);
-  }
-
-  virtual void GetJobType(std::string& type)
-  {
-    type = "DummyJob";
-  }
-
-  virtual void Serialize(Json::Value& value)
-  {
-  }
-
-  virtual void GetPublicContent(Json::Value& value)
-  {
-    value["hello"] = "world";
-  }
-};
-
-
-static bool CheckState(Orthanc::JobsRegistry& registry,
-                       const std::string& id,
-                       Orthanc::JobState state)
-{
-  Orthanc::JobState s;
+  JobState s;
   if (registry.GetState(s, id))
   {
     return state == s;
@@ -203,11 +249,11 @@
 }
 
 
-static bool CheckErrorCode(Orthanc::JobsRegistry& registry,
+static bool CheckErrorCode(JobsRegistry& registry,
                            const std::string& id,
-                           Orthanc::ErrorCode code)
+                           ErrorCode code)
 {
-  Orthanc::JobInfo s;
+  JobInfo s;
   if (registry.GetJobInfo(s, id))
   {
     return code == s.GetStatus().GetErrorCode();
@@ -240,7 +286,7 @@
   ASSERT_TRUE(id.find(i3) != id.end());
   ASSERT_TRUE(id.find(i4) != id.end());
 
-  ASSERT_TRUE(CheckState(registry, i2, Orthanc::JobState_Pending));
+  ASSERT_TRUE(CheckState(registry, i2, JobState_Pending));
 
   {
     JobsRegistry::RunningJob job(registry, 0);
@@ -248,11 +294,11 @@
     ASSERT_EQ(30, job.GetPriority());
     ASSERT_EQ(i2, job.GetId());
 
-    ASSERT_TRUE(CheckState(registry, i2, Orthanc::JobState_Running));
+    ASSERT_TRUE(CheckState(registry, i2, JobState_Running));
   }
 
-  ASSERT_TRUE(CheckState(registry, i2, Orthanc::JobState_Failure));
-  ASSERT_TRUE(CheckState(registry, i3, Orthanc::JobState_Pending));
+  ASSERT_TRUE(CheckState(registry, i2, JobState_Failure));
+  ASSERT_TRUE(CheckState(registry, i3, JobState_Pending));
 
   {
     JobsRegistry::RunningJob job(registry, 0);
@@ -262,10 +308,10 @@
 
     job.MarkSuccess();
 
-    ASSERT_TRUE(CheckState(registry, i3, Orthanc::JobState_Running));
+    ASSERT_TRUE(CheckState(registry, i3, JobState_Running));
   }
 
-  ASSERT_TRUE(CheckState(registry, i3, Orthanc::JobState_Success));
+  ASSERT_TRUE(CheckState(registry, i3, JobState_Success));
 
   {
     JobsRegistry::RunningJob job(registry, 0);
@@ -286,7 +332,7 @@
     ASSERT_FALSE(job.IsValid());
   }
 
-  Orthanc::JobState s;
+  JobState s;
   ASSERT_TRUE(registry.GetState(s, i1));
   ASSERT_FALSE(registry.GetState(s, i2));  // Removed because oldest
   ASSERT_FALSE(registry.GetState(s, i3));  // Removed because second oldest
@@ -306,8 +352,8 @@
   registry.Submit(i1, new DummyJob(), 20);
   registry.Submit(i2, new DummyJob(), 10);
 
-  ASSERT_TRUE(CheckState(registry, i1, Orthanc::JobState_Pending));
-  ASSERT_TRUE(CheckState(registry, i2, Orthanc::JobState_Pending));
+  ASSERT_TRUE(CheckState(registry, i1, JobState_Pending));
+  ASSERT_TRUE(CheckState(registry, i2, JobState_Pending));
 
   {
     JobsRegistry::RunningJob job1(registry, 0);
@@ -319,12 +365,12 @@
     job1.MarkFailure();
     job2.MarkSuccess();
 
-    ASSERT_TRUE(CheckState(registry, i1, Orthanc::JobState_Running));
-    ASSERT_TRUE(CheckState(registry, i2, Orthanc::JobState_Running));
+    ASSERT_TRUE(CheckState(registry, i1, JobState_Running));
+    ASSERT_TRUE(CheckState(registry, i2, JobState_Running));
   }
 
-  ASSERT_TRUE(CheckState(registry, i1, Orthanc::JobState_Failure));
-  ASSERT_TRUE(CheckState(registry, i2, Orthanc::JobState_Success));
+  ASSERT_TRUE(CheckState(registry, i1, JobState_Failure));
+  ASSERT_TRUE(CheckState(registry, i2, JobState_Success));
 }
 
 
@@ -335,26 +381,26 @@
   std::string id;
   registry.Submit(id, new DummyJob(), 10);
 
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Pending));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
 
   registry.Resubmit(id);
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Pending));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
 
   {
     JobsRegistry::RunningJob job(registry, 0);
     ASSERT_TRUE(job.IsValid());
     job.MarkFailure();
 
-    ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Running));
+    ASSERT_TRUE(CheckState(registry, id, JobState_Running));
 
     registry.Resubmit(id);
-    ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Running));
+    ASSERT_TRUE(CheckState(registry, id, JobState_Running));
   }
 
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Failure));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Failure));
 
   registry.Resubmit(id);
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Pending));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
 
   {
     JobsRegistry::RunningJob job(registry, 0);
@@ -362,13 +408,13 @@
     ASSERT_EQ(id, job.GetId());
 
     job.MarkSuccess();
-    ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Running));
+    ASSERT_TRUE(CheckState(registry, id, JobState_Running));
   }
 
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Success));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Success));
 
   registry.Resubmit(id);
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Success));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Success));
 }
 
 
@@ -379,33 +425,33 @@
   std::string id;
   registry.Submit(id, new DummyJob(), 10);
 
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Pending));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
 
   {
     JobsRegistry::RunningJob job(registry, 0);
     ASSERT_TRUE(job.IsValid());
     job.MarkRetry(0);
 
-    ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Running));
+    ASSERT_TRUE(CheckState(registry, id, JobState_Running));
   }
 
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Retry));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Retry));
 
   registry.Resubmit(id);
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Retry));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Retry));
   
   registry.ScheduleRetries();
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Pending));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
 
   {
     JobsRegistry::RunningJob job(registry, 0);
     ASSERT_TRUE(job.IsValid());
     job.MarkSuccess();
 
-    ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Running));
+    ASSERT_TRUE(CheckState(registry, id, JobState_Running));
   }
 
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Success));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Success));
 }
 
 
@@ -416,19 +462,19 @@
   std::string id;
   registry.Submit(id, new DummyJob(), 10);
 
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Pending));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
 
   registry.Pause(id);
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Paused));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Paused));
 
   registry.Pause(id);
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Paused));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Paused));
 
   registry.Resubmit(id);
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Paused));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Paused));
 
   registry.Resume(id);
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Pending));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
 }
 
 
@@ -439,7 +485,7 @@
   std::string id;
   registry.Submit(id, new DummyJob(), 10);
 
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Pending));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
 
   {
     JobsRegistry::RunningJob job(registry, 0);
@@ -447,26 +493,26 @@
 
     registry.Resubmit(id);
     job.MarkPause();
-    ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Running));
+    ASSERT_TRUE(CheckState(registry, id, JobState_Running));
   }
 
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Paused));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Paused));
 
   registry.Resubmit(id);
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Paused));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Paused));
 
   registry.Resume(id);
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Pending));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
 
   {
     JobsRegistry::RunningJob job(registry, 0);
     ASSERT_TRUE(job.IsValid());
 
     job.MarkSuccess();
-    ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Running));
+    ASSERT_TRUE(CheckState(registry, id, JobState_Running));
   }
 
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Success));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Success));
 }
 
 
@@ -477,33 +523,33 @@
   std::string id;
   registry.Submit(id, new DummyJob(), 10);
 
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Pending));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
 
   {
     JobsRegistry::RunningJob job(registry, 0);
     ASSERT_TRUE(job.IsValid());
 
     job.MarkRetry(0);
-    ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Running));
+    ASSERT_TRUE(CheckState(registry, id, JobState_Running));
   }
 
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Retry));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Retry));
 
   registry.Pause(id);
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Paused));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Paused));
 
   registry.Resume(id);
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Pending));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
 
   {
     JobsRegistry::RunningJob job(registry, 0);
     ASSERT_TRUE(job.IsValid());
 
     job.MarkSuccess();
-    ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Running));
+    ASSERT_TRUE(CheckState(registry, id, JobState_Running));
   }
 
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Success));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Success));
 }
 
 
@@ -516,19 +562,19 @@
 
   ASSERT_FALSE(registry.Cancel("nope"));
 
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Pending));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
   ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_Success));
             
   ASSERT_TRUE(registry.Cancel(id));
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Failure));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Failure));
   ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_CanceledJob));
   
   ASSERT_TRUE(registry.Cancel(id));
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Failure));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Failure));
   ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_CanceledJob));
   
   ASSERT_TRUE(registry.Resubmit(id));
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Pending));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
   ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_CanceledJob));
   
   {
@@ -538,14 +584,14 @@
     ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_Success));
 
     job.MarkSuccess();
-    ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Running));
+    ASSERT_TRUE(CheckState(registry, id, JobState_Running));
   }
 
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Success));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Success));
   ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_Success));
 
   ASSERT_TRUE(registry.Cancel(id));
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Success));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Success));
   ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_Success));
 
   registry.Submit(id, new DummyJob(), 10);
@@ -556,28 +602,28 @@
     ASSERT_EQ(id, job.GetId());
 
     ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_Success));
-    ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Running));
+    ASSERT_TRUE(CheckState(registry, id, JobState_Running));
 
     job.MarkCanceled();
   }
 
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Failure));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Failure));
   ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_CanceledJob));
 
   ASSERT_TRUE(registry.Resubmit(id));
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Pending));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
   ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_CanceledJob));
 
   ASSERT_TRUE(registry.Pause(id));
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Paused));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Paused));
   ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_CanceledJob));
 
   ASSERT_TRUE(registry.Cancel(id));
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Failure));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Failure));
   ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_CanceledJob));
 
   ASSERT_TRUE(registry.Resubmit(id));
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Pending));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Pending));
   ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_CanceledJob));
 
   {
@@ -586,16 +632,16 @@
     ASSERT_EQ(id, job.GetId());
 
     ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_Success));
-    ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Running));
+    ASSERT_TRUE(CheckState(registry, id, JobState_Running));
 
     job.MarkRetry(500);
   }
 
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Retry));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Retry));
   ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_Success));
 
   ASSERT_TRUE(registry.Cancel(id));
-  ASSERT_TRUE(CheckState(registry, id, Orthanc::JobState_Failure));
+  ASSERT_TRUE(CheckState(registry, id, JobState_Failure));
   ASSERT_TRUE(CheckErrorCode(registry, id, ErrorCode_CanceledJob));
 }
 
@@ -679,21 +725,22 @@
 }
 
 
-#include "../OrthancServer/ServerContext.h"
-
 TEST(JobsSerialization, GenericValues)
 {
   GenericJobUnserializer unserializer;
     
   Json::Value s;
-  std::auto_ptr<JobOperationValue> value;
 
   {
     NullOperationValue null;
     null.Serialize(s);
   }
 
+  std::auto_ptr<JobOperationValue> value;
+  ASSERT_THROW(unserializer.UnserializeJob(s), OrthancException);
+  ASSERT_THROW(unserializer.UnserializeOperation(s), OrthancException);
   value.reset(unserializer.UnserializeValue(s));
+  
   ASSERT_EQ(JobOperationValue::Type_Null, value->GetType());
 
   {
@@ -701,12 +748,38 @@
     str.Serialize(s);
   }
 
+  ASSERT_THROW(unserializer.UnserializeJob(s), OrthancException);
+  ASSERT_THROW(unserializer.UnserializeOperation(s), OrthancException);
   value.reset(unserializer.UnserializeValue(s));
+
   ASSERT_EQ(JobOperationValue::Type_String, value->GetType());
   ASSERT_EQ("Hello", dynamic_cast<StringOperationValue&>(*value).GetContent());
 }
 
 
+TEST(JobsSerialization, GenericJobs)
+{
+  DummyUnserializer unserializer;
+    
+  Json::Value s;
+
+  {
+    DummyInstancesJob job;
+    job.SetDescription("description");
+    job.AddInstance("nope");
+    job.AddInstance("ok");
+    job.SetPermissive(true);
+    job.Serialize(s);
+  }
+
+  ASSERT_THROW(unserializer.UnserializeValue(s), OrthancException);
+  ASSERT_THROW(unserializer.UnserializeOperation(s), OrthancException);
+
+  std::auto_ptr<IJob> job;
+  job.reset(unserializer.UnserializeJob(s));
+}
+
+
 TEST(JobsSerialization, OrthancValues)
 {
   MemoryStorageArea storage;
@@ -730,13 +803,13 @@
     OrthancJobUnserializer unserializer(context);
     
     Json::Value s;
-    std::auto_ptr<JobOperationValue> value;
 
     {
       DicomInstanceOperationValue instance(context, id);
       instance.Serialize(s);
     }
 
+    std::auto_ptr<JobOperationValue> value;
     value.reset(unserializer.UnserializeValue(s));
     ASSERT_EQ(JobOperationValue::Type_DicomInstance, value->GetType());
     ASSERT_EQ(id, dynamic_cast<DicomInstanceOperationValue&>(*value).GetId());