`

Chapter 6 Exceptions(JAVA EXCEPTION IN NATIVE CODE)

 
阅读更多
Contents | Prev | Next | Index The Java Native Interface
Programmer's Guide and Specification 

--------------------------------------------------------------------------------


Chapter 6
Exceptions

--------------------------------------------------------------------------------


We have encountered numerous situations in which native code checks for possible errors after making JNI function calls. This chapter examines how native code can detect and recover from these error conditions.

We will focus on errors that occur as the result of issuing JNI function calls, not arbitrary errors that happen in native code. If a native method makes an operating systems call, it simply follows the documented way of checking for possible failures in the system call. If, on the other hand, the native method issues a callback to a Java API method, then it must follow the steps described in this chapter to properly check for and recover from possible exceptions that have occurred in the method execution.

6.1 Overview
We introduce JNI exception handling functions through a series of examples.

6.1.1 Caching and Throwing Exceptions in Native Code
The program below shows how to declare a native method that throws an exception. The CatchThrow class declares the doit native method and specifies that it throws an IllegalArgumentException:

class CatchThrow {
     private native void doit()
         throws IllegalArgumentException;
     private void callback() throws NullPointerException {
         throw new NullPointerException("CatchThrow.callback");
     }
     public static void main(String args[]) {
         CatchThrow c = new CatchThrow();
         try {
             c.doit();
         } catch (Exception e) {
             System.out.println("In Java:\n\t" + e);
         }
     }
     static {
         System.loadLibrary("CatchThrow");
     }
}

The CatchThrow.main method calls the native method doit, implemented as follows:

JNIEXPORT void JNICALL
Java_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);
     exc = (*env)->ExceptionOccurred(env);
     if (exc) {
         /* We don't do much with the exception, except that
            we print a debug message for it, clear it, and
            throw a new exception. */
         jclass newExcCls;
         (*env)->ExceptionDescribe(env);
         (*env)->ExceptionClear(env);
         newExcCls = (*env)->FindClass(env,
                       "java/lang/IllegalArgumentException");
         if (newExcCls == NULL) {
             /* Unable to find the exception class, give up. */
             return;
         }
         (*env)->ThrowNew(env, newExcCls, "thrown from C code");
     }
}



Running the program with the native library produces the following output:

java.lang.NullPointerException:
         at CatchThrow.callback(CatchThrow.java)
         at CatchThrow.doit(Native Method)
         at CatchThrow.main(CatchThrow.java)
In Java:
         java.lang.IllegalArgumentException: thrown from C code

The callback method throws a NullPointerException. When the CallVoidMethod returns control to the native method, the native code will detect this exception by calling the JNI function ExceptionOccurred. In our example, when an exception is detected, the native code outputs a descriptive message about the exception by calling ExceptionDescribe, clears the exception using ExceptionClear, and throws an IllegalArgumentException instead.

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.

6.1.2 A Utility Function
Throwing an exception involves first finding the exception class and then issuing a call to the ThrowNew function. To simplify the task, we can write a utility function that throws a named exception:

void
JNU_ThrowByName(JNIEnv *env, const char *name, const char *msg)
{
     jclass cls = (*env)->FindClass(env, name);
     /* if cls is NULL, an exception has already been thrown */
     if (cls != NULL) {
         (*env)->ThrowNew(env, cls, msg);
     }
     /* free the local ref */
     (*env)->DeleteLocalRef(env, cls);
}

In this book, the JNU prefix stands for JNI Ut>tilities. JNU_ThrowByName first finds the exception class using the FindClass function. If FindClass fails (returns NULL), the virtual machine must have thrown an exception (such as NoClassDefFoundError). In this case JNU_ThrowByName does not attempt to throw another exception. If FindClass succeeds, we throw the named exception by calling ThrowNew. When JNU_ThrowByName returns, it guarantees that there is a pending exception, although the pending exception was not necessarily what is specified by the name argument. We make sure to delete the local reference to the exception class created in this function. Passing NULL to DeleteLocalRef is a no-op, which is an appropriate action if FindClass fails and returns NULL.

6.2 Proper Exception Handling
JNI programmers must foresee possible exception conditions and write code that checks for and handles these cases. Proper exception handling is sometimes tedious but is necessary in order to produce robust applications.

6.2.1 Checking for Exceptions
There are two ways to check whether an error has occurred.

Most JNI functions use a distinct return value (such as NULL) to indicate that an error has occurred. The error return value also implies that there is a pending exception in the current thread. (Encoding error conditions in the return value is common practice in C.) The following example illustrates using the NULL value returned by Get-Field-ID in checking for errors. The example consists of two parts: a class Window that defines a number of instance fields (handle, length, and width) and a native method that caches the field IDs of these fields. Even though these fields exist in the Window class, we still need to check for possible errors returned from GetFieldID because the virtual machine may not be able to allocate the memory needed to represent a field ID.
/* a class in the Java programming language */
public class Window {
     long handle;
     int length;
     int width;
     static native void initIDs();
     static {
         initIDs();
     }
}

/* C code that implements Window.initIDs */
jfieldID FID_Window_handle;
jfieldID FID_Window_length;
jfieldID FID_Window_width;

JNIEXPORT void JNICALL
Java_Window_initIDs(JNIEnv *env, jclass classWindow)
{
     FID_Window_handle =
         (*env)->GetFieldID(env, classWindow, "handle", "J");
     if (FID_Window_handle == NULL) {  /* important check. */
         return; /* error occurred. */
     }
     FID_Window_length =
         (*env)->GetFieldID(env, classWindow, "length", "I");
     if (FID_Window_length == NULL) {  /* important check. */
         return; /* error occurred. */
     }
     FID_Window_width =
         (*env)->GetFieldID(env, classWindow, "width", "I");
     /* no checks necessary; we are about to return anyway */
}


When using a JNI function whose return value cannot flag that an error has occurred, native code must rely on the raised exception to do error checks. The JNI function that performs checks for a pending exception in the current thread is ExceptionOccurred. (ExceptionCheck was also added in Java 2 SDK release 1.2.) For example, the JNI function CallIntMethod cannot encode an error condition in the return value. Typical choices of error condition return values, such as NULL and -1, do not work because they could be legal values returned by the method that was called. Consider a Fraction class whose floor method returns the integral part of the value of the fraction, and some native code that calls this method.
public class Fraction {
     // details such as constructors omitted
     int over, under;
     public int floor() {
         return Math.floor((double)over/under);
     }
}
/* Native code that calls Fraction.floor. Assume method ID
    MID_Fraction_floor has been initialized elsewhere. */
void f(JNIEnv *env, jobject fraction)
{
     jint floor = (*env)->CallIntMethod(env, fraction,
                                        MID_Fraction_floor);
     /* important: check if an exception was raised */
     if ((*env)->ExceptionCheck(env)) {
         return;
     }
     ... /* use floor */
}

When the JNI function returns a distinct error code, the native code may still check for exceptions explicitly by calling, for example, ExceptionCheck. However, it is more efficient to check for the distinct error return value instead. If a JNI function returns its error value, a subsequent ExceptionCheck call in the current thread is guaranteed to return JNI_TRUE.

6.2.2 Handling Exceptions
Native code may handle a pending exception in two ways:

The native method implementation can choose to return immediately, causing the exception to be handled in the caller.
The native code can clear the exception by calling ExceptionClear and then execute its own exception handling code.
It is extremely important to check, handle, and clear a pending exception before calling any subsequent JNI functions. Calling most JNI functions with a pending exception--with an exception that you have not explicitly cleared--may lead to unexpected results. You can call only a small number of JNI functions safely when there is a pending exception in the current thread. Section 11.8.2 specifies the complete list of these JNI functions. Generally speaking, when there is a pending exception you can call the JNI functions that are designed to handle exceptions and the JNI functions that release various virtual machine resources exposed through the JNI.

It is often necessary to be able to free resources when exceptions occur. In the following example, the native method first obtains the contents of a string by issuing a GetStringChars call. It calls ReleaseStringChars if a subsequent operation fails:



JNIEXPORT void JNICALL
Java_pkg_Cls_f(JNIEnv *env, jclass cls, jstring jstr)
{
     const jchar *cstr = (*env)->GetStringChars(env, jstr);
     if (c_str == NULL) {
         return;
     }
     ...
     if (...) { /* exception occurred */
         (*env)->ReleaseStringChars(env, jstr, cstr);
         return;
     }
     ...
     /* normal return */
     (*env)->ReleaseStringChars(env, jstr, cstr);
}

The first call to ReleaseStringChars is issued when there is a pending exception. The native method implementation releases the string resource and returns immediately afterwards without first clearing the exception.

6.2.3 Exceptions in Utility Functions
Programmers writing utility functions should pay special attention to ensure that exceptions are propagated to the caller native method. In particular, we emphasize the following two issues:

Preferably, utility functions should provide a special return value to indicate that an exception has occurred. This simplifies the caller's task of checking for pending exceptions.
In addition, utility functions should follow the rules (§5.3) for managing local references in exception handling code.

To illustrate, let us introduce a utility function that performs a callback based on the name and descriptor of an instance method:

jvalue
JNU_CallMethodByName(JNIEnv *env,
                      jboolean *hasException,
                      jobject obj,
                      const char *name,
                      const char *descriptor, ...)
{
     va_list args;
     jclass clazz;
     jmethodID mid;
     jvalue result;
     if ((*env)->EnsureLocalCapacity(env, 2) == JNI_OK) {
         clazz = (*env)->GetObjectClass(env, obj);
         mid = (*env)->GetMethodID(env, clazz, name,
                                   descriptor);
         if (mid) {
             const char *p = descriptor;
             /* skip over argument types to find out the
                return type */
             while (*p != ')') p++;
             /* skip ')' */
             p++;
             va_start(args, descriptor);
             switch (*p) {
             case 'V':
                 (*env)->CallVoidMethodV(env, obj, mid, args);
                 break;
             case '[':
             case 'L':
                 result.l = (*env)->CallObjectMethodV(
                                        env, obj, mid, args);
                 break;
             case 'Z':
                 result.z = (*env)->CallBooleanMethodV(
                                        env, obj, mid, args);
                 break;
             case 'B':
                 result.b = (*env)->CallByteMethodV(
                                        env, obj, mid, args);
                 break;
             case 'C':
                 result.c = (*env)->CallCharMethodV(
                                        env, obj, mid, args);
                 break;
             case 'S':
                 result.s = (*env)->CallShortMethodV(
                                        env, obj, mid, args);
                 break;
             case 'I':
                 result.i = (*env)->CallIntMethodV(
                                        env, obj, mid, args);
                 break;
             case 'J':
                 result.j = (*env)->CallLongMethodV(
                                        env, obj, mid, args);
                 break;
             case 'F':
                 result.f = (*env)->CallFloatMethodV(
                                        env, obj, mid, args);
                 break;
             case 'D':
                 result.d = (*env)->CallDoubleMethodV(
                                        env, obj, mid, args);
                 break;
             default:
                 (*env)->FatalError(env, "illegal descriptor");
             }
             va_end(args);
         }
         (*env)->DeleteLocalRef(env, clazz);
     }
     if (hasException) {
         *hasException = (*env)->ExceptionCheck(env);
     }
     return result;
}

JNU_CallMethodByName takes, among other arguments, a pointer to a jboolean. The jboolean will be set to JNI_FALSE if everything succeeds and to JNI_TRUE if an exception occurs at any point during the execution of this function. This gives the caller of JNU_CallMethodByName an obvious way to check for possible exceptions.

JNU_CallMethodByName first makes sure that it can create two local references: one for the class reference and the other for the result returned from the method call. Next, it obtains the class reference from the object and looks up the method ID. Depending on the return type, the switch statement dispatches to the corresponding JNI method call function. After the callback returns, if hasException is not NULL, we call ExceptionCheck to check for pending exceptions.

The ExceptionCheck function is new in Java 2 SDK release 1.2. It is similar to the ExceptionOccurred function. The difference is that ExceptionCheck does not return a reference to the exception object, but returns JNI_TRUE when there is a pending exception and returns JNI_FALSE when there is no pending exception. ExceptionCheck simplifies local reference management when the native code only needs to know whether an exception has occurred but needs not obtain a reference to the exception object. The previous code would have to be rewritten as follows in JDK release 1.1:

   if (hasException) {
         jthrowable exc = (*env)->ExceptionOccurred(env);
         *hasException = exc != NULL;
         (*env)->DeleteLocalRef(env, exc);
   }

The additional DeleteLocalRef call is necessary in order to delete the local reference to the exception object.

Using the JNU_CallMethodByName function we can rewrite the implementation of Instance-MethodCall.nativeMethod in Section 4.2 as follows:

JNIEXPORT void JNICALL
Java_InstanceMethodCall_nativeMethod(JNIEnv *env, jobject obj)
{
     printf("In C\n");
     JNU_CallMethodByName(env, NULL, obj, "callback", "()V");
}

We need not check for exceptions after the JNU_CallMethodByName call because the native method returns immediately afterwards.



--------------------------------------------------------------------------------

Contents | Prev | Next | Index The Java Native Interface
Programmer's Guide and Specification 


Copyright © 2002 Sun Microsystems, Inc. All rights reserved
Please send any comments or corrections to jni@java.sun.com
分享到:
评论

相关推荐

    Java.SE.7.Programming.Essentials

    分享的图书,有关Java SE 7 SDK标准的书,全英文。相应章节: Chapter 1. Introducing the Basics of Java Chapter 2. Applying Data Types in Java Programming ... Throwing and Catching Exceptions in Java

    java exception

    异常通常由异常类表示,这些类继承自Java的`java.lang.Throwable`类,最常见的是`Exception`类及其子类。 异常分为两种类型:检查性异常(Checked Exceptions)和运行时异常(Runtime Exceptions)。检查性异常是...

    Exceptions java 源码

    Exceptions java 源码

    Java_Programming_Exception_Example_code.rar_advanced java_java p

    `Exception`类是Java异常处理的核心,它位于`java.lang`包中,是所有检查型异常(checked exceptions)的基类。当我们遇到程序运行时可能出现的错误或异常情况时,会抛出一个异常对象。在本例中,"Java_Programming_...

    SCJP Sun® Certified Programmer for Java™ 6 Study Guide chapter 5

    ### SCJP Sun® Certified Programmer for Java™ 6 Study Guide Chapter 5: Flow Control, Exceptions, and Assertions #### Certification Objectives Overview In this chapter, we delve into the critical ...

    java 异常 问题收集 Exception

    Java将异常分为两种类型:检查性异常(Checked Exceptions)和运行时异常(Unchecked Exceptions)。检查性异常在编译阶段需要处理,如IOException;而运行时异常则在程序运行时抛出,如NullPointerException。 2. ...

    About Exceptions and Exception Handling

    当遇到下列情况时,程序会出现异常: 程序访问一个不可用的内存地址(例如,NULL指针); 无限递归导致的栈溢出; 向一个较小的缓冲区写入较大块的数据; 类的纯虚函数被调用;...

    java 异常框架CODE

    Java中的异常分为检查型异常(Checked Exceptions)和运行时异常(Unchecked Exceptions)。检查型异常通常是程序设计问题或外部条件导致的,如文件不存在或网络连接失败,这些异常在编译时就需要处理。运行时异常则...

    Java语言程序设计基础篇课后题答案-Chapter17ExceptionsandAssertions.pdf

    本资源对Java语言程序设计基础篇的Chapter 17 Exceptions and Assertions进行了详细的解释和知识点总结,涵盖了Java异常类的继承结构、claiming exceptions、checked exception和unchecked exception、throw语句和...

    java利用jnative调用DLL.txt

    import org.xvolks.jnative.exceptions.NativeException; import org.xvolks.jnative.pointers.Pointer; ``` 这里导入了几个关键的类: - `Level` 和 `Logger`:用于日志记录。 - `JNative`:核心类,用于封装DLL...

    Beginning Java Programming The Object-Oriented Approach

    Chapter 6 Handling Exceptions and Debugging. 171 Chapter 7 Delving Further into Object‐Oriented Concepts . 221 Chapter 8 Handling Input and Output. 261 Chapter 9 Working with Databases in Java . 307 ...

    JAVA核心技术卷一卷二(中文)之part2分卷

    Chapter 3 Fundamental Programming Structures in Java(新增批注共44条) Chapter 4 Objects and Classes(新增批注共55条) Chapter 5 Inheritance(新增批注共42条) Chapter 6 Interfaces and Inner Classes...

    Core Java 9th Edition(Vol1,Vol2)

    Chapter 6. Interfaces and Inner Classes Chapter 7. Graphics Programming Chapter 8. Event Handling Chapter 9. User Interface Components with Swing Chapter 10. Deploying Applications and Applets Chapter...

    Agile Java Crafting Code with Test-Driven Development

    Presents an expert overview of TDD and agile programming techniques from the Java developer's perspective Brings together practical best practices for Java, TDD, and OO design Walks through setting ...

    深入java虚拟机(inside the java virtual machine)

    6 The Java Class File What is a Java Class File? What's in a Class File? Special Strings Fully Qualified Names Simple Names Descriptors The Constant Pool The CONSTANT_Utf8_info Table The ...

    JAVA核心技术卷一卷二(中文)之part4分卷

    Chapter 3 Fundamental Programming Structures in Java(新增批注共44条) Chapter 4 Objects and Classes(新增批注共55条) Chapter 5 Inheritance(新增批注共42条) Chapter 6 Interfaces and Inner Classes...

    Java in Two Semesters Featuring JavaFX, 4th Edition

    Java in Two Semesters: Featuring JavaFX (Texts in Computer Science) By 作者: Quentin Charatan – Aaron Kans ISBN-10 书号: 3319994190 ISBN-13 书号: 9783319994192 Edition 版本: 4th ed. 2019 出版日期: ...

Global site tag (gtag.js) - Google Analytics