changeset 309:14ef1227120f am-callable-and-promise

web services: better handling of failures
author am@osimis.io
date Fri, 28 Sep 2018 15:02:43 +0200
parents be2660b6e40a
children 3b29c9c77d9b
files Framework/Toolbox/IWebService.h Framework/Toolbox/MessagingToolbox.cpp Framework/Toolbox/MessagingToolbox.h Framework/Toolbox/OrthancApiClient.cpp Framework/Toolbox/OrthancApiClient.h Framework/Widgets/LayerWidget.cpp Platforms/Generic/Oracle.cpp Platforms/Generic/OracleWebService.h Platforms/Generic/WebServiceCommandBase.cpp Platforms/Generic/WebServiceCommandBase.h Platforms/Generic/WebServiceGetCommand.cpp Platforms/Generic/WebServiceGetCommand.h Platforms/Generic/WebServicePostCommand.cpp Platforms/Generic/WebServicePostCommand.h Platforms/Wasm/Defaults.cpp Platforms/Wasm/WasmWebService.cpp Platforms/Wasm/WasmWebService.h Platforms/Wasm/WasmWebService.js
diffstat 18 files changed, 101 insertions(+), 40 deletions(-) [+]
line wrap: on
line diff
--- a/Framework/Toolbox/IWebService.h	Tue Sep 25 15:14:53 2018 +0200
+++ b/Framework/Toolbox/IWebService.h	Fri Sep 28 15:02:43 2018 +0200
@@ -49,9 +49,9 @@
       size_t answerSize_;
       Orthanc::IDynamicObject* payload_;
       HttpRequestSuccessMessage(const std::string& uri,
-                                   const void* answer,
-                                   size_t answerSize,
-                                   Orthanc::IDynamicObject* payload)
+                                const void* answer,
+                                size_t answerSize,
+                                Orthanc::IDynamicObject* payload)
         : BaseMessage(),
           uri_(uri),
           answer_(answer),
@@ -65,7 +65,7 @@
       const std::string& uri_;
       Orthanc::IDynamicObject* payload_;
       HttpRequestErrorMessage(const std::string& uri,
-                                 Orthanc::IDynamicObject* payload)
+                              Orthanc::IDynamicObject* payload)
         : BaseMessage(),
           uri_(uri),
           payload_(payload)
@@ -86,14 +86,16 @@
                           const Headers& headers,
                           Orthanc::IDynamicObject* payload,
                           MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,
-                          MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback) = 0;
+                          MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
+                          unsigned int timeoutInSeconds = 60) = 0;
 
     virtual void PostAsync(const std::string& uri,
                            const Headers& headers,
                            const std::string& body,
                            Orthanc::IDynamicObject* payload,
                            MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,
-                           MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback) = 0;
+                           MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
+                           unsigned int timeoutInSeconds = 60) = 0;
 
   };
 }
--- a/Framework/Toolbox/MessagingToolbox.cpp	Tue Sep 25 15:14:53 2018 +0200
+++ b/Framework/Toolbox/MessagingToolbox.cpp	Fri Sep 28 15:02:43 2018 +0200
@@ -31,6 +31,7 @@
 
 #include <boost/lexical_cast.hpp>
 #include <json/reader.h>
+#include <json/writer.h>
 
 namespace OrthancStone
 {
@@ -114,6 +115,12 @@
                           target);
     }
 
+    void JsonToString(std::string& target,
+                      const Json::Value& source)
+    {
+      Json::FastWriter writer;
+      target = writer.write(source);
+    }
 
     static void ParseJsonException(Json::Value& target,
                                    const std::string& source)
--- a/Framework/Toolbox/MessagingToolbox.h	Tue Sep 25 15:14:53 2018 +0200
+++ b/Framework/Toolbox/MessagingToolbox.h	Fri Sep 28 15:02:43 2018 +0200
@@ -13,7 +13,7 @@
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Affero General Public License for more details.
- * 
+ *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program. If not, see <http://www.gnu.org/licenses/>.
  **/
@@ -38,6 +38,10 @@
                    const void* content,
                    size_t size);
 
+    void JsonToString(std::string& target,
+                      const Json::Value& source);
+
+
     void RestApiGet(Json::Value& target,
                     OrthancPlugins::IOrthancConnection& orthanc,
                     const std::string& uri);
--- a/Framework/Toolbox/OrthancApiClient.cpp	Tue Sep 25 15:14:53 2018 +0200
+++ b/Framework/Toolbox/OrthancApiClient.cpp	Fri Sep 28 15:02:43 2018 +0200
@@ -22,6 +22,7 @@
 
 #include "MessagingToolbox.h"
 #include <Core/OrthancException.h>
+#include "Framework/Toolbox/MessagingToolbox.h"
 
 namespace OrthancStone {
 
@@ -156,5 +157,16 @@
 
   }
 
+  void OrthancApiClient::PostJsonAsyncExpectJson(const std::string& uri,
+                                                 const Json::Value& data,
+                                                 MessageHandler<JsonResponseReadyMessage>* successCallback,
+                                                 MessageHandler<HttpErrorMessage>* failureCallback,
+                                                 Orthanc::IDynamicObject* payload)
+  {
+    std::string body;
+    MessagingToolbox::JsonToString(body, data);
+    return PostBinaryAsyncExpectJson(uri, body, successCallback, failureCallback, payload);
+  }
+
 
 }
--- a/Framework/Toolbox/OrthancApiClient.h	Tue Sep 25 15:14:53 2018 +0200
+++ b/Framework/Toolbox/OrthancApiClient.h	Fri Sep 28 15:02:43 2018 +0200
@@ -135,6 +135,12 @@
                                    MessageHandler<HttpErrorMessage>* failureCallback = NULL,
                                    Orthanc::IDynamicObject* payload = NULL);
 
+    // schedule a POST request expecting a JSON response.
+    void PostJsonAsyncExpectJson(const std::string& uri,
+                                 const Json::Value& data,
+                                 MessageHandler<JsonResponseReadyMessage>* successCallback,
+                                 MessageHandler<HttpErrorMessage>* failureCallback = NULL,
+                                 Orthanc::IDynamicObject* payload = NULL);
 
   };
 }
--- a/Framework/Widgets/LayerWidget.cpp	Tue Sep 25 15:14:53 2018 +0200
+++ b/Framework/Widgets/LayerWidget.cpp	Fri Sep 28 15:02:43 2018 +0200
@@ -548,6 +548,7 @@
     {
       InvalidateLayer(index);
     }
+    EmitMessage(LayerWidget::ContentChangedMessage(*this));
   }
   
 
@@ -561,6 +562,7 @@
         InvalidateLayer(index);
       }
     }
+    EmitMessage(LayerWidget::ContentChangedMessage(*this));
   }
   
   
@@ -588,6 +590,7 @@
         //UpdateLayer(index, new SliceOutlineRenderer(slice), slice);
       }
     }
+    EmitMessage(LayerWidget::ContentChangedMessage(*this));
   }
 
 
--- a/Platforms/Generic/Oracle.cpp	Tue Sep 25 15:14:53 2018 +0200
+++ b/Platforms/Generic/Oracle.cpp	Fri Sep 28 15:02:43 2018 +0200
@@ -66,7 +66,15 @@
         if (item.get() != NULL)
         {
           IOracleCommand& command = dynamic_cast<IOracleCommand&>(*item);
-          command.Execute();
+          try
+          {
+            command.Execute();
+          }
+          catch (Orthanc::OrthancException& ex)
+          {
+            // this is probably a curl error that has been triggered.  We may just ignore it.
+            // The command.success_ will stay at false and this will be handled in the command.Commit
+          }
 
           // Random sleeping to test
           //boost::this_thread::sleep(boost::posix_time::milliseconds(50 * (1 + rand() % 10)));
--- a/Platforms/Generic/OracleWebService.h	Tue Sep 25 15:14:53 2018 +0200
+++ b/Platforms/Generic/OracleWebService.h	Fri Sep 28 15:02:43 2018 +0200
@@ -13,7 +13,7 @@
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Affero General Public License for more details.
- * 
+ *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program. If not, see <http://www.gnu.org/licenses/>.
  **/
@@ -55,9 +55,10 @@
                           const Headers& headers,
                           Orthanc::IDynamicObject* payload, // takes ownership
                           MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,   // takes ownership
-                          MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL)// takes ownership
+                          MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,// takes ownership
+                          unsigned int timeoutInSeconds = 60)
     {
-      oracle_.Submit(new WebServiceGetCommand(broker_, successCallback, failureCallback, parameters_, uri, headers, payload, context_));
+      oracle_.Submit(new WebServiceGetCommand(broker_, successCallback, failureCallback, parameters_, uri, headers, timeoutInSeconds, payload, context_));
     }
 
     virtual void PostAsync(const std::string& uri,
@@ -65,19 +66,20 @@
                            const std::string& body,
                            Orthanc::IDynamicObject* payload, // takes ownership
                            MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback, // takes ownership
-                           MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback) // takes ownership
+                           MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL, // takes ownership
+                           unsigned int timeoutInSeconds = 60)
     {
-      oracle_.Submit(new WebServicePostCommand(broker_, successCallback, failureCallback, parameters_, uri, headers, body, payload, context_));
+      oracle_.Submit(new WebServicePostCommand(broker_, successCallback, failureCallback, parameters_, uri, headers, timeoutInSeconds, body, payload, context_));
     }
 
     void Start()
     {
-        oracle_.Start();
+      oracle_.Start();
     }
 
     void Stop()
     {
-        oracle_.Stop();
+      oracle_.Stop();
     }
   };
 }
--- a/Platforms/Generic/WebServiceCommandBase.cpp	Tue Sep 25 15:14:53 2018 +0200
+++ b/Platforms/Generic/WebServiceCommandBase.cpp	Fri Sep 28 15:02:43 2018 +0200
@@ -31,6 +31,7 @@
                                                const Orthanc::WebServiceParameters& parameters,
                                                const std::string& uri,
                                                const IWebService::Headers& headers,
+                                               unsigned int timeoutInSeconds,
                                                Orthanc::IDynamicObject* payload /* takes ownership */,
                                                NativeStoneApplicationContext& context) :
     IObservable(broker),
@@ -40,7 +41,8 @@
     uri_(uri),
     headers_(headers),
     payload_(payload),
-    context_(context)
+    context_(context),
+    timeoutInSeconds_(timeoutInSeconds)
   {
   }
 
@@ -55,7 +57,7 @@
     }
     else if (!success_ && failureCallback_.get() != NULL)
     {
-      successCallback_->Apply(IWebService::HttpRequestErrorMessage(uri_, payload_.release()));
+      failureCallback_->Apply(IWebService::HttpRequestErrorMessage(uri_, payload_.release()));
     }
 
   }
--- a/Platforms/Generic/WebServiceCommandBase.h	Tue Sep 25 15:14:53 2018 +0200
+++ b/Platforms/Generic/WebServiceCommandBase.h	Fri Sep 28 15:02:43 2018 +0200
@@ -46,6 +46,7 @@
     bool                                    success_;
     std::string                             answer_;
     NativeStoneApplicationContext&          context_;
+    unsigned int                            timeoutInSeconds_;
 
   public:
     WebServiceCommandBase(MessageBroker& broker,
@@ -54,8 +55,10 @@
                           const Orthanc::WebServiceParameters& parameters,
                           const std::string& uri,
                           const std::map<std::string, std::string>& headers,
+                          unsigned int timeoutInSeconds,
                           Orthanc::IDynamicObject* payload /* takes ownership */,
-                          NativeStoneApplicationContext& context);
+                          NativeStoneApplicationContext& context
+                          );
 
     virtual void Execute() = 0;
 
--- a/Platforms/Generic/WebServiceGetCommand.cpp	Tue Sep 25 15:14:53 2018 +0200
+++ b/Platforms/Generic/WebServiceGetCommand.cpp	Fri Sep 28 15:02:43 2018 +0200
@@ -32,9 +32,10 @@
                                              const Orthanc::WebServiceParameters& parameters,
                                              const std::string& uri,
                                              const IWebService::Headers& headers,
+                                             unsigned int timeoutInSeconds,
                                              Orthanc::IDynamicObject* payload /* takes ownership */,
                                              NativeStoneApplicationContext& context) :
-    WebServiceCommandBase(broker, successCallback, failureCallback, parameters, uri, headers, payload, context)
+    WebServiceCommandBase(broker, successCallback, failureCallback, parameters, uri, headers, timeoutInSeconds, payload, context)
   {
   }
 
@@ -42,7 +43,7 @@
   void WebServiceGetCommand::Execute()
   {
     Orthanc::HttpClient client(parameters_, uri_);
-    client.SetTimeout(60);
+    client.SetTimeout(timeoutInSeconds_);
     client.SetMethod(Orthanc::HttpMethod_Get);
 
     for (IWebService::Headers::const_iterator it = headers_.begin(); it != headers_.end(); it++ )
--- a/Platforms/Generic/WebServiceGetCommand.h	Tue Sep 25 15:14:53 2018 +0200
+++ b/Platforms/Generic/WebServiceGetCommand.h	Fri Sep 28 15:02:43 2018 +0200
@@ -34,6 +34,7 @@
                          const Orthanc::WebServiceParameters& parameters,
                          const std::string& uri,
                          const IWebService::Headers& headers,
+                         unsigned int timeoutInSeconds,
                          Orthanc::IDynamicObject* payload /* takes ownership */,
                          NativeStoneApplicationContext& context);
 
--- a/Platforms/Generic/WebServicePostCommand.cpp	Tue Sep 25 15:14:53 2018 +0200
+++ b/Platforms/Generic/WebServicePostCommand.cpp	Fri Sep 28 15:02:43 2018 +0200
@@ -31,10 +31,11 @@
                                                const Orthanc::WebServiceParameters& parameters,
                                                const std::string& uri,
                                                const IWebService::Headers& headers,
+                                               unsigned int timeoutInSeconds,
                                                const std::string& body,
                                                Orthanc::IDynamicObject* payload /* takes ownership */,
                                                NativeStoneApplicationContext& context) :
-    WebServiceCommandBase(broker, successCallback, failureCallback, parameters, uri, headers, payload, context),
+    WebServiceCommandBase(broker, successCallback, failureCallback, parameters, uri, headers, timeoutInSeconds, payload, context),
     body_(body)
   {
   }
@@ -42,7 +43,7 @@
   void WebServicePostCommand::Execute()
   {
     Orthanc::HttpClient client(parameters_, uri_);
-    client.SetTimeout(60);
+    client.SetTimeout(timeoutInSeconds_);
     client.SetMethod(Orthanc::HttpMethod_Post);
     client.GetBody().swap(body_);
 
--- a/Platforms/Generic/WebServicePostCommand.h	Tue Sep 25 15:14:53 2018 +0200
+++ b/Platforms/Generic/WebServicePostCommand.h	Fri Sep 28 15:02:43 2018 +0200
@@ -37,6 +37,7 @@
                           const Orthanc::WebServiceParameters& parameters,
                           const std::string& uri,
                           const IWebService::Headers& headers,
+                          unsigned int timeoutInSeconds,
                           const std::string& body,
                           Orthanc::IDynamicObject* payload /* takes ownership */,
                           NativeStoneApplicationContext& context);
--- a/Platforms/Wasm/Defaults.cpp	Tue Sep 25 15:14:53 2018 +0200
+++ b/Platforms/Wasm/Defaults.cpp	Fri Sep 28 15:02:43 2018 +0200
@@ -262,7 +262,7 @@
     static std::string output; // we don't want the string to be deallocated when we return to JS code so we always use the same string (this is fine since JS is single-thread)
 
     printf("SendMessageToStoneApplication\n");
-    printf(message);
+    printf("%s", message);
 
     if (applicationWasmAdapter.get() != NULL) {
       printf("sending message to C++\n");
--- a/Platforms/Wasm/WasmWebService.cpp	Tue Sep 25 15:14:53 2018 +0200
+++ b/Platforms/Wasm/WasmWebService.cpp	Fri Sep 28 15:02:43 2018 +0200
@@ -11,7 +11,8 @@
                                       void* callableFailure,
                                       const char* uri,
                                       const char* headersInJsonString,
-                                      void* payload);
+                                      void* payload,
+                                      unsigned int timeoutInSeconds);
 
   extern void WasmWebService_PostAsync(void* callableSuccess,
                                        void* callableFailure,
@@ -19,7 +20,8 @@
                                        const char* headersInJsonString,
                                        const void* body,
                                        size_t bodySize,
-                                       void* payload);
+                                       void* payload,
+                                       unsigned int timeoutInSeconds);
 
 
   void EMSCRIPTEN_KEEPALIVE WasmWebService_NotifyError(void* failureCallable,
@@ -100,29 +102,31 @@
   }
 
   void WasmWebService::PostAsync(const std::string& relativeUri,
-                           const Headers& headers,
-                           const std::string& body,
-                           Orthanc::IDynamicObject* payload,
-                           MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallable,
-                           MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallable)
+                                 const Headers& headers,
+                                 const std::string& body,
+                                 Orthanc::IDynamicObject* payload,
+                                 MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallable,
+                                 MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallable,
+                                 unsigned int timeoutInSeconds)
   {
     std::string uri = baseUri_ + relativeUri;
     std::string headersInJsonString;
     ToJsonString(headersInJsonString, headers);
     WasmWebService_PostAsync(successCallable, failureCallable, uri.c_str(), headersInJsonString.c_str(),
-                                       body.c_str(), body.size(), payload);
+                                       body.c_str(), body.size(), payload, timeoutInSeconds);
   }
 
    void WasmWebService::GetAsync(const std::string& relativeUri,
-                          const Headers& headers,
-                          Orthanc::IDynamicObject* payload,
-                          MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallable,
-                          MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallable)
+                                 const Headers& headers,
+                                 Orthanc::IDynamicObject* payload,
+                                 MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallable,
+                                 MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallable,
+                                 unsigned int timeoutInSeconds)
   {
     std::string uri = baseUri_ + relativeUri;
     std::string headersInJsonString;
     ToJsonString(headersInJsonString, headers);
-    WasmWebService_GetAsync(successCallable, failureCallable, uri.c_str(), headersInJsonString.c_str(), payload);
+    WasmWebService_GetAsync(successCallable, failureCallable, uri.c_str(), headersInJsonString.c_str(), payload, timeoutInSeconds);
   }
 
 }
--- a/Platforms/Wasm/WasmWebService.h	Tue Sep 25 15:14:53 2018 +0200
+++ b/Platforms/Wasm/WasmWebService.h	Fri Sep 28 15:02:43 2018 +0200
@@ -41,14 +41,16 @@
                           const Headers& headers,
                           Orthanc::IDynamicObject* payload,
                           MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallable,
-                          MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallable);
+                          MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallable = NULL,
+                          unsigned int timeoutInSeconds = 60);
 
     virtual void PostAsync(const std::string& uri,
                            const Headers& headers,
                            const std::string& body,
                            Orthanc::IDynamicObject* payload,
                            MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallable,
-                           MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallable);
+                           MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallable = NULL,
+                           unsigned int timeoutInSeconds = 60);
 
     virtual void Start()
     {
--- a/Platforms/Wasm/WasmWebService.js	Tue Sep 25 15:14:53 2018 +0200
+++ b/Platforms/Wasm/WasmWebService.js	Fri Sep 28 15:02:43 2018 +0200
@@ -1,5 +1,5 @@
 mergeInto(LibraryManager.library, {
-  WasmWebService_GetAsync: function(callableSuccess, callableFailure, url, headersInJsonString, payload) {
+  WasmWebService_GetAsync: function(callableSuccess, callableFailure, url, headersInJsonString, payload, timeoutInSeconds) {
     // Directly use XMLHttpRequest (no jQuery) to retrieve the raw binary data
     // http://www.henryalgus.com/reading-binary-files-using-jquery-ajax/
     var xhr = new XMLHttpRequest();
@@ -8,6 +8,7 @@
 
     xhr.open('GET', url_, true);
     xhr.responseType = 'arraybuffer';
+    xhr.timeout = timeoutInSeconds * 1000;
     var headers = JSON.parse(headersInJsonString_);
     for (var key in headers) {
       xhr.setRequestHeader(key, headers[key]);
@@ -30,11 +31,12 @@
     xhr.send();
   },
 
-  WasmWebService_PostAsync: function(callableSuccess, callableFailure, url, headersInJsonString, body, bodySize, payload) {
+  WasmWebService_PostAsync: function(callableSuccess, callableFailure, url, headersInJsonString, body, bodySize, payload, timeoutInSeconds) {
     var xhr = new XMLHttpRequest();
     var url_ = UTF8ToString(url);
     var headersInJsonString_ = UTF8ToString(headersInJsonString);
     xhr.open('POST', url_, true);
+    xhr.timeout = timeoutInSeconds * 1000;
     xhr.responseType = 'arraybuffer';
     xhr.setRequestHeader('Content-type', 'application/octet-stream');