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, &params);
+  }
+
+
+
   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, &params);
+  }
+
+
 
   /**
    * @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, &params);
+  }
+
+
+
   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);