Mercurial > hg > orthanc
changeset 1282:7bccdd221e2b
Plugins can do REST calls to other plugins
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 03 Feb 2015 11:52:21 +0100 |
parents | 8dac11c78d71 |
children | 6066529e34c8 |
files | NEWS Plugins/Engine/OrthancPlugins.cpp Plugins/Engine/OrthancPlugins.h Plugins/OrthancCPlugin/OrthancCPlugin.h Plugins/Samples/Basic/Plugin.c |
diffstat | 5 files changed, 260 insertions(+), 26 deletions(-) [+] |
line wrap: on
line diff
--- a/NEWS Tue Feb 03 10:53:35 2015 +0100 +++ b/NEWS Tue Feb 03 11:52:21 2015 +0100 @@ -15,10 +15,11 @@ Plugins ------- -* Introspection of plugins +* Introspection of plugins (cf. the "/plugins" URI) * Plugins can access the command-line arguments used to launch Orthanc * Plugins can extend Orthanc Explorer with custom JavaScript * Plugins can get/set global properties to save their configuration +* Plugins can do REST calls to other plugins (cf. "xxxAfterPlugins()") * Scan of folders for plugins
--- a/Plugins/Engine/OrthancPlugins.cpp Tue Feb 03 10:53:35 2015 +0100 +++ b/Plugins/Engine/OrthancPlugins.cpp Tue Feb 03 11:52:21 2015 +0100 @@ -187,7 +187,7 @@ OnChangeCallbacks onChangeCallbacks_; bool hasStorageArea_; _OrthancPluginRegisterStorageArea storageArea_; - boost::mutex callbackMutex_; + boost::recursive_mutex callbackMutex_; SharedMessageQueue pendingChanges_; boost::thread changeThread_; bool done_; @@ -215,7 +215,7 @@ if (obj.get() != NULL) { - boost::mutex::scoped_lock lock(that->callbackMutex_); + boost::recursive_mutex::scoped_lock lock(that->callbackMutex_); PendingChange& change = *dynamic_cast<PendingChange*>(obj.get()); change.Submit(that->onChangeCallbacks_); } @@ -397,7 +397,7 @@ int32_t error; { - boost::mutex::scoped_lock lock(pimpl_->callbackMutex_); + boost::recursive_mutex::scoped_lock lock(pimpl_->callbackMutex_); error = callback(reinterpret_cast<OrthancPluginRestOutput*>(&output), flatUri.c_str(), &request); @@ -423,7 +423,7 @@ void OrthancPlugins::SignalStoredInstance(DicomInstanceToStore& instance, const std::string& instanceId) { - boost::mutex::scoped_lock lock(pimpl_->callbackMutex_); + boost::recursive_mutex::scoped_lock lock(pimpl_->callbackMutex_); for (PImpl::OnStoredCallbacks::const_iterator callback = pimpl_->onStoredCallbacks_.begin(); @@ -651,7 +651,8 @@ } - void OrthancPlugins::RestApiGet(const void* parameters) + void OrthancPlugins::RestApiGet(const void* parameters, + bool afterPlugins) { const _OrthancPluginRestApiGet& p = *reinterpret_cast<const _OrthancPluginRestApiGet*>(parameters); @@ -666,12 +667,25 @@ StringHttpOutput stream; HttpOutput http(stream, false /* no keep alive */); - LOG(INFO) << "Plugin making REST GET call on URI " << p.uri; + LOG(INFO) << "Plugin making REST GET call on URI " << p.uri + << (afterPlugins ? " (after plugins)" : " (built-in API)"); + + bool ok = false; + std::string result; - if (pimpl_->restApi_ != NULL && - pimpl_->restApi_->Handle(http, HttpMethod_Get, uri, headers, getArguments, body)) + if (afterPlugins) + { + ok = Handle(http, HttpMethod_Get, uri, headers, getArguments, body); + } + + if (!ok) { - std::string result; + ok = (pimpl_->restApi_ != NULL && + pimpl_->restApi_->Handle(http, HttpMethod_Get, uri, headers, getArguments, body)); + } + + if (ok) + { stream.GetOutput(result); CopyToMemoryBuffer(*p.target, result); } @@ -682,7 +696,9 @@ } - void OrthancPlugins::RestApiPostPut(bool isPost, const void* parameters) + void OrthancPlugins::RestApiPostPut(bool isPost, + const void* parameters, + bool afterPlugins) { const _OrthancPluginRestApiPostPut& p = *reinterpret_cast<const _OrthancPluginRestApiPostPut*>(parameters); @@ -700,12 +716,25 @@ HttpOutput http(stream, false /* no keep alive */); HttpMethod method = (isPost ? HttpMethod_Post : HttpMethod_Put); - LOG(INFO) << "Plugin making REST " << EnumerationToString(method) << " call on URI " << p.uri; + LOG(INFO) << "Plugin making REST " << EnumerationToString(method) << " call on URI " << p.uri + << (afterPlugins ? " (after plugins)" : " (built-in API)"); + + bool ok = false; + std::string result; - if (pimpl_->restApi_ != NULL && - pimpl_->restApi_->Handle(http, method, uri, headers, getArguments, body)) + if (afterPlugins) + { + ok = Handle(http, method, uri, headers, getArguments, body); + } + + if (!ok) { - std::string result; + ok = (pimpl_->restApi_ != NULL && + pimpl_->restApi_->Handle(http, method, uri, headers, getArguments, body)); + } + + if (ok) + { stream.GetOutput(result); CopyToMemoryBuffer(*p.target, result); } @@ -716,7 +745,8 @@ } - void OrthancPlugins::RestApiDelete(const void* parameters) + void OrthancPlugins::RestApiDelete(const void* parameters, + bool afterPlugins) { // The "parameters" point to the URI UriComponents uri; @@ -730,10 +760,23 @@ HttpOutput http(stream, false /* no keep alive */); LOG(INFO) << "Plugin making REST DELETE call on URI " - << reinterpret_cast<const char*>(parameters); + << reinterpret_cast<const char*>(parameters) + << (afterPlugins ? " (after plugins)" : " (built-in API)"); + + bool ok = false; - if (pimpl_->restApi_ == NULL || - !pimpl_->restApi_->Handle(http, HttpMethod_Delete, uri, headers, getArguments, body)) + if (afterPlugins) + { + ok = Handle(http, HttpMethod_Delete, uri, headers, getArguments, body); + } + + if (!ok) + { + ok = (pimpl_->restApi_ != NULL && + pimpl_->restApi_->Handle(http, HttpMethod_Delete, uri, headers, getArguments, body)); + } + + if (!ok) { throw OrthancException(ErrorCode_BadRequest); } @@ -963,19 +1006,35 @@ return true; case _OrthancPluginService_RestApiGet: - RestApiGet(parameters); + RestApiGet(parameters, false); + return true; + + case _OrthancPluginService_RestApiGetAfterPlugins: + RestApiGet(parameters, true); return true; case _OrthancPluginService_RestApiPost: - RestApiPostPut(true, parameters); + RestApiPostPut(true, parameters, false); + return true; + + case _OrthancPluginService_RestApiPostAfterPlugins: + RestApiPostPut(true, parameters, true); return true; case _OrthancPluginService_RestApiDelete: - RestApiDelete(parameters); + RestApiDelete(parameters, false); + return true; + + case _OrthancPluginService_RestApiDeleteAfterPlugins: + RestApiDelete(parameters, true); return true; case _OrthancPluginService_RestApiPut: - RestApiPostPut(false, parameters); + RestApiPostPut(false, parameters, false); + return true; + + case _OrthancPluginService_RestApiPutAfterPlugins: + RestApiPostPut(false, parameters, true); return true; case _OrthancPluginService_Redirect:
--- a/Plugins/Engine/OrthancPlugins.h Tue Feb 03 10:53:35 2015 +0100 +++ b/Plugins/Engine/OrthancPlugins.h Tue Feb 03 11:52:21 2015 +0100 @@ -63,11 +63,15 @@ void GetDicomForInstance(const void* parameters); - void RestApiGet(const void* parameters); + void RestApiGet(const void* parameters, + bool afterPlugins); - void RestApiPostPut(bool isPost, const void* parameters); + void RestApiPostPut(bool isPost, + const void* parameters, + bool afterPlugins); - void RestApiDelete(const void* parameters); + void RestApiDelete(const void* parameters, + bool afterPlugins); void LookupResource(_OrthancPluginService service, const void* parameters);
--- a/Plugins/OrthancCPlugin/OrthancCPlugin.h Tue Feb 03 10:53:35 2015 +0100 +++ b/Plugins/OrthancCPlugin/OrthancCPlugin.h Tue Feb 03 11:52:21 2015 +0100 @@ -281,6 +281,10 @@ _OrthancPluginService_LookupSeries = 3007, _OrthancPluginService_LookupInstance = 3008, _OrthancPluginService_LookupStudyWithAccessionNumber = 3009, + _OrthancPluginService_RestApiGetAfterPlugins = 3010, + _OrthancPluginService_RestApiPostAfterPlugins = 3011, + _OrthancPluginService_RestApiDeleteAfterPlugins = 3012, + _OrthancPluginService_RestApiPutAfterPlugins = 3013, /* Access to DICOM instances */ _OrthancPluginService_GetInstanceRemoteAet = 4000, @@ -891,6 +895,33 @@ + /** + * @brief Make a GET call to the REST API, as tainted by the plugins. + * + * Make a GET call to the Orthanc REST API, after all the plugins + * are applied. In other words, if some plugin overrides or adds the + * called URI to the built-in Orthanc REST API, this call will + * return the result provided by this plugin. The result to the + * query is stored into a newly allocated memory buffer. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param target The target memory buffer. + * @param uri The URI in the built-in Orthanc API. + * @return 0 if success, other value if error. + **/ + ORTHANC_PLUGIN_INLINE int OrthancPluginRestApiGetAfterPlugins( + OrthancPluginContext* context, + OrthancPluginMemoryBuffer* target, + const char* uri) + { + _OrthancPluginRestApiGet params; + params.target = target; + params.uri = uri; + return context->InvokeService(context, _OrthancPluginService_RestApiGetAfterPlugins, ¶ms); + } + + + typedef struct { OrthancPluginMemoryBuffer* target; @@ -928,6 +959,38 @@ } + /** + * @brief Make a POST call to the REST API, as tainted by the plugins. + * + * Make a POST call to the Orthanc REST API, after all the plugins + * are applied. In other words, if some plugin overrides or adds the + * called URI to the built-in Orthanc REST API, this call will + * return the result provided by this plugin. The result to the + * query is stored into a newly allocated memory buffer. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param target The target memory buffer. + * @param uri The URI in the built-in Orthanc API. + * @param body The body of the POST request. + * @param bodySize The size of the body. + * @return 0 if success, other value if error. + **/ + ORTHANC_PLUGIN_INLINE int OrthancPluginRestApiPostAfterPlugins( + OrthancPluginContext* context, + OrthancPluginMemoryBuffer* target, + const char* uri, + const char* body, + uint32_t bodySize) + { + _OrthancPluginRestApiPostPut params; + params.target = target; + params.uri = uri; + params.body = body; + params.bodySize = bodySize; + return context->InvokeService(context, _OrthancPluginService_RestApiPostAfterPlugins, ¶ms); + } + + /** * @brief Make a DELETE call to the built-in Orthanc REST API. @@ -946,6 +1009,26 @@ } + /** + * @brief Make a DELETE call to the REST API, as tainted by the plugins. + * + * Make a DELETE call to the Orthanc REST API, after all the plugins + * are applied. In other words, if some plugin overrides or adds the + * called URI to the built-in Orthanc REST API, this call will + * return the result provided by this plugin. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param uri The URI to delete in the built-in Orthanc API. + * @return 0 if success, other value if error. + **/ + ORTHANC_PLUGIN_INLINE int OrthancPluginRestApiDeleteAfterPlugins( + OrthancPluginContext* context, + const char* uri) + { + return context->InvokeService(context, _OrthancPluginService_RestApiDeleteAfterPlugins, uri); + } + + /** * @brief Make a PUT call to the built-in Orthanc REST API. @@ -977,6 +1060,39 @@ + /** + * @brief Make a PUT call to the REST API, as tainted by the plugins. + * + * Make a PUT call to the Orthanc REST API, after all the plugins + * are applied. In other words, if some plugin overrides or adds the + * called URI to the built-in Orthanc REST API, this call will + * return the result provided by this plugin. The result to the + * query is stored into a newly allocated memory buffer. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param target The target memory buffer. + * @param uri The URI in the built-in Orthanc API. + * @param body The body of the PUT request. + * @param bodySize The size of the body. + * @return 0 if success, other value if error. + **/ + ORTHANC_PLUGIN_INLINE int OrthancPluginRestApiPutAfterPlugins( + OrthancPluginContext* context, + OrthancPluginMemoryBuffer* target, + const char* uri, + const char* body, + uint32_t bodySize) + { + _OrthancPluginRestApiPostPut params; + params.target = target; + params.uri = uri; + params.body = body; + params.bodySize = bodySize; + return context->InvokeService(context, _OrthancPluginService_RestApiPutAfterPlugins, ¶ms); + } + + + typedef struct { OrthancPluginRestOutput* output;
--- a/Plugins/Samples/Basic/Plugin.c Tue Feb 03 10:53:35 2015 +0100 +++ b/Plugins/Samples/Basic/Plugin.c Tue Feb 03 11:52:21 2015 +0100 @@ -165,6 +165,58 @@ } +ORTHANC_PLUGINS_API int32_t Callback5(OrthancPluginRestOutput* output, + const char* url, + const OrthancPluginHttpRequest* request) +{ + /** + * Demonstration the difference between the + * "OrthancPluginRestApiXXX()" and the + * "OrthancPluginRestApiXXXAfterPlugins()" mechanisms to forward + * REST calls. + * + * # curl http://localhost:8042/forward/built-in/system + * # curl http://localhost:8042/forward/plugins/system + * # curl http://localhost:8042/forward/built-in/plugin/image + * => FAILURE (because the "/plugin/image" URI is implemented by this plugin) + * # curl http://localhost:8042/forward/plugins/plugin/image => SUCCESS + **/ + + OrthancPluginMemoryBuffer tmp; + int isBuiltIn, error; + + if (request->method != OrthancPluginHttpMethod_Get) + { + OrthancPluginSendMethodNotAllowed(context, output, "GET"); + return 0; + } + + isBuiltIn = strcmp("plugins", request->groups[0]); + + if (isBuiltIn) + { + error = OrthancPluginRestApiGet(context, &tmp, request->groups[1]); + } + else + { + printf("ICI1\n"); + error = OrthancPluginRestApiGetAfterPlugins(context, &tmp, request->groups[1]); + printf("ICI2\n"); + } + + if (error) + { + return -1; + } + else + { + OrthancPluginAnswerBuffer(context, output, tmp.data, tmp.size, "application/octet-stream"); + OrthancPluginFreeMemoryBuffer(context, &tmp); + return 0; + } +} + + ORTHANC_PLUGINS_API int32_t CallbackCreateDicom(OrthancPluginRestOutput* output, const char* url, const OrthancPluginHttpRequest* request) @@ -331,6 +383,8 @@ OrthancPluginRegisterRestCallback(context, "/plu.*/image", Callback2); OrthancPluginRegisterRestCallback(context, "/plugin/instances/([^/]+)/info", Callback3); OrthancPluginRegisterRestCallback(context, "/instances/([^/]+)/preview", Callback4); + OrthancPluginRegisterRestCallback(context, "/forward/(built-in)(/.+)", Callback5); + OrthancPluginRegisterRestCallback(context, "/forward/(plugins)(/.+)", Callback5); OrthancPluginRegisterRestCallback(context, "/plugin/create", CallbackCreateDicom); OrthancPluginRegisterOnStoredInstanceCallback(context, OnStoredCallback);