# HG changeset patch # User Sebastien Jodogne # Date 1526984737 -7200 # Node ID 2f3007bf0708a0dba0d4fc73e86a6dc24870d4f1 # Parent 3200223f9ade1922266adb43d32bd9e04536a456 event queues in Lua, serialization of sequence of operations diff -r 3200223f9ade -r 2f3007bf0708 Core/DicomParsing/DicomModification.cpp --- a/Core/DicomParsing/DicomModification.cpp Mon May 21 09:00:20 2018 +0200 +++ b/Core/DicomParsing/DicomModification.cpp Tue May 22 12:25:37 2018 +0200 @@ -1237,4 +1237,11 @@ patientNameReplaced = (IsReplaced(DICOM_TAG_PATIENT_NAME) && GetReplacement(DICOM_TAG_PATIENT_NAME) == patientName); } + + + void DicomModification::Serialize(Json::Value& value) const + { + // TODO + value = "TODO"; + } } diff -r 3200223f9ade -r 2f3007bf0708 Core/DicomParsing/DicomModification.h --- a/Core/DicomParsing/DicomModification.h Mon May 21 09:00:20 2018 +0200 +++ b/Core/DicomParsing/DicomModification.h Tue May 22 12:25:37 2018 +0200 @@ -172,5 +172,7 @@ { identifierGenerator_ = &generator; } + + void Serialize(Json::Value& value) const; }; } diff -r 3200223f9ade -r 2f3007bf0708 Core/HttpServer/IIncomingHttpRequestFilter.h --- a/Core/HttpServer/IIncomingHttpRequestFilter.h Mon May 21 09:00:20 2018 +0200 +++ b/Core/HttpServer/IIncomingHttpRequestFilter.h Tue May 22 12:25:37 2018 +0200 @@ -49,6 +49,6 @@ const char* ip, const char* username, const IHttpHandler::Arguments& httpHeaders, - const IHttpHandler::GetArguments& getArguments) const = 0; + const IHttpHandler::GetArguments& getArguments) = 0; }; } diff -r 3200223f9ade -r 2f3007bf0708 Core/HttpServer/MongooseServer.cpp --- a/Core/HttpServer/MongooseServer.cpp Mon May 21 09:00:20 2018 +0200 +++ b/Core/HttpServer/MongooseServer.cpp Tue May 22 12:25:37 2018 +0200 @@ -676,7 +676,7 @@ std::string username = GetAuthenticatedUsername(headers); - const IIncomingHttpRequestFilter *filter = server.GetIncomingHttpRequestFilter(); + IIncomingHttpRequestFilter *filter = server.GetIncomingHttpRequestFilter(); if (filter != NULL) { if (!filter->IsAllowed(method, request->uri, remoteIp, diff -r 3200223f9ade -r 2f3007bf0708 Core/HttpServer/MongooseServer.h --- a/Core/HttpServer/MongooseServer.h Mon May 21 09:00:20 2018 +0200 +++ b/Core/HttpServer/MongooseServer.h Tue May 22 12:25:37 2018 +0200 @@ -162,7 +162,7 @@ void SetHttpCompressionEnabled(bool enabled); - const IIncomingHttpRequestFilter* GetIncomingHttpRequestFilter() const + IIncomingHttpRequestFilter* GetIncomingHttpRequestFilter() const { return filter_; } diff -r 3200223f9ade -r 2f3007bf0708 Core/JobsEngine/Operations/IJobOperation.h --- a/Core/JobsEngine/Operations/IJobOperation.h Mon May 21 09:00:20 2018 +0200 +++ b/Core/JobsEngine/Operations/IJobOperation.h Tue May 22 12:25:37 2018 +0200 @@ -48,5 +48,7 @@ virtual void Apply(JobOperationValues& outputs, const JobOperationValue& input, IDicomConnectionManager& dicomConnection) = 0; + + virtual void Serialize(Json::Value& result) const = 0; }; } diff -r 3200223f9ade -r 2f3007bf0708 Core/JobsEngine/Operations/JobOperationValue.h --- a/Core/JobsEngine/Operations/JobOperationValue.h Mon May 21 09:00:20 2018 +0200 +++ b/Core/JobsEngine/Operations/JobOperationValue.h Tue May 22 12:25:37 2018 +0200 @@ -33,6 +33,7 @@ #pragma once +#include #include namespace Orthanc @@ -67,5 +68,7 @@ } virtual JobOperationValue* Clone() const = 0; + + virtual void Serialize(Json::Value& target) const = 0; }; } diff -r 3200223f9ade -r 2f3007bf0708 Core/JobsEngine/Operations/JobOperationValues.cpp --- a/Core/JobsEngine/Operations/JobOperationValues.cpp Mon May 21 09:00:20 2018 +0200 +++ b/Core/JobsEngine/Operations/JobOperationValues.cpp Tue May 22 12:25:37 2018 +0200 @@ -104,4 +104,17 @@ return *values_[index]; } } + + + void JobOperationValues::Serialize(Json::Value& target) const + { + target = Json::arrayValue; + + for (size_t i = 0; i < values_.size(); i++) + { + Json::Value tmp; + values_[i]->Serialize(tmp); + target.append(tmp); + } + } } diff -r 3200223f9ade -r 2f3007bf0708 Core/JobsEngine/Operations/JobOperationValues.h --- a/Core/JobsEngine/Operations/JobOperationValues.h Mon May 21 09:00:20 2018 +0200 +++ b/Core/JobsEngine/Operations/JobOperationValues.h Tue May 22 12:25:37 2018 +0200 @@ -78,5 +78,7 @@ } JobOperationValue& GetValue(size_t index) const; + + void Serialize(Json::Value& target) const; }; } diff -r 3200223f9ade -r 2f3007bf0708 Core/JobsEngine/Operations/LogJobOperation.h --- a/Core/JobsEngine/Operations/LogJobOperation.h Mon May 21 09:00:20 2018 +0200 +++ b/Core/JobsEngine/Operations/LogJobOperation.h Tue May 22 12:25:37 2018 +0200 @@ -43,5 +43,10 @@ virtual void Apply(JobOperationValues& outputs, const JobOperationValue& input, IDicomConnectionManager& connectionManager); + + virtual void Serialize(Json::Value& result) const + { + result["Type"] = "Log"; + } }; } diff -r 3200223f9ade -r 2f3007bf0708 Core/JobsEngine/Operations/NullOperationValue.h --- a/Core/JobsEngine/Operations/NullOperationValue.h Mon May 21 09:00:20 2018 +0200 +++ b/Core/JobsEngine/Operations/NullOperationValue.h Tue May 22 12:25:37 2018 +0200 @@ -49,5 +49,10 @@ { return new NullOperationValue; } + + virtual void Serialize(Json::Value& target) const + { + target["Type"] = "Null"; + } }; } diff -r 3200223f9ade -r 2f3007bf0708 Core/JobsEngine/Operations/SequenceOfOperationsJob.cpp --- a/Core/JobsEngine/Operations/SequenceOfOperationsJob.cpp Mon May 21 09:00:20 2018 +0200 +++ b/Core/JobsEngine/Operations/SequenceOfOperationsJob.cpp Tue May 22 12:25:37 2018 +0200 @@ -42,6 +42,7 @@ class SequenceOfOperationsJob::Operation : public boost::noncopyable { private: + size_t index_; JobOperationValues originalInputs_; JobOperationValues workInputs_; std::auto_ptr operation_; @@ -49,9 +50,11 @@ size_t currentInput_; public: - Operation(IJobOperation* operation) : - operation_(operation), - currentInput_(0) + Operation(size_t index, + IJobOperation* operation) : + index_(index), + operation_(operation), + currentInput_(0) { if (operation == NULL) { @@ -85,6 +88,11 @@ void AddNextOperation(Operation& other) { + if (other.index_ <= index_) + { + throw OrthancException(ErrorCode_InternalError); + } + if (currentInput_ != 0) { // Cannot add input after processing has started @@ -139,15 +147,31 @@ currentInput_ += 1; } + + void Serialize(Json::Value& target) const + { + target = Json::objectValue; + operation_->Serialize(target["Operation"]); + originalInputs_.Serialize(target["OriginalInputs"]); + workInputs_.Serialize(target["WorkInputs"]); + + Json::Value tmp = Json::arrayValue; + for (std::list::const_iterator it = nextOperations_.begin(); + it != nextOperations_.end(); ++it) + { + tmp.append(static_cast((*it)->index_)); + } + + target["NextOperations"] = tmp; + } }; - // Invoked from constructors - void SequenceOfOperationsJob::Setup() + SequenceOfOperationsJob::SequenceOfOperationsJob() : + done_(false), + current_(0), + trailingTimeout_(boost::posix_time::milliseconds(1000)) { - done_ = false; - current_ = 0; - trailingTimeout_ = boost::posix_time::milliseconds(1000); } @@ -163,6 +187,13 @@ } + void SequenceOfOperationsJob::SetDescription(const std::string& description) + { + boost::mutex::scoped_lock lock(mutex_); + description_ = description; + } + + void SequenceOfOperationsJob::Register(IObserver& observer) { boost::mutex::scoped_lock lock(mutex_); @@ -189,10 +220,12 @@ throw OrthancException(ErrorCode_BadSequenceOfCalls); } - that_.operations_.push_back(new Operation(operation)); + size_t index = that_.operations_.size(); + + that_.operations_.push_back(new Operation(index, operation)); that_.operationAdded_.notify_one(); - return that_.operations_.size() - 1; + return index; } @@ -324,5 +357,20 @@ boost::mutex::scoped_lock lock(mutex_); value["CountOperations"] = static_cast(operations_.size()); + value["Description"] = description_; + } + + + void SequenceOfOperationsJob::GetInternalContent(Json::Value& value) + { + boost::mutex::scoped_lock lock(mutex_); + + value = Json::arrayValue; + for (size_t i = 0; i < operations_.size(); i++) + { + Json::Value operation = Json::objectValue; + operations_[i]->Serialize(operation); + value.append(operation); + } } } diff -r 3200223f9ade -r 2f3007bf0708 Core/JobsEngine/Operations/SequenceOfOperationsJob.h --- a/Core/JobsEngine/Operations/SequenceOfOperationsJob.h Mon May 21 09:00:20 2018 +0200 +++ b/Core/JobsEngine/Operations/SequenceOfOperationsJob.h Tue May 22 12:25:37 2018 +0200 @@ -61,7 +61,7 @@ private: class Operation; - std::string jobType_; + std::string description_; bool done_; boost::mutex mutex_; std::vector operations_; @@ -71,23 +71,13 @@ std::list observers_; TimeoutDicomConnectionManager connectionManager_; - void Setup(); - public: - SequenceOfOperationsJob() : - jobType_("SequenceOfOperations") - { - Setup(); - } - - SequenceOfOperationsJob(const std::string& jobType) : - jobType_(jobType) - { - Setup(); - } + SequenceOfOperationsJob(); virtual ~SequenceOfOperationsJob(); + void SetDescription(const std::string& description); + void Register(IObserver& observer); // This lock allows adding new operations to the end of the job, @@ -143,14 +133,11 @@ virtual void GetJobType(std::string& target) { - target = jobType_; + target = "SequenceOfOperations"; } virtual void GetPublicContent(Json::Value& value); - virtual void GetInternalContent(Json::Value& value) - { - // TODO - } + virtual void GetInternalContent(Json::Value& value); }; } diff -r 3200223f9ade -r 2f3007bf0708 Core/JobsEngine/Operations/StringOperationValue.h --- a/Core/JobsEngine/Operations/StringOperationValue.h Mon May 21 09:00:20 2018 +0200 +++ b/Core/JobsEngine/Operations/StringOperationValue.h Tue May 22 12:25:37 2018 +0200 @@ -60,5 +60,11 @@ { return content_; } + + virtual void Serialize(Json::Value& target) const + { + target["Type"] = "String"; + target["Content"] = content_; + } }; } diff -r 3200223f9ade -r 2f3007bf0708 OrthancServer/LuaScripting.cpp --- a/OrthancServer/LuaScripting.cpp Mon May 21 09:00:20 2018 +0200 +++ b/OrthancServer/LuaScripting.cpp Tue May 22 12:25:37 2018 +0200 @@ -47,6 +47,136 @@ namespace Orthanc { + class LuaScripting::IEvent : public IDynamicObject + { + public: + virtual void Apply(LuaScripting& lock) = 0; + }; + + + class LuaScripting::OnStoredInstanceEvent : public LuaScripting::IEvent + { + private: + std::string instanceId_; + Json::Value simplifiedTags_; + Json::Value metadata_; + Json::Value origin_; + + public: + OnStoredInstanceEvent(const std::string& instanceId, + const Json::Value& simplifiedTags, + const Json::Value& metadata, + const DicomInstanceToStore& instance) : + instanceId_(instanceId), + simplifiedTags_(simplifiedTags), + metadata_(metadata) + { + instance.GetOriginInformation(origin_); + } + + virtual void Apply(LuaScripting& that) + { + static const char* NAME = "OnStoredInstance"; + + LuaScripting::Lock lock(that); + + if (lock.GetLua().IsExistingFunction(NAME)) + { + that.InitializeJob(); + + LuaFunctionCall call(lock.GetLua(), NAME); + call.PushString(instanceId_); + call.PushJson(simplifiedTags_); + call.PushJson(metadata_); + call.PushJson(origin_); + call.Execute(); + + that.SubmitJob(); + } + } + }; + + + class LuaScripting::ExecuteEvent : public LuaScripting::IEvent + { + private: + std::string command_; + + public: + ExecuteEvent(const std::string& command) : + command_(command) + { + } + + virtual void Apply(LuaScripting& that) + { + LuaScripting::Lock lock(that); + + if (lock.GetLua().IsExistingFunction(command_.c_str())) + { + LuaFunctionCall call(lock.GetLua(), command_.c_str()); + call.Execute(); + } + } + }; + + + class LuaScripting::StableResourceEvent : public LuaScripting::IEvent + { + private: + ServerIndexChange change_; + + public: + StableResourceEvent(const ServerIndexChange& change) : + change_(change) + { + } + + virtual void Apply(LuaScripting& that) + { + const char* name; + + switch (change_.GetChangeType()) + { + case ChangeType_StablePatient: + name = "OnStablePatient"; + break; + + case ChangeType_StableStudy: + name = "OnStableStudy"; + break; + + case ChangeType_StableSeries: + name = "OnStableSeries"; + break; + + default: + throw OrthancException(ErrorCode_InternalError); + } + + Json::Value tags, metadata; + if (that.context_.GetIndex().LookupResource(tags, change_.GetPublicId(), change_.GetResourceType()) && + that.context_.GetIndex().GetMetadata(metadata, change_.GetPublicId())) + { + LuaScripting::Lock lock(that); + + if (lock.GetLua().IsExistingFunction(name)) + { + that.InitializeJob(); + + LuaFunctionCall call(lock.GetLua(), name); + call.PushString(change_.GetPublicId()); + call.PushJson(tags["MainDicomTags"]); + call.PushJson(metadata); + call.Execute(); + + that.SubmitJob(); + } + } + } + }; + + ServerContext* LuaScripting::GetServerContext(lua_State *state) { const void* value = LuaContext::GetGlobalVariable(state, "_ServerContext"); @@ -206,7 +336,7 @@ } LOG(ERROR) << "Lua: Error in RestApiDelete() for URI: " << uri; - lua_pushnil(state); + lua_pushnil(state); return 1; } @@ -325,7 +455,7 @@ } - void LuaScripting::SubmitJob(const std::string& description) + void LuaScripting::SubmitJob() { Json::Value operations; LuaFunctionCall call2(lua_, "_AccessJob"); @@ -374,7 +504,9 @@ } - LuaScripting::LuaScripting(ServerContext& context) : context_(context) + LuaScripting::LuaScripting(ServerContext& context) : + context_(context), + continue_(true) { lua_.SetGlobalVariable("_ServerContext", &context); lua_.RegisterFunction("RestApiGet", RestApiGet); @@ -382,34 +514,85 @@ lua_.RegisterFunction("RestApiPut", RestApiPut); lua_.RegisterFunction("RestApiDelete", RestApiDelete); lua_.RegisterFunction("GetOrthancConfiguration", GetOrthancConfiguration); + } - lua_.Execute(Orthanc::EmbeddedResources::LUA_TOOLBOX); + + LuaScripting::~LuaScripting() + { + if (continue_) + { + LOG(ERROR) << "INTERNAL ERROR: LuaScripting::Stop() should be invoked manually to avoid mess in the destruction order!"; + Stop(); + } } - void LuaScripting::ApplyOnStoredInstance(const std::string& instanceId, - const Json::Value& simplifiedTags, - const Json::Value& metadata, - const DicomInstanceToStore& instance) + void LuaScripting::EventThread(LuaScripting* that) { - static const char* NAME = "OnStoredInstance"; + for (;;) + { + std::auto_ptr event(that->pendingEvents_.Dequeue(100)); + + if (event.get() == NULL) + { + // The event queue is empty, check whether we should stop + boost::mutex::scoped_lock lock(that->mutex_); - if (lua_.IsExistingFunction(NAME)) - { - InitializeJob(); + if (!that->continue_) + { + return; + } + } + else + { + try + { + dynamic_cast(*event).Apply(*that); + } + catch (OrthancException& e) + { + LOG(ERROR) << "Error while processing Lua events: " << e.What(); + } + } + } + } + - LuaFunctionCall call(lua_, NAME); - call.PushString(instanceId); - call.PushJson(simplifiedTags); - call.PushJson(metadata); + void LuaScripting::Start() + { + boost::mutex::scoped_lock lock(mutex_); + + if (!continue_ || + eventThread_.joinable() /* already started */) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + else + { + LOG(INFO) << "Starting the Lua engine"; + eventThread_ = boost::thread(EventThread, this); + } + } + - Json::Value origin; - instance.GetOriginInformation(origin); - call.PushJson(origin); + void LuaScripting::Stop() + { + { + boost::mutex::scoped_lock lock(mutex_); - call.Execute(); + if (!continue_) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } - SubmitJob(std::string("Lua script: ") + NAME); + continue_ = false; + } + + if (eventThread_.joinable()) + { + LOG(INFO) << "Stopping the Lua engine"; + eventThread_.join(); + LOG(INFO) << "The Lua engine has stopped"; } } @@ -418,8 +601,6 @@ DicomInstanceToStore& instance, const Json::Value& simplifiedTags) { - boost::recursive_mutex::scoped_lock lock(mutex_); - Json::Value metadata = Json::objectValue; for (ServerIndex::MetadataMap::const_iterator @@ -432,64 +613,17 @@ } } - ApplyOnStoredInstance(publicId, simplifiedTags, metadata, instance); + pendingEvents_.Enqueue(new OnStoredInstanceEvent(publicId, simplifiedTags, metadata, instance)); } - - void LuaScripting::OnStableResource(const ServerIndexChange& change) - { - const char* name; - - switch (change.GetChangeType()) - { - case ChangeType_StablePatient: - name = "OnStablePatient"; - break; - - case ChangeType_StableStudy: - name = "OnStableStudy"; - break; - - case ChangeType_StableSeries: - name = "OnStableSeries"; - break; - - default: - throw OrthancException(ErrorCode_InternalError); - } - - - Json::Value tags, metadata; - if (context_.GetIndex().LookupResource(tags, change.GetPublicId(), change.GetResourceType()) && - context_.GetIndex().GetMetadata(metadata, change.GetPublicId())) - { - boost::recursive_mutex::scoped_lock lock(mutex_); - - if (lua_.IsExistingFunction(name)) - { - InitializeJob(); - - LuaFunctionCall call(lua_, name); - call.PushString(change.GetPublicId()); - call.PushJson(tags["MainDicomTags"]); - call.PushJson(metadata); - call.Execute(); - - SubmitJob(std::string("Lua script: ") + name); - } - } - } - - - void LuaScripting::SignalChange(const ServerIndexChange& change) { if (change.GetChangeType() == ChangeType_StablePatient || change.GetChangeType() == ChangeType_StableStudy || change.GetChangeType() == ChangeType_StableSeries) { - OnStableResource(change); + pendingEvents_.Enqueue(new StableResourceEvent(change)); } } @@ -499,7 +633,7 @@ { static const char* NAME = "ReceivedInstanceFilter"; - boost::recursive_mutex::scoped_lock lock(mutex_); + boost::mutex::scoped_lock lock(mutex_); if (lua_.IsExistingFunction(NAME)) { @@ -522,12 +656,28 @@ void LuaScripting::Execute(const std::string& command) { - LuaScripting::Locker locker(*this); - - if (locker.GetLua().IsExistingFunction(command.c_str())) + pendingEvents_.Enqueue(new ExecuteEvent(command)); + } + + + void LuaScripting::LoadGlobalConfiguration() + { + lua_.Execute(Orthanc::EmbeddedResources::LUA_TOOLBOX); + + std::list luaScripts; + Configuration::GetGlobalListOfStringsParameter(luaScripts, "LuaScripts"); + + LuaScripting::Lock lock(*this); + + for (std::list::const_iterator + it = luaScripts.begin(); it != luaScripts.end(); ++it) { - LuaFunctionCall call(locker.GetLua(), command.c_str()); - call.Execute(); + std::string path = Configuration::InterpretStringParameterAsPath(*it); + LOG(INFO) << "Installing the Lua scripts from: " << path; + std::string script; + SystemToolbox::ReadFile(script, path); + + lock.GetLua().Execute(script); } } } diff -r 3200223f9ade -r 2f3007bf0708 OrthancServer/LuaScripting.h --- a/OrthancServer/LuaScripting.h Mon May 21 09:00:20 2018 +0200 +++ b/OrthancServer/LuaScripting.h Tue May 22 12:25:37 2018 +0200 @@ -37,6 +37,7 @@ #include "ServerJobs/LuaJobManager.h" +#include "../Core/MultiThreading/SharedMessageQueue.h" #include "../Core/Lua/LuaContext.h" namespace Orthanc @@ -46,6 +47,11 @@ class LuaScripting : public IServerListener { private: + class ExecuteEvent; + class IEvent; + class OnStoredInstanceEvent; + class StableResourceEvent; + static ServerContext* GetServerContext(lua_State *state); static int RestApiPostOrPut(lua_State *state, @@ -56,35 +62,33 @@ static int RestApiDelete(lua_State *state); static int GetOrthancConfiguration(lua_State *state); - void ApplyOnStoredInstance(const std::string& instanceId, - const Json::Value& simplifiedDicom, - const Json::Value& metadata, - const DicomInstanceToStore& instance); - size_t ParseOperation(LuaJobManager::Lock& lock, const std::string& operation, const Json::Value& parameters); void InitializeJob(); - void SubmitJob(const std::string& description); - - void OnStableResource(const ServerIndexChange& change); + void SubmitJob(); - boost::recursive_mutex mutex_; - LuaContext lua_; - ServerContext& context_; - LuaJobManager jobManager_; + boost::mutex mutex_; + LuaContext lua_; + ServerContext& context_; + LuaJobManager jobManager_; + bool continue_; + boost::thread eventThread_; + SharedMessageQueue pendingEvents_; + + static void EventThread(LuaScripting* that); public: - class Locker : public boost::noncopyable + class Lock : public boost::noncopyable { private: - LuaScripting& that_; - boost::recursive_mutex::scoped_lock lock_; + LuaScripting& that_; + boost::mutex::scoped_lock lock_; public: - Locker(LuaScripting& that) : + Lock(LuaScripting& that) : that_(that), lock_(that.mutex_) { @@ -97,6 +101,12 @@ }; LuaScripting(ServerContext& context); + + ~LuaScripting(); + + void Start(); + + void Stop(); virtual void SignalStoredInstance(const std::string& publicId, DicomInstanceToStore& instance, @@ -108,5 +118,7 @@ const Json::Value& simplifiedTags); void Execute(const std::string& command); + + void LoadGlobalConfiguration(); }; } diff -r 3200223f9ade -r 2f3007bf0708 OrthancServer/OrthancFindRequestHandler.cpp --- a/OrthancServer/OrthancFindRequestHandler.cpp Mon May 21 09:00:20 2018 +0200 +++ b/OrthancServer/OrthancFindRequestHandler.cpp Tue May 22 12:25:37 2018 +0200 @@ -47,6 +47,25 @@ namespace Orthanc { + static LuaScripting& GetLuaScripting(ServerContext& context) + { + // Returns a singleton Lua context + static boost::mutex mutex_; + static std::auto_ptr lua_; + + boost::mutex::scoped_lock lock(mutex_); + + if (lua_.get() == NULL) + { + LOG(INFO) << "Initializing Lua for OrthancFindRequestHandler"; + lua_.reset(new LuaScripting(context)); + lua_->LoadGlobalConfiguration(); + } + + return *lua_; + } + + static void GetChildren(std::list& target, ServerIndex& index, const std::list& source) @@ -485,9 +504,9 @@ const std::string& calledAet) { static const char* LUA_CALLBACK = "IncomingFindRequestFilter"; - - LuaScripting::Locker locker(context_.GetLuaScripting()); - if (!locker.GetLua().IsExistingFunction(LUA_CALLBACK)) + + LuaScripting::Lock lock(GetLuaScripting(context_)); + if (!lock.GetLua().IsExistingFunction(LUA_CALLBACK)) { return false; } @@ -498,7 +517,7 @@ origin["RemoteAet"] = remoteAet; origin["CalledAet"] = calledAet; - LuaFunctionCall call(locker.GetLua(), LUA_CALLBACK); + LuaFunctionCall call(lock.GetLua(), LUA_CALLBACK); call.PushDicom(source); call.PushJson(origin); FromDcmtkBridge::ExecuteToDicom(target, call); @@ -508,6 +527,14 @@ } + OrthancFindRequestHandler::OrthancFindRequestHandler(ServerContext& context) : + context_(context), + maxResults_(0), + maxInstances_(0) + { + } + + void OrthancFindRequestHandler::Handle(DicomFindAnswers& answers, const DicomMap& input, const std::list& sequencesToReturn, diff -r 3200223f9ade -r 2f3007bf0708 OrthancServer/OrthancFindRequestHandler.h --- a/OrthancServer/OrthancFindRequestHandler.h Mon May 21 09:00:20 2018 +0200 +++ b/OrthancServer/OrthancFindRequestHandler.h Tue May 22 12:25:37 2018 +0200 @@ -42,8 +42,8 @@ { private: ServerContext& context_; - unsigned int maxResults_; - unsigned int maxInstances_; + unsigned int maxResults_; + unsigned int maxInstances_; bool HasReachedLimit(const DicomFindAnswers& answers, ResourceType level) const; @@ -60,12 +60,7 @@ const std::string& calledAet); public: - OrthancFindRequestHandler(ServerContext& context) : - context_(context), - maxResults_(0), - maxInstances_(0) - { - } + OrthancFindRequestHandler(ServerContext& context); virtual void Handle(DicomFindAnswers& answers, const DicomMap& input, diff -r 3200223f9ade -r 2f3007bf0708 OrthancServer/OrthancRestApi/OrthancRestSystem.cpp --- a/OrthancServer/OrthancRestApi/OrthancRestSystem.cpp Mon May 21 09:00:20 2018 +0200 +++ b/OrthancServer/OrthancRestApi/OrthancRestSystem.cpp Tue May 22 12:25:37 2018 +0200 @@ -124,8 +124,8 @@ call.BodyToString(command); { - LuaScripting::Locker locker(context.GetLuaScripting()); - locker.GetLua().Execute(result, command); + LuaScripting::Lock lock(context.GetLuaEventHandler()); + lock.GetLua().Execute(result, command); } call.GetOutput().AnswerBuffer(result, "text/plain"); @@ -276,6 +276,7 @@ static void ListJobs(RestApiGetCall& call) { bool expand = call.HasArgument("expand"); + bool internal = call.HasArgument("internal"); Json::Value v = Json::arrayValue; @@ -291,7 +292,7 @@ if (OrthancRestApi::GetContext(call).GetJobsEngine().GetRegistry().GetJobInfo(info, *it)) { Json::Value tmp; - info.Serialize(tmp, false); + info.Serialize(tmp, internal); v.append(tmp); } } @@ -307,12 +308,13 @@ static void GetJobInfo(RestApiGetCall& call) { std::string id = call.GetUriComponent("id", ""); + bool internal = call.HasArgument("internal"); JobInfo info; if (OrthancRestApi::GetContext(call).GetJobsEngine().GetRegistry().GetJobInfo(info, id)) { Json::Value json; - info.Serialize(json, false); + info.Serialize(json, internal); call.GetOutput().AnswerJson(json); } } diff -r 3200223f9ade -r 2f3007bf0708 OrthancServer/QueryRetrieveHandler.cpp --- a/OrthancServer/QueryRetrieveHandler.cpp Mon May 21 09:00:20 2018 +0200 +++ b/OrthancServer/QueryRetrieveHandler.cpp Tue May 22 12:25:37 2018 +0200 @@ -35,21 +35,43 @@ #include "QueryRetrieveHandler.h" #include "OrthancInitialization.h" + #include "../Core/DicomParsing/FromDcmtkBridge.h" +#include "../Core/Logging.h" namespace Orthanc { - static void FixQuery(DicomMap& query, - ServerContext& context, - const std::string& modality) + static LuaScripting& GetLuaScripting(ServerContext& context) + { + // Returns a singleton Lua context + static boost::mutex mutex_; + static std::auto_ptr lua_; + + boost::mutex::scoped_lock lock(mutex_); + + if (lua_.get() == NULL) + { + LOG(INFO) << "Initializing Lua for QueryRetrieveHandler"; + lua_.reset(new LuaScripting(context)); + lua_->LoadGlobalConfiguration(); + } + + return *lua_; + } + + + static void FixQueryLua(DicomMap& query, + ServerContext& context, + const std::string& modality) { static const char* LUA_CALLBACK = "OutgoingFindRequestFilter"; - LuaScripting::Locker locker(context.GetLuaScripting()); - if (locker.GetLua().IsExistingFunction(LUA_CALLBACK)) + LuaScripting::Lock lock(GetLuaScripting(context)); + + if (lock.GetLua().IsExistingFunction(LUA_CALLBACK)) { - LuaFunctionCall call(locker.GetLua(), LUA_CALLBACK); + LuaFunctionCall call(lock.GetLua(), LUA_CALLBACK); call.PushDicom(query); call.PushJson(modality); FromDcmtkBridge::ExecuteToDicom(query, call); @@ -57,8 +79,8 @@ } - static void FixQuery(DicomMap& query, - ModalityManufacturer manufacturer) + static void FixQueryBuiltin(DicomMap& query, + ModalityManufacturer manufacturer) { /** * Introduce patches for specific manufacturers below. @@ -99,10 +121,10 @@ // Firstly, fix the content of the query for specific manufacturers DicomMap fixed; fixed.Assign(query_); - FixQuery(fixed, modality_.GetManufacturer()); + FixQueryBuiltin(fixed, modality_.GetManufacturer()); // Secondly, possibly fix the query with the user-provider Lua callback - FixQuery(fixed, context_, modality_.GetApplicationEntityTitle()); + FixQueryLua(fixed, context_, modality_.GetApplicationEntityTitle()); GetConnection().Find(answers_, level_, fixed); diff -r 3200223f9ade -r 2f3007bf0708 OrthancServer/ServerContext.cpp --- a/OrthancServer/ServerContext.cpp Mon May 21 09:00:20 2018 +0200 +++ b/OrthancServer/ServerContext.cpp Tue May 22 12:25:37 2018 +0200 @@ -114,7 +114,7 @@ storeMD5_(true), provider_(*this), dicomCache_(provider_, DICOM_CACHE_SIZE), - lua_(*this), + luaEventHandler_(*this), #if ORTHANC_ENABLE_PLUGINS == 1 plugins_(NULL), #endif @@ -122,7 +122,7 @@ queryRetrieveArchive_(Configuration::GetGlobalUnsignedIntegerParameter("QueryRetrieveSize", 10)), defaultLocalAet_(Configuration::GetGlobalStringParameter("DicomAet", "ORTHANC")) { - listeners_.push_back(ServerListener(lua_, "Lua")); + listeners_.push_back(ServerListener(luaEventHandler_, "Lua")); jobsEngine_.SetWorkersCount(Configuration::GetGlobalUnsignedIntegerParameter("ConcurrentJobs", 2)); //jobsEngine_.SetMaxCompleted // TODO @@ -580,7 +580,7 @@ // TODO REFACTOR THIS listeners_.clear(); - listeners_.push_back(ServerListener(lua_, "Lua")); + listeners_.push_back(ServerListener(luaEventHandler_, "Lua")); listeners_.push_back(ServerListener(plugins, "plugin")); } @@ -593,7 +593,7 @@ // TODO REFACTOR THIS listeners_.clear(); - listeners_.push_back(ServerListener(lua_, "Lua")); + listeners_.push_back(ServerListener(luaEventHandler_, "Lua")); } diff -r 3200223f9ade -r 2f3007bf0708 OrthancServer/ServerContext.h --- a/OrthancServer/ServerContext.h Mon May 21 09:00:20 2018 +0200 +++ b/OrthancServer/ServerContext.h Tue May 22 12:25:37 2018 +0200 @@ -119,7 +119,7 @@ MemoryCache dicomCache_; JobsEngine jobsEngine_; - LuaScripting lua_; + LuaScripting luaEventHandler_; #if ORTHANC_ENABLE_PLUGINS == 1 OrthancPlugins* plugins_; @@ -257,9 +257,9 @@ return defaultLocalAet_; } - LuaScripting& GetLuaScripting() + LuaScripting& GetLuaEventHandler() { - return lua_; + return luaEventHandler_; } OrthancHttpHandler& GetHttpHandler() diff -r 3200223f9ade -r 2f3007bf0708 OrthancServer/ServerJobs/DeleteResourceOperation.h --- a/OrthancServer/ServerJobs/DeleteResourceOperation.h Mon May 21 09:00:20 2018 +0200 +++ b/OrthancServer/ServerJobs/DeleteResourceOperation.h Tue May 22 12:25:37 2018 +0200 @@ -53,6 +53,11 @@ virtual void Apply(JobOperationValues& outputs, const JobOperationValue& input, IDicomConnectionManager& connectionManager); + + virtual void Serialize(Json::Value& result) const + { + result["Type"] = "DeleteResource"; + } }; } diff -r 3200223f9ade -r 2f3007bf0708 OrthancServer/ServerJobs/DicomInstanceOperationValue.h --- a/OrthancServer/ServerJobs/DicomInstanceOperationValue.h Mon May 21 09:00:20 2018 +0200 +++ b/OrthancServer/ServerJobs/DicomInstanceOperationValue.h Tue May 22 12:25:37 2018 +0200 @@ -54,6 +54,11 @@ { } + ServerContext& GetServerContext() const + { + return context_; + } + const std::string& GetId() const { return id_; @@ -68,5 +73,11 @@ { return new DicomInstanceOperationValue(context_, id_); } + + virtual void Serialize(Json::Value& target) const + { + target["Type"] = "DicomInstance"; + target["ID"] = id_; + } }; } diff -r 3200223f9ade -r 2f3007bf0708 OrthancServer/ServerJobs/LuaJobManager.cpp --- a/OrthancServer/ServerJobs/LuaJobManager.cpp Mon May 21 09:00:20 2018 +0200 +++ b/OrthancServer/ServerJobs/LuaJobManager.cpp Tue May 22 12:25:37 2018 +0200 @@ -119,9 +119,13 @@ { // Need to create a new job, as the previous one is either // finished, or is getting too long - that_.currentJob_ = new SequenceOfOperationsJob("Lua"); - jobLock_.reset(new SequenceOfOperationsJob::Lock(*that_.currentJob_)); - jobLock_->SetTrailingOperationTimeout(that_.trailingTimeout_); + that_.currentJob_ = new SequenceOfOperationsJob; + that_.currentJob_->SetDescription("Lua"); + + { + jobLock_.reset(new SequenceOfOperationsJob::Lock(*that_.currentJob_)); + jobLock_->SetTrailingOperationTimeout(that_.trailingTimeout_); + } } assert(jobLock_.get() != NULL); diff -r 3200223f9ade -r 2f3007bf0708 OrthancServer/ServerJobs/ModifyInstanceOperation.cpp --- a/OrthancServer/ServerJobs/ModifyInstanceOperation.cpp Mon May 21 09:00:20 2018 +0200 +++ b/OrthancServer/ServerJobs/ModifyInstanceOperation.cpp Tue May 22 12:25:37 2018 +0200 @@ -115,7 +115,7 @@ context_.Store(modifiedId, toStore); // Only chain with other commands if this command succeeds - outputs.Append(input.Clone()); + outputs.Append(new DicomInstanceOperationValue(instance.GetServerContext(), modifiedId)); } catch (OrthancException& e) { @@ -123,5 +123,13 @@ << ": " << e.What(); } } + + + void ModifyInstanceOperation::Serialize(Json::Value& target) const + { + target["Type"] = "ModifyInstance"; + target["Origin"] = EnumerationToString(origin_); + modification_->Serialize(target["Modification"]); + } } diff -r 3200223f9ade -r 2f3007bf0708 OrthancServer/ServerJobs/ModifyInstanceOperation.h --- a/OrthancServer/ServerJobs/ModifyInstanceOperation.h Mon May 21 09:00:20 2018 +0200 +++ b/OrthancServer/ServerJobs/ModifyInstanceOperation.h Tue May 22 12:25:37 2018 +0200 @@ -60,6 +60,8 @@ virtual void Apply(JobOperationValues& outputs, const JobOperationValue& input, IDicomConnectionManager& connectionManager); + + virtual void Serialize(Json::Value& target) const; }; } diff -r 3200223f9ade -r 2f3007bf0708 OrthancServer/ServerJobs/StorePeerOperation.cpp --- a/OrthancServer/ServerJobs/StorePeerOperation.cpp Mon May 21 09:00:20 2018 +0200 +++ b/OrthancServer/ServerJobs/StorePeerOperation.cpp Tue May 22 12:25:37 2018 +0200 @@ -80,4 +80,11 @@ outputs.Append(input.Clone()); } + + + void StorePeerOperation::Serialize(Json::Value& result) const + { + result["Type"] = "StorePeer"; + peer_.ToJson(result["Remote"]); + } } diff -r 3200223f9ade -r 2f3007bf0708 OrthancServer/ServerJobs/StorePeerOperation.h --- a/OrthancServer/ServerJobs/StorePeerOperation.h Mon May 21 09:00:20 2018 +0200 +++ b/OrthancServer/ServerJobs/StorePeerOperation.h Tue May 22 12:25:37 2018 +0200 @@ -52,6 +52,8 @@ virtual void Apply(JobOperationValues& outputs, const JobOperationValue& input, IDicomConnectionManager& connectionManager); + + virtual void Serialize(Json::Value& result) const; }; } diff -r 3200223f9ade -r 2f3007bf0708 OrthancServer/ServerJobs/StoreScuOperation.cpp --- a/OrthancServer/ServerJobs/StoreScuOperation.cpp Mon May 21 09:00:20 2018 +0200 +++ b/OrthancServer/ServerJobs/StoreScuOperation.cpp Tue May 22 12:25:37 2018 +0200 @@ -79,4 +79,12 @@ outputs.Append(input.Clone()); } + + + void StoreScuOperation::Serialize(Json::Value& result) const + { + result["Type"] = "StoreScu"; + result["LocalAET"] = localAet_; + modality_.ToJson(result["Modality"]); + } } diff -r 3200223f9ade -r 2f3007bf0708 OrthancServer/ServerJobs/StoreScuOperation.h --- a/OrthancServer/ServerJobs/StoreScuOperation.h Mon May 21 09:00:20 2018 +0200 +++ b/OrthancServer/ServerJobs/StoreScuOperation.h Tue May 22 12:25:37 2018 +0200 @@ -56,6 +56,8 @@ virtual void Apply(JobOperationValues& outputs, const JobOperationValue& input, IDicomConnectionManager& manager); + + virtual void Serialize(Json::Value& result) const; }; } diff -r 3200223f9ade -r 2f3007bf0708 OrthancServer/ServerJobs/SystemCallOperation.cpp --- a/OrthancServer/ServerJobs/SystemCallOperation.cpp Mon May 21 09:00:20 2018 +0200 +++ b/OrthancServer/ServerJobs/SystemCallOperation.cpp Tue May 22 12:25:37 2018 +0200 @@ -111,5 +111,30 @@ LOG(ERROR) << "Lua: Failed system call - \"" << info << "\": " << e.What(); } } + + + void SystemCallOperation::Serialize(Json::Value& result) const + { + result["Type"] = "SystemCall"; + result["Command"] = command_; + + Json::Value tmp; + + tmp = Json::arrayValue; + for (size_t i = 0; i < preArguments_.size(); i++) + { + tmp.append(preArguments_[i]); + } + + result["PreArguments"] = tmp; + + tmp = Json::arrayValue; + for (size_t i = 0; i < postArguments_.size(); i++) + { + tmp.append(postArguments_[i]); + } + + result["PostArguments"] = tmp; + } } diff -r 3200223f9ade -r 2f3007bf0708 OrthancServer/ServerJobs/SystemCallOperation.h --- a/OrthancServer/ServerJobs/SystemCallOperation.h Mon May 21 09:00:20 2018 +0200 +++ b/OrthancServer/ServerJobs/SystemCallOperation.h Tue May 22 12:25:37 2018 +0200 @@ -74,6 +74,8 @@ virtual void Apply(JobOperationValues& outputs, const JobOperationValue& input, IDicomConnectionManager& connectionManager); + + virtual void Serialize(Json::Value& result) const; }; } diff -r 3200223f9ade -r 2f3007bf0708 OrthancServer/main.cpp --- a/OrthancServer/main.cpp Mon May 21 09:00:20 2018 +0200 +++ b/OrthancServer/main.cpp Tue May 22 12:25:37 2018 +0200 @@ -167,16 +167,19 @@ class OrthancApplicationEntityFilter : public IApplicationEntityFilter { private: - ServerContext& context_; - bool alwaysAllowEcho_; - bool alwaysAllowStore_; + LuaScripting lua_; + bool alwaysAllowEcho_; + bool alwaysAllowStore_; public: OrthancApplicationEntityFilter(ServerContext& context) : - context_(context) + lua_(context) { alwaysAllowEcho_ = Configuration::GetGlobalBoolParameter("DicomAlwaysAllowEcho", true); alwaysAllowStore_ = Configuration::GetGlobalBoolParameter("DicomAlwaysAllowStore", true); + + LOG(INFO) << "Initializing Lua for OrthancApplicationEntityFilter"; + lua_.LoadGlobalConfiguration(); } virtual bool IsAllowedConnection(const std::string& remoteIp, @@ -263,13 +266,13 @@ } { - std::string lua = "Is" + configuration; + std::string name = "Is" + configuration; - LuaScripting::Locker locker(context_.GetLuaScripting()); + LuaScripting::Lock lock(lua_); - if (locker.GetLua().IsExistingFunction(lua.c_str())) + if (lock.GetLua().IsExistingFunction(name.c_str())) { - LuaFunctionCall call(locker.GetLua(), lua.c_str()); + LuaFunctionCall call(lock.GetLua(), name.c_str()); call.PushString(remoteAet); call.PushString(remoteIp); call.PushString(calledAet); @@ -290,11 +293,11 @@ { std::string lua = "Is" + std::string(configuration); - LuaScripting::Locker locker(context_.GetLuaScripting()); + LuaScripting::Lock lock(lua_); - if (locker.GetLua().IsExistingFunction(lua.c_str())) + if (lock.GetLua().IsExistingFunction(lua.c_str())) { - LuaFunctionCall call(locker.GetLua(), lua.c_str()); + LuaFunctionCall call(lock.GetLua(), lua.c_str()); call.PushString(remoteAet); call.PushString(remoteIp); call.PushString(calledAet); @@ -310,15 +313,17 @@ class MyIncomingHttpRequestFilter : public IIncomingHttpRequestFilter { private: - ServerContext& context_; + LuaScripting lua_; OrthancPlugins* plugins_; public: MyIncomingHttpRequestFilter(ServerContext& context, OrthancPlugins* plugins) : - context_(context), + lua_(context), plugins_(plugins) { + LOG(INFO) << "Initializing Lua for MyIncomingHttpRequestFilter"; + lua_.LoadGlobalConfiguration(); } virtual bool IsAllowed(HttpMethod method, @@ -326,7 +331,7 @@ const char* ip, const char* username, const IHttpHandler::Arguments& httpHeaders, - const IHttpHandler::GetArguments& getArguments) const + const IHttpHandler::GetArguments& getArguments) { if (plugins_ != NULL && !plugins_->IsAllowed(method, uri, ip, username, httpHeaders, getArguments)) @@ -336,12 +341,12 @@ static const char* HTTP_FILTER = "IncomingHttpRequestFilter"; - LuaScripting::Locker locker(context_.GetLuaScripting()); + LuaScripting::Lock lock(lua_); // Test if the instance must be filtered out - if (locker.GetLua().IsExistingFunction(HTTP_FILTER)) + if (lock.GetLua().IsExistingFunction(HTTP_FILTER)) { - LuaFunctionCall call(locker.GetLua(), HTTP_FILTER); + LuaFunctionCall call(lock.GetLua(), HTTP_FILTER); switch (method) { @@ -640,25 +645,6 @@ -static void LoadLuaScripts(ServerContext& context) -{ - std::list luaScripts; - Configuration::GetGlobalListOfStringsParameter(luaScripts, "LuaScripts"); - for (std::list::const_iterator - it = luaScripts.begin(); it != luaScripts.end(); ++it) - { - std::string path = Configuration::InterpretStringParameterAsPath(*it); - LOG(WARNING) << "Installing the Lua scripts from: " << path; - std::string script; - SystemToolbox::ReadFile(script, path); - - LuaScripting::Locker locker(context.GetLuaScripting()); - locker.GetLua().Execute(script); - } -} - - - #if ORTHANC_ENABLE_PLUGINS == 1 static void LoadPlugins(OrthancPlugins& plugins) { @@ -689,7 +675,8 @@ } #endif - context.GetLuaScripting().Execute("Initialize"); + context.GetLuaEventHandler().Start(); + context.GetLuaEventHandler().Execute("Initialize"); bool restart; @@ -723,7 +710,8 @@ } } - context.GetLuaScripting().Execute("Finalize"); + context.GetLuaEventHandler().Execute("Finalize"); + context.GetLuaEventHandler().Stop(); #if ORTHANC_ENABLE_PLUGINS == 1 if (context.HasPlugins()) @@ -1011,7 +999,8 @@ context.GetIndex().SetMaximumStorageSize(0); } - LoadLuaScripts(context); + LOG(INFO) << "Initializing Lua for the event handler"; + context.GetLuaEventHandler().LoadGlobalConfiguration(); #if ORTHANC_ENABLE_PLUGINS == 1 if (plugins) diff -r 3200223f9ade -r 2f3007bf0708 Plugins/Engine/OrthancPlugins.cpp --- a/Plugins/Engine/OrthancPlugins.cpp Mon May 21 09:00:20 2018 +0200 +++ b/Plugins/Engine/OrthancPlugins.cpp Tue May 22 12:25:37 2018 +0200 @@ -3115,7 +3115,7 @@ const char* ip, const char* username, const IHttpHandler::Arguments& httpHeaders, - const IHttpHandler::GetArguments& getArguments) const + const IHttpHandler::GetArguments& getArguments) { OrthancPluginHttpMethod cMethod = Plugins::Convert(method); diff -r 3200223f9ade -r 2f3007bf0708 Plugins/Engine/OrthancPlugins.h --- a/Plugins/Engine/OrthancPlugins.h Mon May 21 09:00:20 2018 +0200 +++ b/Plugins/Engine/OrthancPlugins.h Tue May 22 12:25:37 2018 +0200 @@ -284,7 +284,7 @@ const char* ip, const char* username, const IHttpHandler::Arguments& httpHeaders, - const IHttpHandler::GetArguments& getArguments) const; + const IHttpHandler::GetArguments& getArguments); bool HasFindHandler();