javabind.cpp revision dc3865c20e9d5cf01d8b6cc88d0f4019e4d5dc90
/**
* @file
* This is a simple mechanism to bind Inkscape to Java, and thence
* to all of the nice things that can be layered upon that.
*
* Authors:
* Bob Jamison
*
* Copyright (C) 2007-2008 Bob Jamison
*
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <jni.h>
#include <dirent.h>
#ifdef __WIN32__
#include <windows.h>
#else
#include <dlfcn.h>
#include <errno.h>
#endif
#if HAVE_SYS_STAT_H
#endif
#include "javabind.h"
#include "javabind-private.h"
#include <path-prefix.h>
#include <prefix.h>
#include <glib/gmessages.h>
//For repr and document
#include <document.h>
#include <inkscape.h>
/**
* Note: We must limit Java or JVM-specific code to this file
* and to dobinding.cpp. It should be hidden from javabind.h
*
* This file is mostly about getting things up and running, and
* providing the basic C-to-Java hooks.
*
* dobinding.cpp will have the rote and repetitious
* class-by-class binding
*/
namespace Inkscape
{
namespace Bind
{
//########################################################################
//# DEFINITIONS
//########################################################################
//########################################################################
//# UTILITY
//########################################################################
/**
* Normalize path. Java wants '/', even on Windows
*/
{
{
if (ch == '\\')
else
}
return buf;
}
/**
* Convert a java string to a C++ string
*/
{
return str;
}
/**
* Check if the VM has encountered an Exception. If so, get the String for it
* and clear the exception
*/
{
if (!exc)
return buf;
env->ExceptionClear();
return buf;
}
{
}
{
}
{
}
{
}
{
}
{
}
{
}
{
}
{
}
{
}
//########################################################################
//########################################################################
{
return JavaBinderyImpl::getInstance();
}
{
if (!_instance)
{
_instance = new JavaBinderyImpl();
}
return _instance;
}
{
gatewayObj = NULL;
}
{
}
//########################################################################
//# MESSAGES
//########################################################################
{
#if 0
#else
g_warning("JavaBinderyImpl err:");
g_warning("\n");
#endif
}
{
#if 0
#else
g_message("JavaBinderyImpl:");
g_message("\n");
#endif
}
//########################################################################
//# W I N 3 2 S T Y L E
//########################################################################
#ifdef __WIN32__
#define DIR_SEPARATOR "\\"
#define PATH_SEPARATOR ";"
{
if (ret != ERROR_SUCCESS)
{
return false;
}
return true;
}
{
for (unsigned int i=0 ; i<s.size() ; i++)
{
char ch = s[i];
if (ch != '"')
}
return buf;
}
/**
* Common places to find jvm.dll under JAVA_HOME
*/
static const char *commonJavaPaths[] =
{
"\\jre\\bin\\client\\jvm.dll",
"\\bin\\client\\jvm.dll",
"\\jvm.dll",
};
/**
* Return the directory of the .exe that is currently running
*/
static String getExePath()
{
if (slashPos)
*slashPos = '\0';
return s;
}
/**
* Check a directory for several possibilities of sub-locations
* under it, where a jvm might exist.
*/
{
{
//msg("trying '%s'", jpath.c_str());
{
//msg("found");
return jpath;
}
}
return "";
}
/**
* Attempt to find and load a jvm.dll file. Find the createVM()
* function's address and return it
*/
static CreateVMFunc getCreateVMFunc()
{
bool found = false;
/**
* First, look for an embedded jre in the .exe's dir.
* This allows us to package our own JRE if we want to.
*/
{
found = true;
}
/**
* Next, look for JAVA_HOME. This will allow the user
* to override what's in the registry
*/
if (!found)
{
if (envStr)
{
{
found = true;
}
}
}
//not at JAVA_HOME. check the registry
if (!found)
{
char verbuf[16];
char regpath[80];
if (!ret)
{
}
else
{
//msg("reg path: %s\n", regpath);
char valbuf[80];
if (ret)
{
found = true;
}
else
{
msg("JVM RuntimeLib not found in registry at '%s'",
regpath);
}
}
}
if (!found)
{
err("JVM not found at JAVA_HOME or in registry");
return NULL;
}
/**
* If we are here, then we seem to have a valid path for jvm.dll
* Give it a try
*/
if (!lib)
{
return NULL;
}
if (!createVM)
{
err("Could not find 'JNI_CreateJavaVM' in shared library '%s'",
return NULL;
}
return createVM;
}
/**
* located
*/
{
/*
javaroot = getExePath();
javaroot.append("\\");
javaroot.append(INKSCAPE_BINDDIR);
javaroot.append("\\java");
*/
}
//########################################################################
//# U N I X S T Y L E
//########################################################################
#else /* !__WIN32__ */
#define DIR_SEPARATOR "/"
#define PATH_SEPARATOR ":"
/**
* Recursively descend into a directory looking for libjvm.so
*/
{
if (!dir)
return false;
bool ret = false;
while (true)
{
if (!de)
break;
continue;
{
ret = true;
continue;
}
{
break;
}
{
}
}
return ret;
}
/**
* Some common places on a Unix filesystem where JVMs are
* often found.
*/
static const char *commonJavaPaths[] =
{
};
/**
* Look for a Java VM (libjvm.so) in several Unix places
*/
{
bool found = false;
/* Is there one specified by the user? */
found = true;
{
{
found = true;
break;
}
}
if (!found)
{
return false;
}
return false;
//Look first for a Client VM
{
{
result = s;
return true;
}
}
//else default to the first
return true;
}
/**
* Attempt to find and load a jvm.dll file. Find the createVM()
* function's address and return it
*/
static CreateVMFunc getCreateVMFunc()
{
{
return NULL;
}
if (!lib)
{
return NULL;
}
if (!createVM)
{
err("Could not find 'JNI_CreateJavaVM' in shared library");
return NULL;
}
return createVM;
}
/**
* located
*/
{
}
#endif /* !__WIN32__ */
//########################################################################
//# COMMON
//########################################################################
bool JavaBinderyImpl::isLoaded()
{
return (jvm != (void *)0);
}
/**
* This will set up the classpath for the launched VM.
* We will add two things:
* 1. INKSCAPE_JAVADIR/classes -- path to loose classes
* 2. A concatenation of all jar files in INKSCAPE_JAVADIR/lib
*
* This will allow people to add classes and jars to the JVM without
* needing to state them explicitly.
*
* @param javaroot. Should be INKSCAPE_JAVADIR
* @param result a string buffer to hold the result of this method
*/
{
if (!dir)
{
return;
}
while (true)
{
if (!de)
break;
continue;
continue;
continue;
}
}
//========================================================================
// Gateway
//========================================================================
/**
* This is provided to scripts can grab the current copy or the
* repr tree. If anyone has a smarter way of doing this, please implement.
*/
{
//JavaBinderyImpl *bind = (JavaBinderyImpl *)ptr;
return jstr;
}
/**
* This is provided to scripts can load an XML tree into Inkscape.
* If anyone has a smarter way of doing this, please implement.
*/
{
/*
JavaBinderyImpl *bind = (JavaBinderyImpl *)ptr;
String s = getString(env, jstr);
*/
return JNI_TRUE;
}
/**
* This method is used to allow the gateway class to
* redirect its logging stream here.
* For the main C++/Java bindings, see dobinding.cpp
*/
{
}
static JNINativeMethod gatewayMethods[] =
{
};
/**
* This sets up the 'Gateway' java class for execution of
* scripts. The class's constructor takes a jlong. This java long
* is used to store the pointer to 'this'. When ScriptRunner makes
* native calls, it passes that jlong back, so that it can call the
* methods of this C++ class.
*/
bool JavaBinderyImpl::setupGateway()
{
{
return false;
}
if (!cls)
{
err("setupGateway: cannot find class '%s' : %s",
return false;
}
if (!mid)
{
err("setupGateway: cannot find constructor for '%s' : %s",
return false;
}
if (!gatewayObj)
{
err("setupGateway: cannot construct '%s' : %s",
return false;
}
msg("Gateway ready");
return true;
}
{
if (!loadJVM())
return false;
return retval.getBoolean();
}
{
if (!loadJVM())
return false;
return retval.getBoolean();
}
bool JavaBinderyImpl::showConsole()
{
if (!loadJVM())
return false;
return retval.getBoolean();
}
//========================================================================
// End Gateway
//========================================================================
/**
* This is used to grab output from the VM itself. See 'options' below.
*/
{
return JNI_TRUE;
}
/**
* This is the most important part of this class. Here we
* attempt to find, load, and initialize a java (or mlvm?) virtual
* machine.
*
* @return true if successful, else false
*/
bool JavaBinderyImpl::loadJVM()
{
if (jvm)
return true;
if (!createVM)
{
err("Could not find 'JNI_CreateJavaVM' in shared library");
return false;
}
int nOptions = 0;
//options[nOptions++].optionString = (char *)"-verbose:jni";
vm_args.ignoreUnrecognized = true;
{
err("JNI_CreateJavaVM() failed");
return false;
}
//get jvm version
if (!setupGateway()) {
// set jvm = NULL, otherwise, this method will return true when called for the second time while the gateway might not have been created!
jvm->DestroyJavaVM();
err("Java bindings: setupGateway() failed");
return false;
}
return true;
}
/**
* This is a difficult method. What we are doing is trying to
* call a static method with a list of arguments. Similar to
* a varargs call, we need to marshal the Values into their
* Java equivalents and make the proper call.
*
* @param type the return type of the method
* @param className the full (package / name) name of the java class
* @param methodName the name of the method being invoked
* that describes the param and return types of the method.
* @param retval the return value of the java method
* @return true if the call was successful, else false. This is not
* the return value of the method.
*/
const String &methodName,
{
if (!cls)
{
err("Could not find class '%s' : %s",
return false;
}
if (!mid)
{
err("Could not find method '%s:%s/%s' : %s",
return false;
}
/**
* Assemble your parameters into a form usable by JNI
*/
{
switch (v.getType())
{
case Value::BIND_BOOLEAN:
{
break;
}
{
break;
}
case Value::BIND_DOUBLE:
{
break;
}
case Value::BIND_STRING:
{
break;
}
default:
{
delete [] jvals;
return false;
}
}
}
switch (type)
{
{
break;
}
case Value::BIND_BOOLEAN:
{
retval.setBoolean(true);
else
retval.setBoolean(false);
break;
}
{
break;
}
case Value::BIND_DOUBLE:
{
break;
}
case Value::BIND_STRING:
{
break;
}
default:
{
return false;
}
}
delete [] jvals;
{
return false;
}
return true;
}
/**
* Another difficult method. However, this time we are operating
* on an existing instance jobject.
*
* @param type the return type of the method
* @param obj the instance upon which to make the call
* @param methodName the name of the method being invoked
* that describes the param and return types of the method.
* @param retval the return value of the java method
* @return true if the call was successful, else false. This is not
* the return value of the method.
*/
bool JavaBinderyImpl::callInstance(
int type,
const String &methodName,
{
if (!mid)
{
err("Could not find method '%s/%s' : %s",
methodName.c_str(),
return false;
}
/**
* Assemble your parameters into a form usable by JNI
*/
{
switch (v.getType())
{
case Value::BIND_BOOLEAN:
{
break;
}
{
break;
}
case Value::BIND_DOUBLE:
{
break;
}
case Value::BIND_STRING:
{
break;
}
default:
{
delete [] jvals;
return false;
}
}
}
switch (type)
{
{
break;
}
case Value::BIND_BOOLEAN:
{
retval.setBoolean(true);
else
retval.setBoolean(false);
break;
}
{
break;
}
case Value::BIND_DOUBLE:
{
break;
}
case Value::BIND_STRING:
{
break;
}
default:
{
return false;
}
}
delete [] jvals;
{
return false;
}
return true;
}
/**
* Fetch the last exception from the JVM, if any. Clear it to
* continue processing
*
* @return the exception's descriptio,if any. Else ""
*/
{
return getExceptionString(env);
}
/**
* Convenience method to call the static void main(String argv[])
* method of a given class
*
* @param className full name of the java class
* @args the argument strings to the method
* @return true if successful, else false
*/
{
{
Value v;
}
}
/**
* Used to register an array of native methods for a named class
*
* @param className the full name of the java class
* @param the method array
* @return true if successful, else false
*/
const JNINativeMethod *methods)
{
if (!cls)
{
return false;
}
//msg("registerNatives: class '%s' found", className.c_str());
/**
* hack for JDK bug http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6493522
*/
"()[Ljava/lang/reflect/Constructor;");
if (!mid)
{
err("Could not get reflect mid for 'getConstructors' : %s",
getException().c_str());
return false;
}
if (!res)
{
return false;
}
/**
* end hack
*/
nrMethods++;
if (ret < 0)
{
err("Could not register %d native methods for '%s' : %s",
return false;
}
return true;
}
} // namespace Bind
} // namespace Inkscape
//########################################################################
//# E N D O F F I L E
//########################################################################