Handling Errors


Native methods are a significant security risk to programs in the Java programming language. The C runtime system has no protection against array bounds errors, indirection through bad pointers, and so on. It is particularly important that programmers of native methods handle all error conditions to preserve the integrity of the Java platform. In particular, when your native method diagnoses a problem that it cannot handle, then it should report this problem to the Java virtual machine. Then, you would naturally throw an exception in this situation. However, C has no exceptions. Instead, you must call the Throw or ThrowNew function to create a new exception object. When the native method exits, the Java virtual machine throws that exception.

To use the THRow function, call NewObject to create an object of a subtype of Throwable. For example, here we allocate an EOFException object and throw it.

 jclass class_EOFException = (*env)->FindClass(env, "java/io/EOFException"); jmethodID id_EOFException = (*env)->GetMethodID(env, class_EOFException, "<init>", "()V");    /* ID of default constructor */ jthrowable obj_exc = (*env)->NewObject(env, class_EOFException, id_EOFException); (*env)->Throw(env, obj_exc); 

It is usually more convenient to call THRowNew, which constructs an exception object, given a class and a "modified UTF-8" byte sequence.

[View full width]

(*env)->ThrowNew(env, (*env)->FindClass(env, "java/io/EOFException"), "Unexpected end of file");

Both Throw and ThrowNew merely post the exception; they do not interrupt the control flow of the native method. Only when the method returns does the Java virtual machine throw the exception. Therefore, every call to THRow and THRowNew should always immediately be followed by a return statement.

C++ NOTE

If you implement native methods in C++, you cannot throw a Java exception object in your C++ code. In a C++ binding, it would be possible to implement a translation between exceptions in the C++ and Java programming languageshowever, this is not currently implemented. Use THRow or ThrowNew to throw a Java exception in a native C++ method, and make sure that your native methods throw no C++ exceptions.


Normally, native code need not be concerned with catching Java exceptions. However, when a native method calls a Java method, that method might throw an exception. Moreover, a number of the JNI functions throw exceptions as well. For example, SetObjectArrayElement throws an ArrayIndexOutOfBoundsException if the index is out of bounds, and an ArrayStoreException if the class of the stored object is not a subclass of the element class of the array. In situations like these, a native method should call the ExceptionOccurred method to determine whether an exception has been thrown. The call

 jthrowable obj_exc = (*env)->ExceptionOccurred(env); 

returns NULL if no exception is pending, or it returns a reference to the current exception object. If you just want to check whether an exception has been thrown, without obtaining a reference to the exception object, use

 jboolean occurred = (*env)->ExceptionCheck(env); 

Normally, a native method should simply return when an exception has occurred so that the virtual machine can propagate it to the Java code. However, a native method may analyze the exception object to determine if it can handle the exception. If it can, then the function

 (*env)->ExceptionClear(env); 

must be called to turn off the exception.

In our next example, we implement the fprint native method with the paranoia that is appropriate for a native method. Here are the exceptions that we throw:

  • A NullPointerException if the format string is NULL

  • An IllegalArgumentException if the format string doesn't contain a % specifier that is appropriate for printing a double

  • An OutOfMemoryError if the call to malloc fails

Finally, to demonstrate how to check for an exception when calling a Java method from a native method, we send the string to the stream, a character at a time, and call ExceptionOccurred after each call. Example 11-17 shows the code for the native method, and Example 11-18 contains the definition of the class containing the native method. Notice that the native method does not immediately terminate when an exception occurs in the call to PrintWriter.printit first frees the cstr buffer. When the native method returns, the virtual machine again raises the exception. The test program in Example 11-19 demonstrates how the native method throws an exception when the formatting string is not valid.

Example 11-17. Printf4.c
  1. #include "Printf4.h"  2. #include <string.h>  3. #include <stdlib.h>  4. #include <float.h>  5.  6. /**  7.    @param format a string containing a printf format specifier  8.    (such as "%8.2f"). Substrings "%%" are skipped.  9.    @return a pointer to the format specifier (skipping the '%') 10.    or NULL if there wasn't a unique format specifier 11.  */ 12. char* find_format(const char format[]) 13. { 14.    char* p; 15.    char* q; 16. 17.    p = strchr(format, '%'); 18.    while (p != NULL && *(p + 1) == '%') /* skip %% */ 19.       p = strchr(p + 2, '%'); 20.    if (p == NULL) return NULL; 21.    /* now check that % is unique */ 22.    p++; 23.    q = strchr(p, '%'); 24.    while (q != NULL && *(q + 1) == '%') /* skip %% */ 25.       q = strchr(q + 2, '%'); 26.    if (q != NULL) return NULL; /* % not unique */ 27.    q = p + strspn(p, " -0+#"); /* skip past flags */ 28.    q += strspn(q, "0123456789"); /* skip past field width */ 29.    if (*q == '.') { q++; q += strspn(q, "0123456789"); } 30.       /* skip past precision */ 31.    if (strchr("eEfFgG", *q) == NULL) return NULL; 32.       /* not a floating point format */ 33.    return p; 34. } 35. 36. JNIEXPORT void JNICALL Java_Printf4_fprint(JNIEnv* env, jclass cl, 37.    jobject out, jstring format, jdouble x) 38. { 39.    const char* cformat; 40.    char* fmt; 41.    jclass class_PrintWriter; 42.    jmethodID id_print; 43.    char* cstr; 44.    int width; 45.    int i; 46. 47.    if (format == NULL) 48.    { 49.       (*env)->ThrowNew(env, 50.          (*env)->FindClass(env, 51.          "java/lang/NullPointerException"), 52.          "Printf4.fprint: format is null"); 53.       return; 54.    } 55. 56.    cformat = (*env)->GetStringUTFChars(env, format, NULL); 57.    fmt = find_format(cformat); 58. 59.    if (fmt == NULL) 60.    { 61.       (*env)->ThrowNew(env, 62.          (*env)->FindClass(env, 63.          "java/lang/IllegalArgumentException"), 64.          "Printf4.fprint: format is invalid"); 65.       return; 66.    } 67. 68.    width = atoi(fmt); 69.    if (width == 0) width = DBL_DIG + 10; 70.    cstr = (char*)malloc(strlen(cformat) + width); 71. 72.    if (cstr == NULL) 73.    { 74.       (*env)->ThrowNew(env, 75.          (*env)->FindClass(env, "java/lang/OutOfMemoryError"), 76.          "Printf4.fprint: malloc failed"); 77.       return; 78.    } 79. 80.    sprintf(cstr, cformat, x); 81. 82.    (*env)->ReleaseStringUTFChars(env, format, cformat); 83. 84.    /* now call ps.print(str) */ 85. 86.    /* get the class */ 87.    class_PrintWriter = (*env)->GetObjectClass(env, out); 88. 89.    /* get the method ID */ 90.    id_print = (*env)->GetMethodID(env, class_PrintWriter, "print", "(C)V"); 91. 92.    /* call the method */ 93.    for (i = 0; cstr[i] != 0 && !(*env)->ExceptionOccurred(env); i++) 94.       (*env)->CallVoidMethod(env, out, id_print, cstr[i]); 95. 96.    free(cstr); 97. } 

Example 11-18. Printf4.java
  1. import java.io.*;  2.  3. class Printf4  4. {  5.    public static native void fprint(PrintWriter ps, String format, double x);  6.  7.    static  8.    {  9.       System.loadLibrary("Printf4"); 10.    } 11. } 

Example 11-19. Printf4Test.java
  1. import java.io.*;  2.  3. class Printf4Test  4. {  5.    public static void main(String[] args)  6.    {  7.       double price = 44.95;  8.       double tax = 7.75;  9.       double amountDue = price * (1 + tax / 100); 10.       PrintWriter out = new PrintWriter(System.out); 11.       /* This call will throw an exception--note the %% */ 12.       Printf4.fprint(out, "Amount due = %%8.2f\n", amountDue); 13.       out.flush(); 14.    } 15. } 


 Error Handling in C Code 

  • jint Throw(JNIEnv *env, jthrowable obj)

    prepares an exception to be thrown upon exiting from the native code. Returns 0 on success, a negative value on failure.

    Parameters:

    env

    The JNI interface pointer

     

    obj

    The exception object to throw


  • jint ThrowNew(JNIEnv *env, jclass clazz, const char msg[])

    prepares an exception to be thrown upon exiting from the native code. Returns 0 on success, a negative value on failure.

    Parameters:

    env

    The JNI interface pointer

     

    cl

    The class of the exception object to throw

     

    msg

    A "modified UTF-8" byte sequence denoting the String construction argument of the exception object


  • jthrowable ExceptionOccurred(JNIEnv *env)

    returns the exception object if an exception is pending, or NULL otherwise.

    Parameters:

    env

    The JNI interface pointer


  • jboolean ExceptionCheck(JNIEnv *env)

    returns true if an exception is pending.

    Parameters:

    env

    The JNI interface pointer


  • void ExceptionClear(JNIEnv *env)

    clears any pending exceptions.

    Parameters:

    env

    The JNI interface pointer




    Core JavaT 2 Volume II - Advanced Features
    Building an On Demand Computing Environment with IBM: How to Optimize Your Current Infrastructure for Today and Tomorrow (MaxFacts Guidebook series)
    ISBN: 193164411X
    EAN: 2147483647
    Year: 2003
    Pages: 156
    Authors: Jim Hoskins

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net