У меня проблемы с использованием внешних классов Java через JNI. Я проиллюстрирую мою проблему на игрушечном примере.
Это мой класс Java, который в качестве примера использует внешний класс FilenameUtils из 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();
}
}
Мой путь к классу устанавливается с помощью $ CLASSPATH:
export CLASSPATH=".:/Applications/eclipse/plugins/*"
Я компилирую его с помощью javac, а затем выполняю. Это вывод, который я получаю, и это правильно:
До
Foo
После
Проблема возникает, когда я вызываю Java-метод «base» из C ++, используя JNI. Это код C ++:
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;
}
Хотя я не думаю, что это необходимо в моем случае, так как он уже настроен с помощью $ CLASSPATH, я снова указал classpath в опциях VM. Я также добавил параметр 5 в функцию CallStaticVoidMethod, потому что я не знаю, как указать нулевые аргументы. Метод Java не получает никаких аргументов, поэтому это игнорируется.
Затем я компилирую этот код C ++:
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
И выполнить эту скомпилированную программу. Это вывод, который я сейчас получаю:
До
Законченный
Выполнение метода «base» просто останавливается, когда он обращается к методу getBaseName. Никаких исключений не возникает, он просто прекращает выполнение и возвращается к собственному коду.
Почему невозможно выполнить метод FilenameUtils.getBaseName ()?
Моя машина работает под управлением Mac OS 10.10 Yosemite, с 64-битной Java 1.7.0.72.
Заранее спасибо.
ОБНОВИТЬ
Я попытался включить commons-io-2.4.jar прямо в classpath, и теперь программа работает:
options[0].optionString = "-Djava.class.path=.:/Applications/eclipse/plugins/commons-io-2.4/commons-io-2.4.jar";
Теперь мой вопрос: почему classpath ведет себя по-разному при использовании JNI?
Так что проблема была проста. При указании пути к классу с помощью $ CLASSPATH или -cp вы можете использовать подстановочные знаки.
Но если classpath установлен с «-Djava.class.path», подстановочные знаки не работают, список фляг и каталогов должен быть определен индивидуально.
Кроме того, «-Djava.class.path» переопределяет $ CLASSPATH.