Как поймать исключение 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.

меры

  1. генерировать example.class от example.java (с помощью javac или maven или ваше любимое IDE Eclipse/Netbeans/IntelliJ IDEA/...)

  2. Создать заголовочный файл 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;
    }
}
Другие вопросы по тегам