I need to call some methods from a jar using JNI in c++.But i need to call it a lot of times in different instances of thesame class.
My JNI is something like this
JavaVM *jvm; /* denotes a Java VM */
JNIEnv *env; /* pointer to native method interface */
JavaVMInitArgs vm_args; /* JDK/JRE 6 VM initialization arguments */
jmethodID Sim_constr = NULL;
jmethodID Read_XML = NULL;
jmethodID configure = NULL;
jmethodID initial = NULL;
jmethodID results = NULL;
jint step = 60;
JavaVMOption* options = new JavaVMOption[1];
options[0].optionString = "-Djava.class.path=<My jar>";
vm_args.version = JNI_VERSION_1_8;
vm_args.nOptions = 1;
vm_args.options = options;
vm_args.ignoreUnrecognized = JNI_TRUE;
/* load and initialize a Java VM, return a JNI interface
* pointer in env */`
long status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
if (status == JNI_ERR){
exit(10);
}
//Some Java method Calls... `
I need to set once the JNI and then just call 3-4 methods OR to kill/delete completely the JNI parameters and then create it again.
I suppose you may try to accomplish the following
After creating a JVM using CreateJavaVM method your JVM remains Global for all instance of the class, the environment variable(env) should be local for each instance of the class. And also env var for each instance must be used to call your methods many times using different instances.
This snippet will get you the env from JVM:(place this such that every instance has local copy of env and global copy of jvm)
After creating the instance you have to attach these env to the current thread(i.e current instance)
JNIEnv * g_env;
int getEnvStat = jvm->GetEnv((void **)&g_env, JNI_VERSION_1_8);
if (getEnvStat == JNI_EDETACHED) {
printf("GetEnv: not attached");
getEnvStat = g_vm->AttachCurrentThread((void **) &g_env, NULL);
if ( getEnvStat != 0) {
printf("Failed to attach");
}
} else if (getEnvStat == JNI_OK) {
} else if (getEnvStat == JNI_EVERSION) {
printf("GetEnv: version not supported");
}
//use the g_env variable for java method calls
//Rememeber to use this code as a local copy for each instance
Related
I've just looked at anyone else questions but it seems that nobody has the same problem of mine. I have a Java class HelloWorldHandler.java (in org.eclipse.gemoc.testapplilauncher.handlers package in org.eclipse.gemoc.testapplilauncher project) that launches another java application. Then I have a c file launcherC.c (in the same project but different directory) that is supposed to call the execute method in the java file, through JNI. Now, all my previous JNI applications worked, this one doesn't. The only difference is that HelloWorldHandler.java is not in the default package (so the .class is not directy in /bin) and there are multiple package imports (maybe something changes with the FindClass call (?)).
I launch the c file with
gcc -fPIC -I"/usr/lib/jvm/java-8-oracle/include" -I"/usr/lib/jvm/java-8-oracle/include/linux" -L/usr/lib/jvm/java-8-oracle/lib/amd64/jli/ -L/usr/lib/va-8-oracle/jre/lib/amd64/server/ -o launcherC launcherC.c -ljli -ljvm
and
export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/lib/jvm/java-8-oracle/jre/lib/amd64/server/
and there is no problem. But when I execute it, it doesn't do anything, without any Expections and errors. I checked and the FindClass returns NULL.
HelloWorldHandler.java
package org.eclipse.gemoc.testapplilauncher.handlers;
public class HelloWorldHandler {
#Execute
public static void execute() {
ILaunchManager manager = DebugPlugin.getDefault().getLaunchManager();
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
IFile ff = root.getFile(new Path("d/Test.launch"));
ILaunchConfiguration res = manager.getLaunchConfiguration(ff);
DebugUITools.launch(res, ILaunchManager.DEBUG_MODE);
}
}
launcherC.c
#include <jni.h>
#include <string.h>
int main()
{
JavaVMOption options[1];
JNIEnv *env;
JavaVM *jvm;
JavaVMInitArgs vm_args;
long status;
jclass cls;
jmethodID mid;
options[0].optionString = "-Djava.class.path=/home/ezambon/Desktop/modeling/org/eclipse/gemoc/testAppliLauncher/bin/org/eclipse/gemoc/testapplilauncher/handlers";
memset(&vm_args, 0, sizeof(vm_args));
vm_args.version = JNI_VERSION_1_2;
vm_args.nOptions = 1;
vm_args.options = options;
status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
if (status != JNI_ERR)
{
cls = (*env)->FindClass(env, "HelloWorldHandler");
if(cls != 0) {
//printf("asdfgh\n");
mid = (*env)->GetStaticMethodID(env, cls, "execute", "()V");
if(mid !=0) {
(*env)->CallStaticVoidMethod(env, cls, mid);
}
}
//printf("qwerty");
(*jvm)->DestroyJavaVM(jvm);
return 0;
}
else
return -1;
}
All the suggestions are really welcome, I'm getting crazy. Thank you :)
First problem you setup classpath wrong
Instead of
options[0].optionString = "-Djava.class.path=/home/ezambon/Desktop/modeling/org/eclipse/gemoc/testAppliLauncher/bin/org/eclipse/gemoc/testapplilauncher/handlers";
you should have
options[0].optionString = "-Djava.class.path=/home/ezambon/Desktop/modeling/org/eclipse/gemoc/testAppliLauncher";
and you should use full class name
cls = (*env)->FindClass(env, "org.eclipse.gemoc.testapplilauncher.handlers.HelloWorldHandler");
But I suppose this is not all your problems.
Here is the code in C++ ,where i create a Java VM and i need to call a function from a jar.
JavaVM *jvm; /* denotes a Java VM */
JNIEnv *env; /* pointer to native method interface */
JavaVMInitArgs vm_args; /* JDK/JRE 6 VM initialization arguments */
jmethodID constr = NULL;
jmethodID Read_XML = NULL;
JavaVMOption* options = new JavaVMOption[1];
options[0].optionString = "-Djava.class.path=<path_to_my_jar>";
vm_args.version = JNI_VERSION_1_6;
vm_args.nOptions = 1;
vm_args.options = options;
vm_args.ignoreUnrecognized = JNI_TRUE;
/* load and initialize a Java VM, return a JNI interface
* pointer in env */
long status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
if (status == JNI_ERR){
cout << "Fail: Unable to load JVM \t Exit" << endl;
}
jclass xml_read = env->FindClass("<MyClass>");
if (env->ExceptionCheck()){
cout << "Fail:";
}
constr = env->GetMethodID(xml_read, "<init>", "()V");
if (env->ExceptionCheck()){
cout << "Fail:";
}
Read_XML = env->GetStaticMethodID(xml_read,"readFromXML", "(Ljava/lang/String;)L<MyClass>;");
if (env->ExceptionCheck()){
cout << "Fail:";
}
const char* filepath = "<My_filepath>";
const jstring file = env->NewStringUTF(filepath);
jobject ret_obj = env->CallStaticObjectMethod(xml_read,Read_XML,file);
if (env->ExceptionCheck()){
cout << "Fail:";
}
Now the ret_obj is NULL.The Java function i am calling takes a String as argument and returns an Object from another Class different from MyClass.
Java Method is like this public static SomeClass readFromXML(String filepath)
UPDATE 1
Inside the CallStaticObjectMethod we have this jobject CallStaticObjectMethod(jclass clazz, jmethodID methodID,
...) {
va_list args;
jobject result;
va_start(args,methodID);
result = functions->CallStaticObjectMethodV(this,clazz,methodID,args);
va_end(args);
return result;
}
After va_start(args,methodID); the value of args is somnething like this 0x002cf5fc "0¦ύ"
So maybe there is something with the arguments?
ok this one solved.It was my bad, in filepath i should define the XML file too and not just the file containing this.Thank you
I need to call java method from c program. i have tried below code to call the java method through Java native interface but facing issues while compilation. i m new to C and have experience in java. so, i m not able to think myself what is happening while creating JVM.
Below is the code.
CTest.c
#include <stdio.h>
#include <jni.h>
JNIEnv* create_vm() {
JavaVM* jvm;
JNIEnv* env;
JavaVMInitArgs args;
JavaVMOption options[1];
args.version = JNI_VERSION_1_6;
args.nOptions = 1;
options[0].optionString = "-Djava.class.path=D:\\Ashish_Review\\JNI\\src";
args.options = options;
args.ignoreUnrecognized = JNI_FALSE;
JNI_CreateJavaVM(&jvm, (void **)&env, &args);
return env;
}
void invoke_class(JNIEnv* env) {
jclass helloWorldClass;
jmethodID mainMethod;
jobjectArray applicationArgs;
jstring applicationArg0;
helloWorldClass = (*env)->FindClass(env, "HelloWorld");
mainMethod = (*env)->GetStaticMethodID(env, helloWorldClass, "main", "([Ljava/lang/String;)V");
applicationArgs = (*env)->NewObjectArray(env, 1, (*env)->FindClass(env, "java/lang/String"), NULL);
applicationArg0 = (*env)->NewStringUTF(env, "From-C-program");
(*env)->SetObjectArrayElement(env, applicationArgs, 0, applicationArg0);
(*env)->CallStaticVoidMethod(env, helloWorldClass, mainMethod, applicationArgs);
}
int main(int argc, char **argv) {
JNIEnv* env = create_vm();
invoke_class( env );
}
C:\Users\Desktop\tcc>tcc C:\TurboC++\Disk\TurboC3\BIN\CTest.c -I "C:\Program Files\Java\jdk1.6.0_16\include" -I "C:\Program Files\Java\jdk1.6.0_16\include\win32" -shared -o CTest.dll
tcc: undefined symbol '_JNI_CreateJavaVM#12'
please help me out.
The error message is about the linking stage, not compilation - you have included the header file, but to create the executable you have to specify the .a (library files) also.
You have to link with JVM's library (add some reference to libjvm.a to the tcc's command line).
If you don't have a precompiled jvm.lib file for TurboC++, there is another option - link with the jvm.dll file and export all the methods from JVM manually. This uses the LoadLibrary/GetProcAddress functions.
For a short sample (sorry, couldn't find the entire export source code), look at this:
/* load library */
HMODULE dll = LoadLibraryA("jvm.dll");
/* declare a function pointer and initialize it with the "pointer" to dll's code */
JNI_CreateJavaVM_func JNI_CreateJavaVM_ptr = GetProcAddress(dll, "JNI_CreateJavaVM");
Later use the JNI_CreateJavaVM_ptr instead of JNI_CreateJavaVM. Also you'll have to declare the JNI_CreateJavaVM_func type - you might just copy the signature from "jni.h"
I do have a WebApp (WAR file) in a Maven project running fine.
I want to call a Java method in a class file which is inside the maven project from a C file. The C program reads the GPIO input of my beagleboard and when it's on, it runs the Java method. I use JNI for doing that.
I can run a classic HelloWorld Java method, but when I want to run a method from my Maven project, I got a fatal error from my JVM when its trying to load the Java static method.
I added the C code in my WebApp, in /WEB-INF/myapp/classes/classfile/
Class file : App
Method : test
my C program:
JNIEnv* create_vm(JavaVM ** jvm) {
JNIEnv *env;
JavaVMInitArgs vm_args;
JavaVMOption options;
//Path to the java source code
options.optionString = "-Djava.class.path=./";
//JDK version. This indicates version 1.6
vm_args.version = JNI_VERSION_1_6;
vm_args.nOptions = 1;
vm_args.options = &options;
vm_args.ignoreUnrecognized = 0;
int ret = JNI_CreateJavaVM(jvm, (void**)&env, &vm_args);
if(ret < 0)
printf("\nUnable to Launch JVM\n");
return env;
}
int main(int argc, char** argv)
{
int fd, ret, data[64];
JNIEnv *env;
JavaVM * jvm;
env = create_vm(&jvm);
if (env == NULL)
return 1;
fd = ret = open(argv[argc - 1], O_RDONLY);
printf("open returned %d\n", ret);
while (1) {
ret = read(fd, &data, sizeof(data));
printf("read returned %d\n", ret);
jclass helloWorldClass = (*env)->FindClass(env, "App");
printf("appel class\n");
jmethodID mainMethod = (*env)->GetStaticMethodID(
env, helloWorldClass, "test", "([Ljava/lang/String;)V");
printf("appel method\n");
(*env)->CallStaticVoidMethod(env, helloWorldClass, mainMethod, NULL);
sleep(2);
}
(*jvm)->DestroyJavaVM(jvm);
exit(0);
}
Java class :
package App;
public class App
{
public static void test (String[] args){
System.out.println("Hello World");
}
}
Have you investigated using Reverse JNI?
I notice that you have the following line:
options.optionString = "-Djava.class.path=./"; //Path to the java source code
The classpath needs to point to either the jar file generated by Maven, or the root directory under which your Java class files are located.
See: Setting the ClassPath,
In a standard Maven project, Maven will create your jar under target/ when you execute mvn package. So you'll likely change -Djava.class.path=./ to -cp /target/App.jar
I'm quite new to JNI and right now I'm using this simple C program to create a JVM and call the main() from my Java project:
#include <stdio.h>
#include <jni.h>
JNIEnv* create_vm() {
JavaVM* jvm;
JNIEnv* env;
JavaVMInitArgs args;
JavaVMOption options[1];
args.version = JNI_VERSION_1_6;
args.nOptions = 1;
options[0].optionString = "-Djava.class.path=CLASSPATH"; //This isn't the actual classhpath, but you get the idea
args.options = options;
args.ignoreUnrecognized = JNI_FALSE;
int ret = JNI_CreateJavaVM(&jvm, (void **)&env, &args);
if (ret<0){
printf("\nUnable to Launch JVM");
} else {
printf("\nJVM launched successfully");
}
return env;
}
void invoke_class(JNIEnv* env) {
jclass UncaughtExceptionClass;
jmethodID mainMethod;
jobjectArray applicationArgs;
jstring applicationArg0;
UncaughtExceptionClass = (*env)->FindClass(env, "exceptioncatcher/ExceptionCatcher");
mainMethod = (*env)->GetStaticMethodID(env, UncaughtExceptionClass, "main", "([Ljava/lang/String;)V");
applicationArgs = (*env)->NewObjectArray(env, 1, (*env)->FindClass(env, "java/lang/String"), NULL);
applicationArg0 = (*env)->NewStringUTF(env, "From C");
(*env)->SetObjectArrayElement(env, applicationArgs, 0, applicationArg0);
(*env)->CallStaticVoidMethod(env, UncaughtExceptionClass, mainMethod, applicationArgs);
}
int main(int argc, char **argv) {
JNIEnv* env = create_vm();
invoke_class( env );
}
This works well when running a HelloWorld type java code, but my code actually sets a Default Uncaught Exception Handler. This handler will send the info collected for each uncaught exception to a thread that will process them and send them by email.
Right now these are pretty much all the classes created and it, obviously, won't catch a lot of uncaught exceptions right now. But I use the main() to try it out:
public static void main(String[] args){
Thread.setDefaultUncaughtExceptionHandler(new DefaultExceptionHandler());
Integer i = null;
i++;
}
It works fine when I simply run it from netbeans, but the only thing that appears when using the C app is "JVM launched successfully".
Can anyone help on this one?
Managed to make it work in the end (never closed the question before), simply had to build the project and change the classpath to the .jar generated.