33

I have a JNI layer in my application. In some cases Java throws an exception. How can I get the Java exception in the JNI layer? I have the code something like as follows.

if((*(pConnDA->penv))->ExceptionCheck(pConnDA->penv))
{
    (*(pConnDA->penv))->ExceptionDescribe(pConnDA->penv); 
    (*(pConnDA->penv))->ExceptionClear(pConnDA->penv);
}

Will this block of code catch only JNI exceptions? Where will the exception description be logged in console (STDERR)? How do I get this into the buffer, so that I can pass it to my logger module?

double-beep
  • 5,031
  • 17
  • 33
  • 41
user210444
  • 341
  • 1
  • 3
  • 4
  • check this: http://www.developer.com/java/data/exception-handling-in-jni.html – Behrouz.M Oct 05 '15 at 13:36
  • @raypixar: Your link seems to tell us barely more than the basics which are to be found in [the official JNI documentation](http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html). – PJTraill Jun 08 '16 at 14:56
  • In [the question JNI Getting Exception Info - Attempted to read or write protected memory](http://stackoverflow.com/questions/12325860/jni-getting-exception-info-attempted-to-read-or-write-protected-memory) you will find an example of how to call `toString` on the exception class (or object). Two things: • you **must** call `ExceptionClear` before almost anything else; • make sure you pass the exception object if appropriate, not the exception class object (the mistake made by that questioner). – PJTraill Jun 08 '16 at 15:32

3 Answers3

36

if you invoke a Java method from JNI, calling ExceptionCheck afterwards will return JNI_TRUE if an exception was thrown by the Java.

if you're just invoking a JNI function (such as FindClass), ExceptionCheck will tell you if that failed in a way that leaves a pending exception (as FindClass will do on error).

ExceptionDescribe outputs to stderr. there's no convenient way to make it go anywhere else, but ExceptionOccurred gives you a jthrowable if you want to play about with it, or you could just let it go up to Java and handle it there. that's the usual style:

jclass c = env->FindClass("class/does/not/Exist");
if (env->ExceptionCheck()) {
  return;
}
// otherwise do something with 'c'...

note that it doesn't matter what value you return; the calling Java code will never see it --- it'll see the pending exception instead.

oHo
  • 51,447
  • 27
  • 165
  • 200
Elliott Hughes
  • 4,595
  • 2
  • 23
  • 21
  • `ExceptionDescribe` prints to “a system error-reporting channel, such as `stderr`”, i.e. it is implementation dependent. – PJTraill Jun 08 '16 at 15:28
  • It does NOT work. Program has been crash at the first line. I have tried some case of try/catch, check Exception but still does not run. What is solution for that ? – Bulma Dec 12 '17 at 06:52
  • 2
    you *must* call `ExceptionClear()` if `ExceptionCheck() == JNI_TRUE` or the java code will throw the exception as soon as the control returns to the JVM – Yusef Maali Jun 21 '20 at 05:47
  • Assume, you have a C++nested call finding a ´env->ExceptionCheck()´ then you would prefer to throw a C++-exception and catch it in the immediate context of the JNI-method. Though, this exception cannot be specific. Or: Is there a way to ask for the type of java exception? – Sam Ginrich Jan 08 '22 at 16:53
17

This is a complement of the Elliott Hughes' answer. My answer provides a step-by-step example explaining how to catch exceptions and how to translate them between C++ and Java words using the JNI layer.

Short answer

See the correct Elliott Hughes' answer.

Reusable example

This answer and snippets are in public domain or in CC0 in order to ease reuse. All the source code here is C++03 backward compatible.

To reuse the above snippet do the following:

  • Replace mypackage::Exception by your own C++ Exception.
  • If the corresponding Java exception my.group.mypackage.Exception is not defined, then replace "my/group/mypackage/Exception" by "java/lang/RuntimeException".

Catch exceptions from Java

See also the snippet on coliru.

void rethrow_cpp_exception_as_java_exception()
{
    try
    {
        throw; // This allows to determine the type of the exception
    }
    catch (const mypackage::Exception& e) {
        jclass jc = env->FindClass("my/group/mypackage/Exception");
        if(jc) env->ThrowNew (jc, e.what());
        /* if null => NoClassDefFoundError already thrown */
    }
    catch (const std::bad_alloc& e) {
        jclass jc = env->FindClass("java/lang/OutOfMemoryError");
        if(jc) env->ThrowNew (jc, e.what());
    }
    catch (const std::ios_base::failure& e) {
        jclass jc = env->FindClass("java/io/IOException");
        if(jc) env->ThrowNew (jc, e.what());                          
    }                                                               
    catch (const std::exception& e) {
        /* unknown exception (may derive from std::exception) */
        jclass jc = env->FindClass("java/lang/Error");
        if(jc) env->ThrowNew (jc, e.what());
    }
    catch (...) {
        /* Oops I missed identifying this exception! */
        jclass jc = env->FindClass("java/lang/Error");
        if(jc) env->ThrowNew (jc, "Unidentified exception => "
          "Improve rethrow_cpp_exception_as_java_exception()" );
    }
}

I thanks Mooing Duck for contribution on the above C++ code.

Adapt the JNI generated source code

The following file Java_my_group_mypackage_example.cpp use the above rethrow_cpp_exception_as_java_exception() function:

JNIEXPORT jlong JNICALL Java_my_group_mypackage_example_function1
        (JNIEnv *env, jobject object, jlong value)
{
  try {
    /* ... my processing ... */
    return jlong(result);
  } catch(...) {
    rethrow_cpp_exception_as_java_exception();
    return 0;
  }
}
   
JNIEXPORT jstring JNICALL Java_my_group_mypackage_example_function2
        (JNIEnv *env, jobject object, jlong value)
{
  jstring jstr = 0
  try {
    /* ... my processing ... */
    jstr = env->NewStringUTF("my result");
  } catch(...) {
    rethrow_cpp_exception_as_java_exception();
  }
  return  jstr;
}

JNIEXPORT void JNICALL Java_my_group_mypackage_example_function3
        (JNIEnv *env, jobject object, jlong value)
{
  try {
    /* ... my processing ... */
  } catch(...) {
    rethrow_cpp_exception_as_java_exception();
  }
}

Corresponding Java code

File example.java

package my.group.mypackage;

public class Example {
  static {
    System.loadLibrary("my-DLL-name");
  }

  public Example() {
    /* ... */
  }

  private native int    function1(int); //declare DLL functions
  private native String function2(int); //using the keyword
  private native void   function3(int); //'native'

  public void dosomething(int value) {
    int result = function1(value);  
    String str = function2(value);  //call your DLL functions
    function3(value);               //as any other java function
  }
}

Note: "my-DLL-name" refers to the dynamic library produced from the above C/C++ code compiled. It can be my-DLL-name.dll on Windows or my-DLL-name.so on GNU/Linux/Unix.

Steps

  1. Generate example.class from example.java (using javac or maven or your favorite IDE Eclipse/Netbeans/IntelliJ IDEA/...)

  2. Generate C/C++ header file Java_my_group_mypackage_example.h from example.class using javah

0xdeadbeef
  • 500
  • 3
  • 17
oHo
  • 51,447
  • 27
  • 165
  • 200
  • Why is this a macro? Completely unnecessary http://coliru.stacked-crooked.com/a/5cf4f3b6b1cb988f Additionally, it doesn't answer the question – Mooing Duck Aug 12 '14 at 22:28
  • Hi @MooingDuck Thanks for your coliru link :-) I have provided more details within my answer. Please tell me if my explanation is enough to understand the usage and customization of macro `CATCH_CPP_EXCEPTION_AND_THROW_JAVA_EXCEPTION`. My aim is to provide an answer to help as many people as possible. Thank you in advance for your help ;-) Cheers – oHo Aug 28 '14 at 12:56
  • Well my questions still hold. I'll try to be clearer. First, why a macro? Macros are bad: http://stackoverflow.com/questions/14041453/why-are-preprocessor-macros-evil-and-what-are-the-alternatives. Secondly, the question is "How can I get[catch] the [currently thrown] Java exception in the JNI layer?" Which your post does not actually answer. :/ – Mooing Duck Aug 28 '14 at 16:24
  • First, thanks for the SO link about macro. But in our case, if you do not use this macro, you will have to duplicate the corresponding 30 lines within each function `Java_my_group_mypackage_example_function*`. Duplication might be worse than macro... Secondly, I am sorry, I misunderstood the question :-/ My answer does not talk about C++ code (JNI) calling Java code and catching Java exception. I thought Java stuff should not be called from JNI layer... – oHo Aug 28 '14 at 16:40
  • 1
    In fact, this technique can be used without a macro and without duplication. It can be put in a function, as I showed with the coliru link. Check the `Java_group_package_class_function1` function in my code. – Mooing Duck Aug 28 '14 at 16:44
  • Very good trick! I have never seen that before. Thanks :-) I am updating my answer... – oHo Aug 28 '14 at 17:05
  • 3
    Here you do not answer the question. – user207421 May 09 '15 at 00:56
  • Hi @EJP. Thank you for your feedback. I have refactored and clarified my answer. Cheers – oHo Jul 19 '17 at 14:03
0

In the case, when the Java exception occurs in a nested C++ call, it's not done with a simple return, but you prefer to remove the intermediate stack levels according to the throw mechanism.

I solved this with a

class JavaException : public std::exception
{
};

Then in a nested call, where the exception is raised

void nestedMethod()
{
    env->CallVoidMethod(...); // Java call creating Exception
    if (env->ExceptionCheck())
    {
        throw JavaException();
    }
}

Finally returning to Java

void JNI_method(JNIenv *env)
{
    try
    {
        doComplexCall(); // --> nestedMethod()
    }
    catch(const JavaException&)
    {
        //coming from positive ExceptionCheck()
        return;
    }
}
Sam Ginrich
  • 661
  • 6
  • 7