I had a previously working JNI lib,
for some reason (I am not aware of any curcial changes) I can't use it anymore, as I get the following error:
Exception in thread "main" java.lang.UnsatisfiedLinkError: de.ifi.lmu.sommerd.ftsserver.jni.IndexJNI.loadIndex(Ljava/lang/String;)V
at de.ifi.lmu.sommerd.ftsserver.jni.IndexJNI.loadIndex(Native Method)
So here is my test setup:
package de.ifi.lmu.sommerd.ftsserver.jni;
public class IndexJNI {
static {
System.load("/home/XXX/workspace/IndexJNI/resources/newLib.so");
System.out.println("Loading library worked");
IndexJNI.loadIndex("");
}
public static native void loadIndex(String indexName);
}
And here the relevant parts of the .h and .c files:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class de_ifi_lmu_sommerd_ftsserver_jni_IndexJNI */
#ifndef _Included_de_ifi_lmu_sommerd_ftsserver_jni_IndexJNI
#define _Included_de_ifi_lmu_sommerd_ftsserver_jni_IndexJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: de_ifi_lmu_sommerd_ftsserver_jni_IndexJNI
* Method: loadIndex
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_de_ifi_lmu_sommerd_ftsserver_jni_IndexJNI_loadIndex
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
The .c file
JNIEXPORT void JNICALL Java_de_ifi_lmu_sommerd_ftsserver_jni_IndexJNI_loadIndex
(JNIEnv * env, jclass jc, jstring indexName) {
...
}
And this is my makefile:
test_gcc_lib: run_queries.o
g++ -shared -fPIC run_queries.o SSA.a -o newLib.so
So the problem was that I had changed the call to static,
so now the signature was not correct anymore.
I just had to change jobject to jclass.
Related
I have those two super basic methods in Java like so:
package com.lisek;
class HelloWorldJNI {
static {
System.loadLibrary("native");
}
public static void main(String[] args) {
HelloWorldJNI helloWorldJNI = new HelloWorldJNI();
helloWorldJNI.sayHello();
helloWorldJNI.sayHello2();
}
// Declare a native method sayHello() that receives no arguments and returns void
private native void sayHello();
private native void sayHello2();
}
I am generating JNI files like so javac -h . HelloWorldJNI.java which results in such com_lisek_HelloWorldJNI.h file:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_lisek_HelloWorldJNI */
#ifndef _Included_com_lisek_HelloWorldJNI
#define _Included_com_lisek_HelloWorldJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_lisek_HelloWorldJNI
* Method: sayHello
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_lisek_HelloWorldJNI_sayHello
(JNIEnv *, jobject);
/*
* Class: com_lisek_HelloWorldJNI
* Method: sayHello2
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_lisek_HelloWorldJNI_sayHello2
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
And I am doing implementation of those in the same exact manner in the com_lisek_HelloWorldJNI.cpp file like so:
#include "com_lisek_HelloWorldJNI.h"
#include <stdio.h>
#include <iostream>
#include <jni.h>
JNIEXPORT void JNICALL Java_com_lisek_HelloWorldJNI_sayHello (JNIEnv* env, jobject thisObject) {
std::cout << "Hello from C++ !!" << std::endl;
}
JNIEXPORT void JNICALL Java_com_lisek_HelloWorldJNI_sayHello2 (JNIEnv* env, jobject thisObject) {
std::cout << "Hello from C++ !!" << std::endl;
}
I am compiling the code with g++ -c -I"C:\Program Files\Java\jdk1.8.0_181\include" -I"C:\Program Files\Java\jdk1.8.0_181\include\win32" com_lisek_HelloWorldJNI.cpp -o com_lisek_HelloWorldJNI.o and generating .dll with g++ -shared -o native.dll com_lisek_HelloWorldJNI.o -Wl,--add-stdcall-alias. All looks very well to me and I can not spot any flaw of this code but nevertheless in Java while running the code I am getting exception only on the second method:
Hello from C++ !!
Exception in thread "main" java.lang.UnsatisfiedLinkError: com.lisek.HelloWorldJNI.sayHello2()V
at com.lisek.HelloWorldJNI.sayHello2(Native Method)
at com.lisek.HelloWorldJNI.main(HelloWorldJNI.java:12)
It means to me that the .dll is loaded properly as if I do two calls of the sayHello() I will get the Hello twice. Seriously, what is going on?
Remember to compile and run your code with -Djava.library.path flag set like so:
java -cp . -Djava.library.path="D:\\Workspace\\JNI\\Callback\\src\\com\\lisek" com.lisek.HelloWorldJNI
But it still dazzles me that it was able to run the first sayHello() but was not able to run sayHello2(). Can't think of any reasonable idea why this happens.
If you are doing example from https://www.baeldung.com/jni then make sure that you add these includes into cpp file
#include <jni.h>
#include <iostream>
#include "Main.h"
I am trying to link a Java swing frontend to the c++ backend using JNI. I am able to generate a class file and header file for that class. I could also write the .cpp file and also generate a library file. But when I try to run my application I get an error undefined symbol: __gxx_personality_v0.
I went through many examples on stackoverflow but none of it worked in my case. I made sure I have libgcc, libgcj and libstd++. Also added pragma GCC java_exceptions on the top of the JNI header file.
But none of these worked.
Java file - InitJNI.java
public class InitJNI {
public native String callMethod(String str);
public static void init(String s){
System.out.println("-------inside InitJNI.java--------");
System.loadLibrary("eg1");
System.out.println("Library loaded in InitJNI--------");
InitJNI init1 = new InitJNI();
init1.callMethod(s);
}
}
JNI header file - InitJNI.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class org_openscience_jchempaint_application_InitJNI */
#ifndef _Included_InitJNI
#define _Included_InitJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: InitJNI
* Method: callMethod
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_InitJNI_callMethod
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
Cpp file - InitJNI.cpp
#include "InitJNI.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#pragma GCC java_exceptions
JNIEXPORT jstring JNICALL Java_InitJNI_callMethod
(JNIEnv * env, jobject, jstring string){
char buf[128];
const char *str = env->GetStringUTFChars(string, NULL);
printf("%s", str);
env->ReleaseStringUTFChars(string, str);
}
I am trying to create a shared lib using command:
g++ -shared -fpic -o libeg1.so -I/usr/java/include -I/usr/java/include/linux InitJNI.cpp
Which generates libeg1.so file. But my application does not run. It gives me
undefined symbol: __gxx_personality_v0
Any help with this is appreciated.
Thank you!
I know this is an often-seen problem and after search on the web, problem still not solved. -Djava.library.path is already added to the command.
Here is my java file
public class TestJni
{
public native void print(A a);
static{
System.loadLibrary("MyJni");
}
public static void main(String []args){
new TestJni().print(new A());
}
}
class A{
void show(){
System.out.println("In a show function");
}
}
I am trying to create an instance 'a' which is A class.
Pass it to native method print and let native method to invoke show() method.
Compile .java into .class
javac TestJni.java
Now I have three files: A.class TestJni.class TestJni.java
Use
javah -classpath . TestJni
to get .h file which is like:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class TestJni */
#ifndef _Included_TestJni
#define _Included_TestJni
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: TestJni
* Method: print
* Signature: (LA;)V
*/
JNIEXPORT void JNICALL Java_TestJni_print
(JNIEnv *, jobject, jobject);
#ifdef __cplusplus
}
#endif
#endif
Rename it to MyJni.h by
mv TestJni.h MyJni.h
Create MyJni.c by
vim MyJni.c
the content of MyJni.c is:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include "MyJni.h"
/* Header for class TestJni */
#ifndef _Included_TestJni
#define _Included_TestJni
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: TestJni
* Method: print
* Signature: (LA;)V
*/
JNIEXPORT void JNICALL Java_TestJni_print
(JNIEnv *env, jobject obj, jobject o){
jclass cls = (*env)->GetObjectClass(env, o);
jmethodID mid = (*env)->GetMethodID(env, cls, "show", "()V");
(*env)->CallVoidMethod(env, o, mid);
}
#ifdef __cplusplus
}
#endif
#endif
Build .so file by
gcc -fPIC -shared -o libMyJni.so MyJni.c -I. -I$JAVA_HOME/include -I$JAVA_HOME/include/linux
And now I have 5 files [A.class libMyJni.so MyJni.c MyJni.h TestJni.class TestJni.java] in my directory: /home/grid/code/tmp
Run java program:
java -cp . -Djava.library.path=. TestJni
Now I get the Error which is
Exception in thread "main" java.lang.UnsatisfiedLinkError: TestJni.print(LA;)V
at TestJni.print(Native Method)
at TestJni.main(TestJni.java:10)
I am coding under Ubuntu 14.10 and gcc program is the default one.
I really not able to figure out what is wrong.
Please help me to correct the program.
Thank you very much in advance!!!
Remove the
#ifndef _Included_TestJni
#define _Included_TestJni
...
#endif
from the source file. You've already #defined the include guard in the header, so this effectively comments out the code.
Remove the #include "MyJni.h" or
#ifndef _Included_TestJni
#define _Included_TestJni
//..
#endif
Exception in thread "main" java.lang.UnsatisfiedLinkError: TestJni.print(LA;)V this usually happen when JNI can't find the symbol definition in your library. You can use nm to analyze the JNI function you call to see if it is defined and exported in your library.
$ nm libMyJni.so
00000000000005ac T Java_TestJni_print
0000000000200e40 a _DYNAMIC
0000000000200fe8 a _GLOBAL_OFFSET_TABLE_
w _Jv_RegisterClasses
0000000000200e20 d __CTOR_END__
0000000000200e18 d __CTOR_LIST__
You can try using System.load. iirc System.loadLibrary loads .so/.dll files from the default location. System.load should work with an absolute path to the .so/.dll file.
AFTER EDIT
I get the this error
1> Creating library D:\Jni tester\Debug\Jni tester.lib and object D:\Jni tester\Debug\Jni tester.exp
1>MSVCRTD.lib(crtexe.obj) : error LNK2019: unresolved external symbol _main referenced in function ___tmainCRTStartup
1>D:\Jni tester\Debug\Jni tester.exe : fatal error LNK1120: 1 unresolved externals
when i run the following code :
#include <jni.h>
#include <stdio.h>
#include "HelloWorld.h"
JNIEXPORT void JNICALL
Java_HelloWorld_print( JNIEnv *env , jobject obj) {
printf("Hello World!\n");
return;
}
This is the header file HelloWorld.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorld */
#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: HelloWorld
* Method: print
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_HelloWorld_print
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
Why do i get this error ?
JNIENv should be JNIEnv (lower case n) as shown by this Getting Started guide.
Edit: As seems from your error, you should define the project as a DLL, in your case it looks for the main method since it tries to create an executable.
This is my first attempt at JNI. My ultimate goal is to get all tasks currently running on a machine, but need to get even a simple example running. I keep getting this error when I try to execute my main program. I have supplied my simple Java main program, the header file generated, and the error.
I do not know what this DLL could be dependent on. It was initially referencing a DLL I tracked down and placed into system32 (msvcr90.dll).
Here is the command I used to compile the C code as well which produced the DLL, OBJ, LIB, EXP and manifest files.
cl -I"C:\Program Files\Java\jdk1.6.0\include" -I"C:\Program Files\Java\jdk1.6.0\include\win32" -MD -LD HelloWorld.c -FeHelloWorld.dll
class HelloWorld {
private native void print();
public static void main(String[] args) {
new HelloWorld().print();
}
static {
System.load("C:\\temp\\HelloWorld.dll");
}
}
#include <jni.h>
#include <stdio.h>
#include "HelloWorld.h"
JNIEXPORT void JNICALL
Java_HelloWorld_print(JNIEnv *env, jobject obj)
{
printf("Hello World!\n");
return;
}
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorld */
#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: HelloWorld
* Method: print
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_HelloWorld_print
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
java.lang.UnsatisfiedLinkError: C:\temp\HelloWorld.dll: A dynamic link library (DLL) initialization routine failed
at java.lang.ClassLoader$NativeLibrary.load(Native Method)
at java.lang.ClassLoader.loadLibrary0(Unknown Source)
at java.lang.ClassLoader.loadLibrary(Unknown Source)
at java.lang.Runtime.load0(Unknown Source)
at java.lang.System.load(Unknown Source)
at HelloWorld.<clinit>(HelloWorld.java:7)
Exception in thread "main"
The Unsatisfied Link Error can mean many things went wrong. I would use
System.loadLibrary("HelloWorld");
Instead of
System.load();
As TwentyMiles suggested.
Also, when invoking your program you need to (assuming your DLL is on the same directory as your class files:
java -Djava.library.path=. HelloWorld
Here's a simple demo I made that calls a Win32 API function (MessageBox)
Java class
class CallApi{
private native String showMessageBox(String msg);
private native double getRandomDouble();
static{
try{
System.loadLibrary("CallApi");
System.out.println("Loaded CallApi");
}catch(UnsatisfiedLinkError e){
//nothing to do
System.out.println("Couldn't load CallApi");
System.out.println(e.getMessage());
}
}
public static void main(String args[]){
CallApi api = new CallApi();
double randomNumber = api.getRandomDouble();
String retval = api.showMessageBox("Hello from Java!\n"+
"The native random number: "+randomNumber);
System.out.println("The native string: "+retval);
}
}
Generated header file
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class CallApi */
#ifndef _Included_CallApi
#define _Included_CallApi
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: CallApi
* Method: showMessageBox
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_CallApi_showMessageBox
(JNIEnv *, jobject, jstring);
/*
* Class: CallApi
* Method: getRandomDouble
* Signature: ()D
*/
JNIEXPORT jdouble JNICALL Java_CallApi_getRandomDouble
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
The C DLL code
#include "CallApi.h"
#include <windows.h>
#include <stdlib.h>
#include <time.h>
#pragma comment(lib,"user32.lib")
JNIEXPORT jstring JNICALL Java_CallApi_showMessageBox
(JNIEnv *env, jobject thisObject, jstring js)
{
//first convert jstring to const char for use in MessageBox
const jbyte* argvv = (*env)->GetStringUTFChars(env, js, NULL);
char* argv =(char *) argvv;
//Call MessageBoxA
MessageBox(NULL, argv, "Called from Java!", MB_ICONEXCLAMATION | MB_OK);
return js;
}
JNIEXPORT jdouble JNICALL Java_CallApi_getRandomDouble
(JNIEnv *env, jobject thisObject)
{
double num1;
srand((unsigned)(time(0)));
num1 = ((double)rand()/(double)RAND_MAX);
return num1;
}
Compile instructions
I compile with the Visual C++ express 2008 cl, removing the -ML flag since it causes an exception when the Java code tries to call the native code:
cl /I"c:\Program Files\Java\jdk1.6.0_10\include" /I"c:\Program Files\Java\jdk1.6.0_10\include\win32" -LD CallApi.c -FeCallApi.dll
Then, to run the code:
java -Djava.library.path=. CallApi
I'm not claiming to understand the situation enough to explain it, however some users reported the error when using the "-MD" compiler flag.
For more information see Java Native Interface (JNI) - Impossible to use VS2005 with Java? which discusses this problem and offers possible work-arounds and think techie blog for alternatives.
I believe that you should be using
System.loadLibrary("HelloWorld");
instead of System.load. LoadLibrary will check your system path (not the Java library path) so make sure that HelloWorld.dll is in a directory where it can found. Also note that it does not require the full path, and you don't need to add the dll extension to the end.
I just removed -MD option and compiled it worked like charm
cl -I"C:\Program Files\Java\jdk1.6.0_21\include" -I"C:\Program Files\Java\jdk1.6.0_21\include\win32" -LD HelloWorld.c -FeHelloWorld.dll
If you change the location (package) of your native function declaration from the java side without updating the h file, and the signature of the method in the c++ side, It won't resolve to the method and will throw unsatisfied..
package x;
public class A {
private native void print();
...
}
moved to:
package x.y;
public class A {
private native void print();
...
}
This will require regeneration of the H file (to something like Java_x_y_A_print ).
Note you can change those signatures manually but I won't recommend