# HG changeset patch # User Sebastien Jodogne # Date 1435669774 -7200 # Node ID 461e7554bff76937c96eb2eb4e3829dbda50bc56 # Parent 0ac74fa21db81b771e660b2808be753ab222277c refactoring: LuaScripting diff -r 0ac74fa21db8 -r 461e7554bff7 CMakeLists.txt --- a/CMakeLists.txt Tue Jun 30 12:51:29 2015 +0200 +++ b/CMakeLists.txt Tue Jun 30 15:09:34 2015 +0200 @@ -173,6 +173,7 @@ OrthancServer/ResourceFinder.cpp OrthancServer/DicomFindQuery.cpp OrthancServer/QueryRetrieveHandler.cpp + OrthancServer/LuaScripting.cpp # From "lua-scripting" branch OrthancServer/DicomInstanceToStore.cpp diff -r 0ac74fa21db8 -r 461e7554bff7 OrthancServer/IServerListener.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/IServerListener.h Tue Jun 30 15:09:34 2015 +0200 @@ -0,0 +1,58 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "DicomInstanceToStore.h" +#include "ServerIndexChange.h" + +#include + +namespace Orthanc +{ + class IServerListener + { + public: + virtual ~IServerListener() + { + } + + virtual void SignalStoredInstance(const std::string& publicId, + DicomInstanceToStore& instance, + const Json::Value& simplifiedTags) = 0; + + virtual void SignalChange(const ServerIndexChange& change) = 0; + + virtual bool FilterIncomingInstance(const Json::Value& simplified, + const std::string& remoteAet) = 0; + }; +} diff -r 0ac74fa21db8 -r 461e7554bff7 OrthancServer/LuaScripting.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/LuaScripting.cpp Tue Jun 30 15:09:34 2015 +0200 @@ -0,0 +1,274 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "PrecompiledHeadersServer.h" +#include "LuaScripting.h" + +#include "ServerContext.h" +#include "OrthancInitialization.h" +#include "../Core/Lua/LuaFunctionCall.h" + +#include "Scheduler/DeleteInstanceCommand.h" +#include "Scheduler/StoreScuCommand.h" +#include "Scheduler/StorePeerCommand.h" +#include "Scheduler/ModifyInstanceCommand.h" +#include "Scheduler/CallSystemCommand.h" +#include "OrthancRestApi/OrthancRestApi.h" + +#include +#include + +static const char* RECEIVED_INSTANCE_FILTER = "ReceivedInstanceFilter"; +static const char* ON_STORED_INSTANCE = "OnStoredInstance"; + + +namespace Orthanc +{ + IServerCommand* LuaScripting::ParseOperation(const std::string& operation, + const Json::Value& parameters) + { + if (operation == "delete") + { + LOG(INFO) << "Lua script to delete instance " << parameters["Instance"].asString(); + return new DeleteInstanceCommand(context_); + } + + if (operation == "store-scu") + { + std::string localAet; + if (parameters.isMember("LocalAet")) + { + localAet = parameters["LocalAet"].asString(); + } + else + { + localAet = context_.GetDefaultLocalApplicationEntityTitle(); + } + + std::string modality = parameters["Modality"].asString(); + LOG(INFO) << "Lua script to send instance " << parameters["Instance"].asString() + << " to modality " << modality << " using Store-SCU"; + return new StoreScuCommand(context_, localAet, + Configuration::GetModalityUsingSymbolicName(modality), true); + } + + if (operation == "store-peer") + { + std::string peer = parameters["Peer"].asString(); + LOG(INFO) << "Lua script to send instance " << parameters["Instance"].asString() + << " to peer " << peer << " using HTTP"; + + OrthancPeerParameters parameters; + Configuration::GetOrthancPeer(parameters, peer); + return new StorePeerCommand(context_, parameters, true); + } + + if (operation == "modify") + { + LOG(INFO) << "Lua script to modify instance " << parameters["Instance"].asString(); + DicomModification modification; + OrthancRestApi::ParseModifyRequest(modification, parameters); + + std::auto_ptr command(new ModifyInstanceCommand(context_, modification)); + return command.release(); + } + + if (operation == "call-system") + { + LOG(INFO) << "Lua script to call system command on " << parameters["Instance"].asString(); + + const Json::Value& argsIn = parameters["Arguments"]; + if (argsIn.type() != Json::arrayValue) + { + throw OrthancException(ErrorCode_BadParameterType); + } + + std::vector args; + args.reserve(argsIn.size()); + for (Json::Value::ArrayIndex i = 0; i < argsIn.size(); ++i) + { + // http://jsoncpp.sourceforge.net/namespace_json.html#7d654b75c16a57007925868e38212b4e + switch (argsIn[i].type()) + { + case Json::stringValue: + args.push_back(argsIn[i].asString()); + break; + + case Json::intValue: + args.push_back(boost::lexical_cast(argsIn[i].asInt())); + break; + + case Json::uintValue: + args.push_back(boost::lexical_cast(argsIn[i].asUInt())); + break; + + case Json::realValue: + args.push_back(boost::lexical_cast(argsIn[i].asFloat())); + break; + + default: + throw OrthancException(ErrorCode_BadParameterType); + } + } + + return new CallSystemCommand(context_, parameters["Command"].asString(), args); + } + + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + + LuaScripting::LuaScripting(ServerContext& context) : context_(context) + { + lua_.Execute(Orthanc::EmbeddedResources::LUA_TOOLBOX); + lua_.SetHttpProxy(Configuration::GetGlobalStringParameter("HttpProxy", "")); + } + + + void LuaScripting::ApplyOnStoredInstance(const std::string& instanceId, + const Json::Value& simplifiedTags, + const Json::Value& metadata, + const std::string& remoteAet, + const std::string& calledAet) + { + if (lua_.IsExistingFunction(ON_STORED_INSTANCE)) + { + lua_.Execute("_InitializeJob()"); + + LuaFunctionCall call(lua_, ON_STORED_INSTANCE); + call.PushString(instanceId); + call.PushJson(simplifiedTags); + call.PushJson(metadata); + call.PushJson(remoteAet); + call.PushJson(calledAet); + call.Execute(); + + Json::Value operations; + LuaFunctionCall call2(lua_, "_AccessJob"); + call2.ExecuteToJson(operations); + + if (operations.type() != Json::arrayValue) + { + throw OrthancException(ErrorCode_InternalError); + } + + ServerJob job; + ServerCommandInstance* previousCommand = NULL; + + for (Json::Value::ArrayIndex i = 0; i < operations.size(); ++i) + { + if (operations[i].type() != Json::objectValue || + !operations[i].isMember("Operation")) + { + throw OrthancException(ErrorCode_InternalError); + } + + const Json::Value& parameters = operations[i]; + std::string operation = parameters["Operation"].asString(); + + ServerCommandInstance& command = job.AddCommand(ParseOperation(operation, operations[i])); + + if (!parameters.isMember("Instance")) + { + throw OrthancException(ErrorCode_InternalError); + } + + std::string instance = parameters["Instance"].asString(); + if (instance.empty()) + { + previousCommand->ConnectOutput(command); + } + else + { + command.AddInput(instance); + } + + previousCommand = &command; + } + + job.SetDescription(std::string("Lua script: ") + ON_STORED_INSTANCE); + context_.GetScheduler().Submit(job); + } + } + + + void LuaScripting::SignalStoredInstance(const std::string& publicId, + DicomInstanceToStore& instance, + const Json::Value& simplifiedTags) + { + boost::mutex::scoped_lock lock(mutex_); + + Json::Value metadata = Json::objectValue; + + for (ServerIndex::MetadataMap::const_iterator + it = instance.GetMetadata().begin(); + it != instance.GetMetadata().end(); ++it) + { + if (it->first.first == ResourceType_Instance) + { + metadata[EnumerationToString(it->first.second)] = it->second; + } + } + + ApplyOnStoredInstance(publicId, simplifiedTags, metadata, + instance.GetRemoteAet(), instance.GetCalledAet()); + } + + + void LuaScripting::SignalChange(const ServerIndexChange& change) + { + boost::mutex::scoped_lock lock(mutex_); + + // TODO + } + + + bool LuaScripting::FilterIncomingInstance(const Json::Value& simplified, + const std::string& remoteAet) + { + boost::mutex::scoped_lock lock(mutex_); + + if (lua_.IsExistingFunction(RECEIVED_INSTANCE_FILTER)) + { + LuaFunctionCall call(lua_, RECEIVED_INSTANCE_FILTER); + call.PushJson(simplified); + call.PushString(remoteAet); + + if (!call.ExecutePredicate()) + { + return false; + } + } + + return true; + } +} diff -r 0ac74fa21db8 -r 461e7554bff7 OrthancServer/LuaScripting.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/LuaScripting.h Tue Jun 30 15:09:34 2015 +0200 @@ -0,0 +1,93 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "IServerListener.h" +#include "../Core/Lua/LuaContext.h" +#include "Scheduler/IServerCommand.h" + +namespace Orthanc +{ + class ServerContext; + + class LuaScripting : public IServerListener + { + private: + void ApplyOnStoredInstance(const std::string& instanceId, + const Json::Value& simplifiedDicom, + const Json::Value& metadata, + const std::string& remoteAet, + const std::string& calledAet); + + IServerCommand* ParseOperation(const std::string& operation, + const Json::Value& parameters); + + boost::mutex mutex_; + LuaContext lua_; + ServerContext& context_; + + public: + class Locker : public boost::noncopyable + { + private: + LuaScripting& that_; + + public: + Locker(LuaScripting& that) : that_(that) + { + that.mutex_.lock(); + } + + ~Locker() + { + that_.mutex_.unlock(); + } + + LuaContext& GetLua() + { + return that_.lua_; + } + }; + + LuaScripting(ServerContext& context); + + virtual void SignalStoredInstance(const std::string& publicId, + DicomInstanceToStore& instance, + const Json::Value& simplifiedTags); + + virtual void SignalChange(const ServerIndexChange& change); + + virtual bool FilterIncomingInstance(const Json::Value& simplified, + const std::string& remoteAet); + }; +} diff -r 0ac74fa21db8 -r 461e7554bff7 OrthancServer/OrthancRestApi/OrthancRestSystem.cpp --- a/OrthancServer/OrthancRestApi/OrthancRestSystem.cpp Tue Jun 30 12:51:29 2015 +0200 +++ b/OrthancServer/OrthancRestApi/OrthancRestSystem.cpp Tue Jun 30 15:09:34 2015 +0200 @@ -100,7 +100,7 @@ ServerContext& context = OrthancRestApi::GetContext(call); { - ServerContext::LuaContextLocker locker(context); + LuaScripting::Locker locker(context.GetLua()); locker.GetLua().Execute(result, call.GetPostBody()); } diff -r 0ac74fa21db8 -r 461e7554bff7 OrthancServer/ServerContext.cpp --- a/OrthancServer/ServerContext.cpp Tue Jun 30 12:51:29 2015 +0200 +++ b/OrthancServer/ServerContext.cpp Tue Jun 30 15:09:34 2015 +0200 @@ -34,7 +34,6 @@ #include "ServerContext.h" #include "../Core/HttpServer/FilesystemHttpSender.h" -#include "../Core/Lua/LuaFunctionCall.h" #include "FromDcmtkBridge.h" #include "ServerToolbox.h" #include "OrthancInitialization.h" @@ -55,9 +54,6 @@ #define ENABLE_DICOM_CACHE 1 -static const char* RECEIVED_INSTANCE_FILTER = "ReceivedInstanceFilter"; -static const char* ON_STORED_INSTANCE = "OnStoredInstance"; - static const size_t DICOM_CACHE_SIZE = 2; /** @@ -77,6 +73,7 @@ provider_(*this), dicomCache_(provider_, DICOM_CACHE_SIZE), scheduler_(Configuration::GetGlobalIntegerParameter("LimitJobs", 10)), + lua_(*this), plugins_(NULL), pluginsManager_(NULL), queryRetrieveArchive_(Configuration::GetGlobalIntegerParameter("QueryRetrieveSize", 10)), @@ -85,8 +82,7 @@ uint64_t s = Configuration::GetGlobalIntegerParameter("DicomAssociationCloseDelay", 5); // In seconds scu_.SetMillisecondsBeforeClose(s * 1000); // Milliseconds are expected here - lua_.Execute(Orthanc::EmbeddedResources::LUA_TOOLBOX); - lua_.SetHttpProxy(Configuration::GetGlobalStringParameter("HttpProxy", "")); + listeners_.push_back(ServerListener(lua_, "Lua")); } void ServerContext::SetCompressionEnabled(bool enabled) @@ -106,191 +102,6 @@ } - bool ServerContext::ApplyReceivedInstanceFilter(const Json::Value& simplified, - const std::string& remoteAet) - { - LuaContextLocker locker(*this); - - if (locker.GetLua().IsExistingFunction(RECEIVED_INSTANCE_FILTER)) - { - LuaFunctionCall call(locker.GetLua(), RECEIVED_INSTANCE_FILTER); - call.PushJson(simplified); - call.PushString(remoteAet); - - if (!call.ExecutePredicate()) - { - return false; - } - } - - return true; - } - - - static IServerCommand* ParseOperation(ServerContext& context, - const std::string& operation, - const Json::Value& parameters) - { - if (operation == "delete") - { - LOG(INFO) << "Lua script to delete instance " << parameters["Instance"].asString(); - return new DeleteInstanceCommand(context); - } - - if (operation == "store-scu") - { - std::string localAet; - if (parameters.isMember("LocalAet")) - { - localAet = parameters["LocalAet"].asString(); - } - else - { - localAet = context.GetDefaultLocalApplicationEntityTitle(); - } - - std::string modality = parameters["Modality"].asString(); - LOG(INFO) << "Lua script to send instance " << parameters["Instance"].asString() - << " to modality " << modality << " using Store-SCU"; - return new StoreScuCommand(context, localAet, - Configuration::GetModalityUsingSymbolicName(modality), true); - } - - if (operation == "store-peer") - { - std::string peer = parameters["Peer"].asString(); - LOG(INFO) << "Lua script to send instance " << parameters["Instance"].asString() - << " to peer " << peer << " using HTTP"; - - OrthancPeerParameters parameters; - Configuration::GetOrthancPeer(parameters, peer); - return new StorePeerCommand(context, parameters, true); - } - - if (operation == "modify") - { - LOG(INFO) << "Lua script to modify instance " << parameters["Instance"].asString(); - DicomModification modification; - OrthancRestApi::ParseModifyRequest(modification, parameters); - - std::auto_ptr command(new ModifyInstanceCommand(context, modification)); - return command.release(); - } - - if (operation == "call-system") - { - LOG(INFO) << "Lua script to call system command on " << parameters["Instance"].asString(); - - const Json::Value& argsIn = parameters["Arguments"]; - if (argsIn.type() != Json::arrayValue) - { - throw OrthancException(ErrorCode_BadParameterType); - } - - std::vector args; - args.reserve(argsIn.size()); - for (Json::Value::ArrayIndex i = 0; i < argsIn.size(); ++i) - { - // http://jsoncpp.sourceforge.net/namespace_json.html#7d654b75c16a57007925868e38212b4e - switch (argsIn[i].type()) - { - case Json::stringValue: - args.push_back(argsIn[i].asString()); - break; - - case Json::intValue: - args.push_back(boost::lexical_cast(argsIn[i].asInt())); - break; - - case Json::uintValue: - args.push_back(boost::lexical_cast(argsIn[i].asUInt())); - break; - - case Json::realValue: - args.push_back(boost::lexical_cast(argsIn[i].asFloat())); - break; - - default: - throw OrthancException(ErrorCode_BadParameterType); - } - } - - return new CallSystemCommand(context, parameters["Command"].asString(), args); - } - - throw OrthancException(ErrorCode_ParameterOutOfRange); - } - - - void ServerContext::ApplyLuaOnStoredInstance(const std::string& instanceId, - const Json::Value& simplifiedDicom, - const Json::Value& metadata, - const std::string& remoteAet, - const std::string& calledAet) - { - LuaContextLocker locker(*this); - - if (locker.GetLua().IsExistingFunction(ON_STORED_INSTANCE)) - { - locker.GetLua().Execute("_InitializeJob()"); - - LuaFunctionCall call(locker.GetLua(), ON_STORED_INSTANCE); - call.PushString(instanceId); - call.PushJson(simplifiedDicom); - call.PushJson(metadata); - call.PushJson(remoteAet); - call.PushJson(calledAet); - call.Execute(); - - Json::Value operations; - LuaFunctionCall call2(locker.GetLua(), "_AccessJob"); - call2.ExecuteToJson(operations); - - if (operations.type() != Json::arrayValue) - { - throw OrthancException(ErrorCode_InternalError); - } - - ServerJob job; - ServerCommandInstance* previousCommand = NULL; - - for (Json::Value::ArrayIndex i = 0; i < operations.size(); ++i) - { - if (operations[i].type() != Json::objectValue || - !operations[i].isMember("Operation")) - { - throw OrthancException(ErrorCode_InternalError); - } - - const Json::Value& parameters = operations[i]; - std::string operation = parameters["Operation"].asString(); - - ServerCommandInstance& command = job.AddCommand(ParseOperation(*this, operation, operations[i])); - - if (!parameters.isMember("Instance")) - { - throw OrthancException(ErrorCode_InternalError); - } - - std::string instance = parameters["Instance"].asString(); - if (instance.empty()) - { - previousCommand->ConnectOutput(command); - } - else - { - command.AddInput(instance); - } - - previousCommand = &command; - } - - job.SetDescription(std::string("Lua script: ") + ON_STORED_INSTANCE); - scheduler_.Submit(job); - } - } - - StoreStatus ServerContext::Store(std::string& resultPublicId, DicomInstanceToStore& dicom) { @@ -303,7 +114,27 @@ SimplifyTags(simplified, dicom.GetJson()); // Test if the instance must be filtered out - if (!ApplyReceivedInstanceFilter(simplified, dicom.GetRemoteAet())) + bool accepted = true; + + for (ServerListeners::iterator it = listeners_.begin(); it != listeners_.end(); ++it) + { + try + { + if (!it->GetListener().FilterIncomingInstance(simplified, dicom.GetRemoteAet())) + { + accepted = false; + break; + } + } + catch (OrthancException& e) + { + LOG(ERROR) << "Error in the " << it->GetDescription() + << " callback while receiving an instance: " << e.What(); + throw; + } + } + + if (!accepted) { LOG(INFO) << "An incoming instance has been discarded by the filter"; return StoreStatus_FilteredOut; @@ -330,6 +161,7 @@ StoreStatus status = index_.Store(instanceMetadata, dicom.GetSummary(), attachments, dicom.GetRemoteAet(), dicom.GetMetadata()); + // Only keep the metadata for the "instance" level dicom.GetMetadata().clear(); for (InstanceMetadata::const_iterator it = instanceMetadata.begin(); @@ -367,33 +199,16 @@ if (status == StoreStatus_Success || status == StoreStatus_AlreadyStored) { - Json::Value metadata = Json::objectValue; - for (std::map::const_iterator - it = instanceMetadata.begin(); - it != instanceMetadata.end(); ++it) - { - metadata[EnumerationToString(it->first)] = it->second; - } - - try - { - ApplyLuaOnStoredInstance(resultPublicId, simplified, metadata, - dicom.GetRemoteAet(), dicom.GetCalledAet()); - } - catch (OrthancException& e) - { - LOG(ERROR) << "Error in " << ON_STORED_INSTANCE << " callback (Lua): " << e.What(); - } - - if (plugins_ != NULL) + for (ServerListeners::iterator it = listeners_.begin(); it != listeners_.end(); ++it) { try { - plugins_->SignalStoredInstance(dicom, resultPublicId); + it->GetListener().SignalStoredInstance(resultPublicId, dicom, simplified); } catch (OrthancException& e) { - LOG(ERROR) << "Error in " << ON_STORED_INSTANCE << " callback (plugins): " << e.What(); + LOG(ERROR) << "Error in the " << it->GetDescription() + << " callback while receiving an instance: " << e.What(); } } } @@ -546,15 +361,16 @@ void ServerContext::SignalChange(const ServerIndexChange& change) { - if (plugins_ != NULL) + for (ServerListeners::iterator it = listeners_.begin(); it != listeners_.end(); ++it) { try { - plugins_->SignalChange(change); + it->GetListener().SignalChange(change); } catch (OrthancException& e) { - LOG(ERROR) << "Error in OnChangeCallback (plugins): " << e.What(); + LOG(ERROR) << "Error in the " << it->GetDescription() + << " callback while signaling a change: " << e.What(); } } } diff -r 0ac74fa21db8 -r 461e7554bff7 OrthancServer/ServerContext.h --- a/OrthancServer/ServerContext.h Tue Jun 30 12:51:29 2015 +0200 +++ b/OrthancServer/ServerContext.h Tue Jun 30 15:09:34 2015 +0200 @@ -33,25 +33,25 @@ #pragma once #include "../Core/Cache/MemoryCache.h" +#include "../Core/Cache/SharedArchive.h" #include "../Core/FileStorage/CompressedFileStorageAccessor.h" #include "../Core/FileStorage/IStorageArea.h" +#include "../Core/Lua/LuaContext.h" #include "../Core/RestApi/RestApiOutput.h" -#include "../Core/Lua/LuaContext.h" -#include "ServerIndex.h" -#include "ParsedDicomFile.h" +#include "../Plugins/Engine/OrthancPlugins.h" +#include "../Plugins/Engine/PluginsManager.h" +#include "DicomInstanceToStore.h" #include "DicomProtocol/ReusableDicomUserConnection.h" +#include "IServerListener.h" +#include "LuaScripting.h" +#include "ParsedDicomFile.h" #include "Scheduler/ServerScheduler.h" -#include "DicomInstanceToStore.h" -#include "ServerIndexChange.h" -#include "../Core/Cache/SharedArchive.h" +#include "ServerIndex.h" #include namespace Orthanc { - class OrthancPlugins; - class PluginsManager; - /** * This class is responsible for maintaining the storage area on the * filesystem (including compression), as well as the index of the @@ -73,14 +73,33 @@ virtual IDynamicObject* Provide(const std::string& id); }; - bool ApplyReceivedInstanceFilter(const Json::Value& simplified, - const std::string& remoteAet); + class ServerListener + { + private: + IServerListener *listener_; + std::string description_; + + public: + ServerListener(IServerListener& listener, + const std::string& description) : + listener_(&listener), + description_(description) + { + } - void ApplyLuaOnStoredInstance(const std::string& instanceId, - const Json::Value& simplifiedDicom, - const Json::Value& metadata, - const std::string& remoteAet, - const std::string& calledAet); + IServerListener& GetListener() + { + return *listener_; + } + + const std::string& GetDescription() + { + return description_; + } + }; + + typedef std::list ServerListeners; + ServerIndex index_; CompressedFileStorageAccessor accessor_; @@ -92,9 +111,9 @@ ReusableDicomUserConnection scu_; ServerScheduler scheduler_; - boost::mutex luaMutex_; - LuaContext lua_; - OrthancPlugins* plugins_; // TODO Turn it into a listener pattern (idem for Lua callbacks) + LuaScripting lua_; + OrthancPlugins* plugins_; + ServerListeners listeners_; const PluginsManager* pluginsManager_; SharedArchive queryRetrieveArchive_; @@ -120,29 +139,6 @@ } }; - class LuaContextLocker : public boost::noncopyable - { - private: - ServerContext& that_; - - public: - LuaContextLocker(ServerContext& that) : that_(that) - { - that.luaMutex_.lock(); - } - - ~LuaContextLocker() - { - that_.luaMutex_.unlock(); - } - - LuaContext& GetLua() - { - return that_.lua_; - } - }; - - ServerContext(IDatabaseWrapper& database); void SetStorageArea(IStorageArea& storage) @@ -208,12 +204,17 @@ { pluginsManager_ = &manager; plugins_ = &plugins; + listeners_.clear(); + listeners_.push_back(ServerListener(lua_, "Lua")); // TODO REFACTOR THIS + listeners_.push_back(ServerListener(plugins, "plugin")); // TODO REFACTOR THIS } void ResetOrthancPlugins() { pluginsManager_ = NULL; plugins_ = NULL; + listeners_.clear(); + listeners_.push_back(ServerListener(lua_, "Lua")); // TODO REFACTOR THIS } bool DeleteResource(Json::Value& target, @@ -237,5 +238,10 @@ { return defaultLocalAet_; } + + LuaScripting& GetLua() + { + return lua_; + } }; } diff -r 0ac74fa21db8 -r 461e7554bff7 OrthancServer/main.cpp --- a/OrthancServer/main.cpp Tue Jun 30 12:51:29 2015 +0200 +++ b/OrthancServer/main.cpp Tue Jun 30 15:09:34 2015 +0200 @@ -232,7 +232,7 @@ { std::string lua = "Is" + configuration; - ServerContext::LuaContextLocker locker(context_); + LuaScripting::Locker locker(context_.GetLua()); if (locker.GetLua().IsExistingFunction(lua.c_str())) { @@ -265,7 +265,7 @@ { static const char* HTTP_FILTER = "IncomingHttpRequestFilter"; - ServerContext::LuaContextLocker locker(context_); + LuaScripting::Locker locker(context_.GetLua()); // Test if the instance must be filtered out if (locker.GetLua().IsExistingFunction(HTTP_FILTER)) @@ -363,7 +363,7 @@ std::string script; Toolbox::ReadFile(script, path); - ServerContext::LuaContextLocker locker(context); + LuaScripting::Locker locker(context.GetLua()); locker.GetLua().Execute(script); } } diff -r 0ac74fa21db8 -r 461e7554bff7 Plugins/Engine/OrthancPlugins.cpp --- a/Plugins/Engine/OrthancPlugins.cpp Tue Jun 30 12:51:29 2015 +0200 +++ b/Plugins/Engine/OrthancPlugins.cpp Tue Jun 30 15:09:34 2015 +0200 @@ -33,13 +33,15 @@ #include "OrthancPlugins.h" #include "../../Core/ChunkedBuffer.h" -#include "../../Core/OrthancException.h" -#include "../../Core/Toolbox.h" #include "../../Core/HttpServer/HttpOutput.h" #include "../../Core/ImageFormats/PngWriter.h" -#include "../../OrthancServer/ServerToolbox.h" +#include "../../Core/MultiThreading/SharedMessageQueue.h" +#include "../../Core/OrthancException.h" +#include "../../Core/Toolbox.h" #include "../../OrthancServer/OrthancInitialization.h" -#include "../../Core/MultiThreading/SharedMessageQueue.h" +#include "../../OrthancServer/OrthancRestApi/OrthancRestApi.h" +#include "../../OrthancServer/ServerContext.h" +#include "../../OrthancServer/ServerToolbox.h" #include #include @@ -449,8 +451,9 @@ } - void OrthancPlugins::SignalStoredInstance(DicomInstanceToStore& instance, - const std::string& instanceId) + void OrthancPlugins::SignalStoredInstance(const std::string& instanceId, + DicomInstanceToStore& instance, + const Json::Value& simplifiedTags) { boost::recursive_mutex::scoped_lock lock(pimpl_->callbackMutex_); diff -r 0ac74fa21db8 -r 461e7554bff7 Plugins/Engine/OrthancPlugins.h --- a/Plugins/Engine/OrthancPlugins.h Tue Jun 30 12:51:29 2015 +0200 +++ b/Plugins/Engine/OrthancPlugins.h Tue Jun 30 15:09:34 2015 +0200 @@ -32,18 +32,24 @@ #pragma once -#include "PluginsManager.h" +#include "../../Core/FileStorage/IStorageArea.h" #include "../../Core/HttpServer/HttpHandler.h" -#include "../../OrthancServer/ServerContext.h" -#include "../../OrthancServer/OrthancRestApi/OrthancRestApi.h" +#include "../../OrthancServer/IServerListener.h" #include "OrthancPluginDatabase.h" +#include "PluginsManager.h" #include #include namespace Orthanc { - class OrthancPlugins : public HttpHandler, public IPluginServiceProvider + class OrthancRestApi; + class ServerContext; + + class OrthancPlugins : + public HttpHandler, + public IPluginServiceProvider, + public IServerListener { private: struct PImpl; @@ -105,10 +111,17 @@ virtual bool InvokeService(_OrthancPluginService service, const void* parameters); - void SignalChange(const ServerIndexChange& change); + virtual void SignalChange(const ServerIndexChange& change); + + virtual void SignalStoredInstance(const std::string& instanceId, + DicomInstanceToStore& instance, + const Json::Value& simplifiedTags); - void SignalStoredInstance(DicomInstanceToStore& instance, - const std::string& instanceId); + virtual bool FilterIncomingInstance(const Json::Value& simplified, + const std::string& remoteAet) + { + return true; // TODO Enable filtering of instances from plugins + } void SetOrthancRestApi(OrthancRestApi& restApi);