I have a JNI layer in my application. In some cases Java throws an exception. How can I get the Java exception in the JNI layer? I have the code something like as follows.
if((*(pConnDA->penv))->ExceptionCheck(pConnDA->penv))
{
(*(pConnDA->penv))->ExceptionDescribe(pConnDA->penv);
(*(pConnDA->penv))->ExceptionClear(pConnDA->penv);
}
Will this block of code catch only JNI exceptions? Where will the exception description be logged in console(stderr)? How do I get this into the buffer, so that I can pass it to my logger module?
if you invoke a Java method from JNI, calling ExceptionCheck afterwards will return JNI_TRUE if an exception was thrown by the Java.
if you're just invoking a JNI function (such as FindClass), ExceptionCheck will tell you if that failed in a way that leaves a pending exception (as FindClass will do on error).
ExceptionDescribe outputs to stderr. there's no convenient way to make it go anywhere else, but ExceptionOccurred gives you a jthrowable if you want to play about with it, or you could just let it go up to Java and handle it there. that's the usual style:
jclass c = env->FindClass("class/does/not/Exist");
if (env->ExceptionCheck()) {
return;
}
// otherwise do something with 'c'...
note that it doesn't matter what value you return; the calling Java code will never see it --- it'll see the pending exception instead.
This is a complement of the Elliott Hughes' answer. My answer provides a step-by-step example explaining how to catch exceptions and how to translate them between C++ and Java words using the JNI layer.
Short answer
See the correct Elliott Hughes' answer.
Reusable example
This answer and snippets are in public domain or in CC0 in order to ease reuse. All the source code here is C++03 backward compatible.
To reuse the above snippet do the following:
Replace mypackage::Exception by your own C++ Exception.
If the corresponding Java exception my.group.mypackage.Exception is not defined, then replace "my/group/mypackage/Exception" by "java/lang/RuntimeException".
Catch exceptions from Java
See also the snippet on coliru.
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()" );
}
}
I thanks Mooing Duck for contribution on the above C++ code.
Adapt the JNI generated source code
The following file Java_my_group_mypackage_example.cpp use the above rethrow_cpp_exception_as_java_exception() function:
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();
}
}
Corresponding Java code
File 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
}
}
Note: "my-DLL-name" refers to the dynamic library produced from the above C/C++ code compiled. It can be my-DLL-name.dll on Windows or my-DLL-name.so on GNU/Linux/Unix.
Steps
Generate example.class from example.java (using javac or maven or your favorite IDE Eclipse/Netbeans/IntelliJ IDEA/...)
Generate C/C++ header file Java_my_group_mypackage_example.h from example.class using javah
In the case, when the Java exception occurs in a nested C++ call, it's not done with a simple return, but you prefer to remove the intermediate stack levels according to the throw mechanism.
I solved this with a
class JavaException : public std::exception
{
};
Then in a nested call, where the exception is raised
void nestedMethod()
{
env->CallVoidMethod(...); // Java call creating Exception
if (env->ExceptionCheck())
{
throw JavaException();
}
}
Finally returning to Java
void JNI_method(JNIenv *env)
{
try
{
doComplexCall(); // --> nestedMethod()
}
catch(const JavaException&)
{
//coming from positive ExceptionCheck()
return;
}
}
Related
i want to call method inside java and retrun a string to ndk but my app will crash when i calling java method .
i checked more stackoverflow site but when im using other codes,it dont work.
help me thanks
inside ndk code :
extern "C"
JNIEXPORT jstring JNICALL
Java_com_hppni_battleword_view_SplashScreen_tkk(JNIEnv *env, jclass type) {
jclass jClass = env->FindClass("com/hppni/battleword/view/SplashScreen");
if (jClass != nullptr) {
jmethodID mid2 = env->GetStaticMethodID(jClass, "encryptThisString",
"(Ljava/lang/String;)Ljava/lang/String;"); // app will crash here
if (mid2 != nullptr) {
env->CallStaticVoidMethod(jClass, mid2, (jstring) "ali"); // app will crash here
}
}
return env->NewStringUTF(getSignature(env));
}
inside java class/Activity :
public static String encryptThisString(String input) {
Log.d("NDK", input);
return input;
}
You can't just cast char * string to jstring. You need to create jstring object using JNI functions like NewStringUTF, for example.
I am trying to pass back a string from a Java method called from C++. I am not able to find out what JNI function should I call to access the method and be returned a jstring value.
My code follows:
C++ part
main() {
jclass cls;
jmethodID mid;
jstring rv;
/** ... omitted code ... */
cls = env->FindClass("ClassifierWrapper");
mid = env->GetMethodID(cls, "getString","()Ljava/lang/String");
rv = env->CallStatic<TYPE>Method(cls, mid, 0);
const char *strReturn = env->GetStringUTFChars(env, rv, 0);
env->ReleaseStringUTFChars(rv, strReturn);
}
Java Code
public class ClassifierWrapper {
public String getString() { return "TEST";}
}
The Method Signature (from "javap -s Class")
public java.lang.String getString();
Signature: ()Ljava/lang/String;
You should have
cls = env->FindClass("ClassifierWrapper");
Then you need to invoke the constructor to get a new object:
jmethodID classifierConstructor = env->GetMethodID(cls,"<init>", "()V");
if (classifierConstructor == NULL) {
return NULL; /* exception thrown */
}
jobject classifierObj = env->NewObject( cls, classifierConstructor);
You are getting static method (even though the method name is wrong). But you need to get the instance method since getString() is not static.
jmethodID getStringMethod = env->GetMethodID(cls, "getString", "()Ljava/lang/String;");
Now invoke the method:
rv = env->CallObjectMethod(classifierObj, getStringMethod, 0);
const char *strReturn = env->GetStringUTFChars(env, rv, 0);
The complete working solution is as below:
Java Side
public class ClassifierWrapper {
public ClassifierWrapper(){}
public String getString() { return "TEST";}
}
Native Side
jclass cls;
jmethodID mid;
jstring rv;
cls = jniEnv->FindClass("ClassifierWrapper"); //plase also consider your package name as package\name\classname
jmethodID classifierConstructor = jniEnv->GetMethodID(cls,"<init>", "()V");
if (classifierConstructor == NULL) {
return NULL; /* exception thrown */
}
jobject classifierObj = jniEnv->NewObject( cls, classifierConstructor);
jmethodID getStringMethod = jniEnv->GetMethodID(cls, "getString", "()Ljava/lang/String;");
rv = (jstring)(jniEnv->CallObjectMethod(classifierObj, getStringMethod));
const char *strReturn = jniEnv->GetStringUTFChars( rv, 0);
jniEnv->ReleaseStringUTFChars(rv, strReturn);
The signature ()Ljava/lang/String is wrong, due that a class name into JVM must terminate with ;, then in this case signature must be ()Ljava/lang/String;
The first problem is that ClassifierWrapper.getString() is not static. You will need to make it static or instantiate ClassifierWrapper.
The second problem is that you are using GetMethodId instead of GetStaticMethodId.
To invoke a method that returns an Object (such as a String) you would call CallStaticObjectMethod(). That will return a jobject local reference to the String that the method returned. You can safely cast the jobject to a jstring (see http://java.sun.com/docs/books/jni/html/types.html) and use GetStringUTFChars to retrieve the characters and GetStringUTFLength to get the number of characters.
JNI is very tricky. You need to check the error code for everything (use ExceptionCheck() when there is no error code). If you don't check for errors it will fail silently in most cases and usually not at the point where the actual bug is.
You also need to understand the difference between local and global references (and what methods generate new references) in order to not leak memory and run into the reference limit. For instance, FindClass returns a local reference to a class object, but GetMethodId returns a MethodID.
Good luck
How can I register call back on JNI from android app? My requirement is, I want to make a JNI call from Android application and want to register a call back, so that I can get call back on java application from JNI.
Thanks
First, check out Swig. It wraps C++ in Java and also has a "director" fcility that makes it easier to call C++ methods from Java.
With raw JINI, you cannot do this directly. Although the RegisterNatives() call can be made to bind a native method, it cannot be changed. If you want to call a C function by pointer, you will need to do this in two steps. I am glossing over a lot, because JNI is incredibly verbose and tedious. The basic trick is, wrap the C function pointer in a java long.
First declare a java class:
public class Callback {
public Callback(long cMethodPointer) { this.cMethod = cMethod; }
public void doCallback() { callCMethod(cMethod); }
public static native void callCMethod(long cMethod);
public long cMethod;
}
Run javah on that class file, and you'll get a stub header generated that looks like:
JNIEXPORT void JNICALL Java_Callback_callCMethod
(JNIEnv *, jclass, jlong);
Implement that method in a DLL/so:
typedef void (*FP)();
JNIEXPORT void JNICALL Java_Callback_callCMethod
(JNIEnv *, jclass, jlong pointer) {
((FP)(void*)pointer)();
}
and call System.loadLibrary() on that DLL/so in your Java main() method.
Finally, from your C/C++ code, you will need to create an instance of the Callback object via JNI, and pass the pointer to an actual method to it:
void MyFunction() { ... }
void Register() {
jclass cls = env->FindClass("Callback");
jmethodID mid = env->GetMethodID(cls, "<init>", "(J)V");
jvalue arg;
arg.j = (jlong)(void*)MyFunction;
jobject callback = env->NewObjectA(confCls, mid, &arg);
}
So this gives you a brand new Callback object that points to your C function! But then, how do you do anything with that? Well, you have to pass the Callback object to Java via JNI (step omitted), so that your Java code has the Callback object from which to call your C method:
public class Foo {
public Callback callback;
public void doSomeStuff() {
...
callback.doCallback();
}
}
I am developing a server-client application in which the client calls a server's API which gives a Python interface for user input. It means the client interface and server interface is written in Python whereas the socket code is in C++.
On the server side:-
I have a class, Test, in C++ and this class is inherited in Python named TestPython using director feature of SWIG.
Also I have an exception class MyException in C++.
Now a function of TestPython class throws MyException() from Python code.
I want to handle exception thrown from Python in C++ code using SWIG.
Below is code snippet:
C++ Code-
class MyException
{
public:
string errMsg;
MyException();
MyException(string);
~MyException();
};
class Test
{
int value;
public:
void TestException(int val);
Test(int);
};
Python Code -
class TestPython(Test):
def __init__(self):
Test.__init__(self)
def TestException(self,val):
if val > 20:
throw MyException("MyException : Value Exceeded !!!")
else:
print "Value passed = ",val
Now, if the TestException() function is called, it should throw MyException. I want to handle this MyException() exception in my C++ code.
So can anyone suggest my how to do that, I mean what should I write in my *.i(interface) file to handle this.
The above TestException() written in Python is called by the client, so I have to notify the client if any exception is thrown by the server.
To do this you basically need to write a %feature("director:except") that can handle a Python exception and re-throw it as a C++ one. Here's a small but complete example:
Suppose we have the following header file we wish to wrap:
#include <iostream>
#include <exception>
class MyException : public std::exception {
};
class AnotherException : public std::exception {
};
class Callback {
public:
virtual ~Callback() { std::cout << "~Callback()" << std:: endl; }
virtual void run() { std::cout << "Callback::run()" << std::endl; }
};
inline void call(Callback *callback) { if (callback) callback->run(); }
And this Python code that uses it:
import example
class PyCallback(example.Callback):
def __init__(self):
example.Callback.__init__(self)
def run(self):
print("PyCallback.run()")
raise example.MyException()
callback = PyCallback()
example.call(callback)
We can define the following SWIG interface file:
%module(directors="1") example
%{
#include "example.h"
%}
%include "std_string.i"
%include "std_except.i"
%include "pyabc.i"
// Python requires that anything we raise inherits from this
%pythonabc(MyException, Exception);
%feature("director:except") {
PyObject *etype = $error;
if (etype != NULL) {
PyObject *obj, *trace;
PyErr_Fetch(&etype, &obj, &trace);
Py_DecRef(etype);
Py_DecRef(trace);
// Not too sure if I need to call Py_DecRef for obj
void *ptr;
int res = SWIG_ConvertPtr(obj, &ptr, SWIGTYPE_p_MyException, 0);
if (SWIG_IsOK(res) && ptr) {
MyException *e = reinterpret_cast< MyException * >(ptr);
// Throw by pointer (Yucky!)
throw e;
}
res = SWIG_ConvertPtr(obj, &ptr, SWIGTYPE_p_AnotherException, 0);
if (SWIG_IsOK(res) && ptr) {
AnotherException *e = reinterpret_cast< AnotherException * >(ptr);
throw e;
}
throw Swig::DirectorMethodException();
}
}
%feature("director") Callback;
%include "example.h"
Which handles an error from a director call, looks to see if it was one of our MyException instances and then re-throws the pointer if it was. If you have multiple types of exception being thrown then you will probably need to use PyErr_ExceptionMatches to work out what type it is first.
We could throw also by value or reference using:
// Throw by value (after a copy!)
MyException temp = *e;
if (SWIG_IsNewObj(res))
delete e;
throw temp;
instead, but note that if you threw a subclass of MyException in Python this would fall foul of the object slicing problem.
I'm not quite sure if the code is 100% correct - in particular I think the reference counting is correct, but I could be wrong.
Note: In order to make this example work (%pythonabc wouldn't work otherwise) I had to call SWIG with -py3. This in turn meant I had to upgrade to SWIG 2.0, because my installed copy of Python 3.2 had removed some deprecated functions from the C-API that SWIG 1.3.40 called.
I have this layers in my application:
Entities
Database (with Entities reference)
Business (with database and Entities references)
User Interface (with Business and Entities references)
Here is an example of my codes:
UserDAL class in database layer:
public class UsersDal
{
databaseDataContext db;
public UsersDal()
{
try
{
db = new databaseDataContext(ConnectToDatabase.GetConnectionString());
}
catch (Exception ex)
{
throw ex;
}
}
public List<User> GetAllUsers()
{
try
{
return (from u in db.Users select u).ToList();
}
catch (Exception ex)
{
throw ex;
}
}
And so on...
In the UserBLL class in Business layer i write like this:
public class UsersBll
{
UsersDal user;
public UsersBll()
{
try
{
user = new UsersDal();
}
catch(Exception ex)
{
throw new ProjectException(Errors.CannotCreateObject, ex);
}
}
public List<User> GetAllUsers()
{
try
{
return user.GetAllUsers();
}
catch(Exception ex)
{
throw new ProjectException(Errors.CannotReadData, ex);
}
}
And in UI i write:
private void GetUsers()
{
try
{
UsersBll u = new UsersBll();
datagrid.DataSource = u.GetAllUsers();
}
catch(ProjectException ex)
{
MessageBox(ex.UserMessage);// and also can show ex.InnerException.Message for more info
}
}
Well, I use a ProjectException named class to produce an error contain a BLL created message by me and an Exception message that the OS automatically manipulate.
Also i create an enum of possible errors and a dictionary
here is some details about it:
namespace Entities
{
public enum Errors
{
CannotCreateObject,
CannotReadData,
CannotAdd,
CannotEdit,
CannotDelete,...
}
[global::System.Serializable]
public class ProjectException : Exception
{
public ProjectException(Errors er, Exception ex)
: base(errors[er], ex)
{
currentEx = er;//er is Errors enum
}
static ProjectException()
{
errors = new Dictionary<Errors, string>();
errors.Add(Errors.CannotCreateObject, "the application cannot connect to database!");
errors.Add(Errors.CannotReadData, "the application cannot read data from database"); //...
}
public string UserMessage
{
get
{
try
{
return errors[currentEx];
}
catch
{
return "Unknown error!";
}
}
}
Is this good?
it work for me fine.
what's your idea?
It is almost always wrong within a catch (ex) to do a throw ex;. Either just do throw; or else throw new whateverException("someMessage", ex);. The decision of whether to use the former form or the latter generally depends upon whether you are crossing an application layer. If an AcmeServerDatabaseWrapper, which derives from DatabaseWrapper type, is performing a query when an AcmeDatabaseTableNotFoundException is thrown, it should catch that and rethrow it as a DatabaseWrapperTableNotFoundException (if such a type exists) or as a DatabaseWrapperOperationFailedException. Client code which has an object derived from DatabaseWrapper should be able to know what types of exceptions that object will throw, without having to know what type of object it is. Any exception which escapes from the database layer without being wrapped is an exception the client code is unlikely to be able to handle sensibly, but might handle erroneously (thinking it occurred in a context other than where it actually happened).