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