Reputation: 137
I'm having trouble using external Java classes through JNI. I'll illustrate my problem with a toy example.
This is my Java class, which as an example uses the external class FilenameUtils from Apache Commons IO:
Example.java
import org.apache.commons.io.FilenameUtils;
class Example {
static void base () {
String str = "/usr/foo.bar";
System.out.println("Before");
try {
System.out.println(FilenameUtils.getBaseName(str));
}
catch (Exception e) {
System.out.println(e.getMessage());
}
System.out.println("After");
}
public static void main(String[] args) {
base();
}
}
My classpath is set with $CLASSPATH:
export CLASSPATH=".:/Applications/eclipse/plugins/*"
I compile it with javac, and then execute it. This is the output I get, which is correct:
Before
foo
After
The problem arises when I invoke the Java method "base" from C++ using JNI. This is the C++ code:
test.cpp
#include <jni.h>
#include <cstring>
int main()
{
JavaVMOption options[1];
JNIEnv *env;
JavaVM *jvm;
JavaVMInitArgs vm_args;
jclass cls;
jmethodID method;
jobject simpleJNITestInstance;
options[0].optionString = "-Djava.class.path=.:/Applications/eclipse/plugins/*";
memset(&vm_args, 0, sizeof(vm_args));
vm_args.version = JNI_VERSION_1_6;
vm_args.nOptions = 1;
vm_args.options = options;
long status = JNI_CreateJavaVM(&jvm, (void **)&env, &vm_args);
if (status != JNI_ERR)
{
cls = env->FindClass("Example");
if (cls != 0)
{
method = env->GetStaticMethodID(cls, "base", "()V");
env->CallStaticVoidMethod(cls, method, 5);
}
jvm->DestroyJavaVM();
}
printf("Finished\n");
return 0;
}
Although I don't think it's necessary in my case since it's already configured with $CLASSPATH, I specified again the classpath in the VM options. I also added a parameter 5 to the CallStaticVoidMethod function because I don't know how to specify zero arguments. The Java method doesn't receive any arguments, so this is ignored.
I then compile this C++ code:
g++ -o test \
-I/Library/Java/JavaVirtualMachines/jdk1.7.0_72.jdk/Contents/Home/include \
-I/Library/Java/JavaVirtualMachines/jdk1.7.0_72.jdk/Contents/Home/include/darwin \
-L/Library/Java/JavaVirtualMachines/jdk1.7.0_72.jdk/Contents/Home/jre/lib/server/ \
test.cpp \
-ljvm
And execute this compiled program. This is the output I now get:
Before
Finished
The execution of the method "base" just stops when it access the getBaseName method. No exceptions are raised, it just stops executing, and returns to the native code.
Why is it unable to execute the method FilenameUtils.getBaseName()?
My machine is running Mac OS 10.10 Yosemite, with Java 1.7.0.72 64 bit.
Thanks in advance.
UPDATE
I tried including the commons-io-2.4.jar directly in the classpath, and now the program works:
options[0].optionString = "-Djava.class.path=.:/Applications/eclipse/plugins/commons-io-2.4/commons-io-2.4.jar";
Now my question is, why does the classpath behave differently when using JNI?
Upvotes: 1
Views: 940
Reputation: 137
So the problem was simple. When specifying the classpath with $CLASSPATH or -cp, you can use wildcards.
But if the classpath is set with "-Djava.class.path" wildcards don't work, the list of jars and directories have to be specified individually.
Also, "-Djava.class.path" overrides $CLASSPATH.
Upvotes: 1