usar - metodos nativos en java
¿Qué sucede cuando lanzo una excepción de C++ desde un método Java nativo? (5)
Supongamos que estoy incorporando la JVM de Sun en una aplicación de C ++. A través de JNI llamo un método Java (el mío), que a su vez llama un método nativo que implementé en una biblioteca compartida.
¿Qué sucede si este método nativo lanza una excepción de C ++?
edit: el compilador es gcc 3.4.x, jvm es el 1.6.20 de sun.
Dentro de la literatura de JNI, la palabra excepción parece ser utilizada exclusivamente para referirse a las excepciones de Java. Los eventos inesperados en el código nativo se conocen como errores de programación . JNI explícitamente no requiere que las JVM verifiquen los errores de programación. Si se produce un error de programación, el comportamiento es indefinido. Diferentes JVM pueden comportarse de manera diferente.
Es responsabilidad del código nativo traducir todos los errores de programación en códigos de retorno o excepciones de Java. Las excepciones de Java no se lanzan inmediatamente desde el código nativo. Pueden estar pendientes , solo lanzarse una vez que el código nativo regresa al llamador de Java. El código nativo puede verificar las excepciones pendientes con ExceptionOccurred
y borrarlas con ExceptionClear
.
El compilador de Java no comprende las excepciones de C ++, por lo que tendrá que trabajar con las excepciones de Java y C ++. Por suerte, eso no es demasiado complicado. Primero tenemos una excepción de C ++ que nos dice si se ha producido una excepción de Java.
#include <stdexcept>
//This is how we represent a Java exception already in progress
struct ThrownJavaException : std::runtime_error {
ThrownJavaException() :std::runtime_error("") {}
ThrownJavaException(const std::string& msg ) :std::runtime_error(msg) {}
};
y una función para lanzar una excepción de C ++ si ya existe una excepción de Java:
inline void assert_no_exception(JNIEnv * env) {
if (env->ExceptionCheck()==JNI_TRUE)
throw ThrownJavaException("assert_no_exception");
}
También tenemos una excepción de C ++ para lanzar nuevas excepciones de Java:
//used to throw a new Java exception. use full paths like:
//"java/lang/NoSuchFieldException"
//"java/lang/NullPointerException"
//"java/security/InvalidParameterException"
struct NewJavaException : public ThrownJavaException{
NewJavaException(JNIEnv * env, const char* type="", const char* message="")
:ThrownJavaException(type+std::string(" ")+message)
{
jclass newExcCls = env->FindClass(type);
if (newExcCls != NULL)
env->ThrowNew(newExcCls, message);
//if it is null, a NoClassDefFoundError was already thrown
}
};
También necesitamos una función para tragar excepciones de C ++ y reemplazarlas con excepciones de Java
void swallow_cpp_exception_and_throw_java(JNIEnv * env) {
try {
throw;
} catch(const ThrownJavaException&) {
//already reported to Java, ignore
} catch(const std::bad_alloc& rhs) {
//translate OOM C++ exception to a Java exception
NewJavaException(env, "java/lang/OutOfMemoryError", rhs.what());
} catch(const std::ios_base::failure& rhs) { //sample translation
//translate IO C++ exception to a Java exception
NewJavaException(env, "java/io/IOException", rhs.what());
//TRANSLATE ANY OTHER C++ EXCEPTIONS TO JAVA EXCEPTIONS HERE
} catch(const std::exception& e) {
//translate unknown C++ exception to a Java exception
NewJavaException(env, "java/lang/Error", e.what());
} catch(...) {
//translate unknown C++ exception to a Java exception
NewJavaException(env, "java/lang/Error", "Unknown exception type");
}
}
Con las funciones anteriores, es fácil usar excepciones híbridas Java / C ++ en su código C ++, como se muestra a continuación.
extern "C" JNIEXPORT
void JNICALL Java_MyClass_MyFunc(JNIEnv * env, jclass jc_, jstring param)
{
try { //do not let C++ exceptions outside of this function
//YOUR CODE BELOW THIS LINE. HERES SOME RANDOM CODE
if (param == NULL) //if something is wrong, throw a java exception
throw NewJavaException(env, "java/lang/NullPointerException", "param");
do_stuff(param); //might throw java or C++ exceptions
assert_no_exception(env); //throw a C++ exception if theres a java exception
do_more_stuff(param); //might throw C++ exceptions
//prefer Java exceptions where possible:
if (condition1) throw NewJavaException(env, "java/lang/NullPointerException", "condition1");
//but C++ exceptions should be fine too
if (condition0) throw std::bad_alloc("BAD_ALLOC");
//YOUR CODE ABOVE THIS LINE. HERES SOME RANDOM CODE
} catch(...) { //do not let C++ exceptions outside of this function
swallow_cpp_exception_and_throw_java(env);
}
}
Si eres realmente ambicioso, es posible hacer un seguimiento de un StackTraceElement[]
de tus funciones más grandes y obtener un seguimiento parcial de la pila. El método básico es dar a cada función un StackTraceElement
, y como se les llama, empujar un puntero hacia ellos en una "pila de llamadas" local del subproceso y, cuando regresen, saque el puntero. Luego, modifique el constructor de NewJavaException
para hacer una copia de esa pila y setStackTrace
a setStackTrace
.
JNI usa funciones c para interactuar con el código nativo. C no puede manejar las excepciones correctamente ya que no es consciente de su existencia. Por lo tanto, debe capturar las excepciones en su código nativo y convertirlas a excepciones de Java o su jvm se bloqueará. (Esto funciona porque la excepción de Java solo se lanza una vez que el código nativo regresa a Java)
Supongo que su JVM se estrellará. Las excepciones nativas de C ++ no se propagan a Java a través de JNI. Una razón para eso es que JNI es una interfaz C, y C no sabe nada de las excepciones de C ++.
Lo que debe hacer es capturar las excepciones de C ++ antes de ingresar a la capa C de su código JNI y hacer que la función JNI C devuelva un código de error. Luego puede verificar el código de error dentro de Java y lanzar una excepción de Java si es necesario.
Yo lo etiquetaría como comportamiento indefinido. La propagación de excepciones al código C (eso es lo que está ejecutando la JVM) es un comportamiento indefinido.
En Windows, los compiladores tienen que usar el Manejo de excepciones estructurado de Microsoft para implementar excepciones, por lo que las excepciones de C ++ se llevarán a cabo de manera "segura" a través del código C. Sin embargo , ese código C no se escribe con excepciones en mente , por lo que sufrirá una caída si tiene suerte, y pérdidas de recursos y de estado inconsistentes si no lo está.
En otras plataformas, bueno, no lo sé, pero no puede ser más bonito. Cuando escribo el código JNI, envuelvo todas las funciones de C ++ en un bloque try
: incluso si no throw
, aún podría obtener algunas de las excepciones estándar ( std::bad_alloc
viene a la mente, pero otras también son posibles).