Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : */
8 :
9 : #ifndef HAVE_KDBCONFIG
10 : #include "kdbconfig.h"
11 : #endif
12 :
13 : #include <string.h>
14 :
15 : #include <jni.h>
16 : #include <stdlib.h>
17 :
18 :
19 : #include <kdberrors.h>
20 : #include <kdbplugin.h>
21 :
22 : // forward declarations
23 : int elektraJniOpen (Plugin * handle, Key * errorKey);
24 : int elektraJniClose (Plugin * handle, Key * errorKey);
25 : int elektraJniGet (Plugin * handle, KeySet * ks, Key * parentKey);
26 : int elektraJniSet (Plugin * handle, KeySet * ks, Key * parentKey);
27 : int elektraJniError (Plugin * handle, KeySet * ks, Key * parentKey);
28 :
29 : // plugin's data handle
30 : typedef struct
31 : {
32 : JNIEnv * env;
33 : JavaVM * jvm;
34 : jclass clsPlugin;
35 : jclass clsKey;
36 : jclass clsKeySet;
37 : int module;
38 : int printException;
39 : jmethodID midKeyConstr;
40 : jmethodID midKeySetConstr;
41 : jmethodID midKeyRelease;
42 : jmethodID midKeySetRelease;
43 : jobject plugin;
44 : } Data;
45 :
46 0 : static void checkException (Data * data, const char * when, Key * warningKey)
47 : {
48 0 : if ((*data->env)->ExceptionCheck (data->env))
49 : {
50 0 : if (data->printException)
51 : {
52 0 : (*data->env)->ExceptionDescribe (data->env);
53 : }
54 0 : jthrowable ex = (*data->env)->ExceptionOccurred (data->env);
55 0 : jmethodID toString = (*data->env)
56 0 : ->GetMethodID (data->env, (*data->env)->FindClass (data->env, "java/lang/Object"), "toString",
57 : "()Ljava/lang/String;");
58 0 : jstring estr = (jstring) (*data->env)->CallObjectMethod (data->env, ex, toString);
59 :
60 0 : jboolean iseCopy = JNI_FALSE;
61 0 : const char * which = "unknown";
62 0 : if (estr)
63 : {
64 0 : which = (*data->env)->GetStringUTFChars (data->env, estr, &iseCopy);
65 : }
66 :
67 0 : ELEKTRA_ADD_PLUGIN_MISBEHAVIOR_WARNINGF (warningKey, "During \"%s\", java exception was thrown: %s", when, which);
68 :
69 0 : if (iseCopy == JNI_TRUE)
70 : {
71 0 : (*data->env)->ReleaseStringUTFChars (data->env, estr, which);
72 : }
73 0 : (*data->env)->ExceptionClear (data->env);
74 : }
75 0 : }
76 :
77 0 : static int call1Arg (Data * data, Key * errorKey, const char * method)
78 : {
79 0 : jobject jerrorKey = (*data->env)->NewObject (data->env, data->clsKey, data->midKeyConstr, errorKey);
80 0 : checkException (data, method, errorKey);
81 0 : if (jerrorKey == 0)
82 : {
83 0 : ELEKTRA_SET_RESOURCE_ERRORF (errorKey, "Cannot create errorKey in %s", method);
84 0 : return -1;
85 : }
86 :
87 0 : jmethodID mid = (*data->env)->GetMethodID (data->env, data->clsPlugin, method, "(Lorg/libelektra/Key;)I");
88 0 : checkException (data, method, errorKey);
89 0 : if (mid == 0)
90 : {
91 0 : ELEKTRA_SET_INSTALLATION_ERRORF (errorKey, "Cannot find org/libelektra/Key in %s", method);
92 0 : return -1;
93 : }
94 :
95 0 : jint result = 0;
96 0 : result = (*data->env)->CallIntMethod (data->env, data->plugin, mid, jerrorKey);
97 0 : if ((*data->env)->ExceptionCheck (data->env))
98 : {
99 0 : ELEKTRA_SET_PLUGIN_MISBEHAVIOR_ERRORF (errorKey, "Method '%s' failed with exception", method);
100 0 : result = -1;
101 : }
102 0 : checkException (data, method, errorKey);
103 :
104 0 : (*data->env)->CallVoidMethod (data->env, jerrorKey, data->midKeyRelease);
105 0 : checkException (data, method, errorKey);
106 :
107 0 : return result;
108 : }
109 :
110 0 : static int call2Arg (Data * data, KeySet * ks, Key * errorKey, const char * method)
111 : {
112 0 : jobject jks = (*data->env)->NewObject (data->env, data->clsKeySet, data->midKeySetConstr, ks);
113 0 : checkException (data, method, errorKey);
114 0 : if (jks == 0)
115 : {
116 0 : ELEKTRA_SET_RESOURCE_ERROR (errorKey, "Cannot create ks");
117 0 : return -1;
118 : }
119 :
120 0 : jobject jkey = (*data->env)->NewObject (data->env, data->clsKey, data->midKeyConstr, errorKey);
121 0 : checkException (data, method, errorKey);
122 0 : if (jkey == 0)
123 : {
124 0 : ELEKTRA_SET_RESOURCE_ERROR (errorKey, "Cannot create key");
125 0 : return -1;
126 : }
127 :
128 0 : jmethodID mid = (*data->env)->GetMethodID (data->env, data->clsPlugin, method, "(Lorg/libelektra/KeySet;Lorg/libelektra/Key;)I");
129 0 : checkException (data, method, errorKey);
130 0 : if (mid == 0)
131 : {
132 0 : ELEKTRA_SET_RESOURCE_ERRORF (errorKey, "Cannot find %s", method);
133 0 : return -1;
134 : }
135 :
136 0 : jint result = 0;
137 0 : result = (*data->env)->CallIntMethod (data->env, data->plugin, mid, jks, jkey);
138 0 : if ((*data->env)->ExceptionCheck (data->env))
139 : {
140 0 : ELEKTRA_SET_PLUGIN_MISBEHAVIOR_ERRORF (errorKey, "Method '%s' failed with exception", method);
141 0 : result = -1;
142 : }
143 0 : checkException (data, method, errorKey);
144 :
145 0 : (*data->env)->CallVoidMethod (data->env, jks, data->midKeySetRelease);
146 0 : checkException (data, method, errorKey);
147 :
148 0 : (*data->env)->CallVoidMethod (data->env, jkey, data->midKeyRelease);
149 0 : checkException (data, method, errorKey);
150 :
151 0 : return result;
152 : }
153 :
154 15 : int elektraJniOpen (Plugin * handle, Key * errorKey)
155 : {
156 15 : Data * data = elektraMalloc (sizeof (Data));
157 15 : data->module = 0;
158 15 : data->printException = 0;
159 15 : elektraPluginSetData (handle, data);
160 :
161 15 : KeySet * config = elektraPluginGetConfig (handle);
162 15 : Key * k = ksLookupByName (config, "/module", 0);
163 15 : if (k)
164 : {
165 15 : data->module = 1;
166 15 : return 0;
167 : }
168 :
169 0 : k = ksLookupByName (config, "/print", 0);
170 0 : if (k)
171 : {
172 0 : data->printException = 1;
173 : }
174 :
175 0 : k = ksLookupByName (config, "/classpath", 0);
176 0 : if (!k)
177 : {
178 0 : ELEKTRA_SET_RESOURCE_ERROR (errorKey, "Could not find plugin config /classpath");
179 0 : return -1;
180 : }
181 0 : char classpatharg[] = "-Djava.class.path=";
182 0 : char * classpath = elektraMalloc (sizeof (classpatharg) + keyGetValueSize (k));
183 0 : strcpy (classpath, classpatharg);
184 0 : strcat (classpath, keyString (k));
185 :
186 0 : k = ksLookupByName (config, "/option", 0);
187 0 : char * option = 0;
188 0 : if (!k)
189 : {
190 : option = "-verbose:gc,class,jni";
191 : }
192 : else
193 : {
194 0 : option = (char *) keyString (k);
195 : }
196 :
197 0 : k = ksLookupByName (config, "/ignore", 0);
198 0 : jboolean ign = JNI_FALSE;
199 0 : if (k) ign = JNI_TRUE;
200 :
201 : /* TODO: check if JVM is already started:
202 : jsize nVMs;
203 : JNI_GetCreatedJavaVMs(NULL, 0, &nVMs); // get array length
204 : JavaVM** buffer = elektraMalloc(nVMs, sizeof(JavaVM*));
205 : JNI_GetCreatedJavaVMs(buffer, nVMs, &nVMs); // get data
206 : */
207 :
208 : JavaVMInitArgs vmArgs;
209 : JavaVMOption options[2];
210 0 : options[0].optionString = classpath;
211 0 : options[1].optionString = option;
212 0 : vmArgs.version = JNI_VERSION_1_8;
213 0 : vmArgs.nOptions = 2;
214 0 : vmArgs.options = options;
215 0 : vmArgs.ignoreUnrecognized = ign;
216 :
217 0 : jint res = JNI_CreateJavaVM (&data->jvm, (void **) &data->env, (void **) &vmArgs);
218 0 : elektraFree (classpath);
219 0 : if (res < 0)
220 : {
221 0 : switch (res)
222 : {
223 : case JNI_EDETACHED:
224 0 : ELEKTRA_SET_INSTALLATION_ERROR (errorKey, "Cannot create Java VM: Thread detached from the VM");
225 0 : return -1;
226 : case JNI_EVERSION:
227 0 : ELEKTRA_SET_INSTALLATION_ERROR (errorKey, "Cannot create Java VM: JNI version error");
228 0 : return -1;
229 : case JNI_ENOMEM:
230 0 : ELEKTRA_SET_OUT_OF_MEMORY_ERROR (errorKey, "Cannot create Java VM: Not enough memory");
231 0 : return -1;
232 : case JNI_EEXIST:
233 0 : ELEKTRA_SET_RESOURCE_ERROR (errorKey, "Cannot create Java VM: VM already created");
234 0 : return -1;
235 : case JNI_EINVAL:
236 0 : ELEKTRA_SET_INTERFACE_ERROR (errorKey, "Cannot create Java VM: Invalid arguments");
237 0 : return -1;
238 : default:
239 0 : ELEKTRA_SET_INSTALLATION_ERROR (errorKey, "Cannot create Java VM: Unknown error");
240 0 : return -1;
241 : }
242 : }
243 :
244 0 : k = ksLookupByName (config, "/classname", 0);
245 0 : if (!k)
246 : {
247 0 : ELEKTRA_SET_INSTALLATION_ERROR (errorKey, "Could not find plugin config /classname");
248 0 : return -1;
249 : }
250 :
251 0 : const char * classname = keyString (k);
252 :
253 0 : data->clsPlugin = (*data->env)->FindClass (data->env, classname);
254 0 : if (data->clsPlugin == 0)
255 : {
256 0 : ELEKTRA_SET_RESOURCE_ERRORF (errorKey, "Cannot find class %s", classname);
257 0 : return -1;
258 : }
259 :
260 0 : data->clsKey = (*data->env)->FindClass (data->env, "org/libelektra/Key");
261 0 : if (data->clsKey == 0)
262 : {
263 0 : ELEKTRA_SET_RESOURCE_ERROR (errorKey, "Cannot find class Key");
264 0 : return -1;
265 : }
266 :
267 0 : data->clsKeySet = (*data->env)->FindClass (data->env, "org/libelektra/KeySet");
268 0 : if (data->clsKeySet == 0)
269 : {
270 0 : ELEKTRA_SET_RESOURCE_ERROR (errorKey, "Cannot find class KeySet");
271 0 : return -1;
272 : }
273 :
274 0 : data->midKeyConstr = (*data->env)->GetMethodID (data->env, data->clsKey, "<init>", "(J)V");
275 0 : if (data->midKeyConstr == 0)
276 : {
277 0 : ELEKTRA_SET_RESOURCE_ERROR (errorKey, "Cannot find constructor of Key");
278 0 : return -1;
279 : }
280 :
281 0 : data->midKeySetConstr = (*data->env)->GetMethodID (data->env, data->clsKeySet, "<init>", "(J)V");
282 0 : if (data->midKeySetConstr == 0)
283 : {
284 0 : ELEKTRA_SET_RESOURCE_ERROR (errorKey, "Cannot find constructor of KeySet");
285 0 : return -1;
286 : }
287 :
288 0 : data->midKeyRelease = (*data->env)->GetMethodID (data->env, data->clsKey, "release", "()V");
289 0 : if (data->midKeyRelease == 0)
290 : {
291 0 : ELEKTRA_SET_RESOURCE_ERROR (errorKey, "Cannot find release of Key");
292 0 : return -1;
293 : }
294 :
295 0 : data->midKeySetRelease = (*data->env)->GetMethodID (data->env, data->clsKeySet, "release", "()V");
296 0 : if (data->midKeySetRelease == 0)
297 : {
298 0 : ELEKTRA_SET_RESOURCE_ERROR (errorKey, "Cannot find release of KeySet");
299 0 : return -1;
300 : }
301 :
302 0 : jmethodID midPluginConstructor = (*data->env)->GetMethodID (data->env, data->clsPlugin, "<init>", "()V");
303 0 : if (midPluginConstructor == 0)
304 : {
305 0 : ELEKTRA_SET_RESOURCE_ERROR (errorKey, "Cannot find constructor of plugin");
306 0 : return -1;
307 : }
308 :
309 0 : data->plugin = (*data->env)->NewObject (data->env, data->clsPlugin, midPluginConstructor);
310 0 : checkException (data, "creating plugin", errorKey);
311 0 : if (data->plugin == 0)
312 : {
313 0 : ELEKTRA_SET_PLUGIN_MISBEHAVIOR_ERROR (errorKey, "Cannot create plugin");
314 0 : return -1;
315 : }
316 :
317 0 : return call2Arg (data, config, errorKey, "open");
318 : }
319 :
320 15 : int elektraJniClose (Plugin * handle, Key * errorKey)
321 : {
322 15 : Data * data = elektraPluginGetData (handle);
323 :
324 15 : if (!data || data->module == 1)
325 : {
326 : return 0;
327 : }
328 :
329 0 : int ret = call1Arg (data, errorKey, "close");
330 :
331 0 : (*data->jvm)->DestroyJavaVM (data->jvm);
332 0 : elektraFree (data);
333 :
334 0 : return ret;
335 : }
336 :
337 15 : int elektraJniGet (Plugin * handle, KeySet * returned, Key * parentKey)
338 : {
339 15 : if (!strcmp (keyName (parentKey), "system/elektra/modules/jni"))
340 : {
341 15 : KeySet * contract =
342 15 : ksNew (30, keyNew ("system/elektra/modules/jni", KEY_VALUE, "jni plugin waits for your orders", KEY_END),
343 : keyNew ("system/elektra/modules/jni/exports", KEY_END),
344 : keyNew ("system/elektra/modules/jni/exports/open", KEY_FUNC, elektraJniOpen, KEY_END),
345 : keyNew ("system/elektra/modules/jni/exports/close", KEY_FUNC, elektraJniClose, KEY_END),
346 : keyNew ("system/elektra/modules/jni/exports/get", KEY_FUNC, elektraJniGet, KEY_END),
347 : keyNew ("system/elektra/modules/jni/exports/set", KEY_FUNC, elektraJniSet, KEY_END),
348 : keyNew ("system/elektra/modules/jni/exports/error", KEY_FUNC, elektraJniError, KEY_END),
349 : #include ELEKTRA_README
350 : keyNew ("system/elektra/modules/jni/infos/version", KEY_VALUE, PLUGINVERSION, KEY_END), KS_END);
351 15 : ksAppend (returned, contract);
352 15 : ksDel (contract);
353 : }
354 :
355 15 : Data * data = elektraPluginGetData (handle);
356 :
357 15 : if (!data || data->module == 1)
358 : {
359 : return 0;
360 : }
361 :
362 0 : return call2Arg (data, returned, parentKey, "get");
363 : }
364 :
365 0 : int elektraJniSet (Plugin * handle, KeySet * returned, Key * parentKey)
366 : {
367 0 : Data * data = elektraPluginGetData (handle);
368 0 : return call2Arg (data, returned, parentKey, "set");
369 : }
370 :
371 0 : int elektraJniError (Plugin * handle, KeySet * returned, Key * parentKey)
372 : {
373 0 : Data * data = elektraPluginGetData (handle);
374 0 : return call2Arg (data, returned, parentKey, "error");
375 : }
376 :
377 15 : Plugin * ELEKTRA_PLUGIN_EXPORT
378 : {
379 : // clang-format off
380 15 : return elektraPluginExport("jni",
381 : ELEKTRA_PLUGIN_OPEN, &elektraJniOpen,
382 : ELEKTRA_PLUGIN_CLOSE, &elektraJniClose,
383 : ELEKTRA_PLUGIN_GET, &elektraJniGet,
384 : ELEKTRA_PLUGIN_SET, &elektraJniSet,
385 : ELEKTRA_PLUGIN_ERROR, &elektraJniError,
386 : ELEKTRA_PLUGIN_END);
387 : }
388 :
|