## Capturar y Lanzar Excepciones

Cuando se lanza una excepción en Java, la Máquina Virtual busca automáticamente el manejador de excepción más cercano y despliega la pila si es necesario. La ventaja de este estilo de manejo de excepciones es que libera al programador de tener cuidado y del manejo inusual de los errores situados en cada operación del programa. En su lugar, la Máquina Virtual Java propaga automáticamente las condiciones del error a la posición (la clausula catch de Java) que maneja el mismo tipo de condiciones de error de una forma centralizada.

Mientras que otros lenguajes como C++ soportan una noción similar de manejo de excepciones, no existe una manera uniformada de lanzar y capturar excepciones en los lenguajes nativos. Por lo tanto, el JNI requiere que se comprueben las posibles excepciones después de llamar a sus funciones. El JNI también proporciona funciones que permiten a los métodos nativos lanzar excepciones Java. Estas excepciones pueden ser manejadas por otras partes del código nativo o por la Máquina Virtual Java. Después de que el código nativo lance o capture una excepción, puede eliminar las excepciones pendientes para que el cálculo continúe, o puede lanzar otra excepción para un manejador exterior.

Muchas funciones JNI pueden lanzar excpeciones. Por ejemplo, la función GetFieldID descrita en la página anterior lanza un NoSuchFieldError si el campo especificado no existe. Para simplificar el chequeo de errores, la mayoría de las funciones JNI utilizan una combinación de códigos de error y excepciones Java para informar de las condiciones de error. Por ejemplo, se podría chequear si el campo jfieldID devuelto por GetFieldID es cero (0) en vez de llamar a la función JNI ExceptionOccurred. Cuando el resultado de GetFieldID no es cero (0), se puede estar seguro de que no hay una excepción pendiente.

El resto de la página ilustra cómo capturar y lanzar excepciones en código nativo. El código de ejemplo está en CatchThrow.java.

El método CatchThrow.main llama al método nativo. El método nativo, definido en CatchThrow.c, primero llama al método CatchThrow.callback Java, de esta forma:

      jclass cls = (*env)->GetObjectClass(env, obj);
      jmethodID mid = (*env)->GetMethodID(env, cls, "callback", "()V");
      jthrowable exc;
      if (mid == 0)
        return;
      (*env)->CallVoidMethod(env, obj, mid);

Observa que como CallVoidMethod lanza una NullPointerException, el código nativo puede detectar esta excepción después de que CallVoidMethod retorne llamando al método ExceptionOccurred:

      exc = (*env)->ExceptionOccurred(env);
      if (exc) {

Como se puede ver es muy sencillo capturar y lanzar una excepción. En nuestro ejemplo, no hacemos mucho con la excepción en CatchThrow.c excepto utilizar el método ExceptionDescribe para sacar un mensaje de depurado. El método nativo lanza entonces una IllegalArgumentException. Es esta exepción IllegalArgumentException es la que verá el código Java que llamó al método nativo.

        (*env)->ExceptionDescribe(env);
        (*env)->ExceptionClear(env);

        newExcCls = (*env)->FindClass(env, "java/lang/IllegalArgumentException");
        if (newExcCls == 0) /* Unable to find the new exception class, give up. */
          return;
        (*env)->ThrowNew(env, newExcCls, "thrown from C code");

La función ThrowNew construye un objeto exception desde una clase exception dada y un string de mensaje y postea la excepción en el thread actual.

Observa que es extremadamente importante comprobar, manejar y limpiar las excepciones pendientes antes de llamar a las siguientes funciones JNI. Llamar arbitrariamente a funciones JNI con una excepción pendiente podría generar resultados inexperados. Solo se pueden llamar de forma segura a unas pocas funciones JNI cuando existe una excepción pendiente. Estas funciones son: ExceptionOccurred, ExceptionDescribe, y ExceptionClear.

Ozito