I am trying to call non static method from java to C++ using JNI
My Java Code is here:
public class hellojava
{
public static void main(String args[])
{
System.out.println("Hello World!");
System.out.println("This is the main function from the HelloWorld java class.");
}
public void message()
{
System.out.println("call from object");
}
}
And my C++ code is here:
#include <stdio.h>
#include <jni.h>
JNIEnv* create_vm(JavaVM ** jvm) {
JNIEnv *env;
JavaVMInitArgs vm_args;
JavaVMOption options;
options.optionString = "-Djava.class.path=/home/../nonstaticJavaMethods/";
//Path to the java source code
vm_args.version = JNI_VERSION_1_6; //JDK version. This indicates 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[])
{
JNIEnv *env;
JavaVM * jvm;
env = create_vm(&jvm);
if (env == NULL)
return 1;
//jclass clsH=NULL;
jmethodID midMain = NULL;
jstring square;
jclass clsH = env->FindClass("helloWorld");
jmethodID constructor = env->GetMethodID(clsH, "<init>", "void(V)");
jobject object = env->NewObject(clsH, constructor);
//Obtaining Method IDs
if (clsH != NULL)
{ midMain = env->GetMethodID(clsH, "message", "void(V)");
env->CallVoidMethod(clsH, midMain, object,NULL);
}
else
{
printf("\nUnable to find the requested class\n");
}
//Release resources.
int n = jvm->DestroyJavaVM();
return 0;
}
My code compiles but it is giving me runtime error. Following is the error
A fatal error has been detected by the Java Runtime Environment:
SIGSEGV (0xb) at pc=0x00007fdf3f5126bb, pid=11302, tid=140596827092800
JRE version: OpenJDK Runtime Environment (7.0_55-b14) (build 1.7.0_55-b14)
Java VM: OpenJDK 64-Bit Server VM (24.51-b03 mixed mode linux-amd64 compressed oops)
Problematic frame:
V [libjvm.so+0x5c46bb] alloc_object(_jclass*, Thread*)+0x1b
Failed to write core dump. Core dumps have been disabled. To enable core dumping,
try
"ulimit -c unlimited" before starting Java again
An error report file with more information is saved as
/home/../nonstaticJavaMethods/hs_err_pid11302.log
Aborted!
Additionally to immibis' answer, i also think the call
jclass clsH = env->FindClass("helloWorld");
isn't returning anything, as your class is called
public class hellojava
So your application is probably seg-faulting in GetMethodID() or NewObject()
void(V) is not a valid method descriptor.
Because there is no method called <init> with the descriptor void(V) (which there can't be, because it's invalid), GetMethodID returns 0. Then you try to create a new object using this invalid method ID.
The method descriptor for a method that takes no arguments and returns void (which a constructor is) is ()V.
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 am trying to call the following java method from C++
final String[] games = GameLoader.listGames();
where GameLoader was imported with import player.GameLoader;. The GameLoader class exists within a jar file. I am trying to use JNI to load the jar file from C++ and then call the above method. The following is my C++ code which I have tried to extend from this SO post.
#include <iostream>
#include <string.h>
#include <jni.h>
#include <stdlib.h>
using namespace std;
#define PATH_SEPARATOR ';'
#define USER_CLASSPATH "Ludii-0.3.0.jar"
#define GAME_LOADER "player/GameLoader"
JNIEnv *env;
JavaVM *jvm;
jint res;
void initJVM() {
#ifdef JNI_VERSION_1_2
JavaVMInitArgs vm_args;
JavaVMOption options[1];
options[0].optionString =
"-Djava.class.path=" USER_CLASSPATH;
vm_args.version = 0x00010002;
vm_args.options = options;
vm_args.nOptions = 1;
vm_args.ignoreUnrecognized = JNI_TRUE;
/* Create the Java VM */
res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
#else
JDK1_1InitArgs vm_args;
char classpath[1024];
vm_args.version = 0x00010001;
JNI_GetDefaultJavaVMInitArgs(&vm_args);
/* Append USER_CLASSPATH to the default system class path */
sprintf(classpath, "%s%c%s",
vm_args.classpath, PATH_SEPARATOR, USER_CLASSPATH);
vm_args.classpath = classpath;
/* Create the Java VM */
res = JNI_CreateJavaVM(&jvm, &env, &vm_args);
#endif /* JNI_VERSION_1_2 */
}
void closeJVM() {
jvm->DestroyJavaVM();
}
int main() {
initJVM();
jclass gameLoader = env->FindClass(GAME_LOADER);
//final String[] games = GameLoader.listGames();
jmethodID mid = env->GetMethodID(gameLoader,"listGames","()[Ljava/lang/String;");
jobjectArray stringArray = (jobjectArray) env->CallObjectMethod(gameLoader,mid);
closeJVM();
}
I am able to successfully create the JVM and the code compiles however at runtime I get the error
# A fatal error has been detected by the Java Runtime Environment:
#
# SIGSEGV (0xb) at pc=0x00007f0e835619d7, pid=3504, tid=0x00007f0e84028740
#
# JRE version: OpenJDK Runtime Environment (8.0_222-b10) (build 1.8.0_222-8u222-b10-1ubuntu1~16.04.1-b10)
# Java VM: OpenJDK 64-Bit Server VM (25.222-b10 mixed mode linux-amd64 compressed oops)
# Problematic frame:
# V [libjvm.so+0x6849d7]
#
# Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
#
# An error report file with more information is saved as:
# /home/alex/jni_examples/hs_err_pid3504.log
#
# If you would like to submit a bug report, please visit:
# http://bugreport.java.com/bugreport/crash.jsp
#
Aborted (core dumped)
I think that the error is related to the CallObjectMethod call but I'm not sure.
Also I am using Java 8
EDIT:
It looks like mid evaluated to 0 which means that the method was not found. I guess this is the problem however I'm not sure why it wasn't found. This was checked with
int main() {
initJVM();
jclass gameLoader = env->FindClass("player/GameLoader");
//final String[] games = GameLoader.listGames();
if(gameLoader == NULL)
{
cout << "Could not load class!" << endl;
return 1;
}
jmethodID mid = env->GetMethodID(gameLoader,"listGames","()[Ljava/lang/String;");
if(mid == NULL)
{
cout << "Could not load method!" << endl;
return 1;
}
jobjectArray stringArray = (jobjectArray) env->CallObjectMethod(gameLoader,mid);
if(stringArray == NULL)
{
cout << "Could not load object!" << endl;
return 1;
}
closeJVM();
}
ANOTHER EDIT:
So it looks like the problem was that listGames() should be found with GetStaticMethodID rather than GetMethodID and it should be called with CallStaticObjectMethod rather than CallObjectMethod. Thank you.
I have in my Java class a string variable:
public class myclass {
protected final String file;
myclass(String f) {
file = f;
}
public native void processFiles();
public static void main(String[] args) {
myclass mc = new myclass(args[0]);
mc.processFiles();
}
}
In C++, I have:
JNIEXPORT void JNICALL Java_myclass_processFiles(JNIEnv *env, jobject obj) {
jclass baseClass = env->GetObjectClass(obj);
jfieldID fid = env->GetFieldID(baseClass, "file", "Ljava/lang/String;");
jchar filename = env->GetCharField(baseClass, fid);
jstring fileFieldString = env->NewString(&filename, sizeof(filename));
const char *nativeFileString = env->GetStringUTFChars(fileFieldString, NULL);
printf("JNI: File path: %s\n", nativeFileString);
env->ReleaseStringUTFChars(fileFieldString, nativeFileString);
}
My output is:
JNI: File path: ??2
What am I doing wrong that isn't converting the Java string to the char* string properly? I am providing the path ~/Desktop/myfile as the sole argument, so there is a value in args[0]. My thought was that sizeof(filename) wasn't right, but there's no other option from what I can tell.
I did try this: JNI. How to get jstring from jobject and convert it to char* but when I typecast the result from GetObjectField() to jstring, I get an error:
# A fatal error has been detected by the Java Runtime Environment:
#
# SIGSEGV (0xb) at pc=0x00000001043111e8, pid=6191, tid=3591
#
# JRE version: Java(TM) SE Runtime Environment (8.0_45-b14) (build 1.8.0_45-b14)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.45-b02 mixed mode bsd-amd64 compressed oops)
# Problematic frame:
# V [libjvm.dylib+0x3111e8] jni_GetStringUTFChars+0x66
#
# Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
Also, this is just JNI and Java 8 on OSX, nothing related to Android.
Thank you.
Update:
I was able to get a friend to look at it, and got it working with:
jfieldID fid = env->GetFieldID(baseClass, "file", "Ljava/lang/String;");
jstring jstr = (jstring) env->GetObjectField(thiz, fid);
const char *nativeFileString = env->GetStringUTFChars(jstr, NULL);
printf("JNI: File path: %s\n", nativeFileString);
You are doing jchar filename = env->GetCharField(baseClass, fid);
But fid is a field of type Ljava/lang/String;, not a char. So you should get that String using env->GetObjectField() and then follow what that link says.
You can also debug this better by adding env->ExceptionDescribe() after every line to see if an Exception is being thrown after your calls to env (that would be just to debug, in real production code you should be checking for exceptions after every env call and do something if something goes wrong).
By the way, maybe your code is just an example, but if that's your real code, it would be a lot easier to declare the native method as static and just pass the string as a parameter.
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.