JNI: Callback from JVM to C++ fails to run - java

I start JVM from C++ program.
C++ code:
JNIEXPORT jobject JNICALL com_javelin_JavelinMarketData_callBackIntoNative(JNIEnv* env, jobject obj, jlong ptr)
{
std::cout << "com_javelin_JavelinMarketData_callBackIntoNative called" << std::endl;
}
int main()
{
JavaVM* jvm;
JNIEnv* env;
...
JNI_CreateJavaVM(&jvm, (void **)&env, &args);
jmethodID mainMethod = env->GetStaticMethodID(helloWorldClass, "main", "([Ljava/lang/String;)V");
env->CallStaticVoidMethod(helloWorldClass, mainMethod, ...);
}
then I need my C++ function called back from java:
Java code:
native void callBackIntoNative(long ptr);
public void onResponse(long param)
{
System.out.println("Calling callBackIntoNative()");
callBackIntoNative(param);
}
When I run my C++ program, JVM starts correctly, but after it prints "Calling callBackIntoNative()" the following acception appears:
Exception during callBackIntoNative(): 'com.javelin.JavelinMarketData.callBackIntoNative(J)V'
java.lang.UnsatisfiedLinkError: com.javelin.JavelinMarketData.callBackIntoNative(J)V
Can you please help?

Thanks, but I found a solution: I should have registered my C++ function as a native method using the code:
const JNINativeMethod methods[] = { { "callBackIntoNative", "(J)V", (void*)&com_javelin_JavelinMarketData_callBackIntoNative } };
const int methods_size = sizeof(methods) / sizeof(methods[0]);
jclass jClass = env->FindClass("com/javelin/JavelinMarketData");
env->RegisterNatives(jClass, methods, methods_size);
Now it works fine.

I would compile your callback apart into a dynamic library (.dll, .so, whatever your OS) and put it accesible to your java program. Then you just load your library by using JNI and call from your java classes whatever functionality you have in the library.

Related

How to call getStackTrace method from java native interface (jni)

I am trying to understand the jni with some examples. I am trying to get the java stack trace using jni and so this is what I was doing
HelloJNI.java
package test.com.jni;
public class HelloJNI {
static {
System.loadLibrary("hello"); // Load native library at runtime
}
private native StackTraceElement[] getStackTraceNative(Throwable throwable);
private native void printStackTraceNative(Throwable throwable);
public static void main(String[] args) {
test();
}
public static void test() {
new HelloJNI().printStackTraceNative(new Throwable()); //Invoke native method
new HelloJNI().getStackTraceNative(new Throwable());
}
}
Native code (keeping the error handling out for simplicity)
test_com_jni_HelloJNI.c
JNIEXPORT jobjectArray JNICALL Java_test_com_jni_HelloJNI_getStackTraceNative (JNIEnv * env, jobject object, jthrowable exception) {
jclass exceptionClazz = (*env)->GetObjectClass(env, exception);
jmethodID getStackTraceMethod = (*env)->GetMethodID(env, exceptionClazz, "getStackTrace", "()[Ljava.lang.StackTraceElement");
jobjectArray stacktraces = (*env)->CallObjectMethod(env, exception, getStackTraceMethod);
return stacktraces;
}
JNIEXPORT void JNICALL Java_test_com_jni_HelloJNI_printStackTraceNative (JNIEnv * env, jobject object, jthrowable exception) {
jclass exceptionClazz = (*env)->GetObjectClass(env, exception); // can't fail
jmethodID printStackTraceMethod = (*env)->GetMethodID(env, exceptionClazz, "printStackTrace", "()V");
(*env)->CallVoidMethod(env, exception, printStackTraceMethod);
}
Now in this code native printStackTraceNative method works and it prints the stack trace however, getStackTraceNative doesn't. When I check the core dump file it says that java has thrown an exception java/lang/NoSuchMethodError for getStackTrace. I am confused because the parameter I am passing to getStackTraceNative method is of the type throwable and throwable should have the method getStackTrace.
What concept I possibly be missing here, any help on this is appreciated. Thanks
You got the method signature wrong. In method signatures the periods in the qualified class name are replaced by forward slashes. The signature for getStackTrace is therefore ()[Ljava/lang/StackTraceElement; (also note the semicolon)
You can get the method signatures from class files with javap with -s option:
javap -classpath '/path/to/jre/lib/rt.jar' -s java.lang.Throwable

ndk-build 'JNI_CreateJavaVM' was not declared in this scope

I have error while trying invoke java method from native code.
[arm64-v8a] Compile++ : hell <= hell.cpp
/home/zns/AndroidStudioProjects/Test/app/src/main/jni/hell.cpp: In function 'int main()':
/home/zns/AndroidStudioProjects/Test/app/src/main/jni/hell.cpp:8:42: error: 'JNI_CreateJavaVM' was not declared in this scope
JNI_CreateJavaVM(&jvm, &env, &vm_args);
^
make: *** [/home/zns/AndroidStudioProjects/Test/app/src/main/obj/local/arm64-v8a/objs/hell/hell.o] Error 1
hell.cpp
#include <string.h>
#include <jni.h>
int main(){
JavaVM *jvm; /* denotes a Java VM */
JNIEnv *env; /* pointer to native method interface */
JavaVMInitArgs vm_args; /* JDK/JRE 6 VM initialization arguments */
vm_args.version = JNI_VERSION_1_6;
JNI_CreateJavaVM(&jvm, &env, &vm_args);
jclass cls = env->FindClass("MainActivity");
jmethodID mid = env->GetStaticMethodID(cls, "test", "()V");
env->CallStaticVoidMethod(cls, mid, 100);
jvm->DestroyJavaVM();
}
extern "C" {
jstring
Java_com_oxide_app_MainActivity_stringFromJNI
(JNIEnv *env, jobject obj)
{
main();
return env->NewStringUTF("Hello from C++ over JNI!");
}
}
MainActivity.java
public class MainActivity extends ActionBarActivity {
static{
System.loadLibrary("hell");
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = new TextView(this);
tv.setText(stringFromJNI());
setContentView(tv);
}
public native String stringFromJNI();
public void test(){
Log.d("NATIVE", "WHOA");
}
}
OS: linux;
jdk:/opt/icedtea-bin-6.1.12.7/;
P.S.
I have seen two similar questions, but they did not help to solve the problem
Calling a JAVA method from C++ with JNI, no parameters
Using JNI to execute a java jar from a C++ program, using g++ or eclipse
From the NDK's jni.h
#if 0 /* In practice, these are not exported by the NDK so don't declare them */
jint JNI_GetDefaultJavaVMInitArgs(void*);
jint JNI_CreateJavaVM(JavaVM**, JNIEnv**, void*);
jint JNI_GetCreatedJavaVMs(JavaVM**, jsize, jsize*);
#endif
As the only supported way to use the NDK is from a Java application so the Java JM is already loaded.
I think you should remove your main function and look into replacing it with JNI_OnLoad and remove the calls to control the VM's lifetime.

Is it possible to make a Java JNI which calls jdbc?

I am new to jni and very confused if I can use jni to achieve what I need done. I want to make a java api what will use jdbc to update database, but this particular api will be called from C++ program.
So I think I probably should write jni code which access the database via jdbc (is that even possible?), create C++ code and generate dll so other C++ programs can just call the dll to update database. Is this all possible? If so, how do I really call jdbc in jni?
If this dll is finally made, can Fortran call it as well?
My other thought is maybe I should make a regular java program to update the database, then use say ikvm to wrap the java class into C++ dll?
The thing is I have to use access database using Java. Our C++ programs will not access database at all, and it would be better if this java api can be accessed via system call.
Or is there any better way to do it?
I hope I explained it well. I am not all familiar with what I am assigned here and cannot find much relevant reference.
Thank you so much!!
UPDATED:
The problem is not all computers have C++ postgresql driver installed but they do have Java postgresql driver installed. We don't want to force everyone to install the C++ db driver and no major changes in those C++ program will be made. So it will make sense to come up something in Java to access database. The java system service (preferred, like dll?) /API basically is called to record start time and end time of a C++ program. C++ program will make a "function" call (with pass-in parameter and returned value) to this system service/Java API to record start/end time.
I'm not going to lecture you on the right or wrong way to approach what you are trying to do. However, if you are trying to invoke Java code (JDBC.jar) then the following is for you.. Otherwise feel free to downvote.
JVM.hpp:
#ifndef JVM_HPP_INCLUDED
#define JVM_HPP_INCLUDED
#include "../java/jni.h"
#include <windows.h>
#include <iostream>
#include <stdexcept>
#include <algorithm>
class Jvm
{
private:
JavaVM* jvm;
JNIEnv* env;
JavaVMInitArgs jvm_args;
jclass systemClassLoader;
public:
Jvm(std::string ClassPath = ".");
~Jvm();
inline JavaVM* GetJVM() const {return jvm;}
inline JNIEnv* GetENV() const {return env;}
inline jclass GetSystemClassLoader() const {return systemClassLoader;}
void DestroyJVM();
void PrintStackTrace();
jclass DefineClass(const char* FullClassName, const void* ClassBuffer, std::uint32_t BufferLength);
jclass DefineClass(const char* FullClassName, jobject ClassLoader, const void* ClassBuffer, std::uint32_t BufferLength);
void RegisterNativeMethod(const char* MethodName, const char* MethodSignature, void* func_ptr);
void RegisterNativeMethod(jobject ClassLoader, const char* MethodName, const char* MethodSignature, void* func_ptr);
void RegisterNativeMethods(JNINativeMethod* Methods, std::uint32_t MethodCount);
void RegisterNativeMethods(jobject ClassLoader, JNINativeMethod* Methods, std::uint32_t MethodCount);
protected:
void InitClassLoader();
};
#endif // JVM_HPP_INCLUDED
JVM.cpp:
#include "JVM.hpp"
Jvm::~Jvm()
{
env->DeleteGlobalRef(this->systemClassLoader);
jvm->DestroyJavaVM();
}
Jvm::Jvm(std::string ClassPath) : jvm(NULL), env(NULL), jvm_args(), systemClassLoader(NULL)
{
JavaVMOption* options = new JavaVMOption[2];
jvm_args.version = JNI_VERSION_1_6;
JNI_GetDefaultJavaVMInitArgs(&jvm_args);
options[0].optionString = const_cast<char*>("-Djava.compiler=NONE");
options[1].optionString = const_cast<char*>(("-Djava.class.path=" + ClassPath).c_str());
jvm_args.nOptions = 2;
jvm_args.options = options;
jvm_args.ignoreUnrecognized = false;
if (JNI_CreateJavaVM(&jvm, reinterpret_cast<void**>(&env), &jvm_args))
{
delete[] options;
throw std::runtime_error("Failed To Create JVM Instance.");
}
delete[] options;
}
void Jvm::InitClassLoader()
{
if (!this->systemClassLoader)
{
jclass classloader = env->FindClass("Ljava/lang/ClassLoader;");
if (!classloader)
{
throw std::runtime_error("Failed To find ClassLoader.");
}
jmethodID SystemLoaderMethod = env->GetStaticMethodID(classloader, "getSystemClassLoader", "()Ljava/lang/ClassLoader;");
jobject loader = env->CallStaticObjectMethod(classloader, SystemLoaderMethod);
this->systemClassLoader = reinterpret_cast<jclass>(env->NewGlobalRef(loader));
}
}
void Jvm::PrintStackTrace()
{
if (env->ExceptionOccurred())
{
env->ExceptionDescribe();
env->ExceptionClear();
}
}
jclass Jvm::DefineClass(const char* FullClassName, const void* ClassBuffer, std::uint32_t BufferLength)
{
this->InitClassLoader();
return this->DefineClass(FullClassName, this->systemClassLoader, ClassBuffer, BufferLength);
}
jclass Jvm::DefineClass(const char* FullClassName, jobject ClassLoader, const void* ClassBuffer, std::uint32_t BufferLength)
{
return ClassLoader ? env->DefineClass(FullClassName, ClassLoader, static_cast<const jbyte*>(ClassBuffer), BufferLength) : NULL;
}
void Jvm::RegisterNativeMethod(const char* MethodName, const char* MethodSignature, void* func_ptr)
{
JNINativeMethod method;
method.name = const_cast<char*>(MethodName);
method.signature = const_cast<char*>(MethodSignature);
method.fnPtr = func_ptr;
this->RegisterNativeMethods(&method, 1);
}
void Jvm::RegisterNativeMethod(jobject ClassLoader, const char* MethodName, const char* MethodSignature, void* func_ptr)
{
JNINativeMethod method;
method.name = const_cast<char*>(MethodName);
method.signature = const_cast<char*>(MethodSignature);
method.fnPtr = func_ptr;
this->RegisterNativeMethods(ClassLoader, &method, 1);
}
void Jvm::RegisterNativeMethods(JNINativeMethod* Methods, std::uint32_t MethodCount)
{
this->InitClassLoader();
this->RegisterNativeMethods(this->systemClassLoader, Methods, MethodCount);
}
void Jvm::RegisterNativeMethods(jobject ClassLoader, JNINativeMethod* Methods, std::uint32_t MethodCount)
{
if (ClassLoader)
{
env->RegisterNatives(static_cast<jclass>(ClassLoader), Methods, MethodCount);
}
}
You can then create an instance of it that loads your jar.
int main()
{
Jvm VM("C:/Users/Brandon/IdeaProjects/Eos/out/production/Eos/Bot.jar");
jclass jMain = VM.GetENV()->FindClass("eos/Main");
if (jMain != nullptr)
{
jmethodID mainMethod = env->GetStaticMethodID(jMain, "main", "([Ljava/lang/String;)V");
jclass StringClass = env->FindClass("java/lang/String");
jobjectArray Args = env->NewObjectArray(0, StringClass, 0);
env->CallStaticVoidMethod(jMain, MainMethod, Args);
}
}
Now this just shows how to run a jar with a Main Method. However, you can access ANY class from the jar and invoke it with however many parameters needed. It doesn't need a main.
Now it IS a lot MORE work to do this, but I won't lecture you. The question was whether or not is was possible and the answer is YES.. it is. So long as you create an instance of the "JVM". After that, it's a matter of accessing the class via the "Package/Class" NOT "Package.Class" as done in Java.. then invoking whatever methods you want.

calling java method based on jobject fails (invalid reference?)

I have the following problem:
in my java program I'm calling a native function that creates an object of the "MyEventReceiver" class in C++, later in that java program I'm calling an native function that calls the "test" method of that object. In this "test"-method I want to call a method of the java object that I used to call the second native function, but as soon as make this call, the jvm crashes.
This is the native code that creates the MyEventReceiver object:
JNIEXPORT jlong JNICALL Java_irr4jEventReceiver_native_1createEventReceiver
(JNIEnv *env, jobject obj){
MyEventReceiver *rec = new MyEventReceiver(env, obj);
return (long)rec;
}
this is the native code that i use later in the program to call the "test" method in that object:
JNIEXPORT void JNICALL Java_irr4jEventReceiver_native_1testmethode
(JNIEnv *env, jobject obj, jlong ptrrec){
MyEventReceiver *rec = (MyEventReceiver*)ptrrec;
rec->test();
}
and this is the MyEventReceiver class:
class MyEventReceiver : public IEventReceiver
{
public:
JNIEnv *myenv;
jobject receiverobj;
jclass SEventclass;
jobject eventobj;
jmethodID cid;
jclass cevrec;
jmethodID meth2;
public:
void test(){
eventobj = myenv->AllocObject(SEventclass);
eventobj = myenv->NewObject(SEventclass, cid);
myenv->CallVoidMethod(receiverobj,meth2,eventobj); //this is the line that causes the crash
}
MyEventReceiver(JNIEnv *env, jobject obj)
{
this->myenv=env;
receiverobj = env->NewGlobalRef(obj);
SEventclass = myenv->FindClass("SEvent");
cid = myenv->GetMethodID(SEventclass,"<init>", "()V");
cevrec = myenv->FindClass("MyEventReceiver");
meth2 =myenv->GetMethodID(cevrec, "OnEvent", "(LSEvent;)V");
//test();
}
};
if I call the method "test" at the end of the constructor, it works... only if I make the call later out of the java program... I think it has something to do with the jobject "receiverobj"... seems to get invalid after some time but I don't know why
...I stripped the code a bit...deleted some debug code. The "eventobj" I'm creating is fine... I can call other methods of that object, the methodIDs are also fine, just the line:
myenv->CallVoidMethod(receiverobj,meth2,eventobj);
gives me problems and i dont know why :)
crash message is:
#
# A fatal error has been detected by the Java Runtime Environment:
#
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x67d594f4, pid=5292, tid=2400
#
# JRE version: 7.0_21-b11
# Java VM: Java HotSpot(TM) Client VM (23.21-b01 mixed mode, sharing windows-x86 )
# Problematic frame:
# V [jvm.dll+0xa94f4]
#
# Failed to write core dump. Minidumps are not enabled by default on client versions of Windows
#
# An error report file with more information is saved as:
# G:\irr4jjava\irr4j\hs_err_pid5292.log
#
# If you would like to submit a bug report, please visit:
# http://bugreport.sun.com/bugreport/crash.jsp
#
EDIT:
Thanks for the help so far. I tried to use global refs and attached thread but I can't get it to work. I'm not sure if I understood the thing with the attached thread...
I modified the first native method to create a global reference to the jobject and pass it to the constructor:
JavaVM *jvm;
JNIEXPORT jlong JNICALL Java_irr4jEventReceiver_native_1createEventReceiver
(JNIEnv *env, jobject obj){
jobject recobj = env->NewGlobalRef(obj);
env->GetJavaVM(&jvm);
MyEventReceiver *rec = new MyEventReceiver(recobj);
return (long)rec;
}
in the class "MyReceiverObject" I try attach the thread but it still crashes:
class MyEventReceiver : public IEventReceiver
{
public:
JNIEnv *myenv;
jobject receiverobj;
jclass SEventclass;
jobject eventobj;
jmethodID cid;
jclass cevrec;
jmethodID meth2;
public:
void test(){
jvm->AttachCurrentThread((void**)&myenv,NULL);
eventobj = myenv->AllocObject(SEventclass); //crash
eventobj = myenv->NewObject(SEventclass, cid);
myenv->CallVoidMethod(receiverobj,meth2,eventobj);
}
MyEventReceiver(jobject obj)
{
jvm->AttachCurrentThread((void**)&myenv,NULL);
receiverobj=obj;
SEventclass = myenv->FindClass("SEvent");
cid = myenv->GetMethodID(SEventclass,"<init>", "()V");
cevrec = myenv->FindClass("MyEventReceiver");
meth2 =myenv->GetMethodID(cevrec, "OnEvent", "(LSEvent;)V");
//test();
}
};
You need to read the chapter in the JNI Specification about global references. The jobject you were passed in your JNI method is only valid for the life of that method. You need to convert it to a GlobalRef, maybe a WeakGlobalRef, before you store it anywhere that is going to outlive the current invocation.
A similar thing applies to the JNIEnv pointer. It's only valid for the duration of the method it is passed to. If you want to use one in a different thread or at a later time you must get a new one via AttachThread().

Where can I find the native implementations of these functions?

I found these in open JDK (System.c file)
static JNINativeMethod methods[] = {
{"currentTimeMillis", "()J", (void *)&JVM_CurrentTimeMillis},
{"nanoTime", "()J", (void *)&JVM_NanoTime},
{"arraycopy", "(" OBJ "I" OBJ "II)V", (void *)&JVM_ArrayCopy},
};
#undef OBJ
JNIEXPORT void JNICALL
Java_java_lang_System_registerNatives(JNIEnv *env, jclass cls)
{
(*env)->RegisterNatives(env, cls,
methods, sizeof(methods)/sizeof(methods[0]));
}
but I was not able to find the native implemetations of these functions
currentTimeMillis
nanoTime
arraycopy
Form where can I get the native implementations of these functions ?
Is that available in open JDK?
if found it in
jdk7/hotspot/src/share/vm/prims/jvm.cpp:229
JVM_LEAF(jlong, JVM_CurrentTimeMillis(JNIEnv *env, jclass ignored))
JVMWrapper("JVM_CurrentTimeMillis");
return os::javaTimeMillis();
JVM_END
the real implementation (for linux) is in
/jdk7/hotspot/src/os/linux/vm/os_linux.cpp
the other methods are just below it

Categories