19.05 Exceptions mit JNI
Ein weiteres, wichtiges Element in der JNI Programmierung ist die Fehlerbehandlung. Sie haben die Möglichkeit Fehlermeldungen, die der Java-Teil geworfen hat, in Ihrer JNI-Bibliothek auszulesen, aber auch direkt im nativen Teil Ihrer Anwendung Fehlermeldungen zu werfen, die dann der Java-Teil abfangen kann/muss. In diesem Kapitel werden Sie eine Java Exception in C abfragen und ausgeben sowie in C eine Exception werfen, die das Java Programm abfängt und ausgibt.
Der Java Code
Ihren Java Code können Sie ganz gewöhnlich aufbauen. Es spielt für Java keine Rolle, ob eine Exception von einer nativen oder einer Java Methode geworfen bzw. gefangen werden muss. Um die Funktionalität zu demonstrieren, wird eine neue Klasse angelegt. Sie enthält folgende Elemente:
- Java Methode
checkNull
: Als Übergabeparameter wird einObject
erwartet. Falls diesesObject
gleichnull
ist, wird eineNullPointerException
geworfen. - Native Methode
checkValues
: Diese Methode bekommt zwei Parameter übergeben – einendouble
und einObject
. Diese Methode soll überprüfen, ob diese beiden Parameter valide sind (Object
ungleichnull
unddouble
ungleich 0). Sind sie es nicht, wird eine Fehlermeldung geworfen. - Main Methode: Ruft die native Methode
checkValues
dreimal auf. Beim ersten Mal mit gültigen Parametern, das zweite Mal ist derdouble
ungültig, und beim dritten Mal ist dasObject
gleichnull
.
package de.jbb.jni; public class JNIExceptionTest { private native void checkValues(double d, Object obj); private void checkNull(Object obj) throws NullPointerException { if (obj == null) { throw new NullPointerException("obj is null!"); } } public static void main(String[] args) { JNIExceptionTest jniet = new JNIExceptionTest(); jniet.checkValues(42, "Hello!"); try { jniet.checkValues(0, "Hello!"); } catch (IllegalArgumentException iae) { System.err.println("Java: " + iae.getMessage()); } try { jniet.checkValues(42, null); } catch (IllegalArgumentException iae) { System.err.println("Java: " + iae.getMessage()); } } static { System.loadLibrary("exceptiontest"); } }
Vorbereitung des C Codes
Zuerst müssen Sie wie gewohnt die Header einbinden und die Funktion anlegen:
#include <jni.h> #include "de_jbb_jni_JNIExceptionTest.h" JNIEXPORT void JNICALL Java_de_jbb_jni_JNIExceptionTest_checkValues(JNIEnv *env, jobject jniet, jdouble d, jobject obj) {}
Im C Code soll eine IllegalArgumentException
geworfen werden, falls ein Parameter nicht valide ist. Deshalb wird zuerst eine Referenz auf diese Exception
benötigt.
jclass illegalArgumentExceptionCls = (*env)->FindClass(env, "java/lang/IllegalArgumentException");
Die Überprüfung, ob das übergebene Object
gleich null
ist, wäre zwar theoretisch auch direkt im C Code möglich, soll aber über die Methode checkNull
unserer Java Klasse realisiert werden.
jclass jnietCls = (*env)->GetObjectClass(env, jniet); jmethodID checkNull = (*env)->GetMethodID(env, jnietCls, "checkNull", "(Ljava/lang/Object;)V");
Außerdem wird noch eine Variable benötigt, in welcher eine evtl. geworfene Exception
gespeichert werden kann. Da eine Exception
immer von Throwable
erbt, wird hierzu der Typ jthrowable
verwendet.
jthrowable thr;
Eine Exception aus C werfen
Falls der übergebene jdouble
gleich 0 ist, soll eine IllegalArgumentException
geworfen werden. Um die Exception
zu werfen, wird die Funktion ThrowNew
der JNI Environment verwendet. Dieser Funktion wird die Klasse der gewünschten Exception
übergeben, welche von Throwable
oder einer entsprechenden Kindklasse erben muss. Zusätzlich kann noch ein Fehlertext übergeben werden. ThrowNew
erzeugt und wirft dann ein entsprechendes Objekt dieser Exception
.
if (d == 0) { (*env)->ThrowNew(env, illegalArgumentExceptionCls, "jdouble can't be zero!"); }
Eine Exception in C ausgeben
Um zu testen, ob eine Java Methode eine Exception geworfen hat, wird diese zuerst einmal ganz gewöhnlich aufgerufen.
(*env)->CallVoidMethod(env, jniet, checkNull, obj);
Anschließend kann über den Aufruf ExceptionOccurred
der JNI Umgebung abgefragt werden, ob eine Exception
geworfen wurde. Falls dies der Fall ist, wird die entsprechende Exception
von dieser Methode zurückgegeben. Mit einer simplen If-Abfrage kann nun überprüft werden, ob eine Exception
aufgetreten ist:
thr = (*env)->ExceptionOccurred(env); if (thr) { // Exception wurde geworfen }
Die geworfene Exception
können Sie auf der Konsole mit (*env)->ExceptionDescribe(env)
ausgeben lassen. Anschließend müssen Sie diese Exception
als abgefangen markieren, damit diese nicht zu einem späteren Zeitpunkt erneut verarbeitet wird. Dies bewerkstelligen Sie mit dem Aufruf (*env)->ExceptionClear(env)
. Wenn Sie möchten, können Sie Ihre Bibliothek nun analog zum letzten Abschnitt eine eigene Exception
werfen lassen.
Zusammenfassung C Code
#include <jni.h> #include "de_jbb_jni_JNIExceptionTest.h" JNIEXPORT void JNICALL Java_de_jbb_jni_JNIExceptionTest_checkValues(JNIEnv *env, jobject jniet, jdouble d, jobject obj) { jclass illegalArgumentExceptionCls = (*env)->FindClass(env, "java/lang/IllegalArgumentException"); jclass jnietCls = (*env)->GetObjectClass(env, jniet); jmethodID checkNull = (*env)->GetMethodID(env, jnietCls, "checkNull", "(Ljava/lang/Object;)V"); jthrowable thr; (*env)->CallVoidMethod(env, jniet, checkNull, obj); thr = (*env)->ExceptionOccurred(env); if (thr) { (*env)->ExceptionDescribe(env); (*env)->ExceptionClear(env); (*env)->ThrowNew(env, illegalArgumentExceptionCls, "checkNull throws an exception"); } if (d == 0) { (*env)->ThrowNew(env, illegalArgumentExceptionCls, "jdouble can't be zero!"); } return; }