Mercurial > hg > orthanc-java
comparison Plugin/Plugin.cpp @ 5:c8f19e93ff99
reorganization
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 19 Oct 2023 08:49:07 +0200 |
parents | 9032ffb3a7d5 |
children | b14ed1ea3a23 |
comparison
equal
deleted
inserted
replaced
4:9032ffb3a7d5 | 5:c8f19e93ff99 |
---|---|
20 * You should have received a copy of the GNU General Public License | 20 * You should have received a copy of the GNU General Public License |
21 * along with this program. If not, see <http://www.gnu.org/licenses/>. | 21 * along with this program. If not, see <http://www.gnu.org/licenses/>. |
22 **/ | 22 **/ |
23 | 23 |
24 | 24 |
25 #include "JavaEnvironment.h" | |
26 #include "JavaVirtualMachine.h" | |
27 | |
25 #include <orthanc/OrthancCPlugin.h> | 28 #include <orthanc/OrthancCPlugin.h> |
26 | 29 |
27 #include <cassert> | 30 #include <cassert> |
28 #include <iostream> | 31 #include <iostream> |
29 #include <jni.h> | 32 #include <jni.h> |
36 | 39 |
37 #include <json/reader.h> | 40 #include <json/reader.h> |
38 | 41 |
39 #include "Mutex.h" | 42 #include "Mutex.h" |
40 | 43 |
41 static OrthancPluginContext* context_ = NULL; | 44 OrthancPluginContext* context_ = NULL; |
42 | |
43 | |
44 | |
45 static const char* JAVA_EXCEPTION_CLASS = "be/uclouvain/orthanc/OrthancException"; | |
46 | |
47 class JavaVirtualMachine : public NonCopyable | |
48 { | |
49 private: | |
50 JavaVM *jvm_; | |
51 | |
52 public: | |
53 JavaVirtualMachine(const std::string& classPath) | |
54 { | |
55 std::string classPathOption = "-Djava.class.path=" + classPath; | |
56 | |
57 std::vector<JavaVMOption> options; | |
58 options.resize(2); | |
59 options[0].optionString = const_cast<char*>(classPathOption.c_str()); | |
60 options[0].extraInfo = NULL; | |
61 options[1].optionString = const_cast<char*>("-Xcheck:jni"); | |
62 options[1].extraInfo = NULL; | |
63 | |
64 JavaVMInitArgs vm_args; | |
65 vm_args.version = JNI_VERSION_1_6; | |
66 vm_args.nOptions = options.size(); | |
67 vm_args.options = (options.empty() ? NULL : &options[0]); | |
68 vm_args.ignoreUnrecognized = false; | |
69 | |
70 JNIEnv* env = NULL; | |
71 jint res = JNI_CreateJavaVM(&jvm_, (void **) &env, &vm_args); | |
72 if (res != JNI_OK || | |
73 jvm_ == NULL || | |
74 env == NULL) | |
75 { | |
76 throw std::runtime_error("Cannot create the JVM"); | |
77 } | |
78 } | |
79 | |
80 ~JavaVirtualMachine() | |
81 { | |
82 jvm_->DestroyJavaVM(); | |
83 } | |
84 | |
85 JavaVM& GetValue() | |
86 { | |
87 assert(jvm_ != NULL); | |
88 return *jvm_; | |
89 } | |
90 }; | |
91 | |
92 | |
93 class JavaEnvironment : public NonCopyable | |
94 { | |
95 private: | |
96 JavaVM *jvm_; | |
97 JNIEnv *env_; | |
98 | |
99 public: | |
100 JavaEnvironment(JNIEnv* env) : | |
101 jvm_(NULL), | |
102 env_(env) | |
103 { | |
104 if (env_ == NULL) | |
105 { | |
106 throw std::runtime_error("Null pointer"); | |
107 } | |
108 } | |
109 | |
110 JavaEnvironment(JavaVirtualMachine& jvm) : | |
111 jvm_(&jvm.GetValue()) | |
112 { | |
113 jint status = jvm_->GetEnv((void **) &env_, JNI_VERSION_1_6); | |
114 | |
115 switch (status) | |
116 { | |
117 case JNI_OK: | |
118 break; | |
119 | |
120 case JNI_EDETACHED: | |
121 { | |
122 jint code = jvm_->AttachCurrentThread((void **) &env_, NULL); | |
123 if (code != JNI_OK) | |
124 { | |
125 throw std::runtime_error("Cannot attach thread"); | |
126 } | |
127 break; | |
128 } | |
129 | |
130 case JNI_EVERSION: | |
131 throw std::runtime_error("JNI version not supported"); | |
132 | |
133 default: | |
134 throw std::runtime_error("Not implemented"); | |
135 } | |
136 | |
137 if (env_ == NULL) | |
138 { | |
139 throw std::runtime_error("Error inside JNI"); | |
140 } | |
141 } | |
142 | |
143 ~JavaEnvironment() | |
144 { | |
145 if (jvm_ != NULL) | |
146 { | |
147 jvm_->DetachCurrentThread(); | |
148 } | |
149 } | |
150 | |
151 void CheckException() | |
152 { | |
153 if (env_->ExceptionCheck() == JNI_TRUE) | |
154 { | |
155 env_->ExceptionClear(); | |
156 throw std::runtime_error("An exception has occurred in Java"); | |
157 } | |
158 } | |
159 | |
160 JNIEnv& GetValue() | |
161 { | |
162 assert(env_ != NULL); | |
163 return *env_; | |
164 } | |
165 | |
166 void RunGarbageCollector() | |
167 { | |
168 assert(env_ != NULL); | |
169 | |
170 jclass system = FindClass("java/lang/System"); | |
171 | |
172 jmethodID runFinalization = env_->GetStaticMethodID(system, "gc", "()V"); | |
173 if (runFinalization != NULL) | |
174 { | |
175 env_->CallStaticVoidMethod(system, runFinalization); | |
176 CheckException(); | |
177 } | |
178 else | |
179 { | |
180 throw std::runtime_error("Cannot run garbage collector"); | |
181 } | |
182 } | |
183 | |
184 jclass FindClass(const std::string& fqn) | |
185 { | |
186 jclass c = GetValue().FindClass(fqn.c_str()); | |
187 | |
188 if (c == NULL) | |
189 { | |
190 throw std::runtime_error("Unable to find class: " + fqn); | |
191 } | |
192 else | |
193 { | |
194 return c; | |
195 } | |
196 } | |
197 | |
198 jclass GetObjectClass(jobject obj) | |
199 { | |
200 jclass c = GetValue().GetObjectClass(obj); | |
201 | |
202 if (c == NULL) | |
203 { | |
204 throw std::runtime_error("Unable to get class of object"); | |
205 } | |
206 else | |
207 { | |
208 return c; | |
209 } | |
210 } | |
211 | |
212 jmethodID GetMethodID(jclass c, | |
213 const std::string& method, | |
214 const std::string& signature) | |
215 { | |
216 jmethodID m = GetValue().GetMethodID(c, method.c_str(), signature.c_str()); | |
217 | |
218 if (m == NULL) | |
219 { | |
220 throw std::runtime_error("Unable to locate method in class"); | |
221 } | |
222 else | |
223 { | |
224 return m; | |
225 } | |
226 } | |
227 | |
228 jobject ConstructJavaWrapper(const std::string& fqn, | |
229 void* nativeObject) | |
230 { | |
231 jclass cls = FindClass(fqn); | |
232 jmethodID constructor = GetMethodID(cls, "<init>", "(J)V"); | |
233 jobject obj = env_->NewObject(cls, constructor, reinterpret_cast<intptr_t>(nativeObject)); | |
234 | |
235 if (obj == NULL) | |
236 { | |
237 throw std::runtime_error("Cannot create Java wrapper around C/C++ object: " + fqn); | |
238 } | |
239 else | |
240 { | |
241 return obj; | |
242 } | |
243 } | |
244 | |
245 jbyteArray ConstructByteArray(const size_t size, | |
246 const void* data) | |
247 { | |
248 assert(env_ != NULL); | |
249 jbyteArray obj = env_->NewByteArray(size); | |
250 if (obj == NULL) | |
251 { | |
252 throw std::runtime_error("Cannot create a byte array"); | |
253 } | |
254 else | |
255 { | |
256 if (size > 0) | |
257 { | |
258 env_->SetByteArrayRegion(obj, 0, size, reinterpret_cast<const jbyte*>(data)); | |
259 } | |
260 | |
261 return obj; | |
262 } | |
263 } | |
264 | |
265 jbyteArray ConstructByteArray(const std::string& data) | |
266 { | |
267 return ConstructByteArray(data.size(), data.c_str()); | |
268 } | |
269 | |
270 void RegisterNatives(const std::string& fqn, | |
271 const std::vector<JNINativeMethod>& methods) | |
272 { | |
273 if (!methods.empty()) | |
274 { | |
275 if (env_->RegisterNatives(FindClass(fqn), &methods[0], methods.size()) < 0) | |
276 { | |
277 throw std::runtime_error("Unable to register the native methods"); | |
278 } | |
279 } | |
280 } | |
281 | |
282 void ThrowException(const std::string& fqn, | |
283 const std::string& message) | |
284 { | |
285 if (GetValue().ThrowNew(FindClass(fqn), message.c_str()) != 0) | |
286 { | |
287 std::string message = "Cannot throw exception " + fqn; | |
288 OrthancPluginLogError(context_, message.c_str()); | |
289 } | |
290 } | |
291 | |
292 void ThrowException(const std::string& message) | |
293 { | |
294 ThrowException(JAVA_EXCEPTION_CLASS, message); | |
295 } | |
296 | |
297 void ThrowException(OrthancPluginErrorCode code) | |
298 { | |
299 ThrowException(JAVA_EXCEPTION_CLASS, OrthancPluginGetErrorDescription(context_, code)); | |
300 } | |
301 | |
302 jobject ConstructEnumValue(const std::string& fqn, | |
303 int value) | |
304 { | |
305 assert(env_ != NULL); | |
306 jclass cls = FindClass(fqn); | |
307 | |
308 std::string signature = "(I)L" + fqn + ";"; | |
309 jmethodID constructor = env_->GetStaticMethodID(cls, "getInstance", signature.c_str()); | |
310 if (constructor != NULL) | |
311 { | |
312 jobject obj = env_->CallStaticObjectMethod(cls, constructor, static_cast<jint>(value)); | |
313 CheckException(); | |
314 return obj; | |
315 } | |
316 else | |
317 { | |
318 char buf[16]; | |
319 sprintf(buf, "%d", value); | |
320 throw std::runtime_error("Cannot create enumeration value: " + fqn + " " + buf); | |
321 } | |
322 } | |
323 | |
324 | |
325 static void ThrowException(JNIEnv* env, | |
326 const std::string& fqn, | |
327 const std::string& message) | |
328 { | |
329 JavaEnvironment e(env); | |
330 e.ThrowException(fqn, message); | |
331 } | |
332 | |
333 static void ThrowException(JNIEnv* env, | |
334 const std::string& message) | |
335 { | |
336 JavaEnvironment e(env); | |
337 e.ThrowException(message); | |
338 } | |
339 | |
340 static void ThrowException(JNIEnv* env, | |
341 OrthancPluginErrorCode code) | |
342 { | |
343 JavaEnvironment e(env); | |
344 e.ThrowException(code); | |
345 } | |
346 }; | |
347 | |
348 | 45 |
349 static std::unique_ptr<JavaVirtualMachine> java_; | 46 static std::unique_ptr<JavaVirtualMachine> java_; |
350 | 47 |
351 | 48 |
352 | 49 |
931 { | 628 { |
932 callbacksConfiguration_->AddOnChangeCallback(*java_, callback); | 629 callbacksConfiguration_->AddOnChangeCallback(*java_, callback); |
933 } | 630 } |
934 catch (std::runtime_error& e) | 631 catch (std::runtime_error& e) |
935 { | 632 { |
936 JavaEnvironment::ThrowException(env, e.what()); | 633 JavaEnvironment::ThrowOrthancException(env, e.what()); |
937 } | 634 } |
938 catch (...) | 635 catch (...) |
939 { | 636 { |
940 JavaEnvironment::ThrowException(env, OrthancPluginErrorCode_Plugin); | 637 JavaEnvironment::ThrowOrthancException(env, OrthancPluginErrorCode_Plugin); |
941 } | 638 } |
942 } | 639 } |
943 | 640 |
944 | 641 |
945 JNIEXPORT void RegisterOnRestRequestCallback(JNIEnv* env, jobject sdkObject, jstring regex, jobject callback) | 642 JNIEXPORT void RegisterOnRestRequestCallback(JNIEnv* env, jobject sdkObject, jstring regex, jobject callback) |
950 size_t index = callbacksConfiguration_->AddOnRestRequestCallback(*java_, callback); | 647 size_t index = callbacksConfiguration_->AddOnRestRequestCallback(*java_, callback); |
951 OrthancPluginRegisterRestCallbackNoLock(context_, cregex.GetValue(), restCallbacksPool_.GetCallback(index)); | 648 OrthancPluginRegisterRestCallbackNoLock(context_, cregex.GetValue(), restCallbacksPool_.GetCallback(index)); |
952 } | 649 } |
953 catch (std::runtime_error& e) | 650 catch (std::runtime_error& e) |
954 { | 651 { |
955 JavaEnvironment::ThrowException(env, e.what()); | 652 JavaEnvironment::ThrowOrthancException(env, e.what()); |
956 } | 653 } |
957 catch (...) | 654 catch (...) |
958 { | 655 { |
959 JavaEnvironment::ThrowException(env, OrthancPluginErrorCode_Plugin); | 656 JavaEnvironment::ThrowOrthancException(env, OrthancPluginErrorCode_Plugin); |
960 } | 657 } |
961 } | 658 } |
962 | 659 |
963 | 660 |
964 static void ParseJson(Json::Value& target, | 661 static void ParseJson(Json::Value& target, |