5
|
1 /**
|
|
2 * SPDX-FileCopyrightText: 2023 Sebastien Jodogne, UCLouvain, Belgium
|
|
3 * SPDX-License-Identifier: GPL-3.0-or-later
|
|
4 */
|
|
5
|
|
6 /**
|
|
7 * Java plugin for Orthanc
|
|
8 * Copyright (C) 2023 Sebastien Jodogne, UCLouvain, Belgium
|
|
9 *
|
|
10 * This program is free software: you can redistribute it and/or
|
|
11 * modify it under the terms of the GNU General Public License as
|
|
12 * published by the Free Software Foundation, either version 3 of the
|
|
13 * License, or (at your option) any later version.
|
|
14 *
|
|
15 * This program is distributed in the hope that it will be useful, but
|
|
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
18 * General Public License for more details.
|
|
19 *
|
|
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/>.
|
|
22 **/
|
|
23
|
|
24
|
|
25 #include "JavaEnvironment.h"
|
|
26
|
|
27 #include <cassert>
|
|
28 #include <stdexcept>
|
|
29
|
|
30 extern OrthancPluginContext* context_;
|
|
31
|
|
32
|
|
33 static const char* ORTHANC_EXCEPTION_JAVA_CLASS = "be/uclouvain/orthanc/OrthancException";
|
|
34
|
|
35
|
|
36 JavaEnvironment::JavaEnvironment(JNIEnv* env) :
|
|
37 jvm_(NULL),
|
|
38 env_(env)
|
|
39 {
|
|
40 if (env_ == NULL)
|
|
41 {
|
|
42 throw std::runtime_error("Null pointer");
|
|
43 }
|
|
44 }
|
|
45
|
|
46
|
|
47 JavaEnvironment::JavaEnvironment(JavaVirtualMachine& jvm) :
|
|
48 jvm_(&jvm.GetValue())
|
|
49 {
|
|
50 jint status = jvm_->GetEnv((void **) &env_, JNI_VERSION_1_6);
|
|
51
|
|
52 switch (status)
|
|
53 {
|
|
54 case JNI_OK:
|
|
55 break;
|
|
56
|
|
57 case JNI_EDETACHED:
|
|
58 {
|
|
59 jint code = jvm_->AttachCurrentThread((void **) &env_, NULL);
|
|
60 if (code != JNI_OK)
|
|
61 {
|
|
62 throw std::runtime_error("Cannot attach thread");
|
|
63 }
|
|
64 break;
|
|
65 }
|
|
66
|
|
67 case JNI_EVERSION:
|
|
68 throw std::runtime_error("JNI version not supported");
|
|
69
|
|
70 default:
|
|
71 throw std::runtime_error("Not implemented");
|
|
72 }
|
|
73
|
|
74 if (env_ == NULL)
|
|
75 {
|
|
76 throw std::runtime_error("Error inside JNI");
|
|
77 }
|
|
78 }
|
|
79
|
|
80
|
|
81 JavaEnvironment::~JavaEnvironment()
|
|
82 {
|
|
83 if (jvm_ != NULL)
|
|
84 {
|
|
85 jvm_->DetachCurrentThread();
|
|
86 }
|
|
87 }
|
|
88
|
|
89
|
|
90 JNIEnv& JavaEnvironment::GetValue()
|
|
91 {
|
|
92 assert(env_ != NULL);
|
|
93 return *env_;
|
|
94 }
|
|
95
|
|
96
|
|
97 void JavaEnvironment::CheckException()
|
|
98 {
|
|
99 if (env_->ExceptionCheck() == JNI_TRUE)
|
|
100 {
|
|
101 env_->ExceptionClear();
|
|
102 throw std::runtime_error("An exception has occurred in Java");
|
|
103 }
|
|
104 }
|
|
105
|
|
106
|
|
107 void JavaEnvironment::ThrowException(const std::string& fqn,
|
|
108 const std::string& message)
|
|
109 {
|
|
110 if (GetValue().ThrowNew(FindClass(fqn), message.c_str()) != 0)
|
|
111 {
|
|
112 std::string message = "Cannot throw exception " + fqn;
|
|
113 OrthancPluginLogError(context_, message.c_str());
|
|
114 }
|
|
115 }
|
|
116
|
|
117
|
|
118 void JavaEnvironment::ThrowOrthancException(const std::string& message)
|
|
119 {
|
|
120 ThrowException(ORTHANC_EXCEPTION_JAVA_CLASS, message);
|
|
121 }
|
|
122
|
|
123
|
|
124 void JavaEnvironment::ThrowOrthancException(OrthancPluginErrorCode code)
|
|
125 {
|
|
126 ThrowException(ORTHANC_EXCEPTION_JAVA_CLASS, OrthancPluginGetErrorDescription(context_, code));
|
|
127 }
|
|
128
|
|
129
|
|
130 void JavaEnvironment::ThrowOrthancException(JNIEnv* env,
|
|
131 const std::string& message)
|
|
132 {
|
|
133 JavaEnvironment e(env);
|
|
134 e.ThrowOrthancException(message);
|
|
135 }
|
|
136
|
|
137
|
|
138 void JavaEnvironment::ThrowOrthancException(JNIEnv* env,
|
|
139 OrthancPluginErrorCode code)
|
|
140 {
|
|
141 JavaEnvironment e(env);
|
|
142 e.ThrowOrthancException(code);
|
|
143 }
|
|
144
|
|
145
|
|
146 void JavaEnvironment::RegisterNatives(const std::string& fqn,
|
|
147 const std::vector<JNINativeMethod>& methods)
|
|
148 {
|
|
149 if (!methods.empty())
|
|
150 {
|
|
151 if (env_->RegisterNatives(FindClass(fqn), &methods[0], methods.size()) < 0)
|
|
152 {
|
|
153 throw std::runtime_error("Unable to register the native methods");
|
|
154 }
|
|
155 }
|
|
156 }
|
|
157
|
|
158
|
|
159 void JavaEnvironment::RunGarbageCollector()
|
|
160 {
|
|
161 assert(env_ != NULL);
|
|
162
|
|
163 jclass system = FindClass("java/lang/System");
|
|
164
|
|
165 jmethodID runFinalization = env_->GetStaticMethodID(system, "gc", "()V");
|
|
166 if (runFinalization != NULL)
|
|
167 {
|
|
168 env_->CallStaticVoidMethod(system, runFinalization);
|
|
169 CheckException();
|
|
170 }
|
|
171 else
|
|
172 {
|
|
173 throw std::runtime_error("Cannot run garbage collector");
|
|
174 }
|
|
175 }
|
|
176
|
|
177
|
|
178 jclass JavaEnvironment::FindClass(const std::string& fqn)
|
|
179 {
|
|
180 jclass c = GetValue().FindClass(fqn.c_str());
|
|
181
|
|
182 if (c == NULL)
|
|
183 {
|
|
184 throw std::runtime_error("Unable to find class: " + fqn);
|
|
185 }
|
|
186 else
|
|
187 {
|
|
188 return c;
|
|
189 }
|
|
190 }
|
|
191
|
|
192
|
|
193 jclass JavaEnvironment::GetObjectClass(jobject obj)
|
|
194 {
|
|
195 jclass c = GetValue().GetObjectClass(obj);
|
|
196
|
|
197 if (c == NULL)
|
|
198 {
|
|
199 throw std::runtime_error("Unable to get class of object");
|
|
200 }
|
|
201 else
|
|
202 {
|
|
203 return c;
|
|
204 }
|
|
205 }
|
|
206
|
|
207
|
|
208 jmethodID JavaEnvironment::GetMethodID(jclass c,
|
|
209 const std::string& method,
|
|
210 const std::string& signature)
|
|
211 {
|
|
212 jmethodID m = GetValue().GetMethodID(c, method.c_str(), signature.c_str());
|
|
213
|
|
214 if (m == NULL)
|
|
215 {
|
|
216 throw std::runtime_error("Unable to locate method in class");
|
|
217 }
|
|
218 else
|
|
219 {
|
|
220 return m;
|
|
221 }
|
|
222 }
|
|
223
|
|
224
|
|
225 jobject JavaEnvironment::ConstructJavaWrapper(const std::string& fqn,
|
|
226 void* nativeObject)
|
|
227 {
|
|
228 jclass cls = FindClass(fqn);
|
|
229 jmethodID constructor = GetMethodID(cls, "<init>", "(J)V");
|
|
230 jobject obj = env_->NewObject(cls, constructor, reinterpret_cast<intptr_t>(nativeObject));
|
|
231
|
|
232 if (obj == NULL)
|
|
233 {
|
|
234 throw std::runtime_error("Cannot create Java wrapper around C/C++ object: " + fqn);
|
|
235 }
|
|
236 else
|
|
237 {
|
|
238 return obj;
|
|
239 }
|
|
240 }
|
|
241
|
|
242
|
|
243 jbyteArray JavaEnvironment::ConstructByteArray(const size_t size,
|
|
244 const void* data)
|
|
245 {
|
|
246 assert(env_ != NULL);
|
|
247 jbyteArray obj = env_->NewByteArray(size);
|
|
248 if (obj == NULL)
|
|
249 {
|
|
250 throw std::runtime_error("Cannot create a byte array");
|
|
251 }
|
|
252 else
|
|
253 {
|
|
254 if (size > 0)
|
|
255 {
|
|
256 env_->SetByteArrayRegion(obj, 0, size, reinterpret_cast<const jbyte*>(data));
|
|
257 }
|
|
258
|
|
259 return obj;
|
|
260 }
|
|
261 }
|
|
262
|
|
263
|
|
264 jobject JavaEnvironment::ConstructEnumValue(const std::string& fqn,
|
|
265 int value)
|
|
266 {
|
|
267 assert(env_ != NULL);
|
|
268 jclass cls = FindClass(fqn);
|
|
269
|
|
270 std::string signature = "(I)L" + fqn + ";";
|
|
271 jmethodID constructor = env_->GetStaticMethodID(cls, "getInstance", signature.c_str());
|
|
272 if (constructor != NULL)
|
|
273 {
|
|
274 jobject obj = env_->CallStaticObjectMethod(cls, constructor, static_cast<jint>(value));
|
|
275 CheckException();
|
|
276 return obj;
|
|
277 }
|
|
278 else
|
|
279 {
|
|
280 char buf[16];
|
|
281 sprintf(buf, "%d", value);
|
|
282 throw std::runtime_error("Cannot create enumeration value: " + fqn + " " + buf);
|
|
283 }
|
|
284 }
|