comparison Framework/Toolbox/OrthancAsynchronousWebService.cpp @ 70:f73aed014bde wasm

OrthancAsynchronousWebService
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 22 May 2017 21:43:49 +0200
parents
children 0aef120d7e1c
comparison
equal deleted inserted replaced
69:1553b67b24e5 70:f73aed014bde
1 /**
2 * Stone of Orthanc
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4 * Department, University Hospital of Liege, Belgium
5 * Copyright (C) 2017 Osimis, Belgium
6 *
7 * This program is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU Affero General Public License
9 * as published by the Free Software Foundation, either version 3 of
10 * the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 **/
20
21
22 #include "OrthancAsynchronousWebService.h"
23
24 #include "../../Resources/Orthanc/Core/Logging.h"
25 #include "../../Resources/Orthanc/Core/OrthancException.h"
26 #include "../../Resources/Orthanc/Core/MultiThreading/SharedMessageQueue.h"
27 #include "../../Resources/Orthanc/Plugins/Samples/Common/OrthancHttpConnection.h"
28
29 namespace OrthancStone
30 {
31 class OrthancAsynchronousWebService::PendingRequest : public Orthanc::IDynamicObject
32 {
33 private:
34 bool isPost_;
35 ICallback& callback_;
36 std::string uri_;
37 std::string body_;
38 std::auto_ptr<Orthanc::IDynamicObject> payload_;
39
40 PendingRequest(bool isPost,
41 ICallback& callback,
42 const std::string& uri,
43 const std::string& body,
44 Orthanc::IDynamicObject* payload) :
45 isPost_(isPost),
46 callback_(callback),
47 uri_(uri),
48 body_(body),
49 payload_(payload)
50 {
51 }
52
53 public:
54 static PendingRequest* CreateGetRequest(ICallback& callback,
55 const std::string& uri,
56 Orthanc::IDynamicObject* payload)
57 {
58 return new PendingRequest(false, callback, uri, "", payload);
59 }
60
61 static PendingRequest* CreatePostRequest(ICallback& callback,
62 const std::string& uri,
63 const std::string& body,
64 Orthanc::IDynamicObject* payload)
65 {
66 return new PendingRequest(true, callback, uri, body, payload);
67 }
68
69 void Execute(OrthancPlugins::OrthancHttpConnection& connection)
70 {
71 if (payload_.get() == NULL)
72 {
73 // Execute() has already been invoked
74 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
75 }
76
77 std::string answer;
78
79 try
80 {
81 if (isPost_)
82 {
83 connection.RestApiPost(answer, uri_, body_);
84 }
85 else
86 {
87 connection.RestApiGet(answer, uri_);
88 }
89 }
90 catch (Orthanc::OrthancException&)
91 {
92 callback_.NotifyError(uri_, payload_.release());
93 }
94
95 callback_.NotifySuccess(uri_, answer.c_str(), answer.size(), payload_.release());
96 }
97 };
98
99 class OrthancAsynchronousWebService::PImpl : public boost::noncopyable
100 {
101 private:
102 enum State
103 {
104 State_Init,
105 State_Started,
106 State_Stopped
107 };
108
109 boost::mutex mutex_;
110 State state_;
111 Orthanc::WebServiceParameters orthanc_;
112 std::vector<boost::thread> threads_;
113 Orthanc::SharedMessageQueue queue_;
114
115 static void Worker(PImpl* that)
116 {
117 OrthancPlugins::OrthancHttpConnection connection(that->orthanc_);
118
119 for (;;)
120 {
121 State state;
122
123 {
124 boost::mutex::scoped_lock lock(that->mutex_);
125 state = that->state_;
126 }
127
128 printf("."); fflush(stdout);
129 if (state == State_Stopped)
130 {
131 break;
132 }
133
134 std::auto_ptr<Orthanc::IDynamicObject> request(that->queue_.Dequeue(100));
135 if (request.get() != NULL)
136 {
137 dynamic_cast<PendingRequest&>(*request).Execute(connection);
138 }
139 }
140 }
141
142 public:
143 PImpl(const Orthanc::WebServiceParameters& orthanc,
144 unsigned int threadCount) :
145 state_(State_Init),
146 orthanc_(orthanc),
147 threads_(threadCount)
148 {
149 }
150
151 ~PImpl()
152 {
153 if (state_ == State_Started)
154 {
155 LOG(ERROR) << "You should have manually called OrthancAsynchronousWebService::Stop()";
156 Stop();
157 }
158 }
159
160 void Schedule(PendingRequest* request)
161 {
162 std::auto_ptr<PendingRequest> protection(request);
163
164 if (request == NULL)
165 {
166 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
167 }
168
169 boost::mutex::scoped_lock lock(mutex_);
170
171 switch (state_)
172 {
173 case State_Init:
174 LOG(ERROR) << "You must call OrthancAsynchronousWebService::Start()";
175 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
176
177 case State_Started:
178 queue_.Enqueue(protection.release());
179 break;
180
181 case State_Stopped:
182 LOG(ERROR) << "Cannot schedule a Web request after having "
183 << "called OrthancAsynchronousWebService::Stop()";
184 break;
185
186 default:
187 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
188 }
189
190 }
191
192 void Start()
193 {
194 boost::mutex::scoped_lock lock(mutex_);
195
196 if (state_ != State_Init)
197 {
198 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
199 }
200
201 for (size_t i = 0; i < threads_.size(); i++)
202 {
203 threads_[i] = boost::thread(Worker, this);
204 }
205
206 state_ = State_Started;
207 }
208
209 void Stop()
210 {
211 {
212 boost::mutex::scoped_lock lock(mutex_);
213
214 if (state_ != State_Started)
215 {
216 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
217 }
218
219 state_ = State_Stopped;
220 }
221
222 for (size_t i = 0; i < threads_.size(); i++)
223 {
224 if (threads_[i].joinable())
225 {
226 threads_[i].join();
227 }
228 }
229 }
230 };
231
232 OrthancAsynchronousWebService::OrthancAsynchronousWebService(
233 const Orthanc::WebServiceParameters& parameters,
234 unsigned int threadCount) :
235 pimpl_(new PImpl(parameters, threadCount))
236 {
237 }
238
239 void OrthancAsynchronousWebService::ScheduleGetRequest(ICallback& callback,
240 const std::string& uri,
241 Orthanc::IDynamicObject* payload)
242 {
243 pimpl_->Schedule(PendingRequest::CreateGetRequest(callback, uri, payload));
244 }
245
246
247 void OrthancAsynchronousWebService::SchedulePostRequest(ICallback& callback,
248 const std::string& uri,
249 const std::string& body,
250 Orthanc::IDynamicObject* payload)
251 {
252 pimpl_->Schedule(PendingRequest::CreatePostRequest(callback, uri, body, payload));
253 }
254
255 void OrthancAsynchronousWebService::Start()
256 {
257 pimpl_->Start();
258 }
259
260 void OrthancAsynchronousWebService::Stop()
261 {
262 pimpl_->Stop();
263 }
264 }