Mercurial > hg > orthanc-webviewer
annotate Plugin/Plugin.cpp @ 75:e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Wed, 23 Sep 2015 13:33:07 +0200 |
parents | ab5218c18b7a |
children | abdde1dfb3eb |
rev | line source |
---|---|
0 | 1 /** |
2 * Orthanc - A Lightweight, RESTful DICOM Store | |
3 * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics | |
4 * Department, University Hospital of Liege, Belgium | |
5 * | |
6 * This program is free software: you can redistribute it and/or | |
7 * modify it under the terms of the GNU Affero General Public License | |
8 * as published by the Free Software Foundation, either version 3 of | |
9 * the License, or (at your option) any later version. | |
10 * | |
11 * This program is distributed in the hope that it will be useful, but | |
12 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 * Affero General Public License for more details. | |
15 * | |
16 * You should have received a copy of the GNU Affero General Public License | |
17 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
18 **/ | |
19 | |
20 | |
21 #include <boost/thread.hpp> | |
22 #include <boost/lexical_cast.hpp> | |
23 #include <EmbeddedResources.h> | |
4
ecefd45026bf
configuration of the cache
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
24 #include <boost/filesystem.hpp> |
0 | 25 |
26 | 26 #include "../Orthanc/Core/OrthancException.h" |
0 | 27 #include "ViewerToolbox.h" |
28 #include "ViewerPrefetchPolicy.h" | |
29 #include "DecodedImageAdapter.h" | |
30 #include "InstanceInformationAdapter.h" | |
31 #include "SeriesInformationAdapter.h" | |
32 | |
33 | |
74
ab5218c18b7a
compatibility with forthcoming sdk 0.9.5
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
58
diff
changeset
|
34 #if (ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER >= 9 && ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER >= 5) |
ab5218c18b7a
compatibility with forthcoming sdk 0.9.5
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
58
diff
changeset
|
35 # define RETURN_TYPE OrthancPluginErrorCode |
ab5218c18b7a
compatibility with forthcoming sdk 0.9.5
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
58
diff
changeset
|
36 # define RETURN_SUCCESS OrthancPluginErrorCode_Success |
ab5218c18b7a
compatibility with forthcoming sdk 0.9.5
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
58
diff
changeset
|
37 # define RETURN_FAILURE OrthancPluginErrorCode_Plugin |
ab5218c18b7a
compatibility with forthcoming sdk 0.9.5
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
58
diff
changeset
|
38 #else |
ab5218c18b7a
compatibility with forthcoming sdk 0.9.5
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
58
diff
changeset
|
39 # define RETURN_TYPE int32_t |
ab5218c18b7a
compatibility with forthcoming sdk 0.9.5
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
58
diff
changeset
|
40 # define RETURN_SUCCESS 0 |
ab5218c18b7a
compatibility with forthcoming sdk 0.9.5
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
58
diff
changeset
|
41 # define RETURN_FAILURE -1 |
ab5218c18b7a
compatibility with forthcoming sdk 0.9.5
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
58
diff
changeset
|
42 #endif |
ab5218c18b7a
compatibility with forthcoming sdk 0.9.5
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
58
diff
changeset
|
43 |
0 | 44 |
75
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
45 static OrthancPluginContext* context_ = NULL; |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
46 |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
47 |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
48 |
0 | 49 class CacheContext |
50 { | |
51 private: | |
75
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
52 class DynamicString : public Orthanc::IDynamicObject |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
53 { |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
54 private: |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
55 std::string value_; |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
56 |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
57 public: |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
58 DynamicString(const char* value) : value_(value) |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
59 { |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
60 } |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
61 |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
62 const std::string& GetValue() const |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
63 { |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
64 return value_; |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
65 } |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
66 }; |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
67 |
58 | 68 Orthanc::FilesystemStorage storage_; |
69 Orthanc::SQLite::Connection db_; | |
70 | |
0 | 71 std::auto_ptr<OrthancPlugins::CacheManager> cache_; |
72 std::auto_ptr<OrthancPlugins::CacheScheduler> scheduler_; | |
73 | |
75
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
74 Orthanc::SharedMessageQueue newInstances_; |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
75 bool stop_; |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
76 boost::thread newInstancesThread_; |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
77 |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
78 |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
79 static void NewInstancesThread(CacheContext* cache) |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
80 { |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
81 while (!cache->stop_) |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
82 { |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
83 std::auto_ptr<Orthanc::IDynamicObject> obj(cache->newInstances_.Dequeue(100)); |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
84 if (obj.get() != NULL) |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
85 { |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
86 const std::string& instanceId = dynamic_cast<DynamicString&>(*obj).GetValue(); |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
87 printf("[%s]\n", instanceId.c_str()); |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
88 |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
89 // On the reception of a new instance, precompute its spatial position |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
90 cache->GetScheduler().Prefetch(OrthancPlugins::CacheBundle_InstanceInformation, instanceId); |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
91 |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
92 // Indalidate the parent series of the instance |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
93 std::string uri = "/instances/" + std::string(instanceId); |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
94 Json::Value instance; |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
95 if (OrthancPlugins::GetJsonFromOrthanc(instance, context_, uri)) |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
96 { |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
97 std::string seriesId = instance["ParentSeries"].asString(); |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
98 cache->GetScheduler().Invalidate(OrthancPlugins::CacheBundle_SeriesInformation, seriesId); |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
99 } |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
100 } |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
101 } |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
102 } |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
103 |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
104 |
0 | 105 public: |
75
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
106 CacheContext(const std::string& path) : storage_(path), stop_(false) |
0 | 107 { |
108 boost::filesystem::path p(path); | |
58 | 109 db_.Open((p / "cache.db").string()); |
0 | 110 |
58 | 111 cache_.reset(new OrthancPlugins::CacheManager(db_, storage_)); |
0 | 112 //cache_->SetSanityCheckEnabled(true); // For debug |
113 | |
114 scheduler_.reset(new OrthancPlugins::CacheScheduler(*cache_, 100)); | |
75
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
115 |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
116 newInstancesThread_ = boost::thread(NewInstancesThread, this); |
0 | 117 } |
118 | |
58 | 119 ~CacheContext() |
120 { | |
75
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
121 stop_ = true; |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
122 if (newInstancesThread_.joinable()) |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
123 { |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
124 newInstancesThread_.join(); |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
125 } |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
126 |
58 | 127 scheduler_.reset(NULL); |
128 cache_.reset(NULL); | |
129 } | |
130 | |
0 | 131 OrthancPlugins::CacheScheduler& GetScheduler() |
132 { | |
133 return *scheduler_; | |
134 } | |
75
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
135 |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
136 void SignalNewInstance(const char* instanceId) |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
137 { |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
138 newInstances_.Enqueue(new DynamicString(instanceId)); |
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
139 } |
0 | 140 }; |
141 | |
142 | |
75
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
143 |
0 | 144 static CacheContext* cache_ = NULL; |
145 | |
146 | |
147 | |
74
ab5218c18b7a
compatibility with forthcoming sdk 0.9.5
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
58
diff
changeset
|
148 static RETURN_TYPE OnChangeCallback(OrthancPluginChangeType changeType, |
ab5218c18b7a
compatibility with forthcoming sdk 0.9.5
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
58
diff
changeset
|
149 OrthancPluginResourceType resourceType, |
ab5218c18b7a
compatibility with forthcoming sdk 0.9.5
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
58
diff
changeset
|
150 const char* resourceId) |
0 | 151 { |
152 try | |
153 { | |
154 if (changeType == OrthancPluginChangeType_NewInstance && | |
155 resourceType == OrthancPluginResourceType_Instance) | |
156 { | |
75
e15a59a4b4d4
Fix possible deadlock with other plugins in OnChangeCallback()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
74
diff
changeset
|
157 cache_->SignalNewInstance(resourceId); |
0 | 158 } |
159 | |
74
ab5218c18b7a
compatibility with forthcoming sdk 0.9.5
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
58
diff
changeset
|
160 return RETURN_SUCCESS; |
0 | 161 } |
162 catch (std::runtime_error& e) | |
163 { | |
164 OrthancPluginLogError(context_, e.what()); | |
74
ab5218c18b7a
compatibility with forthcoming sdk 0.9.5
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
58
diff
changeset
|
165 return RETURN_SUCCESS; // Ignore error |
0 | 166 } |
167 } | |
168 | |
169 | |
170 | |
171 template <enum OrthancPlugins::CacheBundle bundle> | |
74
ab5218c18b7a
compatibility with forthcoming sdk 0.9.5
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
58
diff
changeset
|
172 static RETURN_TYPE ServeCache(OrthancPluginRestOutput* output, |
ab5218c18b7a
compatibility with forthcoming sdk 0.9.5
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
58
diff
changeset
|
173 const char* url, |
ab5218c18b7a
compatibility with forthcoming sdk 0.9.5
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
58
diff
changeset
|
174 const OrthancPluginHttpRequest* request) |
0 | 175 { |
176 try | |
177 { | |
178 if (request->method != OrthancPluginHttpMethod_Get) | |
179 { | |
180 OrthancPluginSendMethodNotAllowed(context_, output, "GET"); | |
74
ab5218c18b7a
compatibility with forthcoming sdk 0.9.5
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
58
diff
changeset
|
181 return RETURN_SUCCESS; |
0 | 182 } |
183 | |
184 const std::string id = request->groups[0]; | |
185 std::string content; | |
186 | |
187 if (cache_->GetScheduler().Access(content, bundle, id)) | |
188 { | |
189 OrthancPluginAnswerBuffer(context_, output, content.c_str(), content.size(), "application/json"); | |
190 } | |
191 else | |
192 { | |
193 OrthancPluginSendHttpStatusCode(context_, output, 404); | |
194 } | |
195 | |
74
ab5218c18b7a
compatibility with forthcoming sdk 0.9.5
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
58
diff
changeset
|
196 return RETURN_SUCCESS; |
0 | 197 } |
198 catch (Orthanc::OrthancException& e) | |
199 { | |
200 OrthancPluginLogError(context_, e.What()); | |
74
ab5218c18b7a
compatibility with forthcoming sdk 0.9.5
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
58
diff
changeset
|
201 return RETURN_FAILURE; |
0 | 202 } |
203 catch (std::runtime_error& e) | |
204 { | |
205 OrthancPluginLogError(context_, e.what()); | |
74
ab5218c18b7a
compatibility with forthcoming sdk 0.9.5
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
58
diff
changeset
|
206 return RETURN_FAILURE; |
0 | 207 } |
208 catch (boost::bad_lexical_cast&) | |
209 { | |
210 OrthancPluginLogError(context_, "Bad lexical cast"); | |
74
ab5218c18b7a
compatibility with forthcoming sdk 0.9.5
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
58
diff
changeset
|
211 return RETURN_FAILURE; |
0 | 212 } |
213 } | |
214 | |
215 | |
216 | |
217 | |
218 #if ORTHANC_STANDALONE == 0 | |
219 static int32_t ServeWebViewer(OrthancPluginRestOutput* output, | |
220 const char* url, | |
221 const OrthancPluginHttpRequest* request) | |
222 { | |
223 if (request->method != OrthancPluginHttpMethod_Get) | |
224 { | |
225 OrthancPluginSendMethodNotAllowed(context_, output, "GET"); | |
226 return 0; | |
227 } | |
228 | |
229 const std::string path = std::string(WEB_VIEWER_PATH) + std::string(request->groups[0]); | |
230 const char* mime = OrthancPlugins::GetMimeType(path); | |
231 | |
232 std::string s; | |
233 if (OrthancPlugins::ReadFile(s, path)) | |
234 { | |
235 const char* resource = s.size() ? s.c_str() : NULL; | |
236 OrthancPluginAnswerBuffer(context_, output, resource, s.size(), mime); | |
237 } | |
238 else | |
239 { | |
240 std::string s = "Inexistent file in served folder: " + path; | |
241 OrthancPluginLogError(context_, s.c_str()); | |
242 OrthancPluginSendHttpStatusCode(context_, output, 404); | |
243 } | |
244 | |
245 return 0; | |
246 } | |
247 #endif | |
248 | |
249 | |
250 | |
31 | 251 template <enum Orthanc::EmbeddedResources::DirectoryResourceId folder> |
74
ab5218c18b7a
compatibility with forthcoming sdk 0.9.5
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
58
diff
changeset
|
252 static RETURN_TYPE ServeEmbeddedFolder(OrthancPluginRestOutput* output, |
ab5218c18b7a
compatibility with forthcoming sdk 0.9.5
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
58
diff
changeset
|
253 const char* url, |
ab5218c18b7a
compatibility with forthcoming sdk 0.9.5
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
58
diff
changeset
|
254 const OrthancPluginHttpRequest* request) |
0 | 255 { |
256 if (request->method != OrthancPluginHttpMethod_Get) | |
257 { | |
258 OrthancPluginSendMethodNotAllowed(context_, output, "GET"); | |
74
ab5218c18b7a
compatibility with forthcoming sdk 0.9.5
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
58
diff
changeset
|
259 return RETURN_SUCCESS; |
0 | 260 } |
261 | |
262 std::string path = "/" + std::string(request->groups[0]); | |
263 const char* mime = OrthancPlugins::GetMimeType(path); | |
264 | |
265 try | |
266 { | |
267 std::string s; | |
31 | 268 Orthanc::EmbeddedResources::GetDirectoryResource(s, folder, path.c_str()); |
0 | 269 |
270 const char* resource = s.size() ? s.c_str() : NULL; | |
271 OrthancPluginAnswerBuffer(context_, output, resource, s.size(), mime); | |
272 | |
74
ab5218c18b7a
compatibility with forthcoming sdk 0.9.5
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
58
diff
changeset
|
273 return RETURN_SUCCESS; |
0 | 274 } |
275 catch (std::runtime_error&) | |
276 { | |
277 std::string s = "Unknown static resource in plugin: " + std::string(request->groups[0]); | |
278 OrthancPluginLogError(context_, s.c_str()); | |
279 OrthancPluginSendHttpStatusCode(context_, output, 404); | |
74
ab5218c18b7a
compatibility with forthcoming sdk 0.9.5
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
58
diff
changeset
|
280 return RETURN_SUCCESS; |
0 | 281 } |
282 } | |
283 | |
284 | |
285 | |
74
ab5218c18b7a
compatibility with forthcoming sdk 0.9.5
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
58
diff
changeset
|
286 static RETURN_TYPE IsStableSeries(OrthancPluginRestOutput* output, |
ab5218c18b7a
compatibility with forthcoming sdk 0.9.5
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
58
diff
changeset
|
287 const char* url, |
ab5218c18b7a
compatibility with forthcoming sdk 0.9.5
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
58
diff
changeset
|
288 const OrthancPluginHttpRequest* request) |
19 | 289 { |
290 try | |
291 { | |
292 if (request->method != OrthancPluginHttpMethod_Get) | |
293 { | |
294 OrthancPluginSendMethodNotAllowed(context_, output, "GET"); | |
74
ab5218c18b7a
compatibility with forthcoming sdk 0.9.5
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
58
diff
changeset
|
295 return RETURN_SUCCESS; |
19 | 296 } |
297 | |
298 const std::string id = request->groups[0]; | |
299 Json::Value series; | |
300 | |
301 if (OrthancPlugins::GetJsonFromOrthanc(series, context_, "/series/" + id) && | |
302 series.type() == Json::objectValue) | |
303 { | |
304 bool value = (series["IsStable"].asBool() || | |
305 series["Status"].asString() == "Complete"); | |
306 std::string answer = value ? "true" : "false"; | |
307 OrthancPluginAnswerBuffer(context_, output, answer.c_str(), answer.size(), "application/json"); | |
308 } | |
309 else | |
310 { | |
311 OrthancPluginSendHttpStatusCode(context_, output, 404); | |
312 } | |
313 | |
74
ab5218c18b7a
compatibility with forthcoming sdk 0.9.5
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
58
diff
changeset
|
314 return RETURN_SUCCESS; |
19 | 315 } |
316 catch (Orthanc::OrthancException& e) | |
317 { | |
318 OrthancPluginLogError(context_, e.What()); | |
74
ab5218c18b7a
compatibility with forthcoming sdk 0.9.5
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
58
diff
changeset
|
319 return RETURN_FAILURE; |
19 | 320 } |
321 catch (std::runtime_error& e) | |
322 { | |
323 OrthancPluginLogError(context_, e.what()); | |
74
ab5218c18b7a
compatibility with forthcoming sdk 0.9.5
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
58
diff
changeset
|
324 return RETURN_FAILURE; |
19 | 325 } |
326 catch (boost::bad_lexical_cast&) | |
327 { | |
328 OrthancPluginLogError(context_, "Bad lexical cast"); | |
74
ab5218c18b7a
compatibility with forthcoming sdk 0.9.5
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
58
diff
changeset
|
329 return RETURN_FAILURE; |
19 | 330 } |
331 } | |
332 | |
0 | 333 |
334 extern "C" | |
335 { | |
336 ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext* context) | |
337 { | |
338 using namespace OrthancPlugins; | |
339 | |
340 context_ = context; | |
341 OrthancPluginLogWarning(context_, "Initializing the Web viewer"); | |
342 | |
343 | |
344 /* Check the version of the Orthanc core */ | |
345 if (OrthancPluginCheckVersion(context_) == 0) | |
346 { | |
347 char info[1024]; | |
348 sprintf(info, "Your version of Orthanc (%s) must be above %d.%d.%d to run this plugin", | |
349 context_->orthancVersion, | |
350 ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER, | |
351 ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER, | |
352 ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER); | |
353 OrthancPluginLogError(context_, info); | |
354 return -1; | |
355 } | |
356 | |
357 OrthancPluginSetDescription(context_, "Provides a Web viewer of DICOM series within Orthanc."); | |
358 | |
359 | |
360 /* By default, use half of the available processing cores for the decoding of DICOM images */ | |
361 int decodingThreads = boost::thread::hardware_concurrency() / 2; | |
362 if (decodingThreads == 0) | |
363 { | |
364 decodingThreads = 1; | |
365 } | |
366 | |
367 | |
368 try | |
369 { | |
370 /* Read the configuration of the Web viewer */ | |
371 Json::Value configuration; | |
372 if (!ReadConfiguration(configuration, context)) | |
373 { | |
374 OrthancPluginLogError(context_, "Unable to read the configuration file of Orthanc"); | |
375 return -1; | |
376 } | |
377 | |
4
ecefd45026bf
configuration of the cache
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
378 // By default, the cache of the Web viewer is located inside the |
ecefd45026bf
configuration of the cache
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
379 // "StorageDirectory" of Orthanc |
ecefd45026bf
configuration of the cache
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
380 boost::filesystem::path cachePath = GetStringValue(configuration, "StorageDirectory", "."); |
ecefd45026bf
configuration of the cache
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
381 cachePath /= "WebViewerCache"; |
ecefd45026bf
configuration of the cache
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
382 int cacheSize = 100; // By default, a cache of 100 MB is used |
ecefd45026bf
configuration of the cache
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
383 |
0 | 384 if (configuration.isMember("WebViewer")) |
385 { | |
4
ecefd45026bf
configuration of the cache
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
386 std::string key = "CachePath"; |
ecefd45026bf
configuration of the cache
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
387 if (!configuration["WebViewer"].isMember(key)) |
ecefd45026bf
configuration of the cache
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
388 { |
ecefd45026bf
configuration of the cache
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
389 // For backward compatibility with the initial release of the Web viewer |
ecefd45026bf
configuration of the cache
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
390 key = "Cache"; |
ecefd45026bf
configuration of the cache
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
391 } |
ecefd45026bf
configuration of the cache
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
392 |
ecefd45026bf
configuration of the cache
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
393 cachePath = GetStringValue(configuration["WebViewer"], key, cachePath.string()); |
ecefd45026bf
configuration of the cache
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
394 cacheSize = GetIntegerValue(configuration["WebViewer"], "CacheSize", cacheSize); |
0 | 395 decodingThreads = GetIntegerValue(configuration["WebViewer"], "Threads", decodingThreads); |
396 } | |
397 | |
398 std::string message = ("Web viewer using " + boost::lexical_cast<std::string>(decodingThreads) + | |
399 " threads for the decoding of the DICOM images"); | |
400 OrthancPluginLogWarning(context_, message.c_str()); | |
401 | |
4
ecefd45026bf
configuration of the cache
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
402 if (decodingThreads <= 0 || |
ecefd45026bf
configuration of the cache
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
403 cacheSize <= 0) |
0 | 404 { |
405 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
406 } | |
407 | |
4
ecefd45026bf
configuration of the cache
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
408 message = "Storing the cache of the Web viewer in folder: " + cachePath.string(); |
0 | 409 OrthancPluginLogWarning(context_, message.c_str()); |
410 | |
411 | |
412 /* Create the cache */ | |
4
ecefd45026bf
configuration of the cache
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
413 cache_ = new CacheContext(cachePath.string()); |
0 | 414 cache_->GetScheduler().RegisterPolicy(new ViewerPrefetchPolicy(context_)); |
415 cache_->GetScheduler().Register(CacheBundle_SeriesInformation, | |
416 new SeriesInformationAdapter(context_, cache_->GetScheduler()), 1); | |
417 cache_->GetScheduler().Register(CacheBundle_InstanceInformation, | |
418 new InstanceInformationAdapter(context_), 1); | |
419 cache_->GetScheduler().Register(CacheBundle_DecodedImage, | |
420 new DecodedImageAdapter(context_), decodingThreads); | |
4
ecefd45026bf
configuration of the cache
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
421 |
ecefd45026bf
configuration of the cache
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
422 |
ecefd45026bf
configuration of the cache
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
423 /* Set the quotas */ |
ecefd45026bf
configuration of the cache
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
424 cache_->GetScheduler().SetQuota(CacheBundle_SeriesInformation, 1000, 0); // Keep info about 1000 series |
ecefd45026bf
configuration of the cache
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
425 cache_->GetScheduler().SetQuota(CacheBundle_InstanceInformation, 10000, 0); // Keep info about 10,000 instances |
ecefd45026bf
configuration of the cache
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
426 |
ecefd45026bf
configuration of the cache
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
427 message = "Web viewer using a cache of " + boost::lexical_cast<std::string>(cacheSize) + " MB"; |
ecefd45026bf
configuration of the cache
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
428 OrthancPluginLogWarning(context_, message.c_str()); |
ecefd45026bf
configuration of the cache
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
429 |
ecefd45026bf
configuration of the cache
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
430 cache_->GetScheduler().SetQuota(CacheBundle_DecodedImage, 0, static_cast<uint64_t>(cacheSize) * 1024 * 1024); |
0 | 431 } |
432 catch (std::runtime_error& e) | |
433 { | |
434 OrthancPluginLogError(context_, e.what()); | |
435 return -1; | |
436 } | |
437 catch (Orthanc::OrthancException& e) | |
438 { | |
439 OrthancPluginLogError(context_, e.What()); | |
440 return -1; | |
441 } | |
442 | |
443 | |
444 /* Install the callbacks */ | |
445 OrthancPluginRegisterRestCallback(context_, "/web-viewer/series/(.*)", ServeCache<CacheBundle_SeriesInformation>); | |
19 | 446 OrthancPluginRegisterRestCallback(context_, "/web-viewer/is-stable-series/(.*)", IsStableSeries); |
0 | 447 OrthancPluginRegisterRestCallback(context_, "/web-viewer/instances/(.*)", ServeCache<CacheBundle_DecodedImage>); |
31 | 448 OrthancPluginRegisterRestCallback(context, "/web-viewer/libs/(.*)", ServeEmbeddedFolder<Orthanc::EmbeddedResources::JAVASCRIPT_LIBS>); |
0 | 449 |
450 #if ORTHANC_STANDALONE == 1 | |
31 | 451 OrthancPluginRegisterRestCallback(context, "/web-viewer/app/(.*)", ServeEmbeddedFolder<Orthanc::EmbeddedResources::WEB_VIEWER>); |
0 | 452 #else |
453 OrthancPluginRegisterRestCallback(context, "/web-viewer/app/(.*)", ServeWebViewer); | |
454 #endif | |
455 | |
456 OrthancPluginRegisterOnChangeCallback(context, OnChangeCallback); | |
457 | |
458 | |
459 /* Extend the default Orthanc Explorer with custom JavaScript */ | |
460 std::string explorer; | |
31 | 461 Orthanc::EmbeddedResources::GetFileResource(explorer, Orthanc::EmbeddedResources::ORTHANC_EXPLORER); |
0 | 462 OrthancPluginExtendOrthancExplorer(context_, explorer.c_str()); |
463 | |
464 return 0; | |
465 } | |
466 | |
467 | |
468 ORTHANC_PLUGINS_API void OrthancPluginFinalize() | |
469 { | |
470 OrthancPluginLogWarning(context_, "Finalizing the Web viewer"); | |
471 | |
472 if (cache_ != NULL) | |
473 { | |
474 delete cache_; | |
475 cache_ = NULL; | |
476 } | |
477 } | |
478 | |
479 | |
480 ORTHANC_PLUGINS_API const char* OrthancPluginGetName() | |
481 { | |
482 return "web-viewer"; | |
483 } | |
484 | |
485 | |
486 ORTHANC_PLUGINS_API const char* OrthancPluginGetVersion() | |
487 { | |
12 | 488 return ORTHANC_WEBVIEWER_VERSION; |
0 | 489 } |
490 } |