I have a class in Java which contains certain native method declarations. It contains a call to detroy() within finalize method which is now deprecated. As an alternative to finalize, I arrived at AutoCloseable with try-with-resources. However the issue is that, the close() method provided by AutoCloseable which must be overridden in my JSQ class is conflicting with the existing native close method already defined in my code. If I can find an alternate place from which destroy can be called, that should suffice as a solution. Therefore, I'm attempting to call destroy() from the native close() which would be the last point where shdl would be used. Is this possible / allowed / recommended ? I don't have the option of removing or altering native close() which is why I'm attempting to make the call to destroy from native close.
JSQL.java
class JSQ implements AutoCloseable{
protected long shdl;
protected JSQ() { log("JSQ constructor"); }
protected JSQ(String stmt, boolean isd)
throws DhSQLException
{
// calls a set of native methods
set_shdl();
setstmt(stmt, shdl);
setd(isd, shdl);
prep(shdl);
}
// to be removed
public void finalize()
{
destroy(shdl);
}
// alternative to finalize
#Override
public void close()
{
destroy();
}
protected void open()
{
parOpen (shdl);
}
private native void set_shdl() throws DhSQLException;
private native void setstmt(String s, long shdl) throws DhSQLException;
private native void setd(boolean b, long shdl);
private native void prep(long shdl) throws DhSQLException;
private native void destroy(long shdl);
protected native void parOpen(long shdl);
// following method is called from sub-classes of JSQ
// super.close(shdl);
protected native void close(long shdl);
protected void execute() throws DhSQLException
{
parExec(shdl);
}
protected native void parExec(long shdl);
}
JSQ.cxx
#define SQ ((sq_stsm_t *)(shdl))
JNIEXPORT void JNICALL Java_com_project_package_JSQ_set_shdl
(JNIEnv *env, jobject obj_This)
{
jclass cls;
jmethodID mid;
cls = (env)->GetObjectClass (obj_This);
mid = (env)->GetMethodID (hThis,"set_JSQ_shdl","(J)V");
status_t status;
// memory allocation
sq_stsm_t * S = new sq_stsm_t(status);
if(status)
{
if (S) { delete S; }
return;
}
// I understand that we're attempting to call a Java method from a native method.
// But which method is it calling?
// Also, are we converting S from sq_stms_t type to jlong?
(env)->CallVoidMethod (obj_This,mid,(jlong) S);
return;
}
JNIEXPORT void JNICALL Java_com_project_package_JSQ_setstmt
(JNIEnv *env, jobject, jstring jstmt, jlong shdl)
{
status_t status;
// cstmt is obtained using jstmt
status = SQ->SetStmt(cstmt);
return;
}
JNIEXPORT void JNICALL Java_com_project_package_JSQ_destroy
(JNIEnv *, jobject, jlong shdl)
{
delete SQ;
}
JNIEXPORT void JNICALL Java_com_project_package_JSQ_close
(JNIEnv *env, jobject, jstring jstmt, jlong shdl)
{
status_t status;
status = SQ->CloseSQ();
// Java_com_project_package_JSQ_destroy(shdl); ?
//destroy(shdl); ?
return;
}
Java_com_project_package_JSQ_destroy(shdl); ?
destroy(shdl); ?
or any other alternative that can serve the purpose of removing finalize() and finding a suitable place for destroy?
Calling one native method from another is in fact permitted. At least, I did not receive any errors or warnings when I did this. But the call must include the full function name as expected of any native method - i.e. Java_com_project_package_JSQ_destroy and the parameters should include:
the JNI environment
jobject Object
parameters expected by the method. In this case, shdl
Therefore, the call must be like so:
Java_com_project_package_JSQ_destroy(env, <jobject_object_name>, shdl);
It should place the call to destroy method. However, it doesn't really adhere to the purpose served by the Java Native Interface which is a layer used to allow Java code to make calls to and be called by native applications and libraries written in other languages (here, C++).
Such chaining is legitimate. In your specific use case, it should be straightforward.
But generally, there are some concerns that must not be neglected:
The called method expects correct JNI env pointer. So if you call it from a different thread, it's your responsibility to attach it to JVM. You cannot pass the JNIEnv * across threads.
The called method expects that all local references will be released by JVM immediately after it returns. Luckily, the caller can use PushLocalFrame() before the chaining call (and not forget PopLocalFrame() on return.
The called method expects that no JNI exception is pending. The caller should check it by calling ExceptionCheck() or ExceptionOccurred(), and if it must proceed to calling the other method, also use ExceptionClear(). It would be prudent for the caller to check exceptions after the callee returns.
Related
I have the Java code below:
public class JavaToC {
protected void hereIsYourCallback(long l, double d, boolean b, Object obj) {
// this should be implemented by subclasses
}
public void start() {
try {
while(true) {
Thread.sleep(5000);
hereIsYourCallback(3L, Math.PI, true, "Hello from Java!");
}
} catch(InterruptedException e) {
// NOOP
} catch(Exception e) {
e.printStackTrace();
}
}
}
Is it possible to write a C++ code that would somehow trap every JVM call to hereIsYourCallback? Note that this callback would have to come from an embedded JVM instantiated through JNI_CreateJavaVM.
if you're still looking for a solution: you need to use the RegisterNatives() function in jni.h. With that, you can register functions present on the host (c/c++) environment to native methods on the embedded jvm.
see this question for more informations, but the gist is, supposing a class with a native void printingFromC() method:
create your function like this:
JNIEXPORT void JNICALL printingFromC (JNIEnv* env, jobject thisObject) {
printf("Hello there!");
}
describe your native methods like this (the first string is the method name in java, doesn't need to match the C/C++ function name:
static JNINativeMethod methods[] = {
{"printingFromC", "()V", (void*) &printingFromC },
};
Then after loading the class, before calling any native methods, register them:
myCls = (jclass) (*env)->FindClass(env, "my/path/MyClass");
(*env)->RegisterNatives(env, myCls, methods, sizeof(methods)/sizeof(methods[0]));
I hope this helps.
I'm new in JNI and C++. I have to call lib function with shared pointer. My code:
JNIEXPORT jint JNICALL Java_com_test_NativeClient_subscribe(JNIEnv* env, jobject thisObj, jobject handler) {
jclass handlerClass = env->GetObjectClass(handler);
jmethodID starts = env->GetMethodID(handlerClass, "starts", "(I)V");
jmethodID joins = env->GetMethodID(handlerClass, "joins", "(Ljava/lang/String;Ljava/lang/String;)V");
// int subscribe(std::shared_ptr< SomeHandler > handler) // I need implement this
std::shared_ptr<?> sharedPointer = new std::shared_ptr<?>;
return some::lib::subscribe(sharedPointer);
}
SomeHandler it is an interface from lib - some::lib::SomeHamdler, but also I pass java implementation in the method (jobject handler). How I can properly define sharedPointer to call java implementation after subscribe method performed? Thanks in advance.
UPD: Java code:
public native int subscribe(SomeHandler handler); // native method in NativeClient
SomeHandler interface:
public interface SomeHandler {
void starts(int uptime);
void joins(String mac, String name);
SomeHandlerImpl class:
public class SomeHandlerImpl implements SomeHandler {
#Override
public void starts(int uptime) {
System.out.println("uptime is " + uptime);
}
#Override
public void joins(String mac, String name) {
System.out.println("mac: " + mac + ", nName: " + name);
}
All you need to do is store a global reference to the jobject and write some wrapper code:
class JavaWrapperHandler : public some::lib::callback {
jobject java_handler;
public:
JavaWrapperHandler(jobject handler) {
JNIEnv *env = nullptr;
vm->GetEnv(&env, JNI_VERSION_1_6);
java_handler = env->NewGlobalRef(handler);
}
~JavaWrapperHandler() {
JNIEnv *env = nullptr;
vm->GetEnv(&env, JNI_VERSION_1_6);
env->DeleteGlobalRef(java_handler);
}
virtual joins(std::string mac, std::string name) {
JNIEnv *env = nullptr;
vm->GetEnv(&env, JNI_VERSION_1_6);
jclass handlerClass = env->GetObjectClass(java_handler);
jmethodID joins = env->GetMethodID(handlerClass, "joins", "(Ljava/lang/String;Ljava/lang/String;)V");
env->CallVoidMethod(java_handler, joins, ...);
};
};
And you can instantiate this as follows in your JNI method:
std::make_shared<JavaWrapperHandler>(handler);
Note that you still need to store the shared_ptr again somewhere, otherwise it will immediately be freed. You could for example store it in a std::map<long, shared_ptr<JavaWrapperHandler>> and return the long as a jlong.
Points of note:
This code keeps a global reference to prevent the Java handler object from being garbage collected.
The global reference is freed when the handler is destroyed. Make sure to unregister the callback at some point if you want to free the Java object.
We use the GetEnv method from the JNI Invocation API. It will only produce a useful value if the current (C++) thread has already been attached to the JVM. If it fails, you need to call vm->AttachCurrentThread or vm->AttachCurrentThreadAsDaemon.
I have a compiled library that I need to use in a project. To keep it short, it's a library for interacting with a specific piece of hardware. What I have is .a and .dll library files, for linux and windows respectively, and a bunch of C++ .h headers with all the public functions and classes described there.
The problem is that the project needs to be in Java, so I need to write a JNI wrapper for this library, and honestly, I've never done that. But that's ok, I'm down to learn the thing.
I've read up a bunch of documentation online, and I figured out passing variables, creating java objects from native code, etc.
What I can't figure out, is how to work with native constructors using JNI? I have no idea what the source code of these constructors are, I only have the headers like this:
namespace RFDevice {
class RFDEVICE_API RFEthernetDetector
{
public:
//-----------------------------------------------------------------------------
// FUNCTION RFEthernetDetector::RFEthernetDetector
/// \brief Default constructor of RFEthernetDetector object.
///
/// \return void : N/A
//-----------------------------------------------------------------------------
RFEthernetDetector();
RFEthernetDetector(const WORD wCustomPortNumber);
So basically if I was to write my program in C++ (which I can't), I would do something like
RFEthernetDetector ethernetDetector = new RFEthernerDetector(somePort);
and then work with that object. But... How do I do this in Java using JNI?
I don't understand how am I supposed to create a native method for constructor, that would call the constructor from my .a library, and then have some way of working with that specific object? I know how to create java objects from native code - but the thing is I don't have any information about internal structure of the RFEthernetDetector class - only some of it's public fields and public methods.
And I can't seem to find the right articles on the net to help me out. How do I do that?
Update: A bit further clarification.
I create a .java wrapper class like this:
public class RFEthernetDetector
{
public RFEthernetDetector(int portNumber)
{
Init(portNumber);
}
public native void Init(int portNumber); // Void? Or what?
}
then I compile it with -h parameter to generate JNI .h file:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class RFEthernetDetector */
#ifndef _Included_RFEthernetDetector
#define _Included_RFEthernetDetector
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: RFEthernetDetector
* Method: Init
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_RFEthernetDetector_Init
(JNIEnv *, jobject, jint);
#ifdef __cplusplus
}
#endif
#endif
I then create an implementation that will call the functions from my .a library:
#include "RFEthernetDetector.h" // auto-generated JNI header
#include "RFEthernetDetector_native.h" // h file that comes with the library,
//contains definition of RFEthernetDetector class
/*
* Class: RFEthernetDetector
* Method: Init
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_RFEthernetDetector_Init(JNIEnv *env, jobject thisObj, jint value)
{
RFEthernetDetector *rfeDetector = new RFEthernetDetector(value); // constructor from the library
// now how do I access this new object from Java?
// if I need to later call rfDetector->doSomething() on that exact class instance?
}
You would need to build a RFEthernetDetector Java class that, through a pointer, owns a RFEthernetDetector on the C++ side. This is no fun, but inter-language glue never is.
// In this design, the C++ object needs to be explicitly destroyed by calling
// close() on the Java side.
// I think that Eclipse, at least, is configured by default to complain
// if an AutoCloseable is never close()d.
public class RFEthernetDetector implements AutoCloseable {
private final long cxxThis; // using the "store pointers as longs" convention
private boolean closed = false;
public RFEthernetDetector(int port) {
cxxThis = cxxConstruct(port);
};
#Override
public void close() {
if(!closed) {
cxxDestroy(cxxThis);
closed = true;
}
}
private static native long cxxConstruct(int port);
private static native void cxxDestroy(long cxxThis);
// Works fine as a safety net, I suppose...
#Override
#Deprecated
protected void finalize() {
close();
}
}
And on the C++ side:
#include "RFEthernetDetector.h"
JNIEXPORT jlong JNICALL Java_RFEthernetDetector_cxxConstruct(JNIEnv *, jclass, jint port) {
return reinterpret_cast<jlong>(new RFEthernetDetector(port));
}
JNIEXPORT void JNICALL Java_RFEthernetDetector_cxxDestroy(JNIEnv *, jclass, jlong thiz) {
delete reinterpret_cast<RFEthernetDetector*>(thiz);
// calling other methods is similar:
// pass the cxxThis to C++, cast it, and do something through it
}
If all that reinterpret_casting makes you feel uncomfortable, you could choose to instead keep a map around:
#include <map>
std::map<jlong, RFEthernetDetector> references;
JNIEXPORT jlong JNICALL Java_RFEthernetDetector_cxxConstruct(JNIEnv *, jclass, jint port) {
jlong next = 0;
auto it = references.begin();
for(; it != references.end() && it->first == next; it++) next++;
references.emplace_hint(it, next, port);
return next;
}
JNIEXPORT void JNICALL Java_RFEthernetDetector_cxxDestroy(JNIEnv *, jclass, jlong thiz) {
references.erase(thiz);
}
You'll need to build the native class in Java, then run the javah program which will build the stubs that Java expects. You'll then need to map the java stubs to the C++ code to compile and distribute that binding library along with your java program.
https://www3.ntu.edu.sg/home/ehchua/programming/java/JavaNativeInterface.html
So, what I ended up doing for now is basically storing the address in my .java class as "long" variable, and then have Init() method return the address of C++ instance as jlong.
Then, when I need to call something on that instance of a class - I pass the address as an additional argument and do the transformation in the C code like this (tested on test class / custom .so):
//constructor wrapper
JNIEXPORT jlong JNICALL Java_Test_Greetings(JNIEnv *env, jobject thisObj, jint value)
{
Greetings *greetings = new Greetings(value);
return (jlong)greetings;
}
JNIEXPORT void JNICALL Java_Test_HelloWorld(JNIEnv *env, jobject thisObj, jlong reference)
{
Greetings *greetings;
greetings = (Greetings*)reference;
greetings->helloValue();
}
I have no idea if that's the correct way to do it, but it works... would appreciate if someone tells me how wrong I am.
Is there some way to add native hooks dynamically using JNI? What I mean by that is, I would like to override some methods in a class (or in a new class) so that the override calls my native code, without writing any Java code for that.
If you are referring to native methods, maybe registering might be the answer for you.
For example, here, I am registering native method
JNIEXPORT jint JNICALL addOne(JNIEnv *env, jclass obj, jint a) {
return a + 1;
}
...
...
static JNINativeMethod methods[] = {
{"addOne", "(I)I", (void *)&addOne}
};
...
...
(*env)->RegisterNatives(env, cls_Main,
methods, sizeof(methods)/sizeof(methods[0]));
that will be assigned to class
public class Main {
public static native int addOne(int a);
...
...
}
Full sample: https://github.com/mkowsiak/jnicookbook/tree/master/recipes/recipeNo052
I have:
public class MyEntity {
private String _myEntityType;
public String get_myEntityType() {
return _myEntityType;
}
public void set_myEntityType(String _myEntityType) {
this._myEntityType = _myEntityType;
}
}
Then :
public class MyObjectEntity extends MyEntity {
public MyObjectEntity() {
super();
}
private String _myObjectDescription;
public String get_myObjectDescription() {
return _myObjectDescription;
}
public void set_myObjectDescription(String _myObjectDescription) {
this._myObjectDescription = _myObjectDescription;
}
}
Now I start getting into JNI.
public class MyPers {
// Load the 'my-pers-lib' library on application startup.
static {
System.loadLibrary("my-pers-lib");
}
public native Long myPersInit(MyEntity myEntity);
}
Then :
#include <jni.h>
#include <android/log.h>
extern "C"
JNIEXPORT jobject JNICALL
Java_my_ca_my_1gen_1lib_1pers_c_1libs_1core_RevPers_myPersInit(JNIEnv *env, jobject instance,
jobject myEntity) {
jclass myEntityClazz = env->GetObjectClass(myEntity);
/** START GET STRING **/
const char *myEntityType;
// and the get_myObjectDescription() method
jmethodID get_myObjectDescription = env->GetMethodID
(myEntityClazz, "get_myObjectDescription", "()Ljava/lang/String;");
// call the get_myObjectDescription() method
jstring s = (jstring) env->CallObjectMethod
(myEntity, get_myObjectDescription);
if (s != NULL) {
// convert the Java String to use it in C
myEntityType = env->GetStringUTFChars(s, 0);
__android_log_print(ANDROID_LOG_INFO, "MyApp", "get_myObjectDescription : %s\n",
myEntityType);
env->ReleaseStringUTFChars(s, myEntityType);
}
/** END GET STRING **/
return myEntityGUID;
}
I start it all up :
MyObjectEntity myObjectEntity = new MyObjectEntity();
myObjectEntity.set_myObjectDescription("A Good Day To Find Answers To Life's Important Questions.");
MyPers revPers = new MyPers();
Log.v("MyApp", "GUID : " + myPers.myPersInit(myObjectEntity));
The error I get is :
JNI CallObjectMethodV called with pending exception java.lang.NoSuchMethodError: no non-static method "Lmy_app/MyEntity;.get_myObjectDescription()Ljava/lang/String;"
QUESTION
How can I go about calling a method of a subclass / child class of an Java Object passed to JNI jobject, myEntity in this case?
extern "C"
JNIEXPORT jobject JNICALL
Java_myPersInit(JNIEnv *env, jobject instance, jobject myEntity)
Thank you all in advance.
How can I go about calling a method of a subclass / child class of an
Java Object passed to JNI jobject, myEntity in this case?
The question does not make sense. Objects do not have subclasses, classes do. Whatever the class of a given object happens to be defines the methods that you may invoke on that object. You seem to be trying to ask how to invoke methods of a subtype of the declared type of the native method parameter, but the premise of such a question runs counter to the actual fact that you do so exactly the same way you invoke any other instance method from JNI.
Consider: in Java, you would downcast to the subtype and then, supposing the cast succeeded, you would invoke the wanted method normally. In JNI, on the other hand, object references are not differentiated by pointed-to object type (they are all jobjects), so no casting is involved; you just go straight to invoking the wanted method normally.
Of course, a JNI error will occur if the class of the target object does not provide such a method. Whether it's a good idea to do what you describe is an entirely separate question.