Must call RegisterNatives after getMethodID, else get UnsatisfiedLinkError
Martin Buchholz
martinrb at google.com
Tue Jun 8 16:12:11 PDT 2010
Hi ClassLoader maintainers,
This is a bug report.
(I think this is a hotspot bug, but it might be a core library
ClassLoader bug or even a URLClassLoader bug)
If you use RegisterNatives with a class loaded using URLClassLoader,
you must call GetMethodID
before RegisterNatives, or you get an UnsatisfiedLinkError as in the
test case below
(you will need to edit the below to fill in the location of your jni
include directory and your libjvm.so)
$ cat Test.java; echo ---------------------; cat jvm.cc; echo
--------------------------; g++ -I$JDK/include -I$JDK/include/linux
-ldl jvm.cc && ./a.out
public class Test {
public Test() {
System.out.println("My class loader is " + getClass().getClassLoader());
testNative();
System.out.println("back to Java");
}
public static native void testNative();
}
---------------------
#include <cstdio>
#include <dlfcn.h>
#include <jni.h>
JavaVM* jvm;
JNIEnv* env;
const char* libjvm =
"$JDKDIR/libjvm.so";
const char* loader_url = "file://./";
const char* class_name = "Test";
void InitializeJVM() {
JavaVMOption options[1];
options[0].optionString = (char*)"-Djava.class.path=.";
//options[1].optionString = (char*)"-verbose:jni";
JavaVMInitArgs vm_args;
vm_args.version = JNI_VERSION_1_2;
vm_args.options = options;
vm_args.nOptions = 2;
vm_args.ignoreUnrecognized = JNI_TRUE;
void* handle = dlopen(libjvm, RTLD_LAZY);
if (handle == NULL) perror("dlopen");
jint JNICALL (*func_create_java_vm)(JavaVM**, void**, void*) =
reinterpret_cast<jint JNICALL (*)(JavaVM**, void**, void*)>
(dlsym(handle, "JNI_CreateJavaVM"));
if (func_create_java_vm == NULL) perror("dlsym");
jint result = (*func_create_java_vm)(&jvm, (void**)(&env), &vm_args);
}
void TestNative(JNIEnv* env, jclass cls) {
printf("Successfully called registered native method\n");
}
void RegisterNativeMethod(jclass cls) {
static const JNINativeMethod jni_method =
{ (char*)"testNative",
(char*)"()V",
(void*)&TestNative };
env->RegisterNatives(cls, &jni_method, 1);
if (env->ExceptionCheck()) env->ExceptionDescribe();
}
void Test() {
// URL[] urls = {new URL(url)}
jclass cls = env->FindClass("java/net/URL");
jmethodID method = env->GetMethodID(cls, "<init>", "(Ljava/lang/String;)V");
jstring jurl_str = env->NewStringUTF(loader_url);
jobject jurl = env->NewObject(cls, method, jurl_str);
jobjectArray jurls = env->NewObjectArray(1, cls, jurl);
// URLClassLoader loader = new URLClassLoaer(urls)
cls = env->FindClass("java/net/URLClassLoader");
method = env->GetMethodID(cls, "<init>", "([Ljava/net/URL;)V");
jobject jloader = env->NewObject(cls, method, jurls);
// Class cls = loader.loadClass(name)
method = env->GetMethodID(
cls, "loadClass", "(Ljava/lang/String;Z)Ljava/lang/Class;");
jstring jname = env->NewStringUTF(class_name);
cls = (jclass)env->CallObjectMethod(jloader, method, jname, (jboolean) true);
method = env->GetMethodID(cls, "<init>", "()V");
if (env->ExceptionCheck()) env->ExceptionDescribe();
// RegisterNatives must be called after GetMethodID.
// If the order is reversed, we get UnsatisfiedLinkError,
// which seems like a bug.
RegisterNativeMethod(cls);
env->NewObject(cls, method);
if (env->ExceptionCheck()) env->ExceptionDescribe();
}
int main(int argc, char** argv) {
InitializeJVM();
Test();
return 0;
}
--------------------------
My class loader is sun.misc.Launcher$AppClassLoader at 1f7182c1
Successfully called registered native method
back to Java
Thanks,
Martin
More information about the net-dev
mailing list