Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to obtain a description of a Java exception in C++ when using JNI?

Tags:

I would like to determine what exception has been thrown by a Java function when calling that function from C++ code using JNI. I have the following code that catches the Java exception:

JNIEnv * pEnv; // assume this is properly initialized jclass javaClass; // ditto jmethodID javaFunction; // ditto pEnv->CallStaticVoidMethod(javaClass, javaFunction); jthrowable exc; if(exc = pEnv->ExceptionOccurred()) {     pEnv->ExceptionClear(); } 

I do not know how to obtain descriptive information about the Java exception within this C++ code. Can somebody help?

like image 717
Dan Nissenbaum Avatar asked May 02 '12 06:05

Dan Nissenbaum


1 Answers

I have omitted calling ExceptionCheck() after each JNI call and checking for any failed attempts to locate methods for brevity: you should add these when implementing.

First, store the exception and then acquire the Java methods necessary for obtaining information about the Throwable:

// Get the exception and clear as no // JNI calls can be made while an exception exists. jthrowable exception = pEnv->ExceptionOccurred(); pEnv->ExceptionClear();  jclass throwable_class = pEnv->FindClass("java/lang/Throwable"); jmethodID mid_throwable_getCause =     pEnv->GetMethodID(throwable_class,                       "getCause",                       "()Ljava/lang/Throwable;"); jmethodID mid_throwable_getStackTrace =     pEnv->GetMethodID(throwable_class,                       "getStackTrace",                       "()[Ljava/lang/StackTraceElement;"); jmethodID mid_throwable_toString =     pEnv->GetMethodID(throwable_class,                       "toString",                       "()Ljava/lang/String;");  jclass frame_class = pEnv->FindClass("java/lang/StackTraceElement"); jmethodID mid_frame_toString =     pEnv->GetMethodID(frame_class,                       "toString",                       "()Ljava/lang/String;"); 

Second, recursively construct the error message (you may want to modify this):

std::string error_msg; // Could use ostringstream instead.  _append_exception_trace_messages(*pEnv,                                  error_msg,                                  exception,                                  mid_throwable_getCause,                                  mid_throwable_getStackTrace,                                  mid_throwable_toString,                                  mid_frame_toString);  void _append_exception_trace_messages(                         JNIEnv&      a_jni_env,                         std::string& a_error_msg,                         jthrowable   a_exception,                         jmethodID    a_mid_throwable_getCause,                         jmethodID    a_mid_throwable_getStackTrace,                         jmethodID    a_mid_throwable_toString,                         jmethodID    a_mid_frame_toString) {     // Get the array of StackTraceElements.     jobjectArray frames =         (jobjectArray) a_jni_env.CallObjectMethod(                                         a_exception,                                         a_mid_throwable_getStackTrace);     jsize frames_length = a_jni_env.GetArrayLength(frames);      // Add Throwable.toString() before descending     // stack trace messages.     if (0 != frames)     {         jstring msg_obj =             (jstring) a_jni_env.CallObjectMethod(a_exception,                                                  a_mid_throwable_toString);         const char* msg_str = a_jni_env.GetStringUTFChars(msg_obj, 0);          // If this is not the top-of-the-trace then         // this is a cause.         if (!a_error_msg.empty())         {             a_error_msg += "\nCaused by: ";             a_error_msg += msg_str;         }         else         {             a_error_msg = msg_str;         }          a_jni_env.ReleaseStringUTFChars(msg_obj, msg_str);         a_jni_env.DeleteLocalRef(msg_obj);     }      // Append stack trace messages if there are any.     if (frames_length > 0)     {         jsize i = 0;         for (i = 0; i < frames_length; i++)         {             // Get the string returned from the 'toString()'             // method of the next frame and append it to             // the error message.             jobject frame = a_jni_env.GetObjectArrayElement(frames, i);             jstring msg_obj =                 (jstring) a_jni_env.CallObjectMethod(frame,                                                      a_mid_frame_toString);              const char* msg_str = a_jni_env.GetStringUTFChars(msg_obj, 0);              a_error_msg += "\n    ";             a_error_msg += msg_str;              a_jni_env.ReleaseStringUTFChars(msg_obj, msg_str);             a_jni_env.DeleteLocalRef(msg_obj);             a_jni_env.DeleteLocalRef(frame);         }     }      // If 'a_exception' has a cause then append the     // stack trace messages from the cause.     if (0 != frames)     {         jthrowable cause =              (jthrowable) a_jni_env.CallObjectMethod(                             a_exception,                             a_mid_throwable_getCause);         if (0 != cause)         {             _append_exception_trace_messages(a_jni_env,                                              a_error_msg,                                               cause,                                              a_mid_throwable_getCause,                                              a_mid_throwable_getStackTrace,                                              a_mid_throwable_toString,                                              a_mid_frame_toString);         }     } } 

I copied this from code I wrote a few years ago (modified to eliminate boiler plate ExceptionCheck()s), but I did not compile what I have posted but the general approach is hopefully clear.

like image 182
hmjd Avatar answered Sep 30 '22 17:09

hmjd