1 /**
2 * @file
3 * @brief This is a simple mechanism to bind Inkscape to Java, and thence
4 * to all of the nice things that can be layered upon that.
5 *
6 * Authors:
7 * Bob Jamison
8 *
9 * Copyright (C) 2007-2008 Bob Jamison
10 *
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 3 of the License, or (at your option) any later version.
15 *
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
20 *
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24 */
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <stdarg.h>
33 #include <string.h>
34 #include <jni.h>
36 #include <sys/types.h>
37 #include <dirent.h>
40 #ifdef __WIN32__
41 #include <windows.h>
42 #else
43 #include <dlfcn.h>
44 #include <errno.h>
45 #endif
47 #if HAVE_SYS_STAT_H
48 #include <sys/stat.h>
49 #endif
51 #include "javabind.h"
52 #include "javabind-private.h"
53 #include <path-prefix.h>
54 #include <prefix.h>
55 #include <glib/gmessages.h>
57 //For repr and document
58 #include <document.h>
59 #include <inkscape.h>
60 #include <xml/repr.h>
62 /**
63 * Note: We must limit Java or JVM-specific code to this file
64 * and to dobinding.cpp. It should be hidden from javabind.h
65 *
66 * This file is mostly about getting things up and running, and
67 * providing the basic C-to-Java hooks.
68 *
69 * dobinding.cpp will have the rote and repetitious
70 * class-by-class binding
71 */
74 namespace Inkscape
75 {
77 namespace Bind
78 {
81 //########################################################################
82 //# DEFINITIONS
83 //########################################################################
85 typedef jint (*CreateVMFunc)(JavaVM **, JNIEnv **, void *);
89 //########################################################################
90 //# UTILITY
91 //########################################################################
93 /**
94 * Normalize path. Java wants '/', even on Windows
95 */
96 String normalizePath(const String &str)
97 {
98 String buf;
99 for (unsigned int i=0 ; i<str.size() ; i++)
100 {
101 char ch = str[i];
102 if (ch == '\\')
103 buf.push_back('/');
104 else
105 buf.push_back(ch);
106 }
107 return buf;
108 }
111 /**
112 * Convert a java string to a C++ string
113 */
114 String getString(JNIEnv *env, jstring jstr)
115 {
116 const char *chars = env->GetStringUTFChars(jstr, JNI_FALSE);
117 String str = chars;
118 env->ReleaseStringUTFChars(jstr, chars);
119 return str;
120 }
123 /**
124 * Check if the VM has encountered an Exception. If so, get the String for it
125 * and clear the exception
126 */
127 String getExceptionString(JNIEnv *env)
128 {
129 String buf;
130 jthrowable exc = env->ExceptionOccurred();
131 if (!exc)
132 return buf;
133 jclass cls = env->GetObjectClass(exc);
134 jmethodID mid = env->GetMethodID(cls, "toString", "()Ljava/lang/String;");
135 jstring jstr = (jstring) env->CallObjectMethod(exc, mid);
136 buf.append(getString(env, jstr));
137 env->ExceptionClear();
138 return buf;
139 }
141 jint getObjInt(JNIEnv *env, jobject obj, const char *name)
142 {
143 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "I");
144 return env->GetIntField(obj, fid);
145 }
147 void setObjInt(JNIEnv *env, jobject obj, const char *name, jint val)
148 {
149 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "I");
150 env->SetIntField(obj, fid, val);
151 }
153 jlong getObjLong(JNIEnv *env, jobject obj, const char *name)
154 {
155 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "J");
156 return env->GetLongField(obj, fid);
157 }
159 void setObjLong(JNIEnv *env, jobject obj, const char *name, jlong val)
160 {
161 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "J");
162 env->SetLongField(obj, fid, val);
163 }
165 jfloat getObjFloat(JNIEnv *env, jobject obj, const char *name)
166 {
167 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "F");
168 return env->GetFloatField(obj, fid);
169 }
171 void setObjFloat(JNIEnv *env, jobject obj, const char *name, jfloat val)
172 {
173 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "F");
174 env->SetFloatField(obj, fid, val);
175 }
177 jdouble getObjDouble(JNIEnv *env, jobject obj, const char *name)
178 {
179 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "D");
180 return env->GetDoubleField(obj, fid);
181 }
183 void setObjDouble(JNIEnv *env, jobject obj, const char *name, jdouble val)
184 {
185 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "D");
186 env->SetDoubleField(obj, fid, val);
187 }
189 String getObjString(JNIEnv *env, jobject obj, const char *name)
190 {
191 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "Ljava/lang/String;");
192 jstring jstr = (jstring)env->GetObjectField(obj, fid);
193 return getString(env, jstr);
194 }
196 void setObjString(JNIEnv *env, jobject obj, const char *name, const String &val)
197 {
198 jstring jstr = env->NewStringUTF(val.c_str());
199 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "Ljava/lang/String;");
200 env->SetObjectField(obj, fid, jstr);
201 }
206 //########################################################################
207 //# CONSTRUCTOR/DESTRUCTOR
208 //########################################################################
210 static JavaBinderyImpl *_instance = NULL;
212 JavaBindery *JavaBindery::getInstance()
213 {
214 return JavaBinderyImpl::getInstance();
215 }
217 JavaBinderyImpl *JavaBinderyImpl::getInstance()
218 {
219 if (!_instance)
220 {
221 _instance = new JavaBinderyImpl();
222 }
223 return _instance;
224 }
226 JavaBinderyImpl::JavaBinderyImpl()
227 {
228 jvm = NULL;
229 env = NULL;
230 gatewayObj = NULL;
231 }
233 JavaBinderyImpl::~JavaBinderyImpl()
234 {
235 }
238 //########################################################################
239 //# MESSAGES
240 //########################################################################
242 void err(const char *fmt, ...)
243 {
244 #if 0
245 va_list args;
246 fprintf(stderr, "JavaBinderyImpl err:");
247 va_start(args, fmt);
248 vfprintf(stderr, fmt, args);
249 va_end(args);
250 fprintf(stderr, "\n");
251 #else
252 va_list args;
253 g_warning("JavaBinderyImpl err:");
254 va_start(args, fmt);
255 g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, fmt, args);
256 va_end(args);
257 g_warning("\n");
258 #endif
259 }
261 void msg(const char *fmt, ...)
262 {
263 #if 0
264 va_list args;
265 fprintf(stdout, "JavaBinderyImpl:");
266 va_start(args, fmt);
267 vfprintf(stdout, fmt, args);
268 va_end(args);
269 fprintf(stdout, "\n");
270 #else
271 va_list args;
272 g_message("JavaBinderyImpl:");
273 va_start(args, fmt);
274 g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, fmt, args);
275 va_end(args);
276 g_message("\n");
277 #endif
278 }
282 //########################################################################
283 //# W I N 3 2 S T Y L E
284 //########################################################################
285 #ifdef __WIN32__
288 #define DIR_SEPARATOR "\\"
289 #define PATH_SEPARATOR ";"
293 static bool getRegistryString(HKEY /*root*/, const char *keyName,
294 const char *valName, char *buf, int buflen)
295 {
296 HKEY key;
297 DWORD bufsiz = buflen;
298 RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName, 0, KEY_READ, &key);
299 int ret = RegQueryValueEx(key, TEXT(valName),
300 NULL, NULL, (BYTE *)buf, &bufsiz);
301 if (ret != ERROR_SUCCESS)
302 {
303 err("Key '%s\\%s not found\n", keyName, valName);
304 return false;
305 }
306 RegCloseKey(key);
307 return true;
308 }
311 static String cleanPath(const String &s)
312 {
313 String buf;
314 for (unsigned int i=0 ; i<s.size() ; i++)
315 {
316 char ch = s[i];
317 if (ch != '"')
318 buf.push_back(ch);
319 }
320 return buf;
321 }
324 /**
325 * Common places to find jvm.dll under JAVA_HOME
326 */
327 static const char *commonJavaPaths[] =
328 {
329 "\\jre\\bin\\client\\jvm.dll",
330 "\\bin\\client\\jvm.dll",
331 "\\jvm.dll",
332 NULL
333 };
336 /**
337 * Return the directory of the .exe that is currently running
338 */
339 static String getExePath()
340 {
341 char exeName[MAX_PATH+1];
342 GetModuleFileName(NULL, exeName, MAX_PATH);
343 char *slashPos = strrchr(exeName, '\\');
344 if (slashPos)
345 *slashPos = '\0';
346 String s = exeName;
347 return s;
348 }
351 /**
352 * Check a directory for several possibilities of sub-locations
353 * under it, where a jvm might exist.
354 */
355 static String checkPathUnderRoot(const String &root)
356 {
357 for (const char **path = commonJavaPaths ; *path ; path++)
358 {
359 String jpath = root;
360 jpath.append(*path);
361 //msg("trying '%s'", jpath.c_str());
362 struct stat finfo;
363 if (stat(jpath.c_str(), &finfo)>=0)
364 {
365 //msg("found");
366 return jpath;
367 }
368 }
369 return "";
370 }
374 /**
375 * Attempt to find and load a jvm.dll file. Find the createVM()
376 * function's address and return it
377 */
378 static CreateVMFunc getCreateVMFunc()
379 {
380 bool found = false;
381 String libname;
383 /**
384 * First, look for an embedded jre in the .exe's dir.
385 * This allows us to package our own JRE if we want to.
386 */
387 String inkscapeHome = getExePath();
388 inkscapeHome.append("\\jre");
389 msg("INKSCAPE_HOME='%s'", inkscapeHome.c_str());
390 String path = checkPathUnderRoot(inkscapeHome);
391 if (path.size() > 0)
392 {
393 libname = path;
394 found = true;
395 }
397 /**
398 * Next, look for JAVA_HOME. This will allow the user
399 * to override what's in the registry
400 */
401 if (!found)
402 {
403 const char *envStr = getenv("JAVA_HOME");
404 if (envStr)
405 {
406 String javaHome = cleanPath(envStr);
407 msg("JAVA_HOME='%s'", javaHome.c_str());
408 path = checkPathUnderRoot(javaHome);
409 if (path.size() > 0)
410 {
411 libname = path;
412 found = true;
413 }
414 }
415 }
417 //not at JAVA_HOME. check the registry
418 if (!found)
419 {
420 char verbuf[16];
421 char regpath[80];
422 strcpy(regpath, "SOFTWARE\\JavaSoft\\Java Runtime Environment");
423 bool ret = getRegistryString(HKEY_LOCAL_MACHINE,
424 regpath, "CurrentVersion", verbuf, 15);
425 if (!ret)
426 {
427 msg("JVM CurrentVersion not found in registry at '%s'", regpath);
428 }
429 else
430 {
431 strcat(regpath, "\\");
432 strcat(regpath, verbuf);
433 //msg("reg path: %s\n", regpath);
434 char valbuf[80];
435 ret = getRegistryString(HKEY_LOCAL_MACHINE,
436 regpath, "RuntimeLib", valbuf, 79);
437 if (ret)
438 {
439 found = true;
440 libname = valbuf;
441 }
442 else
443 {
444 msg("JVM RuntimeLib not found in registry at '%s'",
445 regpath);
446 }
447 }
448 }
450 if (!found)
451 {
452 err("JVM not found at JAVA_HOME or in registry");
453 return NULL;
454 }
456 /**
457 * If we are here, then we seem to have a valid path for jvm.dll
458 * Give it a try
459 */
460 msg("getCreateVMFunc: Loading JVM: %s", libname.c_str());
461 HMODULE lib = LoadLibrary(libname.c_str());
462 if (!lib)
463 {
464 err("Java VM not found at '%s'", libname.c_str());
465 return NULL;
466 }
467 CreateVMFunc createVM = (CreateVMFunc)GetProcAddress(lib, "JNI_CreateJavaVM");
468 if (!createVM)
469 {
470 err("Could not find 'JNI_CreateJavaVM' in shared library '%s'",
471 libname.c_str());
472 return NULL;
473 }
474 return createVM;
475 }
477 /**
478 * Return the directory where the Java classes/libs/resources are
479 * located
480 */
481 static void getJavaRoot(String &javaroot)
482 {
483 /*
484 javaroot = getExePath();
485 javaroot.append("\\");
486 javaroot.append(INKSCAPE_BINDDIR);
487 javaroot.append("\\java");
488 */
489 javaroot = INKSCAPE_BINDDIR;
490 javaroot.append("\\java");
491 }
496 //########################################################################
497 //# U N I X S T Y L E
498 //########################################################################
499 #else /* !__WIN32__ */
502 #define DIR_SEPARATOR "/"
503 #define PATH_SEPARATOR ":"
506 /**
507 * Recursively descend into a directory looking for libjvm.so
508 */
509 static bool findJVMRecursive(const String &dirpath,
510 std::vector<String> &results)
511 {
512 DIR *dir = opendir(dirpath.c_str());
513 if (!dir)
514 return false;
515 bool ret = false;
516 while (true)
517 {
518 struct dirent *de = readdir(dir);
519 if (!de)
520 break;
521 String fname = de->d_name;
522 if (fname == "." || fname == "..")
523 continue;
524 String path = dirpath;
525 path.push_back('/');
526 path.append(fname);
527 if (fname == "libjvm.so")
528 {
529 ret = true;
530 results.push_back(path);
531 continue;
532 }
533 struct stat finfo;
534 if (lstat(path.c_str(), &finfo)<0)
535 {
536 break;
537 }
538 if (finfo.st_mode & S_IFDIR)
539 {
540 ret |= findJVMRecursive(path, results);
541 }
542 }
543 closedir(dir);
544 return ret;
545 }
548 /**
549 * Some common places on a Unix filesystem where JVMs are
550 * often found.
551 */
552 static const char *commonJavaPaths[] =
553 {
554 "/usr/lib/jvm/jre",
555 "/usr/lib/jvm",
556 "/usr/local/lib/jvm/jre",
557 "/usr/local/lib/jvm",
558 "/usr/java",
559 "/usr/local/java",
560 NULL
561 };
565 /**
566 * Look for a Java VM (libjvm.so) in several Unix places
567 */
568 static bool findJVM(String &result)
569 {
570 std::vector<String> results;
571 int found = false;
573 /* Is there one specified by the user? */
574 const char *javaHome = getenv("JAVA_HOME");
575 if (javaHome && findJVMRecursive(javaHome, results))
576 found = true;
577 else for (const char **path = commonJavaPaths ; *path ; path++)
578 {
579 if (findJVMRecursive(*path, results))
580 {
581 found = true;
582 break;
583 }
584 }
585 if (!found)
586 {
587 return false;
588 }
589 if (results.size() == 0)
590 return false;
591 //Look first for a Client VM
592 for (unsigned int i=0 ; i<results.size() ; i++)
593 {
594 String s = results[i];
595 if (s.find("client") != s.npos)
596 {
597 result = s;
598 return true;
599 }
600 }
601 //else default to the first
602 result = results[0];
603 return true;
604 }
608 /**
609 * Attempt to find and load a jvm.dll file. Find the createVM()
610 * function's address and return it
611 */
612 static CreateVMFunc getCreateVMFunc()
613 {
614 String libname;
615 if (!findJVM(libname))
616 {
617 err("No Java VM found. Is JAVA_HOME defined? Need to find 'libjvm.so'");
618 return NULL;
619 }
620 msg("getCreateVMFunc: Loading JVM: %s", libname.c_str());
621 void *lib = dlopen(libname.c_str(), RTLD_NOW);
622 if (!lib)
623 {
624 err("Java VM not found at '%s' : %s", libname.c_str(), strerror(errno));
625 return NULL;
626 }
627 CreateVMFunc createVM = (CreateVMFunc)dlsym(lib, "JNI_CreateJavaVM");
628 if (!createVM)
629 {
630 err("Could not find 'JNI_CreateJavaVM' in shared library");
631 return NULL;
632 }
633 return createVM;
634 }
637 /**
638 * Return the directory where the Java classes/libs/resources are
639 * located
640 */
641 static void getJavaRoot(String &javaroot)
642 {
643 javaroot = INKSCAPE_BINDDIR;
644 javaroot.append("/java");
645 }
647 #endif /* !__WIN32__ */
650 //########################################################################
651 //# COMMON
652 //########################################################################
655 bool JavaBinderyImpl::isLoaded()
656 {
657 return (jvm != (void *)0);
658 }
662 /**
663 * This will set up the classpath for the launched VM.
664 * We will add two things:
665 * 1. INKSCAPE_JAVADIR/classes -- path to loose classes
666 * 2. A concatenation of all jar files in INKSCAPE_JAVADIR/lib
667 *
668 * This will allow people to add classes and jars to the JVM without
669 * needing to state them explicitly.
670 *
671 * @param javaroot. Should be INKSCAPE_JAVADIR
672 * @param result a string buffer to hold the result of this method
673 */
674 static void populateClassPath(const String &javaroot,
675 String &result)
676 {
677 String classdir = javaroot;
678 classdir.append(DIR_SEPARATOR);
679 classdir.append("classes");
681 String cp = classdir;
683 String libdir = javaroot;
684 libdir.append(DIR_SEPARATOR);
685 libdir.append("lib");
687 DIR *dir = opendir(libdir.c_str());
688 if (!dir)
689 {
690 result = cp;
691 return;
692 }
694 while (true)
695 {
696 struct dirent *de = readdir(dir);
697 if (!de)
698 break;
699 String fname = de->d_name;
700 if (fname == "." || fname == "..")
701 continue;
702 if (fname.size()<5) //x.jar
703 continue;
704 if (fname.compare(fname.size()-4, 4, ".jar") != 0)
705 continue;
707 String path = libdir;
708 path.append(DIR_SEPARATOR);
709 path.append(fname);
711 cp.append(PATH_SEPARATOR);
712 cp.append(path);
713 }
714 closedir(dir);
716 result = cp;
717 }
721 //========================================================================
722 // Gateway
723 //========================================================================
724 /**
725 * This is provided to scripts can grab the current copy or the
726 * repr tree. If anyone has a smarter way of doing this, please implement.
727 */
728 jstring JNICALL documentGet(JNIEnv *env, jobject /*obj*/, jlong /*ptr*/)
729 {
730 //JavaBinderyImpl *bind = (JavaBinderyImpl *)ptr;
731 String buf = sp_repr_save_buf((SP_ACTIVE_DOCUMENT)->rdoc);
732 jstring jstr = env->NewStringUTF(buf.c_str());
733 return jstr;
734 }
736 /**
737 * This is provided to scripts can load an XML tree into Inkscape.
738 * If anyone has a smarter way of doing this, please implement.
739 */
740 jboolean JNICALL documentSet(JNIEnv */*env*/, jobject /*obj*/, jlong /*ptr*/, jstring /*jstr*/)
741 {
742 /*
743 JavaBinderyImpl *bind = (JavaBinderyImpl *)ptr;
744 String s = getString(env, jstr);
745 SPDocument *doc = sp_document_new_from_mem(s.c_str(), s.size(), true);
746 */
747 return JNI_TRUE;
748 }
750 /**
751 * This method is used to allow the gateway class to
752 * redirect its logging stream here.
753 * For the main C++/Java bindings, see dobinding.cpp
754 */
755 void JNICALL logWrite(JNIEnv */*env*/, jobject /*obj*/, jlong ptr, jint ch)
756 {
757 JavaBinderyImpl *bind = (JavaBinderyImpl *)ptr;
758 bind->log(ch);
759 }
762 static JNINativeMethod gatewayMethods[] =
763 {
764 { (char *)"documentGet", (char *)"(J)Ljava/lang/String;", (void *)documentGet },
765 { (char *)"documentSet", (char *)"(JLjava/lang/String;)Z", (void *)documentSet },
766 { (char *)"logWrite", (char *)"(JI)V", (void *)logWrite },
767 { NULL, NULL, NULL }
768 };
771 /**
772 * This sets up the 'Gateway' java class for execution of
773 * scripts. The class's constructor takes a jlong. This java long
774 * is used to store the pointer to 'this'. When ScriptRunner makes
775 * native calls, it passes that jlong back, so that it can call the
776 * methods of this C++ class.
777 */
778 bool JavaBinderyImpl::setupGateway()
779 {
780 String className = "org/inkscape/cmn/Gateway";
781 if (!registerNatives(className, gatewayMethods))
782 {
783 return false;
784 }
785 jclass cls = env->FindClass(className.c_str());
786 if (!cls)
787 {
788 err("setupGateway: cannot find class '%s' : %s",
789 className.c_str(), getException().c_str());
790 return false;
791 }
792 jmethodID mid = env->GetMethodID(cls, "<init>", "(J)V");
793 if (!mid)
794 {
795 err("setupGateway: cannot find constructor for '%s' : %s",
796 className.c_str(), getException().c_str());
797 return false;
798 }
799 gatewayObj = env->NewObject(cls, mid, ((jlong)this));
800 if (!gatewayObj)
801 {
802 err("setupGateway: cannot construct '%s' : %s",
803 className.c_str(), getException().c_str());
804 return false;
805 }
807 msg("Gateway ready");
808 return true;
809 }
811 bool JavaBinderyImpl::scriptRun(const String &lang, const String &script)
812 {
813 if (!loadJVM())
814 return false;
816 std::vector<Value> params;
817 Value langParm(lang);
818 params.push_back(langParm);
819 Value scriptParm(script);
820 params.push_back(scriptParm);
821 Value retval;
822 callInstance(Value::BIND_VOID, gatewayObj, "scriptRun",
823 "(Ljava/lang/String;Ljava/lang/String;)Z", params, retval);
824 return retval.getBoolean();
825 }
827 bool JavaBinderyImpl::scriptRunFile(const String &lang, const String &fname)
828 {
829 if (!loadJVM())
830 return false;
832 std::vector<Value> params;
833 Value langParm(lang);
834 params.push_back(langParm);
835 Value fnameParm(fname);
836 params.push_back(fnameParm);
837 Value retval;
838 callInstance(Value::BIND_VOID, gatewayObj, "scriptRunFile",
839 "(Ljava/lang/String;Ljava/lang/String;)Z", params, retval);
840 return retval.getBoolean();
841 }
843 bool JavaBinderyImpl::showConsole()
844 {
845 if (!loadJVM())
846 return false;
848 std::vector<Value> params;
849 Value retval;
850 callInstance(Value::BIND_VOID, gatewayObj, "showConsole",
851 "()Z", params, retval);
852 return retval.getBoolean();
853 }
856 //========================================================================
857 // End Gateway
858 //========================================================================
861 /**
862 * This is used to grab output from the VM itself. See 'options' below.
863 */
864 static int JNICALL vfprintfHook(FILE* /*f*/, const char *fmt, va_list args)
865 {
866 g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, fmt, args);
867 return JNI_TRUE;
868 }
871 /**
872 * This is the most important part of this class. Here we
873 * attempt to find, load, and initialize a java (or mlvm?) virtual
874 * machine.
875 *
876 * @return true if successful, else false
877 */
878 bool JavaBinderyImpl::loadJVM()
879 {
880 if (jvm)
881 return true;
883 CreateVMFunc createVM = getCreateVMFunc();
884 if (!createVM)
885 {
886 err("Could not find 'JNI_CreateJavaVM' in shared library");
887 return false;
888 }
890 String javaroot;
891 getJavaRoot(javaroot);
892 String cp;
893 populateClassPath(javaroot, cp);
894 String classpath = "-Djava.class.path=";
895 classpath.append(normalizePath(cp));
896 msg("Class path is: '%s'", classpath.c_str());
898 String libpath = "-Djava.library.path=";
899 libpath.append(javaroot);
900 libpath.append(DIR_SEPARATOR);
901 libpath.append("libm");
902 libpath = normalizePath(libpath);
903 msg("Lib path is: '%s'", libpath.c_str());
905 JavaVMInitArgs vm_args;
906 JavaVMOption options[10];//should be enough
907 int nOptions = 0;
908 options[nOptions++].optionString = (char *)classpath.c_str();
909 options[nOptions++].optionString = (char *)libpath.c_str();
910 //options[nOptions++].optionString = (char *)"-verbose:jni";
911 options[nOptions ].optionString = (char *)"vfprintf";
912 options[nOptions++].extraInfo = (void *)vfprintfHook;
913 vm_args.version = JNI_VERSION_1_4;
914 vm_args.options = options;
915 vm_args.nOptions = nOptions;
916 vm_args.ignoreUnrecognized = true;
918 if (createVM(&jvm, &env, &vm_args) < 0)
919 {
920 err("JNI_CreateJavaVM() failed");
921 return false;
922 }
924 //get jvm version
925 jint vers = env->GetVersion();
926 int versionMajor = (vers>>16) & 0xffff;
927 int versionMinor = (vers ) & 0xffff;
928 msg("Loaded JVM version %d.%d", versionMajor, versionMinor);
930 if (!setupGateway())
931 return false;
933 return true;
934 }
937 /**
938 * This is a difficult method. What we are doing is trying to
939 * call a static method with a list of arguments. Similar to
940 * a varargs call, we need to marshal the Values into their
941 * Java equivalents and make the proper call.
942 *
943 * @param type the return type of the method
944 * @param className the full (package / name) name of the java class
945 * @param methodName the name of the method being invoked
946 * @param signature the method signature (ex: "(Ljava/lang/String;I)V" )
947 * that describes the param and return types of the method.
948 * @param retval the return value of the java method
949 * @return true if the call was successful, else false. This is not
950 * the return value of the method.
951 */
952 bool JavaBinderyImpl::callStatic(int type,
953 const String &className,
954 const String &methodName,
955 const String &signature,
956 const std::vector<Value> ¶ms,
957 Value &retval)
958 {
959 jclass cls = env->FindClass(className.c_str());
960 if (!cls)
961 {
962 err("Could not find class '%s' : %s",
963 className.c_str(), getException().c_str());
964 return false;
965 }
966 jmethodID mid = env->GetStaticMethodID(cls,
967 methodName.c_str(), signature.c_str());
968 if (!mid)
969 {
970 err("Could not find method '%s:%s/%s' : %s",
971 className.c_str(), methodName.c_str(),
972 signature.c_str(), getException().c_str());
973 return false;
974 }
975 /**
976 * Assemble your parameters into a form usable by JNI
977 */
978 jvalue *jvals = new jvalue[params.size()];
979 for (unsigned int i=0 ; i<params.size() ; i++)
980 {
981 Value v = params[i];
982 switch (v.getType())
983 {
984 case Value::BIND_BOOLEAN:
985 {
986 jvals[i].z = (jboolean)v.getBoolean();
987 break;
988 }
989 case Value::BIND_INT:
990 {
991 jvals[i].i = (jint)v.getInt();
992 break;
993 }
994 case Value::BIND_DOUBLE:
995 {
996 jvals[i].d = (jdouble)v.getDouble();
997 break;
998 }
999 case Value::BIND_STRING:
1000 {
1001 jvals[i].l = (jobject) env->NewStringUTF(v.getString().c_str());
1002 break;
1003 }
1004 default:
1005 {
1006 err("Unknown value type: %d", v.getType());
1007 return false;
1008 }
1009 }
1010 }
1011 switch (type)
1012 {
1013 case Value::BIND_VOID:
1014 {
1015 env->CallStaticVoidMethodA(cls, mid, jvals);
1016 break;
1017 }
1018 case Value::BIND_BOOLEAN:
1019 {
1020 jboolean ret = env->CallStaticBooleanMethodA(cls, mid, jvals);
1021 if (ret == JNI_TRUE) //remember, don't truncate
1022 retval.setBoolean(true);
1023 else
1024 retval.setBoolean(false);
1025 break;
1026 }
1027 case Value::BIND_INT:
1028 {
1029 jint ret = env->CallStaticIntMethodA(cls, mid, jvals);
1030 retval.setInt(ret);
1031 break;
1032 }
1033 case Value::BIND_DOUBLE:
1034 {
1035 jdouble ret = env->CallStaticDoubleMethodA(cls, mid, jvals);
1036 retval.setDouble(ret);
1037 break;
1038 }
1039 case Value::BIND_STRING:
1040 {
1041 jobject ret = env->CallStaticObjectMethodA(cls, mid, jvals);
1042 jstring jstr = (jstring) ret;
1043 const char *str = env->GetStringUTFChars(jstr, JNI_FALSE);
1044 retval.setString(str);
1045 env->ReleaseStringUTFChars(jstr, str);
1046 break;
1047 }
1048 default:
1049 {
1050 err("Unknown return type: %d", type);
1051 return false;
1052 }
1053 }
1054 delete jvals;
1055 String errStr = getException();
1056 if (errStr.size()>0)
1057 {
1058 err("callStatic: %s", errStr.c_str());
1059 return false;
1060 }
1061 return true;
1062 }
1066 /**
1067 * Another difficult method. However, this time we are operating
1068 * on an existing instance jobject.
1069 *
1070 * @param type the return type of the method
1071 * @param obj the instance upon which to make the call
1072 * @param methodName the name of the method being invoked
1073 * @param signature the method signature (ex: "(Ljava/lang/String;I)V" )
1074 * that describes the param and return types of the method.
1075 * @param retval the return value of the java method
1076 * @return true if the call was successful, else false. This is not
1077 * the return value of the method.
1078 */
1079 bool JavaBinderyImpl::callInstance(
1080 int type,
1081 const jobject obj,
1082 const String &methodName,
1083 const String &signature,
1084 const std::vector<Value> ¶ms,
1085 Value &retval)
1086 {
1087 jmethodID mid = env->GetMethodID(env->GetObjectClass(obj),
1088 methodName.c_str(), signature.c_str());
1089 if (!mid)
1090 {
1091 err("Could not find method '%s/%s' : %s",
1092 methodName.c_str(),
1093 signature.c_str(), getException().c_str());
1094 return false;
1095 }
1096 /**
1097 * Assemble your parameters into a form usable by JNI
1098 */
1099 jvalue *jvals = new jvalue[params.size()];
1100 for (unsigned int i=0 ; i<params.size() ; i++)
1101 {
1102 Value v = params[i];
1103 switch (v.getType())
1104 {
1105 case Value::BIND_BOOLEAN:
1106 {
1107 jvals[i].z = (jboolean)v.getBoolean();
1108 break;
1109 }
1110 case Value::BIND_INT:
1111 {
1112 jvals[i].i = (jint)v.getInt();
1113 break;
1114 }
1115 case Value::BIND_DOUBLE:
1116 {
1117 jvals[i].d = (jdouble)v.getDouble();
1118 break;
1119 }
1120 case Value::BIND_STRING:
1121 {
1122 jvals[i].l = (jobject) env->NewStringUTF(v.getString().c_str());
1123 break;
1124 }
1125 default:
1126 {
1127 err("Unknown value type: %d", v.getType());
1128 return false;
1129 }
1130 }
1131 }
1132 switch (type)
1133 {
1134 case Value::BIND_VOID:
1135 {
1136 env->CallVoidMethodA(obj, mid, jvals);
1137 break;
1138 }
1139 case Value::BIND_BOOLEAN:
1140 {
1141 jboolean ret = env->CallBooleanMethodA(obj, mid, jvals);
1142 if (ret == JNI_TRUE) //remember, don't truncate
1143 retval.setBoolean(true);
1144 else
1145 retval.setBoolean(false);
1146 break;
1147 }
1148 case Value::BIND_INT:
1149 {
1150 jint ret = env->CallIntMethodA(obj, mid, jvals);
1151 retval.setInt(ret);
1152 break;
1153 }
1154 case Value::BIND_DOUBLE:
1155 {
1156 jdouble ret = env->CallDoubleMethodA(obj, mid, jvals);
1157 retval.setDouble(ret);
1158 break;
1159 }
1160 case Value::BIND_STRING:
1161 {
1162 jobject ret = env->CallObjectMethodA(obj, mid, jvals);
1163 jstring jstr = (jstring) ret;
1164 const char *str = env->GetStringUTFChars(jstr, JNI_FALSE);
1165 retval.setString(str);
1166 env->ReleaseStringUTFChars(jstr, str);
1167 break;
1168 }
1169 default:
1170 {
1171 err("Unknown return type: %d", type);
1172 return false;
1173 }
1174 }
1175 delete jvals;
1176 String errStr = getException();
1177 if (errStr.size()>0)
1178 {
1179 err("callStatic: %s", errStr.c_str());
1180 return false;
1181 }
1182 return true;
1183 }
1188 /**
1189 * Fetch the last exception from the JVM, if any. Clear it to
1190 * continue processing
1191 *
1192 * @return the exception's descriptio,if any. Else ""
1193 */
1194 String JavaBinderyImpl::getException()
1195 {
1196 return getExceptionString(env);
1197 }
1201 /**
1202 * Convenience method to call the static void main(String argv[])
1203 * method of a given class
1204 *
1205 * @param className full name of the java class
1206 * @args the argument strings to the method
1207 * @return true if successful, else false
1208 */
1209 bool JavaBinderyImpl::callMain(const String &className,
1210 const std::vector<String> &args)
1211 {
1212 std::vector<Value> parms;
1213 for (unsigned int i=0 ; i<args.size() ; i++)
1214 {
1215 Value v;
1216 v.setString(args[i]);
1217 parms.push_back(v);
1218 }
1219 Value retval;
1220 return callStatic(Value::BIND_VOID, className, "main",
1221 "([Ljava/lang/String;)V", parms, retval);
1222 }
1225 /**
1226 * Used to register an array of native methods for a named class
1227 *
1228 * @param className the full name of the java class
1229 * @param the method array
1230 * @return true if successful, else false
1231 */
1232 bool JavaBinderyImpl::registerNatives(const String &className,
1233 const JNINativeMethod *methods)
1234 {
1235 jclass cls = env->FindClass(className.c_str());
1236 if (!cls)
1237 {
1238 err("Could not find class '%s'", className.c_str());
1239 return false;
1240 }
1241 //msg("registerNatives: class '%s' found", className.c_str());
1243 /**
1244 * hack for JDK bug http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6493522
1245 */
1246 jmethodID mid = env->GetMethodID(env->GetObjectClass(cls), "getConstructors",
1247 "()[Ljava/lang/reflect/Constructor;");
1248 if (!mid)
1249 {
1250 err("Could not get reflect mid for 'getConstructors' : %s",
1251 getException().c_str());
1252 return false;
1253 }
1254 jobject res = env->CallObjectMethod(cls, mid);
1255 if (!res)
1256 {
1257 err("Could not get constructors : %s", getException().c_str());
1258 return false;
1259 }
1260 /**
1261 * end hack
1262 */
1263 jint nrMethods = 0;
1264 for (const JNINativeMethod *m = methods ; m->name ; m++)
1265 nrMethods++;
1266 jint ret = env->RegisterNatives(cls, methods, nrMethods);
1267 if (ret < 0)
1268 {
1269 err("Could not register %d native methods for '%s' : %s",
1270 nrMethods, className.c_str(), getException().c_str());
1271 return false;
1272 }
1273 return true;
1274 }
1279 } // namespace Bind
1280 } // namespace Inkscape
1282 //########################################################################
1283 //# E N D O F F I L E
1284 //########################################################################