-
Notifications
You must be signed in to change notification settings - Fork 6
Error Handling
All errors in SimpleJNI are reported via exceptions. All exceptions thrown service from std::exception
. If an error is a result of Java exception it will be "wrapped" in java_exception
class. Note that SimpleJNI calls JNIEnv::ExceptionClear
prior to emitting java_exception
so you can freely use JNI calls while handling it.
In your implementation of Java native methods as well as in JNI_OnLoad
you must handle all C++ exceptions and never let them 'escape' into Java. Most Java implementations are not prepared to deal with C++ exceptions, even just letting them "fly through".
Assuming you cannot actually handle the exception, as in "deal with error condition and retry", how do you handle C++ exceptions on Java boundary? One, often used, option is to log the error and report failure to Java via an error code of some sort (perhaps null
return). This works but is not optimal. Error codes are easy to ignore and logs might not be seen. In addition this completely bypasses normal exception handling infrastructure Java code undoubtedly already has.
A better idea is to propagate C++ exception into Java as Java exception and let Java code deals with it as it see fit.
This is very easy if the C++ exception is java_exception
, that is it originated as Java exception already. Just do
JNIEnv * env = ...;
...
catch(java_exception & ex)
{
ex.raise(env);
}
And return default value to Java code (0 for integers, 0.0 for floats, nullptr
for objects). Remember not to issue any JNI calls after call to raise
. It signals Java exception and all subsequent JNI calls will fail.
What to do with other C++ exceptions is up to you. The simplest strategy is to report them as Throwable
with the message being the result of std::exception::what()
call. This strategy is so common that SimpleJNI includes direct support for it. Just wrap all you native methods bodies in the following
some_type SomeNativeMethod(JNIEnv * env, ...other args...)
{
try
{
... method body ...
}
catch(std::exception & ex)
{
java_exception::translate(env, ex);
}
return some_type(0);
}
The java_exception::translate
call will perform exactly the transformations described above: rethrow Java exception reported via java_exception
and throw Throwable
with message of C++ exception for anything else.
In a more complicated code base you might want to include more sophisticated exception translation machinery. You can use void java_exception::raise(JNIEnv * jenv, jthrowable ex)
to raise any kind of jthrowable
you create.
One way of writing exception translation function is as follows
void translate_exception(JNIEnv * env, const std::exception & ex);
some_type SomeNativeMethod(JNIEnv * env, ...other args...)
{
try
{
... method body ...
}
catch(std::exception & ex)
{
translate_exception(env, ex);
}
return some_type(0);
}
void translate_exception(JNIEnv * env, const std::exception & ex)
{
try
{
throw;
}
catch(java_exception & ex)
{
//Rethrow any Java exception
ex.raise(env);
}
catch(my_exception & ex)
{
//Create jMyException from my_exception data
jMyException exception = ...;
java_exception::raise(env, exception.c_ptr());
}
catch(std::exception & ex)
{
//Default case: transalte to throwable
const char * message = ex.what();
auto java_message = java_string::create(env, message);
auto exception = java_runtime::throwable().ctor(env, java_message.c_ptr());
java_exception::raise(env, exception.c_ptr());
}
}
- Building
-
User's Guide
Declaring Java Types
Accessing Methods and Fields
Representing Java Classes
Implementing Native Methods
Smart References
Error Handling
Obtaining JNIEnv
Initialization
Strings
Arrays
Direct Buffers
Booleans
Sizes -
JniGen Code Generator
Integrating JniGen
Annotations
Processor Options