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
Related
I am trying to run a java program using c++ code. I tried below code
#include<jni.h>
#include<stdio.h>
int main(int argc, char** argv) {
JavaVM* vm;
JNIEnv* env;
JavaVMInitArgs vm_args;
vm_args.version = JNI_VERSION_1_2;
vm_args.nOptions = 0;
vm_args.ignoreUnrecognized = 1;
// Construct a VM
jint results = JNI_CreateJavaVM(&vm, (void**)& env, &vm_args);
// Construct a String
jstring jstr = env->NewStringUTF("Hello World");
// First get the class that contains the method you need to call
jclass clazz = env->FindClass("java/lang/String");
// Get the method that you want to call
jmethodID to_lower = env->GetMethodID(clazz, "toLowerCase",
"()Ljava/lang/String;");
// Call the method on the object
jobject result = env->CallObjectMethod(jstr, to_lower);
// Get a C-style string
const char* str = env->GetStringUTFChars((jstring)result, NULL);
printf("%s\n", str);
// Clean up
env->ReleaseStringUTFChars(jstr, str);
// Shutdown the VM.
vm->DestroyJavaVM();
}
I used below command to compile the code
g++ LoadJVM.c -I/c/apps64/Java/jdk-11.0.1/include -I/c/apps64/Java/jdk-11.0.1/include/win32 -L/c/apps64/Java/jdk-11.0.1/lib/ -ljvm
It compiles fine, but when i run the executable like below, i am facing error
./a.exe
Error
error while loading shared libraries: jvm.dll: cannot open shared object file: No such file or directory
Any Idea why this jvm.dll is not getting loaded?
PS: I am compiling and running from Git-Bash on windows 10.
It looks like your jvm.dll can not be found.
Let's say we have 32bit MinGW installation (this is the version I have).
simple.cc
#include<jni.h>
#include<stdio.h>
int main(int argc, char** argv) {
JavaVM* vm;
JNIEnv* env;
JavaVMInitArgs vm_args;
vm_args.version = JNI_VERSION_1_2;
vm_args.nOptions = 0;
vm_args.ignoreUnrecognized = 1;
// Construct a VM
jint results = JNI_CreateJavaVM(&vm, (void**)& env, &vm_args);
printf("Hello");
// Shutdown the VM.
(*vm).DestroyJavaVM();
}
compilation and execution
> export JAVA_HOME="/c/Program\ Files\ \(x86\)/Java/jdk1.8.0_211/"
> export PATH="/c/Program Files (x86)/Java/jdk1.8.0_211/jre/bin/server/":"$PATH"
> g++ -o simple simple.cc -I"$JAVA_HOME/include/" -I"$JAVA_HOME/include/win32/" -L"$JAVA_HOME/lib" -ljvm
> ./simple
Hello
you have to make sure that jvm.dll is visible on %PATH% - $PATH inside git-bash.
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.
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
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'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.