Search code examples
javacexceptionjava-native-interface

why doesn't the exception get printed?


The following is a java code and next to it is the c code :

package Package;

public class CatchThrow {

private native void doit() throws IllegalArgumentException;

private void callBack() throws NullPointerException {
    System.out.println("In call-back !");
    throw new NullPointerException("CatchThrow.callBack");
}

public static void main(String args[]) {
    CatchThrow c = new CatchThrow();
    try {
        c.doit();
    } catch(Exception exc) {
        System.out.println("\nIn Java : \n\t" + exc);
    }
}

 static {
     System.loadLibrary("CatchThrow");
 }
}

C code :

#include "stdio.h"
#include "Package_CatchThrow.h"

void Java_Package_CatchThrow_doit
   (JNIEnv *env, jobject obj) {
  jthrowable exc;
  jclass cls = (*env)->GetObjectClass(env,obj);
  jmethodID mid = (*env)->GetMethodID(env,cls,"callBack","()V");
  if(mid == NULL) {
    return;
  }
  (*env)->CallVoidMethod(env,obj,mid); // call the java method that throws NullPointerException
  printf("After the call to Call-Back !");
  exc = (*env)->ExceptionOccurred(env);
  if(exc) {
    jclass newExcCls;
    printf("\n");
    //(*env)->ExceptionDescribe(env);  ----> NOTE THE COMMENTED STATEMENT
    //(*env)->ExceptionClear(env);  -----> NOTE THE COMMENTED STATEMENT
    newExcCls = (*env)->FindClass(env,"java/lang/IllegalArgumentException");
    if(newExcCls == NULL) {
        return;
    }
    (*env)->ThrowNew(env,newExcCls,"thrown from c code");
  }

}

When i build and run the above program I get the following output :

In call-back !
After the call to Call-Back !

In Java :
    java.lang.IllegalArgumentException: thrown from c code

From the output : C code calls the java function callBack. There the first statement gets printed. After the call returns the statement next to the call i.e After the call to Call-Back ! gets printed. But what happened to the statement throw new NullPointerException("CatchThrow.callBack"); in the java function callBack ? Why doesn't it print the exception ?

But if i remove the comments from the statements

(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);

commented, I get the desired output :

In call-back !
After the call to Call-Back !
Exception in thread "main" java.lang.NullPointerException: CatchThrow.callBack
    at Package.CatchThrow.callBack(CatchThrow.java:14)
    at Package.CatchThrow.doit(Native Method)
    at Package.CatchThrow.main(CatchThrow.java:20)

In Java :
    java.lang.IllegalArgumentException: thrown from c code

Why is that ?

What is the role of those two statements ? And why doesn't the exception get printed in the absence of those 2 statements ?


Solution

  • Your new NullPointerException("CatchThrow.callBack"); never touches the Java runtime and is handled completely in JNI space.

    As such, no exception details will be printed unless you call the JNI ExceptionDescribe function:

    (*env)->ExceptionDescribe(env); 
    

    You can't return multiple exceptions from the JNI so the only exception information that comes back to your try/catch block in the Java runtime is the one you create later on with:

    (*env)->ThrowNew(env,newExcCls,"thrown from c code"); 
    

    A pending exception raised through the JNI (by calling ThrowNew, for example) does not immediately disrupt the native method execution. This is different from how exceptions behave in the Java programming language. When an exception is thrown in the Java programming language, the virtual machine automatically transfers the control flow to the nearest enclosing try/catch statement that matches the exception type. The virtual machine then clears the pending exception and executes the exception handler. In contrast, JNI programmers must explicitly implement the control flow after an exception has occurred.

    From the JNI documentation on exceptions