|
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.
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
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:
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.c1. #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.java1. 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.java1. 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
|
|