Как поймать исключение JNI/Java
У меня есть слой JNI в моем приложении. В некоторых случаях Java выдает исключение. Как я могу получить исключение Java в слое JNI? У меня есть код что-то вроде следующего.
if((*(pConnDA->penv))->ExceptionCheck(pConnDA->penv))
{
(*(pConnDA->penv))->ExceptionDescribe(pConnDA->penv);
(*(pConnDA->penv))->ExceptionClear(pConnDA->penv);
}
Будет ли этот блок кода перехватывать только исключения JNI? Где будет регистрироваться описание исключения в консоли (stderr)? Как я могу получить это в буфер, чтобы я мог передать его в свой модуль логгера?
3 ответа
Если вы вызываете метод Java из JNI, вызывая ExceptionCheck
потом вернусь JNI_TRUE
если исключение было сгенерировано Java.
если вы просто вызываете функцию JNI (например, FindClass
), ExceptionCheck
скажет вам, если это не удалось способом, который оставляет ожидающее исключение (как FindClass
будем делать по ошибке).
ExceptionDescribe
выводит на stderr. нет никакого удобного способа заставить это пойти куда-нибудь еще, но ExceptionOccurred
дает вам jthrowable
если вы хотите поиграть с ним, или вы можете просто позволить ему перейти на Java и обработать его там. это обычный стиль:
jclass c = env->FindClass("class/does/not/Exist");
if (env->ExceptionCheck()) {
return;
}
// otherwise do something with 'c'...
обратите внимание, что не имеет значения, какое значение вы возвращаете; вызывающий Java-код никогда его не увидит - вместо этого он увидит ожидающее исключение.
Это дополнение к ответу Эллиота Хьюза. Мой ответ содержит пошаговый пример, объясняющий, как перехватывать исключения и как их переводить между словами C++ и Java с использованием уровня JNI.
Короткий ответ
Смотрите правильный ответ Эллиотта Хьюза.
Многоразовый пример
Этот ответ и фрагменты находятся в открытом доступе или в CC0 для упрощения повторного использования. Весь исходный код здесь обратно совместим с C++03.
Чтобы использовать приведенный выше фрагмент, выполните следующие действия.
- замещать
mypackage::Exception
вашим собственным исключением C++. - Если соответствующее исключение Java
my.group.mypackage.Exception
не определено, тогда замени"my/group/mypackage/Exception"
от"java/lang/RuntimeException"
,
Поймать исключения из Java
Смотрите также фрагмент на колиру.
void rethrow_cpp_exception_as_java_exception()
{
try
{
throw; // This allows to determine the type of the exception
}
catch (const mypackage::Exception& e) {
jclass jc = env->FindClass("my/group/mypackage/Exception");
if(jc) env->ThrowNew (jc, e.what());
/* if null => NoClassDefFoundError already thrown */
}
catch (const std::bad_alloc& e) {
jclass jc = env->FindClass("java/lang/OutOfMemoryError");
if(jc) env->ThrowNew (jc, e.what());
}
catch (const std::ios_base::failure& e) {
jclass jc = env->FindClass("java/io/IOException");
if(jc) env->ThrowNew (jc, e.what());
}
catch (const std::exception& e) {
/* unknown exception (may derive from std::exception) */
jclass jc = env->FindClass("java/lang/Error");
if(jc) env->ThrowNew (jc, e.what());
}
catch (...) {
/* Oops I missed identifying this exception! */
jclass jc = env->FindClass("java/lang/Error");
if(jc) env->ThrowNew (jc, "Unidentified exception => "
"Improve rethrow_cpp_exception_as_java_exception()" );
}
}
Я благодарю Mooing Duck за вклад в приведенный выше код C++.
Адаптируйте сгенерированный исходный код JNI
Следующий файл Java_my_group_mypackage_example.cpp
используйте выше rethrow_cpp_exception_as_java_exception()
функция:
JNIEXPORT jlong JNICALL Java_my_group_mypackage_example_function1
(JNIEnv *env, jobject object, jlong value)
{
try {
/* ... my processing ... */
return jlong(result);
} catch(...) {
rethrow_cpp_exception_as_java_exception();
return 0;
}
}
JNIEXPORT jstring JNICALL Java_my_group_mypackage_example_function2
(JNIEnv *env, jobject object, jlong value)
{
jstring jstr = 0
try {
/* ... my processing ... */
jstr = env->NewStringUTF("my result");
} catch(...) {
rethrow_cpp_exception_as_java_exception();
}
return jstr;
}
JNIEXPORT void JNICALL Java_my_group_mypackage_example_function3
(JNIEnv *env, jobject object, jlong value)
{
try {
/* ... my processing ... */
} catch(...) {
rethrow_cpp_exception_as_java_exception();
}
}
Соответствующий код Java
файл example.java
package my.group.mypackage;
public class Example {
static {
System.loadLibrary("my-DLL-name");
}
public Example() {
/* ... */
}
private native int function1(int); //declare DLL functions
private native String function2(int); //using the keyword
private native void function3(int); //'native'
public void dosomething(int value) {
int result = function1(value);
String str = function2(value); //call your DLL functions
function3(value); //as any other java function
}
}
Заметка: "my-DLL-name
"относится к динамической библиотеке, созданной из вышеупомянутого скомпилированного кода C/C++. Это может быть my-DLL-name.dll
в Windows или my-DLL-name.so
в GNU/Linux/Unix.
меры
генерировать
example.class
отexample.java
(с помощьюjavac
или maven или ваше любимое IDE Eclipse/Netbeans/IntelliJ IDEA/...)Создать заголовочный файл C/C++
Java_my_group_mypackage_example.h
отexample.class
с помощьюjavah
В случае, когда исключение Java возникает во вложенном вызове C++, это не делается простым возвратом, а вы предпочитаете удалять промежуточные уровни стека по механизму throw.
Я решил это с помощью
class JavaException : public std::exception
{
};
Затем во вложенном вызове, где возникает исключение
void nestedMethod()
{
env->CallVoidMethod(...); // Java call creating Exception
if (env->ExceptionCheck())
{
throw JavaException();
}
}
Наконец-то возвращаемся на Java
void JNI_method(JNIenv *env)
{
try
{
doComplexCall(); // --> nestedMethod()
}
catch(const JavaException&)
{
//coming from positive ExceptionCheck()
return;
}
}