Code

Use MAX_PATH instead of arbitrary string length. Improve comments.
[inkscape.git] / src / bind / javabind.cpp
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>
56 //For repr and document
57 #include <document.h>
58 #include <inkscape.h>
59 #include <xml/repr.h>
61 /**
62  * Note: We must limit Java or JVM-specific code to this file
63  * and to dobinding.cpp.  It should be hidden from javabind.h
64  * 
65  * This file is mostly about getting things up and running, and
66  * providing the basic C-to-Java hooks.
67  *   
68  * dobinding.cpp will have the rote and repetitious
69  * class-by-class binding   
70  */  
73 namespace Inkscape
74 {
76 namespace Bind
77 {
80 //########################################################################
81 //# DEFINITIONS
82 //########################################################################
84 typedef jint (*CreateVMFunc)(JavaVM **, JNIEnv **, void *);
88 //########################################################################
89 //# UTILITY
90 //########################################################################
92 /**
93  * Normalize path.  Java wants '/', even on Windows
94  */ 
95 String normalizePath(const String &str)
96 {
97     String buf;
98     for (unsigned int i=0 ; i<str.size() ; i++)
99         {
100         char ch = str[i];
101         if (ch == '\\')
102             buf.push_back('/');
103         else
104             buf.push_back(ch);
105                 }
106         return buf;
110 /**
111  * Convert a java string to a C++ string
112  */
113 String getString(JNIEnv *env, jstring jstr)
115     const char *chars = env->GetStringUTFChars(jstr, JNI_FALSE);
116     String str = chars;
117     env->ReleaseStringUTFChars(jstr, chars);
118     return str;
122 /**
123  * Check if the VM has encountered an Exception.  If so, get the String for it
124  * and clear the exception
125  */
126 String getExceptionString(JNIEnv *env)
128     String buf;
129     jthrowable exc = env->ExceptionOccurred();
130     if (!exc)
131         return buf;
132     jclass cls = env->GetObjectClass(exc);
133     jmethodID mid = env->GetMethodID(cls, "toString", "()Ljava/lang/String;");
134     jstring jstr = (jstring) env->CallObjectMethod(exc, mid);
135     buf.append(getString(env, jstr));
136     env->ExceptionClear();
137         return buf;
140 jint getObjInt(JNIEnv *env, jobject obj, const char *name)
142     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "I");
143     return env->GetIntField(obj, fid);
146 void setObjInt(JNIEnv *env, jobject obj, const char *name, jint val)
148     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "I");
149     env->SetIntField(obj, fid, val);
152 jlong getObjLong(JNIEnv *env, jobject obj, const char *name)
154     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "J");
155     return env->GetLongField(obj, fid);
158 void setObjLong(JNIEnv *env, jobject obj, const char *name, jlong val)
160     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "J");
161     env->SetLongField(obj, fid, val);
164 jfloat getObjFloat(JNIEnv *env, jobject obj, const char *name)
166     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "F");
167     return env->GetFloatField(obj, fid);
170 void setObjFloat(JNIEnv *env, jobject obj, const char *name, jfloat val)
172     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "F");
173     env->SetFloatField(obj, fid, val);
176 jdouble getObjDouble(JNIEnv *env, jobject obj, const char *name)
178     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "D");
179     return env->GetDoubleField(obj, fid);
182 void setObjDouble(JNIEnv *env, jobject obj, const char *name, jdouble val)
184     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "D");
185     env->SetDoubleField(obj, fid, val);
188 String getObjString(JNIEnv *env, jobject obj, const char *name)
190     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "Ljava/lang/String;");
191     jstring jstr = (jstring)env->GetObjectField(obj, fid);
192     return getString(env, jstr);
195 void setObjString(JNIEnv *env, jobject obj, const char *name, const String &val)
197     jstring jstr = env->NewStringUTF(val.c_str());
198     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "Ljava/lang/String;");
199     env->SetObjectField(obj, fid, jstr);
205 //########################################################################
206 //# CONSTRUCTOR/DESTRUCTOR
207 //########################################################################
209 static JavaBinderyImpl *_instance = NULL;
211 JavaBindery *JavaBindery::getInstance()
213     return JavaBinderyImpl::getInstance();
216 JavaBinderyImpl *JavaBinderyImpl::getInstance()
218     if (!_instance)
219         {
220         _instance = new JavaBinderyImpl();
221         }
222     return _instance;
225 JavaBinderyImpl::JavaBinderyImpl()
227     jvm        = NULL;
228     env        = NULL;
229     gatewayObj = NULL;
232 JavaBinderyImpl::~JavaBinderyImpl()
237 //########################################################################
238 //# MESSAGES
239 //########################################################################
241 void err(const char *fmt, ...)
243 #if 0
244     va_list args;
245     fprintf(stderr, "JavaBinderyImpl err:");
246     va_start(args, fmt);
247     vfprintf(stderr, fmt, args);
248     va_end(args);
249     fprintf(stderr, "\n");
250 #else
251     va_list args;
252     g_warning("JavaBinderyImpl err:");
253     va_start(args, fmt);
254     g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, fmt, args);
255     va_end(args);
256     g_warning("\n");
257 #endif
260 void msg(const char *fmt, ...)
262 #if 0
263     va_list args;
264     fprintf(stdout, "JavaBinderyImpl:");
265     va_start(args, fmt);
266     vfprintf(stdout, fmt, args);
267     va_end(args);
268     fprintf(stdout, "\n");
269 #else
270     va_list args;
271     g_message("JavaBinderyImpl:");
272     va_start(args, fmt);
273     g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, fmt, args);
274     va_end(args);
275     g_message("\n");
276 #endif
281 //########################################################################
282 //# W I N 3 2      S T Y L E
283 //########################################################################
284 #ifdef __WIN32__
287 #define DIR_SEPARATOR "\\"
288 #define PATH_SEPARATOR ";"
292 static bool getRegistryString(HKEY /*root*/, const char *keyName,
293                const char *valName, char *buf, int buflen)
295     HKEY key;
296     DWORD bufsiz  = buflen;
297     RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName, 0, KEY_READ, &key);
298     int ret = RegQueryValueEx(key, TEXT(valName),
299             NULL, NULL, (BYTE *)buf, &bufsiz);
300     if (ret != ERROR_SUCCESS)
301         {
302         err("Key '%s\\%s not found\n", keyName, valName);
303         return false;
304         }
305     RegCloseKey(key);
306     return true;
310 static String cleanPath(const String &s)
312     String buf;
313     for (unsigned int i=0 ; i<s.size() ; i++)
314         {
315         char ch = s[i];
316         if (ch != '"')
317             buf.push_back(ch);
318                 }
319         return buf;
323 /**
324  * Common places to find jvm.dll under JAVA_HOME
325  */ 
326 static const char *commonJavaPaths[] =
328     "\\jre\\bin\\client\\jvm.dll",
329     "\\bin\\client\\jvm.dll",
330     "\\jvm.dll",
331     NULL
332 };
335 /**
336  * Return the directory of the .exe that is currently running
337  */
338 static String getExePath()
340     char exeName[MAX_PATH+1];
341     GetModuleFileName(NULL, exeName, MAX_PATH);
342     char *slashPos = strrchr(exeName, '\\');
343     if (slashPos)
344         *slashPos = '\0';
345     String s = exeName;
346     return s;
350 /**
351  * Check a directory for several possibilities of sub-locations
352  * under it, where a jvm might exist.
353  */
354 static String checkPathUnderRoot(const String &root)
356     for (const char **path = commonJavaPaths ; *path ; path++)
357         {
358         String jpath = root;
359         jpath.append(*path);
360         //msg("trying '%s'", jpath.c_str());
361         struct stat finfo;
362         if (stat(jpath.c_str(), &finfo)>=0)
363             {
364             //msg("found");
365             return jpath;
366             }
367         }
368     return "";
373 /**
374  * Attempt to find and load a jvm.dll file.  Find the createVM()
375  * function's address and return it
376  */
377 static CreateVMFunc getCreateVMFunc()
379     bool found = false;
380     String libname;
382     /**
383      * First, look for an embedded jre in the .exe's dir.
384      * This allows us to package our own JRE if we want to.
385      */
386     String inkscapeHome = getExePath();
387     inkscapeHome.append("\\jre");
388     msg("INKSCAPE_HOME='%s'", inkscapeHome.c_str());
389     String path = checkPathUnderRoot(inkscapeHome);
390     if (path.size() > 0)
391         {
392         libname = path;
393         found = true;
394         }
396     /**
397      * Next, look for JAVA_HOME.  This will allow the user
398      * to override what's in the registry
399      */
400     if (!found)
401         {
402         const char *envStr = getenv("JAVA_HOME");
403         if (envStr)
404             {
405             String javaHome = cleanPath(envStr);
406             msg("JAVA_HOME='%s'", javaHome.c_str());
407             path = checkPathUnderRoot(javaHome);
408             if (path.size() > 0)
409                 {
410                 libname = path;
411                 found = true;
412                 }
413             }
414         }
416     //not at JAVA_HOME.  check the registry
417     if (!found)
418         {
419         char verbuf[16];
420         char regpath[80];
421         strcpy(regpath, "SOFTWARE\\JavaSoft\\Java Runtime Environment");
422         bool ret = getRegistryString(HKEY_LOCAL_MACHINE,
423                      regpath, "CurrentVersion", verbuf, 15);
424         if (!ret)
425             {
426             msg("JVM CurrentVersion not found in registry at '%s'", regpath);
427             }
428         else
429             {
430             strcat(regpath, "\\");
431             strcat(regpath, verbuf);
432             //msg("reg path: %s\n", regpath);
433             char valbuf[80];
434             ret = getRegistryString(HKEY_LOCAL_MACHINE,
435                      regpath, "RuntimeLib", valbuf, 79);
436             if (ret)
437                 {
438                 found = true;
439                 libname = valbuf;
440                 }
441             else
442                 {
443                 msg("JVM RuntimeLib not found in registry at '%s'",
444                                           regpath);
445                                 }
446                         }
447         }
449     if (!found)
450         {
451         err("JVM not found at JAVA_HOME or in registry");
452         return NULL;
453         }
455     /**
456      * If we are here, then we seem to have a valid path for jvm.dll
457      * Give it a try
458      */              
459     msg("getCreateVMFunc: Loading JVM: %s", libname.c_str());
460     HMODULE lib = LoadLibrary(libname.c_str());
461     if (!lib)
462         {
463         err("Java VM not found at '%s'", libname.c_str());
464         return NULL;
465         }
466     CreateVMFunc createVM = (CreateVMFunc)GetProcAddress(lib, "JNI_CreateJavaVM");
467     if (!createVM)
468         {
469         err("Could not find 'JNI_CreateJavaVM' in shared library '%s'",
470                                    libname.c_str());
471         return NULL;
472         }
473     return createVM;
476 /**
477  * Return the directory where the Java classes/libs/resources are
478  * located
479  */
480 static void getJavaRoot(String &javaroot)
482     javaroot = getExePath();
483     javaroot.append("\\");
484     javaroot.append(INKSCAPE_BINDDIR);
485     javaroot.append("\\java");
491 //########################################################################
492 //# U N I X    S T Y L E
493 //########################################################################
494 #else /* !__WIN32__ */
497 #define DIR_SEPARATOR "/"
498 #define PATH_SEPARATOR ":"
501 /**
502  * Recursively descend into a directory looking for libjvm.so
503  */
504 static bool findJVMRecursive(const String &dirpath,
505                              std::vector<String> &results)
507     DIR *dir = opendir(dirpath.c_str());
508     if (!dir)
509         return false;
510     bool ret = false;
511     while (true)
512         {
513         struct dirent *de = readdir(dir);
514         if (!de)
515             break;
516         String fname = de->d_name;
517         if (fname == "." || fname == "..")
518             continue;
519         String path = dirpath;
520         path.push_back('/');
521         path.append(fname);
522         if (fname == "libjvm.so")
523             {
524             ret = true;
525             results.push_back(path);
526             continue;
527             }
528         struct stat finfo;
529         if (lstat(path.c_str(), &finfo)<0)
530             {
531             break;
532             }
533         if (finfo.st_mode & S_IFDIR)
534             {
535             ret |= findJVMRecursive(path, results);
536             }
537         }
538     closedir(dir);
539     return ret;
543 /**
544  * Some common places on a Unix filesystem where JVMs are
545  * often found.
546  */
547 static const char *commonJavaPaths[] =
549     "/usr/lib/jvm/jre",
550     "/usr/lib/jvm",
551     "/usr/local/lib/jvm/jre",
552     "/usr/local/lib/jvm",
553     "/usr/java",
554     "/usr/local/java",
555     NULL
556 };
560 /**
561  * Look for a Java VM (libjvm.so) in several Unix places
562  */
563 static bool findJVM(String &result)
565     std::vector<String> results;
566     int found = false;
568     /* Is there one specified by the user? */
569     const char *javaHome = getenv("JAVA_HOME");
570     if (javaHome && findJVMRecursive(javaHome, results))
571         found = true;
572     else for (const char **path = commonJavaPaths ; *path ; path++)
573         {
574         if (findJVMRecursive(*path, results))
575             {
576             found = true;
577             break;
578             }
579         }
580     if (!found)
581         {
582         return false;
583         }
584     if (results.size() == 0)
585         return false;
586     //Look first for a Client VM
587     for (unsigned int i=0 ; i<results.size() ; i++)
588         {
589         String s = results[i];
590         if (s.find("client") != s.npos)
591             {
592             result = s;
593             return true;
594             }
595         }
596     //else default to the first
597     result = results[0];
598     return true;
603 /**
604  * Attempt to find and load a jvm.dll file.  Find the createVM()
605  * function's address and return it
606  */
607 static CreateVMFunc getCreateVMFunc()
609     String libname;
610     if (!findJVM(libname))
611         {
612         err("No Java VM found. Is JAVA_HOME defined?  Need to find 'libjvm.so'");
613         return NULL;
614         }
615     msg("getCreateVMFunc: Loading JVM: %s", libname.c_str());
616     void *lib = dlopen(libname.c_str(), RTLD_NOW);
617     if (!lib)
618         {
619         err("Java VM not found at '%s' : %s", libname.c_str(), strerror(errno));
620         return NULL;
621         }
622     CreateVMFunc createVM = (CreateVMFunc)dlsym(lib, "JNI_CreateJavaVM");
623     if (!createVM)
624         {
625         err("Could not find 'JNI_CreateJavaVM' in shared library");
626             return NULL;
627         }
628     return createVM;
632 /**
633  * Return the directory where the Java classes/libs/resources are
634  * located
635  */
636 static void getJavaRoot(String &javaroot)
638     javaroot = INKSCAPE_BINDDIR;
639     javaroot.append("/java");
642 #endif /* !__WIN32__ */
645 //########################################################################
646 //# COMMON
647 //########################################################################
650 bool JavaBinderyImpl::isLoaded()
652     return (jvm != (void *)0);
657 /**
658  * This will set up the classpath for the launched VM.
659  * We will add two things:
660  *   1.  INKSCAPE_JAVADIR/classes -- path to loose classes
661  *   2.  A concatenation of all jar files in INKSCAPE_JAVADIR/lib
662  *
663  * This will allow people to add classes and jars to the JVM without
664  * needing to state them explicitly.
665  * 
666  * @param javaroot.  Should be INKSCAPE_JAVADIR
667  * @param result a string buffer to hold the result of this method   
668  */        
669 static void populateClassPath(const String &javaroot,
670                               String &result)
672     String classdir = javaroot;
673     classdir.append(DIR_SEPARATOR);
674     classdir.append("classes");
676     String cp = classdir;
678     String libdir = javaroot;
679     libdir.append(DIR_SEPARATOR);
680     libdir.append("lib");
682     DIR *dir = opendir(libdir.c_str());
683     if (!dir)
684         {
685         result = cp;
686         return;
687         }
689     while (true)
690         {
691         struct dirent *de = readdir(dir);
692         if (!de)
693             break;
694         String fname = de->d_name;
695         if (fname == "." || fname == "..")
696             continue;
697         if (fname.size()<5) //x.jar
698             continue;
699         if (fname.compare(fname.size()-4, 4, ".jar") != 0)
700             continue;
702         String path = libdir;
703         path.append(DIR_SEPARATOR);
704         path.append(fname);
706         cp.append(PATH_SEPARATOR);
707         cp.append(path);
708         }
709     closedir(dir);
710     
711     result = cp;
716 //========================================================================
717 // Gateway
718 //========================================================================
719 /**
720  * This is provided to scripts can grab the current copy or the
721  * repr tree.  If anyone has a smarter way of doing this, please implement. 
722  */    
723 jstring JNICALL documentGet(JNIEnv *env, jobject /*obj*/, jlong /*ptr*/)
725     //JavaBinderyImpl *bind = (JavaBinderyImpl *)ptr;
726     String buf =  sp_repr_save_buf((SP_ACTIVE_DOCUMENT)->rdoc);
727     jstring jstr = env->NewStringUTF(buf.c_str());
728     return jstr;
731 /**
732  * This is provided to scripts can load an XML tree into Inkscape.
733  * If anyone has a smarter way of doing this, please implement. 
734  */    
735 jboolean JNICALL documentSet(JNIEnv *env, jobject /*obj*/, jlong /*ptr*/, jstring jstr)
737     //JavaBinderyImpl *bind = (JavaBinderyImpl *)ptr;
738     String s = getString(env, jstr);
739     SPDocument *doc = sp_document_new_from_mem(s.c_str(), s.size(), true);
742 /**
743  * This method is used to allow the gateway class to
744  * redirect its logging stream here.
745  * For the main C++/Java bindings, see dobinding.cpp 
746  */    
747 void JNICALL logWrite(JNIEnv */*env*/, jobject /*obj*/, jlong ptr, jint ch)
749     JavaBinderyImpl *bind = (JavaBinderyImpl *)ptr;
750     bind->log(ch);
754 static JNINativeMethod gatewayMethods[] =
756 { (char *)"documentGet", (char *)"(J)Ljava/lang/String;",  (void *)documentGet },
757 { (char *)"documentSet", (char *)"(JLjava/lang/String;)Z", (void *)documentSet },
758 { (char *)"logWrite",    (char *)"(JI)V",                  (void *)logWrite    },
759 { NULL,  NULL, NULL }
760 };
763 /**
764  * This sets up the 'Gateway' java class for execution of
765  * scripts.   The class's constructor takes a jlong.  This java long
766  * is used to store the pointer to 'this'.  When ScriptRunner makes
767  * native calls, it passes that jlong back, so that it can call the
768  * methods of this C++ class.  
769  */  
770 bool JavaBinderyImpl::setupGateway()
772     String className = "org/inkscape/cmn/Gateway";
773     if (!registerNatives(className, gatewayMethods))
774         {
775         return false;
776         }
777     jclass cls = env->FindClass(className.c_str());
778     if (!cls)
779         {
780         err("setupGateway: cannot find class '%s' : %s",
781                          className.c_str(), getException().c_str());
782         return false;
783                 }
784         jmethodID mid = env->GetMethodID(cls, "<init>", "(J)V");
785         if (!mid)
786         {
787         err("setupGateway: cannot find constructor for '%s' : %s",
788                           className.c_str(), getException().c_str());
789         return false;
790                 }
791     gatewayObj = env->NewObject(cls, mid, ((jlong)this));
792     if (!gatewayObj)
793         {
794         err("setupGateway: cannot construct '%s' : %s",
795                          className.c_str(), getException().c_str());
796         return false;
797                 }
799         msg("Gateway ready");
800     return true;
803 bool JavaBinderyImpl::scriptRun(const String &lang, const String &script)
805     if (!loadJVM())
806         return false;
808     std::vector<Value> params;
809     Value langParm(lang);
810     params.push_back(langParm);
811     Value scriptParm(script);
812     params.push_back(scriptParm);
813     Value retval;
814     callInstance(Value::BIND_VOID, gatewayObj, "scriptRun",
815              "(Ljava/lang/String;Ljava/lang/String;)Z", params, retval);
816     return retval.getBoolean();
819 bool JavaBinderyImpl::scriptRunFile(const String &lang, const String &fname)
821     if (!loadJVM())
822         return false;
824     std::vector<Value> params;
825     Value langParm(lang);
826     params.push_back(langParm);
827     Value fnameParm(fname);
828     params.push_back(fnameParm);
829     Value retval;
830     callInstance(Value::BIND_VOID, gatewayObj, "scriptRunFile",
831              "(Ljava/lang/String;Ljava/lang/String;)Z", params, retval);
832     return retval.getBoolean();
835 bool JavaBinderyImpl::showConsole()
837     if (!loadJVM())
838         return false;
839         
840     std::vector<Value> params;
841     Value retval;
842     callInstance(Value::BIND_VOID, gatewayObj, "showConsole",
843              "()Z", params, retval);
844     return retval.getBoolean();
848 //========================================================================
849 // End Gateway
850 //========================================================================
853 /**
854  * This is used to grab output from the VM itself. See 'options' below.
855  */ 
856 static int JNICALL vfprintfHook(FILE* /*f*/, const char *fmt, va_list args)
858     g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, fmt, args);
862 /**
863  * This is the most important part of this class.  Here we
864  * attempt to find, load, and initialize a java (or mlvm?) virtual
865  * machine.
866  * 
867  * @return true if successful, else false    
868  */ 
869 bool JavaBinderyImpl::loadJVM()
871     if (jvm)
872         return true;
874     CreateVMFunc createVM = getCreateVMFunc();
875     if (!createVM)
876         {
877         err("Could not find 'JNI_CreateJavaVM' in shared library");
878         return false;
879         }
881     String javaroot;
882     getJavaRoot(javaroot);
883     String cp;
884     populateClassPath(javaroot, cp);
885     String classpath = "-Djava.class.path=";
886     classpath.append(normalizePath(cp));
887     msg("Class path is: '%s'", classpath.c_str());
889     String libpath = "-Djava.library.path=";
890     libpath.append(javaroot);
891     libpath.append(DIR_SEPARATOR);
892     libpath.append("libm");
893     libpath = normalizePath(libpath);
894     msg("Lib path is: '%s'", libpath.c_str());
896     JavaVMInitArgs vm_args;
897     JavaVMOption options[10];//should be enough
898     int nOptions = 0;
899     options[nOptions++].optionString = (char *)classpath.c_str();
900     options[nOptions++].optionString = (char *)libpath.c_str();
901     //options[nOptions++].optionString = (char *)"-verbose:jni";
902     options[nOptions  ].optionString = (char *)"vfprintf";
903     options[nOptions++].extraInfo    = (void *)vfprintfHook;
904     vm_args.version                  = JNI_VERSION_1_4;
905     vm_args.options                  = options;
906     vm_args.nOptions                 = nOptions;
907     vm_args.ignoreUnrecognized       = true;
909     if (createVM(&jvm, &env, &vm_args) < 0)
910         {
911         err("JNI_CreateJavaVM() failed");
912         return false;
913         }
915     //get jvm version
916     jint vers = env->GetVersion();
917     int versionMajor = (vers>>16) & 0xffff;
918     int versionMinor = (vers    ) & 0xffff;
919     msg("Loaded JVM version %d.%d", versionMajor, versionMinor);
921     if (!setupGateway())
922         return false;
924     return true;
928 /**
929  *  This is a difficult method.  What we are doing is trying to
930  *  call a static method with a list of arguments.  Similar to 
931  *  a varargs call, we need to marshal the Values into their
932  *  Java equivalents and make the proper call.
933  *  
934  * @param type the return type of the method
935  * @param className the full (package / name) name of the java class
936  * @param methodName the name of the method being invoked
937  * @param signature the method signature (ex: "(Ljava/lang/String;I)V" )
938  *    that describes the param and return types of the method.
939  * @param retval the return value of the java method
940  * @return true if the call was successful, else false.  This is not
941  *    the return value of the method.    
942  */    
943 bool JavaBinderyImpl::callStatic(int type,
944                         const String &className,
945                         const String &methodName,
946                         const String &signature,
947                         const std::vector<Value> &params,
948                         Value &retval)
950     jclass cls = env->FindClass(className.c_str());
951     if (!cls)
952         {
953         err("Could not find class '%s' : %s",
954                        className.c_str(), getException().c_str());
955         return false;
956         }
957     jmethodID mid = env->GetStaticMethodID(cls,
958                 methodName.c_str(), signature.c_str());
959     if (!mid)
960         {
961         err("Could not find method '%s:%s/%s' : %s",
962                         className.c_str(), methodName.c_str(),
963                             signature.c_str(), getException().c_str());
964         return false;
965         }
966     /**
967      * Assemble your parameters into a form usable by JNI
968      */
969     jvalue *jvals = new jvalue[params.size()];
970     for (unsigned int i=0 ; i<params.size() ; i++)
971         {
972         Value v = params[i];
973         switch (v.getType())
974             {
975             case Value::BIND_BOOLEAN:
976                 {
977                 jvals[i].z = (jboolean)v.getBoolean();
978                 break;
979                 }
980             case Value::BIND_INT:
981                 {
982                 jvals[i].i = (jint)v.getInt();
983                 break;
984                 }
985             case Value::BIND_DOUBLE:
986                 {
987                 jvals[i].d = (jdouble)v.getDouble();
988                 break;
989                 }
990             case Value::BIND_STRING:
991                 {
992                 jvals[i].l = (jobject) env->NewStringUTF(v.getString().c_str());
993                 break;
994                 }
995             default:
996                 {
997                 err("Unknown value type: %d", v.getType());
998                 return false;
999                 }
1000             }
1001         }
1002     switch (type)
1003         {
1004         case Value::BIND_VOID:
1005             {
1006             env->CallStaticVoidMethodA(cls, mid, jvals);
1007             break;
1008             }
1009         case Value::BIND_BOOLEAN:
1010             {
1011             jboolean ret = env->CallStaticBooleanMethodA(cls, mid, jvals);
1012             if (ret == JNI_TRUE) //remember, don't truncate
1013                 retval.setBoolean(true);
1014             else
1015                 retval.setBoolean(false);
1016             break;
1017             }
1018         case Value::BIND_INT:
1019             {
1020             jint ret = env->CallStaticIntMethodA(cls, mid, jvals);
1021             retval.setInt(ret);
1022             break;
1023             }
1024         case Value::BIND_DOUBLE:
1025             {
1026             jdouble ret = env->CallStaticDoubleMethodA(cls, mid, jvals);
1027             retval.setDouble(ret);
1028             break;
1029             }
1030         case Value::BIND_STRING:
1031             {
1032             jobject ret = env->CallStaticObjectMethodA(cls, mid, jvals);
1033             jstring jstr = (jstring) ret;
1034             const char *str = env->GetStringUTFChars(jstr, JNI_FALSE);
1035             retval.setString(str);
1036             env->ReleaseStringUTFChars(jstr, str);
1037             break;
1038             }
1039         default:
1040             {
1041             err("Unknown return type: %d", type);
1042             return false;
1043             }
1044         }
1045     delete jvals;
1046     String errStr = getException();
1047     if (errStr.size()>0)
1048         {
1049         err("callStatic: %s", errStr.c_str());
1050         return false;
1051                 }
1052     return true;
1057 /**
1058  *  Another difficult method.  However, this time we are operating
1059  *  on an existing instance jobject. 
1060  *  
1061  * @param type the return type of the method
1062  * @param obj the instance upon which to make the call
1063  * @param methodName the name of the method being invoked
1064  * @param signature the method signature (ex: "(Ljava/lang/String;I)V" )
1065  *    that describes the param and return types of the method.
1066  * @param retval the return value of the java method
1067  * @return true if the call was successful, else false.  This is not
1068  *    the return value of the method.    
1069  */    
1070 bool JavaBinderyImpl::callInstance(
1071                         int type,
1072                         const jobject obj,
1073                         const String &methodName,
1074                         const String &signature,
1075                         const std::vector<Value> &params,
1076                         Value &retval)
1078     jmethodID mid = env->GetMethodID(env->GetObjectClass(obj),
1079                 methodName.c_str(), signature.c_str());
1080     if (!mid)
1081         {
1082         err("Could not find method '%s/%s' : %s",
1083                         methodName.c_str(),
1084                             signature.c_str(), getException().c_str());
1085         return false;
1086         }
1087     /**
1088      * Assemble your parameters into a form usable by JNI
1089      */
1090     jvalue *jvals = new jvalue[params.size()];
1091     for (unsigned int i=0 ; i<params.size() ; i++)
1092         {
1093         Value v = params[i];
1094         switch (v.getType())
1095             {
1096             case Value::BIND_BOOLEAN:
1097                 {
1098                 jvals[i].z = (jboolean)v.getBoolean();
1099                 break;
1100                 }
1101             case Value::BIND_INT:
1102                 {
1103                 jvals[i].i = (jint)v.getInt();
1104                 break;
1105                 }
1106             case Value::BIND_DOUBLE:
1107                 {
1108                 jvals[i].d = (jdouble)v.getDouble();
1109                 break;
1110                 }
1111             case Value::BIND_STRING:
1112                 {
1113                 jvals[i].l = (jobject) env->NewStringUTF(v.getString().c_str());
1114                 break;
1115                 }
1116             default:
1117                 {
1118                 err("Unknown value type: %d", v.getType());
1119                 return false;
1120                 }
1121             }
1122         }
1123     switch (type)
1124         {
1125         case Value::BIND_VOID:
1126             {
1127             env->CallVoidMethodA(obj, mid, jvals);
1128             break;
1129             }
1130         case Value::BIND_BOOLEAN:
1131             {
1132             jboolean ret = env->CallBooleanMethodA(obj, mid, jvals);
1133             if (ret == JNI_TRUE) //remember, don't truncate
1134                 retval.setBoolean(true);
1135             else
1136                 retval.setBoolean(false);
1137             break;
1138             }
1139         case Value::BIND_INT:
1140             {
1141             jint ret = env->CallIntMethodA(obj, mid, jvals);
1142             retval.setInt(ret);
1143             break;
1144             }
1145         case Value::BIND_DOUBLE:
1146             {
1147             jdouble ret = env->CallDoubleMethodA(obj, mid, jvals);
1148             retval.setDouble(ret);
1149             break;
1150             }
1151         case Value::BIND_STRING:
1152             {
1153             jobject ret = env->CallObjectMethodA(obj, mid, jvals);
1154             jstring jstr = (jstring) ret;
1155             const char *str = env->GetStringUTFChars(jstr, JNI_FALSE);
1156             retval.setString(str);
1157             env->ReleaseStringUTFChars(jstr, str);
1158             break;
1159             }
1160         default:
1161             {
1162             err("Unknown return type: %d", type);
1163             return false;
1164             }
1165         }
1166     delete jvals;
1167     String errStr = getException();
1168     if (errStr.size()>0)
1169         {
1170         err("callStatic: %s", errStr.c_str());
1171         return false;
1172                 }
1173     return true;
1179 /**
1180  * Fetch the last exception from the JVM, if any.  Clear it to
1181  * continue processing
1182  * 
1183  * @return the exception's descriptio,if any.  Else ""  
1184  */  
1185 String JavaBinderyImpl::getException()
1187     return getExceptionString(env);
1192 /**
1193  * Convenience method to call the static void main(String argv[])
1194  * method of a given class
1195  * 
1196  * @param className full name of the java class
1197  * @args the argument strings to the method 
1198  * @return true if successful, else false   
1199  */ 
1200 bool JavaBinderyImpl::callMain(const String &className,
1201                                const std::vector<String> &args)
1203     std::vector<Value> parms;
1204     for (unsigned int i=0 ; i<args.size() ; i++)
1205         {
1206         Value v;
1207         v.setString(args[i]);
1208         parms.push_back(v);
1209                 }
1210     Value retval;
1211     return callStatic(Value::BIND_VOID, className, "main",
1212              "([Ljava/lang/String;)V", parms, retval);
1216 /**
1217  * Used to register an array of native methods for a named class
1218  * 
1219  * @param className the full name of the java class
1220  * @param the method array
1221  * @return true if successful, else false     
1222  */ 
1223 bool JavaBinderyImpl::registerNatives(const String &className,
1224                            const JNINativeMethod *methods)
1226     jclass cls = env->FindClass(className.c_str());
1227     if (!cls)
1228         {
1229         err("Could not find class '%s'", className.c_str());
1230         return false;
1231         }
1232     //msg("registerNatives: class '%s' found", className.c_str());
1233     
1234     /**
1235      * hack for JDK bug http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6493522
1236      */
1237         jmethodID mid = env->GetMethodID(env->GetObjectClass(cls), "getConstructors",
1238                   "()[Ljava/lang/reflect/Constructor;");
1239         if (!mid)
1240             {
1241             err("Could not get reflect mid for 'getConstructors' : %s",
1242              getException().c_str());
1243                 return false;
1244                 }
1245         jobject res = env->CallObjectMethod(cls, mid);
1246         if (!res)
1247             {
1248             err("Could not get constructors : %s", getException().c_str());
1249                 return false;
1250                 }
1251         /**
1252          * end hack
1253          */             
1254     jint nrMethods = 0;
1255     for (const JNINativeMethod *m = methods ; m->name ; m++)
1256         nrMethods++;
1257     jint ret = env->RegisterNatives(cls, methods, nrMethods);
1258     if (ret < 0)
1259         {
1260         err("Could not register %d native methods for '%s' : %s",
1261                     nrMethods, className.c_str(), getException().c_str());
1262         return false;
1263         }
1264     return true;
1270 } // namespace Bind
1271 } // namespace Inkscape
1273 //########################################################################
1274 //# E N D    O F    F I L E
1275 //########################################################################