Reputation: 11
I am working on a very large and complex C++ application that is using JNI in several unrelated places for different tasks. The platform is RHEL 6. Because I don't know if a JNI was already created by the application, I am using this method to find a JVM and attach to it:
JOI_RetCode JavaObjectInterface::initJVM()
{
if (jvm_ == NULL)
{
// Is there an existing JVM already created?
jsize jvm_count = 0;
jint result = JNI_GetCreatedJavaVMs (&jvm_, 1, &jvm_count);
if (result != JNI_OK)
return JOI_ERROR_CHECK_JVM;
if (jvm_count == 1)
{
// Yes - use it.
result = jvm_->GetEnv((void**) &jenv_, JNI_VERSION_1_6);
switch (result)
{
case JNI_OK:
logger::log(CAT_JNI, LL_DEBUG, "Attached to an existing JVM.");
jenv_ = env;
break;
case JNI_EDETACHED:
result = jvm_->AttachCurrentThread((void**) &jenv_, NULL);
if (result != JNI_OK)
return JOI_ERROR_ATTACH_JVM;
needToDetach_ = true;
logger::log(CAT_JNI, LL_DEBUG, "Attached to an existing JVM from another thread.");
break;
case JNI_EVERSION:
logger::log(CAT_JNI, LL_DEBUG, "Attaching to a JVM of the wrong version.");
return JOI_ERROR_JVM_VERSION;
break;
default:
logger::log(CAT_JNI, LL_DEBUG, "Unknown error Attaching to an existing JVM.");
return JOI_ERROR_ATTACH_JVM;
break;
}
}
else
{
// No - create a new one.
result = createJVM();
if (result != JNI_OK)
return JOI_ERROR_CREATE_JVM;
needToDetach_ = false;
logger::log(CAT_JNI, LL_DEBUG, "Created a new JVM.");
}
}
return JOI_OK;
}
The JVM pointers are defined as class data members as follows:
JavaVM* jvm_;
JNIEnv* jenv_;
Now, when a JVM was created elsewhere before I call thid method, the result is that the call to JNI_GetCreatedJavaVMs() returns a JVM pointer, and the call to GetEnv() returns JNI_OK, and updates jenv_ with a pointer that looks valid. So far so good. However, when I next do:
jclass javaClass = jenv_->FindClass(className);
I get an exception thrown. I cannot get any data from the exception itself because stderr is redirected to /dev/null, so if I call ExceptionDescribe() the output goes to hell. Interrogating the exception object requires a working JNI to call FindClass("Throwable"), so that's not working either.
The call to FindClass() is good because when I am creating the first JVM, it worksd fine.
Can anyone help me find the stupid mistake that I must have done here somewhere? Thanks, Yuval.
Upvotes: 1
Views: 2856
Reputation: 10069
The JNIEnv
pointer is usually thread-specific. It's easiest to simply look it up on demand using the JVM reference.
For whatever block of code that needs to access JNIEnv
, look it up in scope for that local block:
JNIEnv* jenv_;
int result = jvm_->GetEnv((void**) &jenv_, JNI_VERSION_1_6);
if (result == JNI_EDETACHED) {
// attach
}
// Do what you gotta do here
// ...
if (needs_detach) {
// detach
}
That way you'll be sure to get a valid JNIEnv
no matter your context.
The main takeaway from this is "don't cache JNIEnv
".
Upvotes: 2