Can't use external Java class using JNI

934 Views Asked by At

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?

1

There are 1 best solutions below

0
On BEST ANSWER

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.