Code

Fixed stat include
[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>
60 namespace Inkscape
61 {
63 namespace Bind
64 {
67 //########################################################################
68 //# DEFINITIONS
69 //########################################################################
71 typedef jint (*CreateVMFunc)(JavaVM **, JNIEnv **, void *);
75 //########################################################################
76 //# UTILITY
77 //########################################################################
79 jint getInt(JNIEnv *env, jobject obj, const char *name)
80 {
81     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "I");
82     return env->GetIntField(obj, fid);
83 }
85 void setInt(JNIEnv *env, jobject obj, const char *name, jint val)
86 {
87     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "I");
88     env->SetIntField(obj, fid, val);
89 }
91 jlong getLong(JNIEnv *env, jobject obj, const char *name)
92 {
93     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "J");
94     return env->GetLongField(obj, fid);
95 }
97 void setLong(JNIEnv *env, jobject obj, const char *name, jlong val)
98 {
99     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "J");
100     env->SetLongField(obj, fid, val);
103 jfloat getFloat(JNIEnv *env, jobject obj, const char *name)
105     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "F");
106     return env->GetFloatField(obj, fid);
109 void setFloat(JNIEnv *env, jobject obj, const char *name, jfloat val)
111     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "F");
112     env->SetFloatField(obj, fid, val);
115 jdouble getDouble(JNIEnv *env, jobject obj, const char *name)
117     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "D");
118     return env->GetDoubleField(obj, fid);
121 void setDouble(JNIEnv *env, jobject obj, const char *name, jdouble val)
123     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "D");
124     env->SetDoubleField(obj, fid, val);
127 String getString(JNIEnv *env, jobject obj, const char *name)
129     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "Ljava/lang/String;");
130     jstring jstr = (jstring)env->GetObjectField(obj, fid);
131     const char *chars = env->GetStringUTFChars(jstr, JNI_FALSE);
132     String str = chars;
133     env->ReleaseStringUTFChars(jstr, chars);
134     return str;
137 void setString(JNIEnv *env, jobject obj, const char *name, const String &val)
139     jstring jstr = env->NewStringUTF(val.c_str());
140     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "Ljava/lang/String;");
141     env->SetObjectField(obj, fid, jstr);
147 //########################################################################
148 //# CONSTRUCTOR/DESTRUCTOR
149 //########################################################################
151 static JavaBinderyImpl *_instance = NULL;
153 JavaBindery *JavaBindery::getInstance()
155     return JavaBinderyImpl::getInstance();
158 JavaBinderyImpl *JavaBinderyImpl::getInstance()
160     if (!_instance)
161         {
162         _instance = new JavaBinderyImpl();
163         }
164     return _instance;
167 JavaBinderyImpl::JavaBinderyImpl()
169     jvm  = NULL;
170     env  = NULL;
173 JavaBinderyImpl::~JavaBinderyImpl()
177 void err(const char *fmt, ...)
179 #if 0
180     va_list args;
181     fprintf(stderr, "JavaBinderyImpl err:");
182     va_start(args, fmt);
183     vfprintf(stderr, fmt, args);
184     va_end(args);
185     fprintf(stderr, "\n");
186 #else
187     va_list args;
188     g_warning("JavaBinderyImpl err:");
189     va_start(args, fmt);
190     g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, fmt, args);
191     va_end(args);
192     g_warning("\n");
193 #endif
196 void msg(const char *fmt, ...)
198 #if 0
199     va_list args;
200     fprintf(stdout, "JavaBinderyImpl:");
201     va_start(args, fmt);
202     vfprintf(stdout, fmt, args);
203     va_end(args);
204     fprintf(stdout, "\n");
205 #else
206     va_list args;
207     g_message("JavaBinderyImpl:");
208     va_start(args, fmt);
209     g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, fmt, args);
210     va_end(args);
211     g_message("\n");
212 #endif
215 bool JavaBinderyImpl::isLoaded()
217     return (jvm != (void *)0);
222 #ifdef __WIN32__
225 //########################################################################
226 //# W I N 3 2      S T Y L E
227 //########################################################################
230 #define DIR_SEPARATOR "\\"
231 #define PATH_SEPARATOR ";"
235 static bool getRegistryString(HKEY root, const char *keyName,
236                const char *valName, char *buf, int buflen)
238     HKEY key;
239     DWORD bufsiz  = buflen;
240     RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName, 0, KEY_READ, &key);
241     int ret = RegQueryValueEx(key, TEXT(valName),
242             NULL, NULL, (BYTE *)buf, &bufsiz);
243     if (ret != ERROR_SUCCESS)
244         {
245         err("Key '%s\\%s not found\n", keyName, valName);
246         return false;
247         }
248     RegCloseKey(key);
249     return true;
253 static CreateVMFunc getCreateVMFunc()
255     char verbuf[16];
256     char regpath[80];
257     strcpy(regpath, "SOFTWARE\\JavaSoft\\Java Runtime Environment");
258     bool ret = getRegistryString(HKEY_LOCAL_MACHINE,
259                      regpath, "CurrentVersion", verbuf, 15);
260     if (!ret)
261         {
262         err("JVM CurrentVersion not found in registry\n");
263         return NULL;
264         }
265     strcat(regpath, "\\");
266     strcat(regpath, verbuf);
267     //msg("reg path: %s\n", regpath);
268     char libname[80];
269     ret = getRegistryString(HKEY_LOCAL_MACHINE,
270                      regpath, "RuntimeLib", libname, 79);
271     if (!ret)
272         {
273         err("Current JVM RuntimeLib not found in registry\n");
274         return NULL;
275         }
276     //msg("jvm path: %s\n", libname);
277     HMODULE lib = LoadLibrary(libname);
278     if (!lib)
279         {
280         err("Java VM not found at '%s'", libname);
281         return NULL;
282         }
283     CreateVMFunc createVM = (CreateVMFunc)GetProcAddress(lib, "JNI_CreateJavaVM");
284     if (!createVM)
285         {
286         err("Could not find 'JNI_CreateJavaVM' in shared library");
287         return NULL;
288         }
289     return createVM;
292 static void getJavaRoot(String &javaroot)
294     char exeName[80];
295     GetModuleFileName(NULL, exeName, 80);
296     char *slashPos = strrchr(exeName, '\\');
297     if (slashPos)
298         *slashPos = '\0';
299     javaroot = exeName;
300     javaroot.append("\\");
301     javaroot.append(INKSCAPE_JAVADIR);
305 #else
308 //########################################################################
309 //# U N I X    S T Y L E
310 //########################################################################
313 #define DIR_SEPARATOR "/"
314 #define PATH_SEPARATOR ":"
317 /**
318  * Recursively descend into a directory looking for libjvm.so
319  */
320 static bool findJVMRecursive(const String &dirpath,
321                              std::vector<String> &results)
323     DIR *dir = opendir(dirpath.c_str());
324     if (!dir)
325         return false;
326     bool ret = false;
327     while (true)
328         {
329         struct dirent *de = readdir(dir);
330         if (!de)
331             break;
332         String fname = de->d_name;
333         if (fname == "." || fname == "..")
334             continue;
335         String path = dirpath;
336         path.push_back('/');
337         path.append(fname);
338         if (fname == "libjvm.so")
339             {
340             ret = true;
341             results.push_back(path);
342             continue;
343             }
344         struct stat finfo;
345         if (lstat(path.c_str(), &finfo)<0)
346             {
347             break;
348             }
349         if (finfo.st_mode & S_IFDIR)
350             {
351             ret |= findJVMRecursive(path, results);
352             }
353         }
354     closedir(dir);
355     return ret;
359 static const char *commonJavaPaths[] =
361     "/usr/java",
362     "/usr/local/java",
363     "/usr/lib/jvm",
364     "/usr/local/lib/jvm",
365     NULL
366 };
368 /**
369  * Look for a Java VM (libjvm.so) in several Unix places
370  */
371 static bool findJVM(String &result)
373     std::vector<String> results;
374     int found = false;
376     /* Is there one specified by the user? */
377     const char *javaHome = getenv("JAVA_HOME");
378     if (javaHome && findJVMRecursive(javaHome, results))
379         found = true;
380     else for (const char **path = commonJavaPaths ; *path ; path++)
381         {
382         if (findJVMRecursive(*path, results))
383             {
384             found = true;
385             break;
386             }
387         }
388     if (!found)
389         {
390         return false;
391         }
392     if (results.size() == 0)
393         return false;
394     //Look first for a Client VM
395     for (unsigned int i=0 ; i<results.size() ; i++)
396         {
397         String s = results[i];
398         if (s.find("client") != s.npos)
399             {
400             result = s;
401             return true;
402             }
403         }
404     //else default to the first
405     result = results[0];
406     return true;
411 static CreateVMFunc getCreateVMFunc()
413     String libname;
414     if (!findJVM(libname))
415         {
416         err("No Java VM found. Is JAVA_HOME defined?  Need to find 'libjvm.so'");
417         return NULL;
418         }
419     void *lib = dlopen(libname.c_str(), RTLD_NOW);
420     if (!lib)
421         {
422         err("Java VM not found at '%s' : %s", libname.c_str(), strerror(errno));
423         return NULL;
424         }
425     CreateVMFunc createVM = (CreateVMFunc)dlsym(lib, "JNI_CreateJavaVM");
426     if (!createVM)
427         {
428         err("Could not find 'JNI_CreateJavaVM' in shared library");
429             return NULL;
430         }
431     return createVM;
435 static void getJavaRoot(String &javaroot)
437     javaroot = INKSCAPE_JAVADIR;
440 #endif
446 static void populateClassPath(const String &javaroot,
447                               String &result)
449     String classdir = javaroot;
450     classdir.append(DIR_SEPARATOR);
451     classdir.append("classes");
453     String cp = classdir;
455     String libdir = javaroot;
456     libdir.append(DIR_SEPARATOR);
457     libdir.append("lib");
459     DIR *dir = opendir(libdir.c_str());
460     if (!dir)
461         {
462         result = cp;
463         return;
464         }
466     while (true)
467         {
468         struct dirent *de = readdir(dir);
469         if (!de)
470             break;
471         String fname = de->d_name;
472         if (fname == "." || fname == "..")
473             continue;
474         if (fname.size()<5) //x.jar
475             continue;
476         if (fname.compare(fname.size()-4, 4, ".jar") != 0)
477             continue;
479         String path = libdir;
480         path.append(DIR_SEPARATOR);
481         path.append(fname);
483         cp.append(PATH_SEPARATOR);
484         cp.append(path);
485         }
486     closedir(dir);
488     result = cp;
490     return;
494 static void stdOutWrite(jlong ptr, jint ch)
496     JavaBinderyImpl *bind = (JavaBinderyImpl *)ptr;
497     bind->stdOut(ch);
500 static void stdErrWrite(jlong ptr, jint ch)
502     JavaBinderyImpl *bind = (JavaBinderyImpl *)ptr;
503     bind->stdErr(ch);
507 static JNINativeMethod scriptRunnerMethods[] =
509 { (char *)"stdOutWrite", (char *)"(JI)V", (void *)stdOutWrite },
510 { (char *)"stdErrWrite", (char *)"(JI)V", (void *)stdErrWrite },
511 { NULL,  NULL, NULL }
512 };
514 bool JavaBinderyImpl::loadJVM()
516     if (jvm)
517         return true;
519     CreateVMFunc createVM = getCreateVMFunc();
520     if (!createVM)
521         {
522         err("Could not find 'JNI_CreateJavaVM' in shared library");
523         return false;
524         }
526     String javaroot;
527     getJavaRoot(javaroot);
528     String cp;
529     populateClassPath(javaroot, cp);
530     String classpath = "-Djava.class.path=";
531     classpath.append(cp);
532     msg("Class path is: '%s'", classpath.c_str());
534     String libpath = "-Djava.library.path=";
535     libpath.append(javaroot);
536     libpath.append(DIR_SEPARATOR);
537     libpath.append("libm");
538     msg("Lib path is: '%s'", libpath.c_str());
540     JavaVMInitArgs vm_args;
541     JavaVMOption options[2];
542     options[0].optionString    = (char *)classpath.c_str();
543     options[1].optionString    = (char *)libpath.c_str();
544     vm_args.version            = JNI_VERSION_1_2;
545     vm_args.options            = options;
546     vm_args.nOptions           = 2;
547     vm_args.ignoreUnrecognized = true;
549     if (createVM(&jvm, &env, &vm_args) < 0)
550         {
551         err("JNI_GetDefaultJavaVMInitArgs() failed");
552         return false;
553         }
555     if (!registerNatives("org/inkscape/cmn/ScriptRunner",
556              scriptRunnerMethods))
557         {
558         return false;
559         }
560     return true;
566 bool JavaBinderyImpl::callStatic(int type,
567                         const String &className,
568                         const String &methodName,
569                         const String &signature,
570                         const std::vector<Value> &params,
571                         Value &retval)
573     jclass cls = env->FindClass(className.c_str());
574     if (!cls)
575         {
576         err("Could not find class '%s'", className.c_str());
577         return false;
578         }
579     jmethodID mid = env->GetStaticMethodID(cls,
580                 methodName.c_str(), signature.c_str());
581     if (!mid)
582         {
583         err("Could not find method '%s:%s/%s'", className.c_str(),
584                 methodName.c_str(), signature.c_str());
585         return false;
586         }
587     /**
588      * Assemble your parameters into a form usable by JNI
589      */
590     jvalue *jvals = new jvalue[params.size()];
591     for (unsigned int i=0 ; i<params.size() ; i++)
592         {
593         Value v = params[i];
594         switch (v.getType())
595             {
596             case Value::BIND_BOOLEAN:
597                 {
598                 jvals[i].z = (jboolean)v.getBoolean();
599                 break;
600                 }
601             case Value::BIND_INT:
602                 {
603                 jvals[i].i = (jint)v.getInt();
604                 break;
605                 }
606             case Value::BIND_DOUBLE:
607                 {
608                 jvals[i].d = (jdouble)v.getDouble();
609                 break;
610                 }
611             case Value::BIND_STRING:
612                 {
613                 jvals[i].l = (jobject) env->NewStringUTF(v.getString().c_str());
614                 break;
615                 }
616             default:
617                 {
618                 err("Unknown value type: %d", v.getType());
619                 return false;
620                 }
621             }
622         }
623     switch (type)
624         {
625         case Value::BIND_VOID:
626             {
627             env->CallStaticVoidMethodA(cls, mid, jvals);
628             break;
629             }
630         case Value::BIND_BOOLEAN:
631             {
632             env->CallStaticBooleanMethodA(cls, mid, jvals);
633             break;
634             }
635         case Value::BIND_INT:
636             {
637             env->CallStaticIntMethodA(cls, mid, jvals);
638             break;
639             }
640         case Value::BIND_DOUBLE:
641             {
642             env->CallStaticDoubleMethodA(cls, mid, jvals);
643             break;
644             }
645         case Value::BIND_STRING:
646             {
647             env->CallStaticObjectMethodA(cls, mid, jvals);
648             break;
649             }
650         default:
651             {
652             err("Unknown return type: %d", type);
653             return false;
654             }
655         }
656     delete jvals;
657     return true;
663 bool JavaBinderyImpl::callMain(const String &className)
665     std::vector<Value> parms;
666     Value retval;
667     return callStatic(Value::BIND_VOID, className, "main",
668              "([Ljava/lang/String;)V", parms, retval);
673 bool JavaBinderyImpl::registerNatives(const String &className,
674                            const JNINativeMethod *methods)
676     jclass cls = env->FindClass(className.c_str());
677     if (!cls)
678         {
679         err("Could not find class '%s'", className.c_str());
680         return false;
681         }
682     int nrMethods = 0;
683     for (const JNINativeMethod *m = methods ; m->name ; m++)
684         nrMethods++;
685     if (env->RegisterNatives(cls, (const JNINativeMethod *)methods, nrMethods) < 0)
686         {
687         err("Could not register natives");
688         return false;
689         }
690     return true;
696 } // namespace Bind
697 } // namespace Inkscape
699 //########################################################################
700 //# E N D    O F    F I L E
701 //########################################################################