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 ein Object erwartet. Falls dieses Object gleich null ist, wird eine NullPointerException geworfen.
  • Native Methode checkValues: Diese Methode bekommt zwei Parameter übergeben – einen double und ein Object. Diese Methode soll überprüfen, ob diese beiden Parameter valide sind (Object ungleich null und double 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 der double ungültig, und beim dritten Mal ist das Object gleich null.
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;
}

Schreibe einen Kommentar

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.