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,