1 /**
2 * This is a simple mechanism to bind Inkscape to Java, and thence
3 * to all of the nice things that can be layered upon that.
4 *
5 * Authors:
6 * Bob Jamison
7 *
8 * Copyright (C) 2007-2008 Bob Jamison
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 3 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 */
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <stdarg.h>
32 #include <string.h>
33 #include <jni.h>
35 #include <sys/types.h>
36 #include <dirent.h>
39 #ifdef __WIN32__
40 #include <windows.h>
41 #else
42 #include <dlfcn.h>
43 #include <errno.h>
44 #endif
46 #if HAVE_SYS_STAT_H
47 #include <sys/stat.h>
48 #endif
50 #include "javabind.h"
51 #include "javabind-private.h"
52 #include <path-prefix.h>
53 #include <prefix.h>
54 #include <glib/gmessages.h>
57 /**
58 * Note: We must limit Java or JVM-specific code to this file
59 * and to dobinding.cpp. It should be hidden from javabind.h
60 */
63 namespace Inkscape
64 {
66 namespace Bind
67 {
70 //########################################################################
71 //# DEFINITIONS
72 //########################################################################
74 typedef jint (*CreateVMFunc)(JavaVM **, JNIEnv **, void *);
78 //########################################################################
79 //# UTILITY
80 //########################################################################
82 /**
83 * Normalize path. Java wants '/', even on Windows
84 */
85 String normalizePath(const String &str)
86 {
87 String buf;
88 for (unsigned int i=0 ; i<str.size() ; i++)
89 {
90 char ch = str[i];
91 if (ch == '\\')
92 buf.push_back('/');
93 else
94 buf.push_back(ch);
95 }
96 return buf;
97 }
100 String getException(JNIEnv *env)
101 {
102 String buf;
103 jthrowable exc = env->ExceptionOccurred();
104 if (!exc)
105 return buf;
106 jclass cls = env->GetObjectClass(exc);
107 jmethodID mid = env->GetMethodID(cls, "toString", "()Ljava/lang/String;");
108 jstring jstr = (jstring) env->CallObjectMethod(exc, mid);
109 const char *str = env->GetStringUTFChars(jstr, JNI_FALSE);
110 buf.append(str);
111 env->ReleaseStringUTFChars(jstr, str);
112 env->ExceptionClear();
113 return buf;
114 }
116 jint getInt(JNIEnv *env, jobject obj, const char *name)
117 {
118 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "I");
119 return env->GetIntField(obj, fid);
120 }
122 void setInt(JNIEnv *env, jobject obj, const char *name, jint val)
123 {
124 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "I");
125 env->SetIntField(obj, fid, val);
126 }
128 jlong getLong(JNIEnv *env, jobject obj, const char *name)
129 {
130 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "J");
131 return env->GetLongField(obj, fid);
132 }
134 void setLong(JNIEnv *env, jobject obj, const char *name, jlong val)
135 {
136 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "J");
137 env->SetLongField(obj, fid, val);
138 }
140 jfloat getFloat(JNIEnv *env, jobject obj, const char *name)
141 {
142 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "F");
143 return env->GetFloatField(obj, fid);
144 }
146 void setFloat(JNIEnv *env, jobject obj, const char *name, jfloat val)
147 {
148 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "F");
149 env->SetFloatField(obj, fid, val);
150 }
152 jdouble getDouble(JNIEnv *env, jobject obj, const char *name)
153 {
154 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "D");
155 return env->GetDoubleField(obj, fid);
156 }
158 void setDouble(JNIEnv *env, jobject obj, const char *name, jdouble val)
159 {
160 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "D");
161 env->SetDoubleField(obj, fid, val);
162 }
164 String getString(JNIEnv *env, jobject obj, const char *name)
165 {
166 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "Ljava/lang/String;");
167 jstring jstr = (jstring)env->GetObjectField(obj, fid);
168 const char *chars = env->GetStringUTFChars(jstr, JNI_FALSE);
169 String str = chars;
170 env->ReleaseStringUTFChars(jstr, chars);
171 return str;
172 }
174 void setString(JNIEnv *env, jobject obj, const char *name, const String &val)
175 {
176 jstring jstr = env->NewStringUTF(val.c_str());
177 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "Ljava/lang/String;");
178 env->SetObjectField(obj, fid, jstr);
179 }
184 //########################################################################
185 //# CONSTRUCTOR/DESTRUCTOR
186 //########################################################################
188 static JavaBinderyImpl *_instance = NULL;
190 JavaBindery *JavaBindery::getInstance()
191 {
192 return JavaBinderyImpl::getInstance();
193 }
195 JavaBinderyImpl *JavaBinderyImpl::getInstance()
196 {
197 if (!_instance)
198 {
199 _instance = new JavaBinderyImpl();
200 }
201 return _instance;
202 }
204 JavaBinderyImpl::JavaBinderyImpl()
205 {
206 jvm = NULL;
207 env = NULL;
208 }
210 JavaBinderyImpl::~JavaBinderyImpl()
211 {
212 }
215 //########################################################################
216 //# MESSAGES
217 //########################################################################
219 void err(const char *fmt, ...)
220 {
221 #if 0
222 va_list args;
223 fprintf(stderr, "JavaBinderyImpl err:");
224 va_start(args, fmt);
225 vfprintf(stderr, fmt, args);
226 va_end(args);
227 fprintf(stderr, "\n");
228 #else
229 va_list args;
230 g_warning("JavaBinderyImpl err:");
231 va_start(args, fmt);
232 g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, fmt, args);
233 va_end(args);
234 g_warning("\n");
235 #endif
236 }
238 void msg(const char *fmt, ...)
239 {
240 #if 0
241 va_list args;
242 fprintf(stdout, "JavaBinderyImpl:");
243 va_start(args, fmt);
244 vfprintf(stdout, fmt, args);
245 va_end(args);
246 fprintf(stdout, "\n");
247 #else
248 va_list args;
249 g_message("JavaBinderyImpl:");
250 va_start(args, fmt);
251 g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, fmt, args);
252 va_end(args);
253 g_message("\n");
254 #endif
255 }
259 //########################################################################
260 //# W I N 3 2 S T Y L E
261 //########################################################################
262 #ifdef __WIN32__
265 #define DIR_SEPARATOR "\\"
266 #define PATH_SEPARATOR ";"
270 static bool getRegistryString(HKEY root, const char *keyName,
271 const char *valName, char *buf, int buflen)
272 {
273 HKEY key;
274 DWORD bufsiz = buflen;
275 RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName, 0, KEY_READ, &key);
276 int ret = RegQueryValueEx(key, TEXT(valName),
277 NULL, NULL, (BYTE *)buf, &bufsiz);
278 if (ret != ERROR_SUCCESS)
279 {
280 err("Key '%s\\%s not found\n", keyName, valName);
281 return false;
282 }
283 RegCloseKey(key);
284 return true;
285 }
288 static String cleanPath(const String &s)
289 {
290 String buf;
291 for (unsigned int i=0 ; i<s.size() ; i++)
292 {
293 char ch = s[i];
294 if (ch != '"')
295 buf.push_back(ch);
296 }
297 return buf;
298 }
301 /**
302 * Common places to find jvm.dll under JAVA_HOME
303 */
304 static const char *commonJavaPaths[] =
305 {
306 "\\jre\\bin\\client\\jvm.dll",
307 "\\bin\\client\\jvm.dll",
308 "\\jvm.dll",
309 NULL
310 };
312 static CreateVMFunc getCreateVMFunc()
313 {
314 bool found = false;
315 String libname;
317 /**
318 * First, look for JAVA_HOME. This will allow the user
319 * to override what's in the registry
320 */
321 const char *envStr = getenv("JAVA_HOME");
322 if (envStr)
323 {
324 String javaHome = cleanPath(envStr);
325 msg("JAVA_HOME='%s'", javaHome.c_str());
326 for (const char **path = commonJavaPaths ; *path ; path++)
327 {
328 String jpath = javaHome;
329 jpath.append(*path);
330 //msg("trying '%s'", jpath.c_str());
331 struct stat finfo;
332 if (stat(jpath.c_str(), &finfo)>=0)
333 {
334 //msg("found");
335 libname = jpath;
336 found = true;
337 break;
338 }
339 }
340 }
342 //not at JAVA_HOME. check the registry
343 if (!found)
344 {
345 char verbuf[16];
346 char regpath[80];
347 strcpy(regpath, "SOFTWARE\\JavaSoft\\Java Runtime Environment");
348 bool ret = getRegistryString(HKEY_LOCAL_MACHINE,
349 regpath, "CurrentVersion", verbuf, 15);
350 if (!ret)
351 {
352 msg("JVM CurrentVersion not found in registry at '%s'", regpath);
353 }
354 else
355 {
356 strcat(regpath, "\\");
357 strcat(regpath, verbuf);
358 //msg("reg path: %s\n", regpath);
359 char valbuf[80];
360 ret = getRegistryString(HKEY_LOCAL_MACHINE,
361 regpath, "RuntimeLib", valbuf, 79);
362 if (ret)
363 {
364 found = true;
365 libname = valbuf;
366 }
367 else
368 {
369 msg("JVM RuntimeLib not found in registry at '%s'",
370 regpath);
371 }
372 }
373 }
375 if (!found)
376 {
377 err("JVM not found at JAVA_HOME or in registry");
378 return NULL;
379 }
381 /**
382 * If we are here, then we seem to have a valid path for jvm.dll
383 * Give it a try
384 */
385 msg("getCreateVMFunc: Loading JVM: %s", libname.c_str());
386 HMODULE lib = LoadLibrary(libname.c_str());
387 if (!lib)
388 {
389 err("Java VM not found at '%s'", libname.c_str());
390 return NULL;
391 }
392 CreateVMFunc createVM = (CreateVMFunc)GetProcAddress(lib, "JNI_CreateJavaVM");
393 if (!createVM)
394 {
395 err("Could not find 'JNI_CreateJavaVM' in shared library '%s'",
396 libname.c_str());
397 return NULL;
398 }
399 return createVM;
400 }
402 static void getJavaRoot(String &javaroot)
403 {
404 char exeName[80];
405 GetModuleFileName(NULL, exeName, 80);
406 char *slashPos = strrchr(exeName, '\\');
407 if (slashPos)
408 *slashPos = '\0';
409 javaroot = exeName;
410 javaroot.append("\\");
411 javaroot.append(INKSCAPE_JAVADIR);
412 }
417 //########################################################################
418 //# U N I X S T Y L E
419 //########################################################################
420 #else /* !__WIN32__ */
423 #define DIR_SEPARATOR "/"
424 #define PATH_SEPARATOR ":"
427 /**
428 * Recursively descend into a directory looking for libjvm.so
429 */
430 static bool findJVMRecursive(const String &dirpath,
431 std::vector<String> &results)
432 {
433 DIR *dir = opendir(dirpath.c_str());
434 if (!dir)
435 return false;
436 bool ret = false;
437 while (true)
438 {
439 struct dirent *de = readdir(dir);
440 if (!de)
441 break;
442 String fname = de->d_name;
443 if (fname == "." || fname == "..")
444 continue;
445 String path = dirpath;
446 path.push_back('/');
447 path.append(fname);
448 if (fname == "libjvm.so")
449 {
450 ret = true;
451 results.push_back(path);
452 continue;
453 }
454 struct stat finfo;
455 if (lstat(path.c_str(), &finfo)<0)
456 {
457 break;
458 }
459 if (finfo.st_mode & S_IFDIR)
460 {
461 ret |= findJVMRecursive(path, results);
462 }
463 }
464 closedir(dir);
465 return ret;
466 }
469 static const char *commonJavaPaths[] =
470 {
471 "/usr/java",
472 "/usr/local/java",
473 "/usr/lib/jvm",
474 "/usr/local/lib/jvm",
475 NULL
476 };
478 /**
479 * Look for a Java VM (libjvm.so) in several Unix places
480 */
481 static bool findJVM(String &result)
482 {
483 std::vector<String> results;
484 int found = false;
486 /* Is there one specified by the user? */
487 const char *javaHome = getenv("JAVA_HOME");
488 if (javaHome && findJVMRecursive(javaHome, results))
489 found = true;
490 else for (const char **path = commonJavaPaths ; *path ; path++)
491 {
492 if (findJVMRecursive(*path, results))
493 {
494 found = true;
495 break;
496 }
497 }
498 if (!found)
499 {
500 return false;
501 }
502 if (results.size() == 0)
503 return false;
504 //Look first for a Client VM
505 for (unsigned int i=0 ; i<results.size() ; i++)
506 {
507 String s = results[i];
508 if (s.find("client") != s.npos)
509 {
510 result = s;
511 return true;
512 }
513 }
514 //else default to the first
515 result = results[0];
516 return true;
517 }
521 static CreateVMFunc getCreateVMFunc()
522 {
523 String libname;
524 if (!findJVM(libname))
525 {
526 err("No Java VM found. Is JAVA_HOME defined? Need to find 'libjvm.so'");
527 return NULL;
528 }
529 msg("getCreateVMFunc: Loading JVM: %s", libname.c_str());
530 void *lib = dlopen(libname.c_str(), RTLD_NOW);
531 if (!lib)
532 {
533 err("Java VM not found at '%s' : %s", libname.c_str(), strerror(errno));
534 return NULL;
535 }
536 CreateVMFunc createVM = (CreateVMFunc)dlsym(lib, "JNI_CreateJavaVM");
537 if (!createVM)
538 {
539 err("Could not find 'JNI_CreateJavaVM' in shared library");
540 return NULL;
541 }
542 return createVM;
543 }
546 static void getJavaRoot(String &javaroot)
547 {
548 javaroot = INKSCAPE_JAVADIR;
549 }
551 #endif /* !__WIN32__ */
554 //########################################################################
555 //# COMMON
556 //########################################################################
559 bool JavaBinderyImpl::isLoaded()
560 {
561 return (jvm != (void *)0);
562 }
566 /**
567 * This will set up the classpath for the launched VM.
568 * We will add two things:
569 * 1. INKSCAPE_JAVADIR/classes -- path to loose classes
570 * 2. A concatenation of all jar files in INKSCAPE_JAVADIR/lib
571 *
572 * This will allow people to add classes and jars to the JVM without
573 * needing to state them explicitly.
574 *
575 * @param javaroot. Should be INKSCAPE_JAVADIR
576 * @param result a string buffer to hold the result of this method
577 */
578 static void populateClassPath(const String &javaroot,
579 String &result)
580 {
581 String classdir = javaroot;
582 classdir.append(DIR_SEPARATOR);
583 classdir.append("classes");
585 String cp = classdir;
587 String libdir = javaroot;
588 libdir.append(DIR_SEPARATOR);
589 libdir.append("lib");
591 DIR *dir = opendir(libdir.c_str());
592 if (!dir)
593 {
594 result = cp;
595 return;
596 }
598 while (true)
599 {
600 struct dirent *de = readdir(dir);
601 if (!de)
602 break;
603 String fname = de->d_name;
604 if (fname == "." || fname == "..")
605 continue;
606 if (fname.size()<5) //x.jar
607 continue;
608 if (fname.compare(fname.size()-4, 4, ".jar") != 0)
609 continue;
611 String path = libdir;
612 path.append(DIR_SEPARATOR);
613 path.append(fname);
615 cp.append(PATH_SEPARATOR);
616 cp.append(path);
617 }
618 closedir(dir);
620 result = cp;
621 }
625 //========================================================================
626 // SCRIPT RUNNER
627 //========================================================================
628 /**
629 * These methods are used to allow the ScriptRunner class to
630 * redirect its stderr and stdout streams to here, to be caught
631 * by two string buffers. We can then use those buffers how we
632 * want. These native methods are only those needed for running
633 * a script. For the main C++/Java bindings, see dobinding.cpp
634 */
635 void JNICALL stdOutWrite(JNIEnv */*env*/, jobject /*obj*/, jlong ptr, jint ch)
636 {
637 JavaBinderyImpl *bind = (JavaBinderyImpl *)ptr;
638 bind->stdOut(ch);
639 }
641 void JNICALL stdErrWrite(JNIEnv */*env*/, jobject /*obj*/, jlong ptr, jint ch)
642 {
643 JavaBinderyImpl *bind = (JavaBinderyImpl *)ptr;
644 bind->stdErr(ch);
645 }
648 void JNICALL logWrite(JNIEnv */*env*/, jobject /*obj*/, jlong ptr, jint ch)
649 {
650 JavaBinderyImpl *bind = (JavaBinderyImpl *)ptr;
651 bind->log(ch);
652 }
655 static JNINativeMethod scriptRunnerMethods[] =
656 {
657 { (char *)"stdOutWrite", (char *)"(JI)V", (void *)stdOutWrite },
658 { (char *)"stdErrWrite", (char *)"(JI)V", (void *)stdErrWrite },
659 { (char *)"logWrite", (char *)"(JI)V", (void *)logWrite },
660 { NULL, NULL, NULL }
661 };
663 /**
664 * This sets up the 'ScriptRunner' java class for execution of
665 * scripts
666 */
667 bool JavaBinderyImpl::setupScriptRunner()
668 {
669 String className = "org/inkscape/cmn/ScriptRunner";
670 if (!registerNatives(className, scriptRunnerMethods))
671 {
672 return false;
673 }
674 jclass cls = env->FindClass(className.c_str());
675 if (!cls)
676 {
677 err("setupScriptRunner: cannot find class '%s'", className.c_str());
678 return false;
679 }
680 jmethodID mid = env->GetMethodID(cls, "<init>", "(J)V");
681 if (!mid)
682 {
683 err("setupScriptRunner: cannot find constructor for '%s'", className.c_str());
684 return false;
685 }
686 jobject obj = env->NewObject(cls, mid, ((jlong)this));
687 if (!obj)
688 {
689 err("setupScriptRunner: cannot construct '%s'", className.c_str());
690 return false;
691 }
692 msg("ScriptRunner ready");
693 return true;
694 }
697 //========================================================================
698 // End SCRIPT RUNNER
699 //========================================================================
702 /**
703 * This is used to grab output from the VM itself. See 'options' below.
704 */
705 static int JNICALL vfprintfHook(FILE* /*f*/, const char *fmt, va_list args)
706 {
707 g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, fmt, args);
708 }
711 /**
712 * This is the most important part of this class. Here we
713 * attempt to find, load, and initialize a java (or mlvm?) virtual
714 * machine.
715 *
716 * @return true if successful, else false
717 */
718 bool JavaBinderyImpl::loadJVM()
719 {
720 if (jvm)
721 return true;
723 CreateVMFunc createVM = getCreateVMFunc();
724 if (!createVM)
725 {
726 err("Could not find 'JNI_CreateJavaVM' in shared library");
727 return false;
728 }
730 String javaroot;
731 getJavaRoot(javaroot);
732 String cp;
733 populateClassPath(javaroot, cp);
734 String classpath = "-Djava.class.path=";
735 classpath.append(normalizePath(cp));
736 msg("Class path is: '%s'", classpath.c_str());
738 String libpath = "-Djava.library.path=";
739 libpath.append(javaroot);
740 libpath.append(DIR_SEPARATOR);
741 libpath.append("libm");
742 libpath = normalizePath(libpath);
743 msg("Lib path is: '%s'", libpath.c_str());
745 JavaVMInitArgs vm_args;
746 JavaVMOption options[4];
747 options[0].optionString = (char *)classpath.c_str();
748 options[1].optionString = (char *)libpath.c_str();
749 options[2].optionString = (char *)"-verbose:jni";
750 options[3].optionString = (char *)"vfprintf";
751 options[3].extraInfo = (void *)vfprintfHook;
752 vm_args.version = JNI_VERSION_1_4;
753 vm_args.options = options;
754 vm_args.nOptions = 4;
755 vm_args.ignoreUnrecognized = true;
757 if (createVM(&jvm, &env, &vm_args) < 0)
758 {
759 err("JNI_CreateJavaVM() failed");
760 return false;
761 }
763 //get jvm version
764 jint vers = env->GetVersion();
765 int versionMajor = (vers>>16) & 0xffff;
766 int versionMinor = (vers ) & 0xffff;
767 msg("Loaded JVM version %d.%d", versionMajor, versionMinor);
769 if (!setupScriptRunner())
770 return false;
773 return true;
774 }
777 /**
778 * This is a difficult method. What we are doing is trying to
779 * call a static method with a list of arguments. Similar to
780 * a varargs call, we need to marshal the Values into their
781 * Java equivalents and make the proper call.
782 *
783 * @param type the return type of the method
784 * @param className the full (package / name) name of the java class
785 * @param methodName the name of the method being invoked
786 * @param signature the method signature (ex: "(Ljava/lang/String;I)V" )
787 * that describes the param and return types of the method.
788 * @param retval the return value of the java method
789 * @return true if the call was successful, else false. This is not
790 * the return value of the method.
791 */
792 bool JavaBinderyImpl::callStatic(int type,
793 const String &className,
794 const String &methodName,
795 const String &signature,
796 const std::vector<Value> ¶ms,
797 Value &retval)
798 {
799 jclass cls = env->FindClass(className.c_str());
800 if (!cls)
801 {
802 err("Could not find class '%s'", className.c_str());
803 return false;
804 }
805 jmethodID mid = env->GetStaticMethodID(cls,
806 methodName.c_str(), signature.c_str());
807 if (!mid)
808 {
809 err("Could not find method '%s:%s/%s'", className.c_str(),
810 methodName.c_str(), signature.c_str());
811 return false;
812 }
813 /**
814 * Assemble your parameters into a form usable by JNI
815 */
816 jvalue *jvals = new jvalue[params.size()];
817 for (unsigned int i=0 ; i<params.size() ; i++)
818 {
819 Value v = params[i];
820 switch (v.getType())
821 {
822 case Value::BIND_BOOLEAN:
823 {
824 jvals[i].z = (jboolean)v.getBoolean();
825 break;
826 }
827 case Value::BIND_INT:
828 {
829 jvals[i].i = (jint)v.getInt();
830 break;
831 }
832 case Value::BIND_DOUBLE:
833 {
834 jvals[i].d = (jdouble)v.getDouble();
835 break;
836 }
837 case Value::BIND_STRING:
838 {
839 jvals[i].l = (jobject) env->NewStringUTF(v.getString().c_str());
840 break;
841 }
842 default:
843 {
844 err("Unknown value type: %d", v.getType());
845 return false;
846 }
847 }
848 }
849 switch (type)
850 {
851 case Value::BIND_VOID:
852 {
853 env->CallStaticVoidMethodA(cls, mid, jvals);
854 break;
855 }
856 case Value::BIND_BOOLEAN:
857 {
858 env->CallStaticBooleanMethodA(cls, mid, jvals);
859 break;
860 }
861 case Value::BIND_INT:
862 {
863 env->CallStaticIntMethodA(cls, mid, jvals);
864 break;
865 }
866 case Value::BIND_DOUBLE:
867 {
868 env->CallStaticDoubleMethodA(cls, mid, jvals);
869 break;
870 }
871 case Value::BIND_STRING:
872 {
873 env->CallStaticObjectMethodA(cls, mid, jvals);
874 break;
875 }
876 default:
877 {
878 err("Unknown return type: %d", type);
879 return false;
880 }
881 }
882 delete jvals;
883 String errStr = getException(env);
884 if (errStr.size()>0)
885 {
886 err("callStatic: %s", errStr.c_str());
887 return false;
888 }
889 return true;
890 }
894 /**
895 * Convenience method to call the static void main(String argv[])
896 * method of a given class
897 *
898 * @param className full name of the java class
899 * @return true if successful, else false
900 */
901 bool JavaBinderyImpl::callMain(const String &className)
902 {
903 std::vector<Value> parms;
904 Value retval;
905 return callStatic(Value::BIND_VOID, className, "main",
906 "([Ljava/lang/String;)V", parms, retval);
907 }
910 /**
911 * Used to register an array of native methods for a named class
912 *
913 * @param className the full name of the java class
914 * @param the method array
915 * @return true if successful, else false
916 */
917 bool JavaBinderyImpl::registerNatives(const String &className,
918 const JNINativeMethod *methods)
919 {
920 jclass cls = env->FindClass(className.c_str());
921 if (!cls)
922 {
923 err("Could not find class '%s'", className.c_str());
924 return false;
925 }
926 //msg("registerNatives: class '%s' found", className.c_str());
928 /**
929 * hack for JDK bug http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6493522
930 */
931 jmethodID mid = env->GetMethodID(env->GetObjectClass(cls), "getConstructors",
932 "()[Ljava/lang/reflect/Constructor;");
933 if (!mid)
934 {
935 err("Could not get reflect mid for 'getConstructors' : %s",
936 getException(env).c_str());
937 return false;
938 }
939 jobject res = env->CallObjectMethod(cls, mid);
940 if (!res)
941 {
942 err("Could not get constructors");
943 return false;
944 }
945 /**
946 * end hack
947 */
948 jint nrMethods = 0;
949 for (const JNINativeMethod *m = methods ; m->name ; m++)
950 nrMethods++;
951 jint ret = env->RegisterNatives(cls, methods, nrMethods);
952 if (ret < 0)
953 {
954 err("Could not register %d native methods for '%s' : %s",
955 nrMethods, className.c_str(), getException(env).c_str());
956 return false;
957 }
958 return true;
959 }
964 } // namespace Bind
965 } // namespace Inkscape
967 //########################################################################
968 //# E N D O F F I L E
969 //########################################################################