Hello Guys!!! Hope You all are doing well.
Today I am going to discuss “Exception handling in JNI and how to throw exception in java from JNI”.
Exception handling generally makes error reporting and handling more readable and easier. But it will be difficult if more than one programming language is used. Here I explain exception handling in JNI (Java Native Interface).
In the ExceptionDemo native method, we used the ThrowNew function to throw java.lang.NullPointerException and a Throw function to throw java.lang.RuntimeException.
Today I am going to discuss “Exception handling in JNI and how to throw exception in java from JNI”.
Exception handling generally makes error reporting and handling more readable and easier. But it will be difficult if more than one programming language is used. Here I explain exception handling in JNI (Java Native Interface).
1.Many JNI functions return a special value to indicate failure. As for example :- FindClass function returns NULL to indicate it failed to load the class. In that case we can simply check the return value to see if an error occurs.
2. Many other functions do not use the return value to signal failure; instead an exception is thrown. JNI defines two functions to check for exceptions, as follows:
- jboolean ExceptionCheck(JNIEnv *env);
- jthrowable ExceptionOccurred(JNIEnv *env);
- void ExceptionDescribe(JNIEnv *env);
In my example code, I used both approaches to check for occurrence of exceptions and ExceptionDescribe to print out the exception details.
There are generally two ways to handle an exception in JNI side.
There are generally two ways to handle an exception in JNI side.
- The first approach is to free the resources allocated at JNI and return. This will leave the responsibility of handling the exception to the caller of the native method.
- The second practice is to clear the exception and continue executing. This is done through the following JNI function call: void ExceptionClear(JNIEnv *env);
Throw exceptions in the native code:
JNI provides two functions to throw an exception from native code. They have the following prototypes:
- jint Throw(JNIEnv *env, jthrowable obj);
- jint ThrowNew(JNIEnv *env, jclass clazz, const char *message);
In the ExceptionDemo native method, we used the ThrowNew function to throw java.lang.NullPointerException and a Throw function to throw java.lang.RuntimeException.
Fatal error:
A special type of error is the fatal error, which is not recoverable. JNI defines a function FatalError, as follows, to raise a fatal error:- void FatalError(JNIEnv *env, const char *msg);
In my example the usage of this function, you find in FatalErrorDemo and Java method callFatalErrorDemo.
Note:-
- After FatalError function no other code is executed, in neither the native nor Java code, because FatalError never returns, and the VM instance is terminated.
- C++ exception is currently not supported on Android JNI programming. Therefore, we should handle C++ exceptions within C++ code. Alternatively, we can write a C wrapper to throw an exception or return an error code to Java.
exceptiontest.c
#include <jni.h>
#include <stdio.h>
#include "ExceptionHandling.h"
JNIEXPORT void JNICALL Java_ExceptionHandling_ExceptionDemo(JNIEnv *pEnv, jobject pObj) {
// jboolean ExceptionCheck(JNIEnv *env);
// jthrowable ExceptionOccurred(JNIEnv *env);
//if no exception pending
jboolean ifExceptionPending =(*pEnv)->ExceptionCheck(pEnv);
printf("\n ExceptionCheckDemo :1 %d \n", ifExceptionPending);
fflush(stdout);
jthrowable exception = (*pEnv)->ExceptionOccurred(pEnv);
printf("\n ExceptionOccurred :2 returned NULL? : %d \n",(*pEnv)->IsSameObject(pEnv, exception, NULL));
fflush(stdout);
//search for a class which are not available, which will cause an exception
(*pEnv)->FindClass(pEnv, "java/lang/XXYY");
//use ExceptionCheck to check
ifExceptionPending =(*pEnv)->ExceptionCheck(pEnv);
printf("\nExceptionCheck:3 after finding non-existing class: %d\n", ifExceptionPending);
fflush(stdout);
(*pEnv)->ExceptionClear(pEnv); //clear the exception, so we can proceed
ifExceptionPending =(*pEnv)->ExceptionCheck(pEnv);
printf("\nExceptionCheck:4 after clear: %d\n",ifExceptionPending);
fflush(stdout);
//throw a java.lang.NullPointerException using ThrowNew
jclass cls = (*pEnv)->FindClass(pEnv, "java/lang/NullPointerException");
jint st = (*pEnv)->ThrowNew(pEnv, cls, "throw null pointer exception");
if (st == 0) {
printf("\nCheckDemo:5 null pointer exception thrown using ThrowNew\n");
fflush(stdout);
(*pEnv)->DeleteLocalRef(pEnv, cls);
}
//use ExceptionOccurred to check
jthrowable exObj = (*pEnv)->ExceptionOccurred(pEnv);
if (exObj == NULL) {
printf("\nExceptionCheckDemo :6 no exception\n");
fflush(stdout);
} else {
printf("\nExceptionCheckDemo :7 there's pending exception, call ExceptionDescribe\n");
fflush(stdout);
(*pEnv)->ExceptionDescribe(pEnv); //this does not clear the exception
ifExceptionPending =(*pEnv)->ExceptionCheck(pEnv);
printf("\nExceptionCheckDemo :8 ExceptionCheck after ExceptionDescribe: %d\n",ifExceptionPending);
fflush(stdout);
(*pEnv)->ExceptionClear(pEnv); //clear the exception, so we can proceed
ifExceptionPending =(*pEnv)->ExceptionCheck(pEnv);
printf("\nExceptionCheckDemo :9 ExceptionCheck after clear: %d\n",ifExceptionPending);
fflush(stdout);
(*pEnv)->DeleteLocalRef(pEnv, exObj);
}
//throw a java.lang.RuntimeException using Throw
cls = (*pEnv)->FindClass(pEnv, "java/lang/RuntimeException");
jmethodID exConstructor = (*pEnv)->GetMethodID(pEnv, cls, "<init>","(Ljava/lang/String;)V");
//passing UTF-8 string directly to exConstructor won't work because it expects jstring
//jthrowable rtExObj = (*pEnv)->NewObject(pEnv, reCls, exConstructor, "throw runtime exception");
jstring msg = (*pEnv)->NewStringUTF(pEnv, "throw runtime exception");
exObj = (*pEnv)->NewObject(pEnv, cls, exConstructor, msg);
(*pEnv)->DeleteLocalRef(pEnv, msg);
if (exObj == NULL) {
printf("\nExceptionCheckDemo :10 create RuntimeException failed\n");
fflush(stdout);
} else {
jint st = (*pEnv)->Throw(pEnv, exObj);
if (st == 0) {
printf("\nExceptionCheckDemo :11 exception thrown using Throw\n");
fflush(stdout);
(*pEnv)->DeleteLocalRef(pEnv, cls);
(*pEnv)->DeleteLocalRef(pEnv, exObj);
}
//do not clear the exception, let the caller handle it
}
}
JNIEXPORT void JNICALL Java_ExceptionHandling_FatalErrorDemo(JNIEnv *pEnv, jobject pObj) {
(*pEnv)->FatalError(pEnv, "fatal error");
printf("\nFatalErrorDemo :12 after calling FatalError\n");
fflush(stdout);
}
//jboolean ExceptionCheck(JNIEnv *env);
//Determines if an exception has been thrown. The exception stays thrown until either the
//native code calls ExceptionClear, or the caller of the native method handles the exception.
//Returns the JNI_TRUE if there is a pending exception, or
//JNI_FALSE if there is no pending exception.
//void ExceptionClear(JNIEnv *env);
//Clears any pending exception that is currently being thrown in the current thread.
//If no exception is currently being thrown, this function has no effect.
//This function has no effect on exceptions pending on other threads.
//void ExceptionDescribe(JNIEnv *env);
//Prints the pending exception and a backtrace of the stack to the system error-reporting
//channel System.out.err. This is a convenience routine provided for debugging.
//jthrowable ExceptionOccurred(JNIEnv *env);
//Determines if an exception is pending in the current thread. The exception stays pending until either
//the native code calls ExceptionClear, or the caller of the native method handles the exception.
//void FatalError(JNIEnv *env, const char *msg);
//Raises a fatal error and does not expect the virtual machine implementation to recover.
//Prints the message in a system debugging channel, such as stderr, and terminates
//the virtual machine instance. This function does not return.
//jint Throw(JNIEnv *env, jthrowable obj);
//Causes a java.lang.Throwable object to be thrown. A thrown exception will be pending in the current thread,
//but does not immediately disrupt native code execution.
//Returns zero on success; otherwise, returns a negative value
//if the specified exception cannot be thrown.
//jint ThrowNew(JNIEnv *env, jclass clazz, const char *message);
//Constructs an exception object from the specified class with the message specified by message
//and causes that exception to be thrown.
ExceptionHandling.javaimport java.io.File;
import java.lang.reflect.Field ;
public class ExceptionHandling{
static {
String libPath = null;
try{
String mPath = new File (".").getCanonicalPath()+"/";
String langKey = "java.library.path" ;
System.setProperty ( langKey, mPath ) ;
libPath = System.getProperty("java.library.path");
System.out.println("java.library.path=" + libPath);
}catch(Exception e){
e.printStackTrace();
}
System.load(libPath+"libExceptionHandling.so");
// System.loadLibrary("ExceptionHandling");
}
public static void main(String[] args) {
ExceptionHandling obj = new ExceptionHandling();
obj.callExceptionDemo();
// obj.callFatalErrorDemo();
}
private void callExceptionDemo() {
try {
ExceptionDemo();
} catch (RuntimeException e) {
String msg = e.getMessage();
//tv.setText(msg);
System.out.println(msg);
}
}
private void callFatalErrorDemo() {
FatalErrorDemo();
//tv.setText("after calling FatalErrorDemo");
System.out.println("after calling FatalErrorDemo");
}
private native void ExceptionDemo();
private native void FatalErrorDemo();
}
Thanks
Saurabh
Happy Coding!!!!
Please put your comment in comment box. It will encourage me.
Saurabh
Happy Coding!!!!
Please put your comment in comment box. It will encourage me.