1 /*
2 * Copyright (C) 2008 - 2011 Murray Cumming <murrayc@murrayc.com>
3 * Copyright (C) 2008 - 2013 Vivien Malerba <malerba@gnome-db.org>
4 * Copyright (C) 2009 Bas Driessen <bas.driessen@xobas.com>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21 #include <jni-wrapper.h>
22 #include <gda-value.h>
23 #include <gda-server-provider.h>
24
25 gboolean jni_wrapper_describe_exceptions = FALSE;
26
27 static jclass SQLException__class = NULL;
28 static JniWrapperMethod *get_message_method = NULL;
29 static JniWrapperMethod *get_error_code_method = NULL;
30 static JniWrapperMethod *get_sql_state_method = NULL;
31
32 #define JNI_WRAPPER_DEBUG
33 #undef JNI_WRAPPER_DEBUG
34
35 #ifdef G_OS_WIN32
36 #define PATH_SEPARATOR ';'
37 #else
38 #define PATH_SEPARATOR ':'
39 #endif
40
41 /*
42 * Returns: @iostring if it was not NULL, or a new string if a JAR has been added, or %NULL
43 */
44 static GString *
locate_jars(GString * iostring,const gchar * path)45 locate_jars (GString *iostring, const gchar *path)
46 {
47 if (g_str_has_suffix (path, ".jar") ||
48 g_str_has_suffix (path, ".JAR") ||
49 g_str_has_suffix (path, ".Jar")) {
50 if (!iostring)
51 iostring = g_string_new ("-Djava.class.path=");
52 else
53 g_string_append_c (iostring, PATH_SEPARATOR);
54 g_string_append (iostring, path);
55 }
56 else {
57 GDir *dir;
58 dir = g_dir_open (path, 0, NULL);
59 if (dir) {
60 const gchar *file;
61 for (file = g_dir_read_name (dir); file; file = g_dir_read_name (dir)) {
62 if (g_str_has_suffix (file, ".jar") ||
63 g_str_has_suffix (file, ".JAR") ||
64 g_str_has_suffix (file, ".Jar")) {
65 if (!iostring)
66 iostring = g_string_new ("-Djava.class.path=");
67 else
68 g_string_append_c (iostring, PATH_SEPARATOR);
69 g_string_append_printf (iostring, "%s%c%s", path,
70 G_DIR_SEPARATOR, file);
71 }
72 }
73 g_dir_close (dir);
74 }
75 }
76
77 return iostring;
78 }
79
80 /**
81 * jni_wrapper_create
82 * @out_jvm: a pointer to store the JVM instance (NOT %NULL)
83 * @lib_path: a path to where native libraries may be, or %NULL
84 * @class_path: extra path to add to CLASSPATH, or %NULL
85 * @error: a place to store errors, or %NULL
86 *
87 * Creates a new JAVA virtual machine and the new associated JNIEnv
88 *
89 * Returns: a new #JNIEnv, or %NULL if an error occurred
90 */
91 JNIEnv *
jni_wrapper_create_vm(JavaVM ** out_jvm,CreateJavaVMFunc create_func,const gchar * lib_path,const gchar * class_path,GError ** error)92 jni_wrapper_create_vm (JavaVM **out_jvm, CreateJavaVMFunc create_func,
93 const gchar *lib_path, const gchar *class_path, GError **error)
94 {
95 *out_jvm = NULL;
96 #ifndef JNI_VERSION_1_2
97 g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_MISUSE_ERROR,
98 "%s", "Java 1.2 or more is needed");
99 return NULL;
100 #else
101 GString *classpath = NULL;
102 gint nopt;
103 JavaVMOption options[4];
104 JavaVMInitArgs vm_args;
105 JavaVM *jvm;
106 JNIEnv *env;
107 long result;
108 const gchar *tmp;
109
110 if (!create_func) {
111 g_set_error (error,GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_INTERNAL_ERROR,
112 "%s",
113 "The JNI_CreateJavaVM is not identified (as the create_func argument)");
114 return NULL;
115 }
116
117 gchar * confdir;
118 confdir = g_build_path (G_DIR_SEPARATOR_S, g_get_user_data_dir (), "libgda", NULL);
119 if (!g_file_test (confdir, G_FILE_TEST_EXISTS)) {
120 g_free (confdir);
121 confdir = g_build_path (G_DIR_SEPARATOR_S, g_get_home_dir (), ".libgda", NULL);
122 }
123 classpath = locate_jars (classpath, confdir);
124 g_free (confdir);
125
126 if (class_path) {
127 if (!classpath)
128 classpath = g_string_new ("-Djava.class.path=");
129 g_string_append_c (classpath, PATH_SEPARATOR);
130 g_string_append (classpath, class_path);
131 }
132
133 if ((tmp = g_getenv ("CLASSPATH")) && *tmp) {
134 gchar **arr = g_strsplit (tmp, ":", 0);
135 gchar **ptr;
136 for (ptr = arr; ptr && *ptr; ptr++)
137 classpath = locate_jars (classpath, *ptr);
138 g_strfreev (arr);
139 }
140
141 nopt = 0;
142 if (classpath)
143 options[nopt++].optionString = classpath->str;
144 options[nopt++].optionString = "-Djava.compiler=NONE";
145
146 if (lib_path)
147 options[nopt++].optionString = g_strdup_printf ("-Djava.library.path=%s", lib_path);
148 vm_args.nOptions = nopt;
149
150 if (g_getenv ("GDA_JAVA_OPTION")) {
151 const gchar *opt = g_getenv ("GDA_JAVA_OPTION");
152 options[vm_args.nOptions].optionString = (gchar*) opt;
153 vm_args.nOptions++;
154 }
155 vm_args.version = JNI_VERSION_1_2;
156 vm_args.options = options;
157
158 #ifdef JNI_WRAPPER_DEBUG
159 {
160 gint i;
161 for (i = 0; i < vm_args.nOptions; i++)
162 g_print ("VMOption %d: %s\n", i, options[i].optionString);
163 }
164 #endif
165
166 vm_args.ignoreUnrecognized = JNI_FALSE;
167 result = create_func (&jvm,(void **) &env, &vm_args);
168 g_string_free (classpath, TRUE);
169 g_free (options[2].optionString);
170 if ((result == JNI_ERR) || !env) {
171 g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_INTERNAL_ERROR,
172 "%s", "Can't invoke the JVM");
173 return NULL;
174 }
175
176 *out_jvm = jvm;
177
178 #ifdef JNI_WRAPPER_DEBUG
179 g_print ("JVM loaded\n");
180 #endif
181
182 jclass klass;
183 klass = jni_wrapper_class_get (env, "java/lang/Throwable", NULL);
184 if (!klass)
185 g_warning ("Error loading '%s' class (error messages won't be detailed)",
186 "java.lang.Throwable");
187 else {
188 get_message_method = jni_wrapper_method_create (env, klass,
189 "getMessage", "()Ljava/lang/String;",
190 FALSE, NULL);
191 if (!get_message_method)
192 g_warning ("Error loading '%s' method (error messages won't be detailed)",
193 "java.lang.Throwable.getMessage");
194 (*env)->DeleteGlobalRef (env, klass);
195 }
196
197 klass = jni_wrapper_class_get (env, "java/sql/SQLException", NULL);
198 if (!klass)
199 g_warning ("Error loading '%s' class (error messages won't be detailed)",
200 "java.sql.SqlException");
201 else {
202 SQLException__class = klass;
203 get_error_code_method = jni_wrapper_method_create (env, SQLException__class,
204 "getErrorCode", "()I",
205 FALSE, NULL);
206 if (!get_error_code_method)
207 g_warning ("Error loading '%s' method (error messages won't be detailed)",
208 "java.SQLException.getErrorCode");
209
210 get_sql_state_method = jni_wrapper_method_create (env, SQLException__class,
211 "getSQLState", "()Ljava/lang/String;",
212 FALSE, NULL);
213
214 if (!get_sql_state_method)
215 g_warning ("Error loading '%s' method (error messages won't be detailed)",
216 "java.SQLException.getSQLState");
217 }
218
219 return env;
220 #endif
221 }
222
223 /**
224 * jni_wrapper_destroy
225 */
226 void
jni_wrapper_destroy_vm(JavaVM * jvm)227 jni_wrapper_destroy_vm (JavaVM *jvm)
228 {
229 g_return_if_fail (jvm);
230 (*jvm)->DestroyJavaVM (jvm);
231 #ifdef JNI_WRAPPER_DEBUG
232 g_print ("JVM destroyed\n");
233 #endif
234 }
235
236 /**
237 * jni_wrapper_class_create
238 *
239 * Returns: a jclass object as a JNI global reference
240 */
241 jclass
jni_wrapper_class_get(JNIEnv * jenv,const gchar * class_name,GError ** error)242 jni_wrapper_class_get (JNIEnv *jenv, const gchar *class_name, GError **error)
243 {
244 jclass cls, gcls;
245
246 g_return_val_if_fail (jenv, NULL);
247 cls = (*jenv)->FindClass (jenv, class_name);
248 if (jni_wrapper_handle_exception (jenv, NULL, NULL, error)) {
249 #ifdef JNI_WRAPPER_DEBUG
250 g_warning ("Class '%s' NOT found\n", class_name);
251 #endif
252 return NULL;
253 }
254
255 #ifdef JNI_WRAPPER_DEBUG
256 g_print ("Class '%s' found\n", class_name);
257 #endif
258 gcls = (*jenv)->NewGlobalRef (jenv, cls);
259 (*jenv)-> DeleteLocalRef (jenv, cls);
260
261 return gcls;
262 }
263
264 /**
265 * jni_wrapper_instantiate_object
266 *
267 * Returns: a new GValue with a pointer to the new java object
268 */
269 GValue *
jni_wrapper_instantiate_object(JNIEnv * jenv,jclass klass,const gchar * signature,GError ** error,...)270 jni_wrapper_instantiate_object (JNIEnv *jenv, jclass klass, const gchar *signature,
271 GError **error, ...)
272 {
273 JniWrapperMethod *method;
274 GValue *retval;
275 va_list args;
276 JavaVM *jvm;
277
278 g_return_val_if_fail (klass, NULL);
279
280 method = jni_wrapper_method_create (jenv, klass, "<init>", signature, FALSE, error);
281 if (!method)
282 return NULL;
283
284 if ((*jenv)->GetJavaVM (jenv, &jvm))
285 g_error ("Could not attach JAVA virtual machine's current thread");
286
287 retval = g_new0 (GValue, 1);
288 g_value_init (retval, GDA_TYPE_JNI_OBJECT);
289
290 va_start (args, error);
291 gda_value_set_jni_object (retval, jvm, jenv,
292 (*jenv)->NewObjectV (jenv, klass, method->mid, args));
293 va_end (args);
294
295 if (jni_wrapper_handle_exception (jenv, NULL, NULL, error)) {
296 gda_value_free (retval);
297 retval = NULL;
298 }
299
300 jni_wrapper_method_free (jenv, method);
301 return retval;
302 }
303
304 /**
305 * jni_wrapper_handle_exception
306 * @jenv:
307 * @out_error_code: place to store an error code, or %NULL
308 * @out_sql_state: place to store a (new) sql state string, or %NULL
309 * @error: place to store errors, or %NULL
310 *
311 * Returns: TRUE if there was an exception, and clears the exceptions
312 */
313 gboolean
jni_wrapper_handle_exception(JNIEnv * jenv,gint * out_error_code,gchar ** out_sql_state,GError ** error)314 jni_wrapper_handle_exception (JNIEnv *jenv, gint *out_error_code, gchar **out_sql_state, GError **error)
315 {
316 jthrowable exc;
317 GValue *exc_value = NULL;
318
319 if (out_error_code)
320 *out_error_code = 0;
321 if (out_sql_state)
322 *out_sql_state = NULL;
323
324 exc = (*jenv)->ExceptionOccurred (jenv);
325 if (!exc)
326 return FALSE; /* no exception */
327
328 if (jni_wrapper_describe_exceptions) {
329 static gint c = 0;
330 g_print ("JAVA EXCEPTION %d\n", c);
331 (*jenv)->ExceptionDescribe (jenv);
332 g_print ("JAVA EXCEPTION %d\n", c);
333 c++;
334 }
335
336 if (out_error_code || out_sql_state || error) {
337 JavaVM *jvm;
338 exc_value = g_new0 (GValue, 1);
339 g_value_init (exc_value, GDA_TYPE_JNI_OBJECT);
340 if ((*jenv)->GetJavaVM (jenv, &jvm))
341 g_error ("Could not attach JAVA virtual machine's current thread");
342
343 gda_value_set_jni_object (exc_value, jvm, jenv, exc);
344 }
345 (*jenv)->ExceptionClear (jenv);
346
347 if (out_error_code || out_sql_state) {
348 if ((*jenv)->IsInstanceOf (jenv, exc, SQLException__class)) {
349 if (out_error_code) {
350 GValue *res;
351
352 res = jni_wrapper_method_call (jenv, get_error_code_method, exc_value,
353 NULL, NULL, NULL);
354 if (res) {
355 if (G_VALUE_TYPE (res) == G_TYPE_INT)
356 *out_error_code = g_value_get_int (res);
357 gda_value_free (res);
358 }
359 }
360 if (out_sql_state) {
361 GValue *res;
362
363 res = jni_wrapper_method_call (jenv, get_sql_state_method, exc_value,
364 NULL, NULL, NULL);
365 if (res) {
366 if (G_VALUE_TYPE (res) == G_TYPE_STRING)
367 *out_sql_state = g_value_dup_string (res);
368 gda_value_free (res);
369 }
370 }
371 }
372 }
373 (*jenv)->DeleteLocalRef(jenv, exc);
374
375 if (error) {
376 if (get_message_method) {
377 GValue *res;
378
379 res = jni_wrapper_method_call (jenv, get_message_method, exc_value, NULL, NULL, NULL);
380
381 if (res) {
382 if (G_VALUE_TYPE (res) == G_TYPE_STRING) {
383 g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
384 GDA_SERVER_PROVIDER_INTERNAL_ERROR,
385 "%s", g_value_get_string (res));
386 gda_value_free (res);
387 }
388 else {
389 gda_value_free (res);
390 goto fallback;
391 }
392 }
393 else
394 goto fallback;
395 }
396 else
397 goto fallback;
398 }
399
400 gda_value_free (exc_value);
401
402 return TRUE;
403
404 fallback:
405 g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
406 GDA_SERVER_PROVIDER_INTERNAL_ERROR,
407 "%s", "An exception occurred");
408 gda_value_free (exc_value);
409 (*jenv)->DeleteLocalRef(jenv, exc);
410 return TRUE;
411 }
412
413 /**
414 * jni_wrapper_method_create
415 * @jenv:
416 * @klass:
417 * @method_name: name of the requested method
418 * @signature: methods's signature (use "java -p -s <classname>" to get it)
419 * @is_static: TRUE if the requested method is a class method (and FALSE if it's an instance method)
420 * @error: a place to store errors, or %NULL
421 *
422 * Creates a new #JniWrapperMethod structure
423 */
424 JniWrapperMethod *
jni_wrapper_method_create(JNIEnv * jenv,jclass klass,const gchar * method_name,const gchar * signature,gboolean is_static,GError ** error)425 jni_wrapper_method_create (JNIEnv *jenv, jclass klass,
426 const gchar *method_name, const gchar *signature,
427 gboolean is_static, GError **error)
428 {
429 JniWrapperMethod *method;
430 jmethodID mid;
431 const gchar *ptr;
432 g_return_val_if_fail (klass, NULL);
433 if (is_static)
434 mid = (*jenv)->GetStaticMethodID (jenv, klass, method_name, signature);
435 else
436 mid = (*jenv)->GetMethodID (jenv, klass, method_name, signature);
437 if (jni_wrapper_handle_exception (jenv, NULL, NULL, error))
438 return NULL;
439
440 method = g_new0 (JniWrapperMethod, 1);
441 method->klass = (*jenv)->NewGlobalRef (jenv, klass);
442 method->is_static = is_static;
443 method->mid = mid;
444
445 for (ptr = signature; *ptr && (*ptr != ')'); ptr++);
446 g_assert (*ptr);
447 ptr++;
448 method->ret_type = g_strdup (ptr);
449
450 return method;
451 }
452
453 /**
454 * jni_wrapper_method_call
455 * @jenv:
456 * @method:
457 * @object: instance on which the method is executed, or %NULL for a static method call
458 * @out_error_code: place to store an error code, or %NULL
459 * @out_sql_state: place to store a (new) sql state string, or %NULL
460 * @error: a place to store errors, or %NULL
461 * @...: methods' arguments, USED AS IS by the JNI
462 *
463 * Executes an instance method.
464 *
465 * Returns: a GValue (may return a GDA_TYPE_NULL value for functions returning void), or %NULL if an error occurred
466 */
467 GValue *
jni_wrapper_method_call(JNIEnv * jenv,JniWrapperMethod * method,GValue * object,gint * out_error_code,gchar ** out_sql_state,GError ** error,...)468 jni_wrapper_method_call (JNIEnv *jenv, JniWrapperMethod *method, GValue *object,
469 gint *out_error_code, gchar **out_sql_state, GError **error, ...)
470 {
471 GValue *retval;
472 va_list args;
473 jobject jobj = NULL;
474 g_return_val_if_fail (method, NULL);
475 if (method->is_static)
476 g_return_val_if_fail (!object, NULL);
477 else {
478 g_return_val_if_fail (object, NULL);
479 g_return_val_if_fail (G_VALUE_TYPE (object) == GDA_TYPE_JNI_OBJECT, NULL);
480 jobj = gda_value_get_jni_object (object);
481 g_return_val_if_fail (jobj, NULL);
482 }
483
484 if (out_error_code)
485 *out_error_code = 0;
486 if (out_sql_state)
487 *out_sql_state = NULL;
488
489 retval = g_new0 (GValue, 1);
490
491 /* actual method call */
492 va_start (args, error);
493 switch (*method->ret_type) {
494 case 'V':
495 if (method->is_static)
496 (*jenv)->CallStaticVoidMethodV (jenv, method->klass, method->mid, args);
497 else
498 (*jenv)->CallVoidMethodV (jenv, jobj, method->mid, args);
499 gda_value_set_null (retval);
500 break;
501 case '[':
502 case 'L':
503 if (!strcmp (method->ret_type, "Ljava/lang/String;")) {
504 jstring string;
505 gchar *str;
506 if (method->is_static)
507 string = (*jenv)->CallStaticObjectMethodV (jenv, method->klass,
508 method->mid, args);
509 else
510 string = (*jenv)->CallObjectMethodV (jenv, jobj, method->mid, args);
511 if (string) {
512 gint len, ulen;
513 g_value_init (retval, G_TYPE_STRING);
514 len = (*jenv)->GetStringUTFLength (jenv, string);
515 if ((*jenv)->ExceptionCheck (jenv))
516 break;
517 ulen = (*jenv)->GetStringLength (jenv, string);
518 if ((*jenv)->ExceptionCheck (jenv))
519 break;
520 str = g_new (gchar, len + 1);
521 str [len] = 0;
522 if (ulen > 0)
523 (*jenv)->GetStringUTFRegion (jenv, string, 0, ulen, str);
524 if ((*jenv)->ExceptionCheck (jenv)) {
525 g_free (str);
526 break;
527 }
528 g_value_take_string (retval, str);
529
530 (*jenv)-> DeleteLocalRef (jenv, string);
531 }
532 else
533 gda_value_set_null (retval);
534 }
535 else {
536 JavaVM *jvm;
537 if ((*jenv)->GetJavaVM (jenv, &jvm))
538 g_error ("Could not attach JAVA virtual machine's current thread");
539 g_value_init (retval, GDA_TYPE_JNI_OBJECT);
540 if (method->is_static)
541 gda_value_set_jni_object (retval, jvm, jenv,
542 (*jenv)->CallStaticObjectMethodV (jenv, method->klass,
543 method->mid, args));
544 else
545 gda_value_set_jni_object (retval, jvm, jenv,
546 (*jenv)->CallObjectMethodV (jenv,
547 jobj, method->mid, args));
548 }
549 break;
550 case 'Z':
551 g_value_init (retval, G_TYPE_BOOLEAN);
552 if (method->is_static)
553 g_value_set_boolean (retval,
554 (*jenv)->CallStaticBooleanMethodV (jenv, method->klass, method->mid, args));
555 else
556 g_value_set_boolean (retval,
557 (*jenv)->CallBooleanMethodV (jenv, jobj, method->mid, args));
558 break;
559 case 'B':
560 g_value_init (retval, G_TYPE_CHAR);
561 if (method->is_static)
562 g_value_set_schar (retval,
563 (*jenv)->CallStaticByteMethodV (jenv, method->klass, method->mid, args));
564 else
565 g_value_set_schar (retval,
566 (*jenv)->CallByteMethodV (jenv, jobj, method->mid, args));
567 break;
568 case 'C':
569 g_value_init (retval, G_TYPE_INT); // FIXME: should be an unsigned 16 bits value
570 if (method->is_static)
571 g_value_set_int (retval,
572 (*jenv)->CallStaticCharMethodV (jenv, method->klass, method->mid, args));
573 else
574 g_value_set_int (retval,
575 (*jenv)->CallCharMethodV (jenv, jobj, method->mid, args));
576 break;
577 case 'S':
578 g_value_init (retval, G_TYPE_INT); // FIXME: should be a signed 16 bits value
579 if (method->is_static)
580 g_value_set_int (retval,
581 (*jenv)->CallStaticShortMethodV (jenv, method->klass, method->mid, args));
582 else
583 g_value_set_int (retval,
584 (*jenv)->CallShortMethodV (jenv, jobj, method->mid, args));
585 break;
586 case 'I':
587 g_value_init (retval, G_TYPE_INT);
588 if (method->is_static)
589 g_value_set_int (retval,
590 (*jenv)->CallStaticIntMethodV (jenv, method->klass, method->mid, args));
591 else
592 g_value_set_int (retval,
593 (*jenv)->CallIntMethodV (jenv, jobj, method->mid, args));
594 break;
595 case 'J':
596 g_value_init (retval, G_TYPE_INT64);
597 if (method->is_static)
598 g_value_set_int64 (retval,
599 (*jenv)->CallStaticLongMethodV (jenv, method->klass, method->mid, args));
600 else
601 g_value_set_int64 (retval,
602 (*jenv)->CallLongMethodV (jenv, jobj, method->mid, args));
603 break;
604 case 'F':
605 g_value_init (retval, G_TYPE_FLOAT);
606 if (method->is_static)
607 g_value_set_float (retval,
608 (*jenv)->CallStaticFloatMethodV (jenv, method->klass, method->mid, args));
609 else
610 g_value_set_float (retval,
611 (*jenv)->CallFloatMethodV (jenv, jobj, method->mid, args));
612 break;
613 case 'D':
614 g_value_init (retval, G_TYPE_DOUBLE);
615 if (method->is_static)
616 g_value_set_double (retval,
617 (*jenv)->CallStaticDoubleMethodV (jenv, method->klass, method->mid, args));
618 else
619 g_value_set_double (retval,
620 (*jenv)->CallDoubleMethodV (jenv, jobj, method->mid, args));
621 break;
622 default:
623 (*jenv)->FatalError (jenv, "illegal descriptor");
624 }
625 va_end (args);
626
627 if (jni_wrapper_handle_exception (jenv, out_error_code, out_sql_state, error)) {
628 gda_value_free (retval);
629 return NULL;
630 }
631
632 return retval;
633 }
634
635 /**
636 * jni_wrapper_method_free
637 * @jenv:
638 * @method:
639 *
640 */
641 void
jni_wrapper_method_free(JNIEnv * jenv,JniWrapperMethod * method)642 jni_wrapper_method_free (JNIEnv *jenv, JniWrapperMethod *method)
643 {
644 g_return_if_fail (method);
645 (*jenv)->DeleteGlobalRef (jenv, method->klass);
646 g_free (method->ret_type);
647 g_free (method);
648 }
649
650 /**
651 * jni_wrapper_field_create
652 * @jenv:
653 * @klass:
654 * @field_name: name of the requested field
655 * @signature: fields's signature (use "java -p -s <classname>" to get it)
656 * @is_static: TRUE if the requested field is a class field (and FALSE if it's an instance field)
657 * @error: a place to store errors, or %NULL
658 *
659 * Creates a new #JniWrapperField structure
660 */
661 JniWrapperField *
jni_wrapper_field_create(JNIEnv * jenv,jclass klass,const gchar * field_name,const gchar * signature,gboolean is_static,GError ** error)662 jni_wrapper_field_create (JNIEnv *jenv, jclass klass,
663 const gchar *field_name, const gchar *signature,
664 gboolean is_static, GError **error)
665 {
666 JniWrapperField *field;
667 jfieldID fid;
668
669 g_return_val_if_fail (klass, NULL);
670 if (is_static)
671 fid = (*jenv)->GetStaticFieldID (jenv, klass, field_name, signature);
672 else
673 fid = (*jenv)->GetFieldID (jenv, klass, field_name, signature);
674 if (jni_wrapper_handle_exception (jenv, NULL, NULL, error))
675 return NULL;
676
677 field = g_new0 (JniWrapperField, 1);
678 field->klass = (*jenv)->NewGlobalRef (jenv, klass);
679 field->is_static = is_static;
680 field->fid = fid;
681
682 field->type = g_strdup (signature);
683
684 return field;
685 }
686
687
688 /**
689 * jni_wrapper_field_get
690 * @jenv:
691 * @field:
692 * @object: instance of which the field is to be obtained, or %NULL for a static field
693 * @error: a place to store errors, or %NULL
694 *
695 * Set the value of a static field or an instance's field.
696 *
697 * Returns: a GValue (may return a GDA_TYPE_NULL value for functions returning void), or %NULL if an error occurred
698 */
699 GValue *
jni_wrapper_field_get(JNIEnv * jenv,JniWrapperField * field,GValue * object,GError ** error)700 jni_wrapper_field_get (JNIEnv *jenv, JniWrapperField *field, GValue *object, GError **error)
701 {
702 GValue *retval;
703 jobject jobj = NULL;
704 g_return_val_if_fail (field, NULL);
705 if (field->is_static)
706 g_return_val_if_fail (!object, NULL);
707 else {
708 g_return_val_if_fail (object, NULL);
709 g_return_val_if_fail (G_VALUE_TYPE (object) == GDA_TYPE_JNI_OBJECT, NULL);
710 jobj = gda_value_get_jni_object (object);
711 g_return_val_if_fail (jobj, NULL);
712 }
713
714 retval = g_new0 (GValue, 1);
715
716 switch (*field->type) {
717 case '[':
718 case 'L':
719 if (!strcmp (field->type, "Ljava/lang/String;")) {
720 jstring string;
721 gchar *str;
722 if (field->is_static)
723 string = (*jenv)->GetStaticObjectField (jenv, field->klass, field->fid);
724 else
725 string = (*jenv)->GetObjectField (jenv, jobj, field->fid);
726 if (string) {
727 gint len, ulen;
728 g_value_init (retval, G_TYPE_STRING);
729 len = (*jenv)->GetStringUTFLength (jenv, string);
730 if ((*jenv)->ExceptionCheck (jenv))
731 break;
732 ulen = (*jenv)->GetStringLength (jenv, string);
733 if ((*jenv)->ExceptionCheck (jenv))
734 break;
735
736 str = g_new (gchar, len + 1);
737 str [len] = 0;
738 if (ulen > 0)
739 (*jenv)->GetStringUTFRegion (jenv, string, 0, ulen, str);
740 if ((*jenv)->ExceptionCheck (jenv)) {
741 g_free (str);
742 break;
743 }
744 g_value_take_string (retval, str);
745
746 (*jenv)-> DeleteLocalRef (jenv, string);
747 }
748 else
749 gda_value_set_null (retval);
750 }
751 else {
752 JavaVM *jvm;
753 if ((*jenv)->GetJavaVM (jenv, &jvm))
754 g_error ("Could not attach JAVA virtual machine's current thread");
755 g_value_init (retval, GDA_TYPE_JNI_OBJECT);
756 if (field->is_static)
757 gda_value_set_jni_object (retval, jvm, jenv,
758 (*jenv)->GetStaticObjectField (jenv, field->klass, field->fid));
759 else
760 gda_value_set_jni_object (retval, jvm, jenv,
761 (*jenv)->GetObjectField (jenv, jobj, field->fid));
762 }
763 break;
764 case 'Z':
765 g_value_init (retval, G_TYPE_BOOLEAN);
766 if (field->is_static)
767 g_value_set_boolean (retval,
768 (*jenv)->GetStaticBooleanField (jenv, field->klass, field->fid));
769 else
770 g_value_set_boolean (retval,
771 (*jenv)->GetBooleanField (jenv, jobj, field->fid));
772 break;
773 case 'B':
774 g_value_init (retval, G_TYPE_CHAR);
775 if (field->is_static)
776 g_value_set_schar (retval,
777 (*jenv)->GetStaticByteField (jenv, field->klass, field->fid));
778 else
779 g_value_set_schar (retval,
780 (*jenv)->GetByteField (jenv, jobj, field->fid));
781 break;
782 case 'C':
783 g_value_init (retval, G_TYPE_INT); // FIXME: should be an unsigned 16 bits value
784 if (field->is_static)
785 g_value_set_int (retval,
786 (*jenv)->GetStaticCharField (jenv, field->klass, field->fid));
787 else
788 g_value_set_int (retval,
789 (*jenv)->GetCharField (jenv, jobj, field->fid));
790 break;
791 case 'S':
792 g_value_init (retval, G_TYPE_INT); // FIXME: should be a signed 16 bits value
793 if (field->is_static)
794 g_value_set_int (retval,
795 (*jenv)->GetStaticShortField (jenv, field->klass, field->fid));
796 else
797 g_value_set_int (retval,
798 (*jenv)->GetShortField (jenv, jobj, field->fid));
799 break;
800 case 'I':
801 g_value_init (retval, G_TYPE_INT);
802 if (field->is_static)
803 g_value_set_int (retval,
804 (*jenv)->GetStaticIntField (jenv, field->klass, field->fid));
805 else
806 g_value_set_int (retval,
807 (*jenv)->GetIntField (jenv, jobj, field->fid));
808 break;
809 case 'J':
810 g_value_init (retval, G_TYPE_INT64);
811 if (field->is_static)
812 g_value_set_int64 (retval,
813 (*jenv)->GetStaticLongField (jenv, field->klass, field->fid));
814 else
815 g_value_set_int64 (retval,
816 (*jenv)->GetLongField (jenv, jobj, field->fid));
817 break;
818 case 'F':
819 g_value_init (retval, G_TYPE_FLOAT);
820 if (field->is_static)
821 g_value_set_float (retval,
822 (*jenv)->GetStaticFloatField (jenv, field->klass, field->fid));
823 else
824 g_value_set_float (retval,
825 (*jenv)->GetFloatField (jenv, jobj, field->fid));
826 break;
827 case 'D':
828 g_value_init (retval, G_TYPE_DOUBLE);
829 if (field->is_static)
830 g_value_set_double (retval,
831 (*jenv)->GetStaticDoubleField (jenv, field->klass, field->fid));
832 else
833 g_value_set_double (retval,
834 (*jenv)->GetDoubleField (jenv, jobj, field->fid));
835 break;
836 default:
837 (*jenv)->FatalError (jenv, "illegal descriptor");
838 }
839
840 if (jni_wrapper_handle_exception (jenv, NULL, NULL, error)) {
841 gda_value_free (retval);
842 return NULL;
843 }
844
845 return retval;
846 }
847
848 /**
849 * jni_wrapper_field_set
850 * @jenv:
851 * @field:
852 * @object: instance of which the field is to be obtained, or %NULL for a static field
853 * @value: a #GValue to set the field to
854 * @error: a place to store errors, or %NULL
855 *
856 * Set the value of a static field or an instance's field.
857 *
858 * Returns: TRUE if no error occurred
859 */
860 gboolean
jni_wrapper_field_set(JNIEnv * jenv,JniWrapperField * field,GValue * object,const GValue * value,GError ** error)861 jni_wrapper_field_set (JNIEnv *jenv, JniWrapperField *field,
862 GValue *object, const GValue *value, GError **error)
863 {
864 jobject jobj = NULL;
865
866 g_return_val_if_fail (field, FALSE);
867 g_return_val_if_fail (value, FALSE);
868 if (field->is_static)
869 g_return_val_if_fail (!object, FALSE);
870 else {
871 g_return_val_if_fail (object, FALSE);
872 g_return_val_if_fail (G_VALUE_TYPE (object) == GDA_TYPE_JNI_OBJECT, FALSE);
873 jobj = gda_value_get_jni_object (object);
874 g_return_val_if_fail (jobj, FALSE);
875 }
876
877 switch (*field->type) {
878 case '[':
879 case 'L':
880 if (!strcmp (field->type, "Ljava/lang/String;")) {
881 jstring string;
882
883 if (G_VALUE_TYPE (value) == G_TYPE_STRING) {
884 string = (*jenv)->NewStringUTF (jenv, g_value_get_string (value));
885 if (field->is_static)
886 (*jenv)->SetStaticObjectField (jenv, field->klass, field->fid, string);
887 else
888 (*jenv)->SetObjectField (jenv, jobj, field->fid, string);
889 (*jenv)-> DeleteLocalRef (jenv, string);
890 }
891 else if (G_VALUE_TYPE (value) == GDA_TYPE_NULL) {
892 if (field->is_static)
893 (*jenv)->SetStaticObjectField (jenv, field->klass, field->fid, NULL);
894 else
895 (*jenv)->SetObjectField (jenv, jobj, field->fid, NULL);
896 }
897 else
898 goto wrong_type;
899 }
900 else {
901 if (G_VALUE_TYPE (value) == GDA_TYPE_JNI_OBJECT) {
902 if (field->is_static)
903 (*jenv)->SetStaticObjectField (jenv, field->klass, field->fid,
904 gda_value_get_jni_object (value));
905 else
906 (*jenv)->SetObjectField (jenv, jobj, field->fid,
907 gda_value_get_jni_object (value));
908 }
909 else if (G_VALUE_TYPE (value) == 0) {
910 if (field->is_static)
911 (*jenv)->SetStaticObjectField (jenv, field->klass, field->fid, NULL);
912 else
913 (*jenv)->SetObjectField (jenv, jobj, field->fid, NULL);
914 }
915 else
916 goto wrong_type;
917 }
918 break;
919 case 'Z':
920 if (G_VALUE_TYPE (value) != G_TYPE_BOOLEAN)
921 goto wrong_type;
922 if (field->is_static)
923 (*jenv)->SetStaticBooleanField (jenv, field->klass, field->fid, g_value_get_boolean (value));
924 else
925 (*jenv)->SetBooleanField (jenv, jobj, field->fid, g_value_get_boolean (value));
926 break;
927 case 'B':
928 if (G_VALUE_TYPE (value) != G_TYPE_CHAR)
929 goto wrong_type;
930 if (field->is_static)
931 (*jenv)->SetStaticByteField (jenv, field->klass, field->fid, g_value_get_schar (value));
932 else
933 (*jenv)->SetByteField (jenv, jobj, field->fid, g_value_get_schar (value));
934 break;
935 case 'C':
936 if (G_VALUE_TYPE (value) != G_TYPE_INT)
937 goto wrong_type;
938 if (field->is_static)
939 (*jenv)->SetStaticCharField (jenv, field->klass, field->fid, g_value_get_int (value));
940 else
941 (*jenv)->SetCharField (jenv, jobj, field->fid, g_value_get_int (value));
942 break;
943 case 'S':
944 if (G_VALUE_TYPE (value) != G_TYPE_INT)
945 goto wrong_type;
946 if (field->is_static)
947 (*jenv)->SetStaticShortField (jenv, field->klass, field->fid, g_value_get_int (value));
948 else
949 (*jenv)->SetShortField (jenv, jobj, field->fid, g_value_get_int (value));
950 break;
951 case 'I':
952 if (G_VALUE_TYPE (value) != G_TYPE_INT)
953 goto wrong_type;
954 if (field->is_static)
955 (*jenv)->SetStaticIntField (jenv, field->klass, field->fid, g_value_get_int (value));
956 else
957 (*jenv)->SetIntField (jenv, jobj, field->fid, g_value_get_int (value));
958 break;
959 case 'J':
960 if (G_VALUE_TYPE (value) != G_TYPE_INT64)
961 goto wrong_type;
962 if (field->is_static)
963 (*jenv)->SetStaticLongField (jenv, field->klass, field->fid, g_value_get_int64 (value));
964 else
965 (*jenv)->SetLongField (jenv, jobj, field->fid, g_value_get_int64 (value));
966 break;
967 case 'F':
968 if (G_VALUE_TYPE (value) != G_TYPE_FLOAT)
969 goto wrong_type;
970 if (field->is_static)
971 (*jenv)->SetStaticFloatField (jenv, field->klass, field->fid, g_value_get_float (value));
972 else
973 (*jenv)->SetFloatField (jenv, jobj, field->fid, g_value_get_float (value));
974 break;
975 case 'D':
976 if (G_VALUE_TYPE (value) != G_TYPE_DOUBLE)
977 goto wrong_type;
978 if (field->is_static)
979 (*jenv)->SetStaticDoubleField (jenv, field->klass, field->fid, g_value_get_double (value));
980 else
981 (*jenv)->SetDoubleField (jenv, jobj, field->fid, g_value_get_double (value));
982 break;
983 default:
984 (*jenv)->FatalError (jenv, "illegal descriptor");
985 }
986
987 if (jni_wrapper_handle_exception (jenv, NULL, NULL, error))
988 return FALSE;
989
990 return TRUE;
991
992 wrong_type:
993 g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_INTERNAL_ERROR,
994 "%s", "Wrong value type");
995 return FALSE;
996 }
997
998 /**
999 * jni_wrapper_field_free
1000 * @jenv:
1001 * @field:
1002 *
1003 */
1004 void
jni_wrapper_field_free(JNIEnv * jenv,JniWrapperField * field)1005 jni_wrapper_field_free (JNIEnv *jenv, JniWrapperField *field)
1006 {
1007 g_return_if_fail (field);
1008 (*jenv)->DeleteGlobalRef (jenv, field->klass);
1009 g_free (field->type);
1010 g_free (field);
1011 }
1012
1013 /*
1014 * jobject wrapper in a GValue
1015 */
1016 jobject
gda_value_get_jni_object(const GValue * value)1017 gda_value_get_jni_object (const GValue *value)
1018 {
1019 GdaJniObject *jnio = (GdaJniObject*) g_value_get_boxed (value);
1020
1021 g_return_val_if_fail (jnio, NULL);
1022 return jnio->jobj;
1023 }
1024
1025 GValue *
gda_value_new_jni_object(JavaVM * jvm,JNIEnv * env,jobject jni_object)1026 gda_value_new_jni_object (JavaVM *jvm, JNIEnv *env, jobject jni_object)
1027 {
1028 GValue *value;
1029 g_return_val_if_fail (jvm, NULL);
1030 g_return_val_if_fail (env, NULL);
1031 value = g_new0 (GValue, 1);
1032 g_value_init (value, GDA_TYPE_JNI_OBJECT);
1033 gda_value_set_jni_object (value, jvm, env, jni_object);
1034 return value;
1035 }
1036
1037 void
gda_value_set_jni_object(GValue * value,JavaVM * jvm,JNIEnv * env,jobject jni_object)1038 gda_value_set_jni_object (GValue *value, JavaVM *jvm, JNIEnv *env, jobject jni_object)
1039 {
1040 GdaJniObject *jnio;
1041
1042 jnio = g_new (GdaJniObject, 1);
1043 jnio->jvm = jvm;
1044 if (jni_object)
1045 jnio->jobj = (*env)->NewGlobalRef (env, jni_object);
1046 else
1047 jnio->jobj = NULL;
1048 g_value_set_boxed (value, jnio);
1049 }
1050
1051 GType
gda_jni_object_get_type(void)1052 gda_jni_object_get_type (void)
1053 {
1054 static GType type = 0;
1055
1056 if (G_UNLIKELY (type == 0)) {
1057 type = g_boxed_type_register_static ("GdaJniObject",
1058 (GBoxedCopyFunc) gda_jni_object_copy,
1059 (GBoxedFreeFunc) gda_jni_object_free);
1060 }
1061
1062 return type;
1063 }
1064
1065 gpointer
gda_jni_object_copy(gpointer boxed)1066 gda_jni_object_copy (gpointer boxed)
1067 {
1068 JNIEnv *env;
1069 GdaJniObject *src = (GdaJniObject*) boxed;
1070 GdaJniObject *copy;
1071 jint atres;
1072
1073 atres = (*src->jvm)->GetEnv (src->jvm, (void**) &env, JNI_VERSION_1_2);
1074 if (atres == JNI_EDETACHED) {
1075 if ((*src->jvm)->AttachCurrentThread (src->jvm, (void**) &env, NULL) < 0)
1076 g_error ("Could not attach JAVA virtual machine's current thread");
1077 }
1078 else if (atres == JNI_EVERSION)
1079 g_error ("Could not attach JAVA virtual machine's current thread");
1080
1081 copy = g_new (GdaJniObject, 1);
1082 copy->jvm = src->jvm;
1083 copy->jobj = (*env)->NewGlobalRef(env, src->jobj);
1084
1085 if (atres == JNI_EDETACHED)
1086 (*src->jvm)->DetachCurrentThread (src->jvm);
1087
1088 return (gpointer) copy;
1089 }
1090
1091 void
gda_jni_object_free(gpointer boxed)1092 gda_jni_object_free (gpointer boxed)
1093 {
1094 JNIEnv *env;
1095 GdaJniObject *jnio = (GdaJniObject*) boxed;
1096
1097 if (jnio->jobj) {
1098 jint atres;
1099
1100 atres = (*jnio->jvm)->GetEnv (jnio->jvm, (void**) &env, JNI_VERSION_1_2);
1101 if (atres == JNI_EDETACHED) {
1102 if ((*jnio->jvm)->AttachCurrentThread (jnio->jvm, (void**) &env, NULL) < 0)
1103 g_error ("Could not attach JAVA virtual machine's current thread");
1104 }
1105 else if (atres == JNI_EVERSION)
1106 g_error ("Could not attach JAVA virtual machine's current thread");
1107
1108 (*env)->DeleteGlobalRef (env, jnio->jobj);
1109 if (atres == JNI_EDETACHED)
1110 (*jnio->jvm)->DetachCurrentThread (jnio->jvm);
1111 }
1112 g_free (jnio);
1113 }
1114
1115 jlong
jni_cpointer_to_jlong(gconstpointer c_pointer)1116 jni_cpointer_to_jlong (gconstpointer c_pointer)
1117 {
1118 jlong retval;
1119
1120 #if SIZEOF_INT_P == 4
1121 gint32 i32;
1122 i32 = (gint32) c_pointer;
1123 retval = i32;
1124 #else
1125 retval = (jlong) c_pointer;
1126 #endif
1127 return retval;
1128 }
1129
1130 gconstpointer
jni_jlong_to_cpointer(jlong value)1131 jni_jlong_to_cpointer (jlong value)
1132 {
1133 gconstpointer retval;
1134 #if SIZEOF_INT_P == 4
1135 gint32 i32;
1136 i32 = (gint32) value;
1137 retval = (gconstpointer) i32;
1138 #else
1139 retval = (gconstpointer) value;
1140 #endif
1141 return retval;
1142 }
1143