JavaData & JavaException Handling in JNI

Exception Handling in JNI

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

An exception handling mechanism is particularly helpful in identifying and subsequently reporting that an error has occurred in the process flow. We have an option to ignore such a situation and let the application crash or behave in a manner it’s not supposed to. This clearly is not a good idea. Exception handling is not only a good code design practice but also provides an opportunity to succumb the situation in a friendly manner. Unlike C/C++, the Java compiler is pretty straightforward and makes it a point for the programmer to follow the exception rules almost to the verge of forcing it. And, in fact, the mechanism is quite simple. However, from the point of view of JNI programming, this otherwise simple mechanism gets trickier due to the need of mapping between Java and the native language, such as C/C++. The article delves into some of the key concepts of exception handling in JNI.

The Exception Handling Mechanism

If the source of the exception is in the native code, JNI is equipped to detect and handle the exceptions that are thrown in the JVM due to JNI function call. Moreover, the native language, such as C++, has inherent exception handling features. If the exception occurs within Java, its own exception handling mechanism is quite extensive and self-sufficient. So far so good, but the problem starts when native code throws an exception and it is propagated to Java code. The reason is that the mechanism of exception handling in Java is quite different from that of native code. When an exception is thrown in Java, the control quickly seeks for an immediate catch block appropriate for handling the exception. This does not occur while executing native code. When an exception is thrown in native code, answering such an urgent call is slated until the control returns back to Java; meanwhile, the native code keeps on executing. Programmers are cautioned not to execute any other native function in between the pending exception response except for freeing up resources. So, the problem is much deeper than just lingual disparity and here the tricky part creeps in.

Exception Detection

Handling an exception situation begins with detection and to find out whether an exception occurred or not, what we can do is the following:

  • Check the occurrence of exception after function return
  • Check the special function return value

A JNI function, such as FindClass(), returns a special value when it cannot find a particular class. The exception that surfaces is any of ClassCircularityError, OutOfmemoryError, ClassFormatError, or NoClassDefFoundError. What FindClass() does is that it returns a NULL if any of the above-mentioned exceptions crops up. Consequently, we can check the returned value and take the appropriate steps to handle the situation.

jclass jcls =
   env->FindClass("org/jnidemo/SomeClass");
{
   /* Handle exception here or free up any resources held
      Exception remains pending until control returns back
      to the Java code.
   */
   return;
}

However, there are cases when it is not possible to return a value to flag an exception, such as, an array out of bound exception when native code tries to access elements of a Java array beyond its array-size and JVM throws ArrayIndexOutOfBoundsException. In this case, we can call a function of a Java object at the point of the exception occurrence. In the native code, what we can do is call either the ExceptionOccurred() or ExceptionCheck() JNI function just after the native function call. ExceptionOccurred() returns a reference of the exception object and ExceptionCheck() returns JNI_TRUE or JNI_FALSE on whether the exception occurs or does not occur, respectively.

jthrowable flag = env->ExceptionOccurred();
{
   /* Handle exception here or free up any resources held
      Exception remains pending until control returns back
      to the Java code.
   */
   return;
}

jboolean flag = env->ExceptionCheck();
if (flag) {
   /* Handle exception here or free up any resources held
      Exception remains pending until control returns back
      to the Java code.
   */
   return;
}

Once the exception is detected, we can:

  • Handle it when control returns back to Java code
  • Handle it within native code
  • Handle it within native code and propagate a new exception for Java

Handling Exceptions in Java Code

Once the control returns back to Java after the return statement, the way to let Java handle the exception is as follows:

jboolean flag = env->ExceptionCheck();
if (flag) {
   /* Handle exception here or free up any resources held
      Exception remains pending until control returns back
      to the Java code.
   */
   return;
}

Handling Exceptions in Native Code

Before handling the exception, we must clear any pending exception with the help of the JNI function ExceptionClear(), and the native code to handle the situation is as follows:

jboolean flag = env->ExceptionCheck();
if (flag) {
   env->ExceptionClear();

   /* code to handle exception */
}

Handling Exceptions in Native Code and Propagating a New Exception

The following code is very similar to the code snippet given above, except for the ThrowNew() method that is used to throw a new exception to the Java code. There is another function, called Throw(), serving the same purpose although there is a significant difference in the function parameter.

  • jint Throw(jthrowable obj)
  • jint ThrowNew(jclass clazz, const char *message)

The point worth mentioning here is that, on immediate encounter of a throw method, control is not transferred to the Java code; instead, it waits until the return statement is encountered. There can be lines of code in between the throw method and return statement. Both the throw and JNI functions return zero on success and a negative value otherwise.

if(...){
   jclass jcls =
      env->FindClass("java/lang/Exception");
   jboolean flag = env->ExceptionCheck();
   if (flag) {
      env->ExceptionClear();
      /* code to handle exception */
   }
   env->ThrowNew(jcls, "error message");
   return;
}

Conclusion

In view of keeping things simple, several intricacies have been overlooked. But, nonetheless, this is the basic mechanism of exception handling in JNI programming. On a hint to further reading, the interested reader may get into the details of JNI function such as ExceptionDescribe(), FatalError(), and so forth. These functions provide some important clues to stack trace exceptions or raise fatal exceptions in native code.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories