I have a Java code ServiceTest.java that invokes JNI code buildCache() to build the cache. This codes compiled and exceuted correctly on Ubuntu; however, on centos I compiled and exceuted and got this error.
Exception in thread "main" java.lang.UnsatisfiedLinkError: com.test.app.ServiceTest.buildCache()V
at com.test.app.ServiceTest.buildCache(Native Method)
at com.test.app.ServiceTest.<init>(ServiceTest.java:15)
at com.test.app.ServiceTest.main(ServiceTest.java:47)
Looks like, it was able to load the so file, but it is not able to invoke the method.
ServiceTest.java
public class ServiceTest{
static {
System.loadLibrary("cacheService");
}
public ServiceTest() {
buildCache();
}
private native void buildCache();
}
libcacheService.so is placed in /usr/local/lib64. I have gcc 9 and jdk 1.8
and have set LD_LIBRARY_PATH=/usr/local/ and export $LD_LIBRARY_PATH.
Any idea on how to debug?
******Adding below simple testcode******
1.
public class Test{
static {
System.loadLibrary("greet");
}
Test(){
}
public static void main(String...s){
Test t = new Test();
System.out.println(t.greetings());
}
private native String greetings();
}
javah Test -> Test.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h> /* Header for class Test */
#ifndef _Included_Test
#define _Included_Test
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: Test
* Method: greetings
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_Test_greetings
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
Greetings.cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <jni.h>
#include "Test.h"
using namespace std;
class Test{
JNIEXPORT jstring JNICALL Java_Test_greetings(JNIEnv *env, jobject thisObject){
jstring result = env->NewStringUTF("hi from JNI");
return result;
}
};
g++ --std=c++17 -g -I$JAVA_HOME/include -I$JAVA_HOME/include/linux -fPIC -shared -o greetings.so Greetings.cpp
$JAVA_HOME/bin/java -Djava.library.path=/lib64 Test
again same issue
Exception in thread "main" java.lang.UnsatisfiedLinkError: Test.greetings()Ljava/lang/String;
at Test.greetings(Native Method)
at Test.main(Test.java:13)
With same GCC 9 and jdk 1.8
Your Greetings.cpp should not include class Test:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <jni.h>
#include "Test.h"
using namespace std;
JNIEXPORT jstring JNICALL Java_Test_greetings(JNIEnv *env, jobject thisObject){
jstring result = env->NewStringUTF("hi from JNI");
return result;
}
Then, the Java statement System.loadLibrary("greet"); expects to find a libgreet.so in its java.library.path, so compile it as follows:
g++ --std=c++17 -g -I$JAVA_HOME/include -I$JAVA_HOME/include/linux -fPIC -shared -o libgreet.so Greetings.cpp
Next, verify that the symbol is named exactly like the function, ie nm libgreet.so | grep Java_Test_greetings should produce output like the following:
0000000000000f20 T _Java_Test_greetings
Finally, run your Java program with the correct java.library.path.
The problem is that native library does not define a method that matches the signature of the Java native method; i.e. method name or signature in the JNI code is wrong ... or maybe it isn't a JNI native library at all.
Related
I am learning graalvm, and I was wondering if it is possible that I can use JNI to call a java native-built shared library?
Let's say there are two Java source codes, the first would be compiled as a shared library ahead of time with graalvm, and the second one would be run on JVM, which would load the shared library generated by the first source code in runtime. I find this difficult in practice since the header files generated by JNI and graalvm native-build are different in their signature.
Is there any good way I can do this?
The two source codes and their generated header files are as follows:
LibEnvMap.java:
import org.graalvm.nativeimage.IsolateThread;
import org.graalvm.nativeimage.c.function.CEntryPoint;
public class LibEnvMap {
//NOTE: this class has no main() method
#CEntryPoint(name = "Java_Main_HelloWorld")
private static int HelloWorld(IsolateThread thread, Object object){
System.out.println("Hello Native World!");
return 0;
}
}
#ifndef __LIBENVMAP_H
#define __LIBENVMAP_H
#include <graal_isolate.h>
#if defined(__cplusplus)
extern "C" {
#endif
int filter_env(graal_isolatethread_t*, char*);
#if defined(__cplusplus)
}
#endif
#endif
Main.java:
public class Main {
static {
System.load("/Users/nealshinoda/Repos/HelloWorld/out/production/HelloWorld/LibEnvMap.dylib");
}
private native int HelloWorld();
public static void main(String[] args) {
System.out.println("Hello world!");
new Main().HelloWorld();
}
}
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Main */
#ifndef _Included_Main
#define _Included_Main
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: Main
* Method: HelloWorld
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_Main_HelloWorld
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
As you can see from the two header files: their signatures are incompatible and it is difficult to hack the type of the parameter since they are all machine-generated.
One potential solution that comes to my mind is that I could use another C/C++ code as a "middle layer", and then the call stack could be JNI ==> C/C++ ==> graalvm native library. However, this solution seems too complicated, and is there any better practice?
I want to use C++ method inside my Java code. So I decided to use JNI. But the link seams to not work properly, du to my error at the execution No implementation found for void com.me.Native.helloWorld() (tried Java_com_me_Native_helloWorld and Java_com_me_Native_helloWorld__)
Native.java (called elsewhere as Native.helloWorld()):
package com.me;
public class Native{
static {
System.loadLibrary("detection_based_tracker");
}
public static native void helloWorld();
}
Android.mk :
...
LOCAL_SRC_FILES += com_me_Native.cpp
LOCAL_C_INCLUDES += $(LOCAL_PATH)
LOCAL_MODULE := detection_based_tracker
include $(BUILD_SHARED_LIBRARY)
com_me_Native.h (generated with javah command):
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_me_Native */
#ifndef _Included_com_me_Native
#define _Included_com_me_Native
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_me_Native
* Method: helloWorld
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_me_Native_helloWorld
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif
com_me_Native.cpp :
#include <com_me_Native.h>
#include <iostream>
#include <android/log.h>
#define LOG_TAG "HelloWorld"
#define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
#ifdef __cplusplus
extern "C" {
#endif
using namespace std;
/*
* Class: com_me_Native
* Method: helloWorld
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_me_Native_helloWorld
(JNIEnv *, jclass)
{
LOGD("Hello from c++");
}
#ifdef __cplusplus
}
#endif
As you see a use JNIEXPORT and JNICALL on my method. I also use extern "C" for C++ use. My .h was generated by javah. I checked the Android.mk and I didn't forgot to add my .cpp file to LOCAL_SRC_FILES. I statically loaded my library in the Native.java to use my static function.
Now I don't know where the error may come from... Any idea ?!
The include guard should only be in the .h file, not in the .cpp file.
So in your .cpp file, remove these lines:
#ifndef _Included_com_me_Native
#define _Included_com_me_Native
As well as the final #endif.
What happens with your curent code is that _Included_com_me_Native gets defined when you include your header file, so then the #ifndef _Included_com_me_Native in the .cpp file will be false, and none of that code gets compiled.
I am trying to build the following in Netbean using c++. However I am unable to do so. I receive the following error.
gcc -shared -m32 -o dist/libJNIDemoCdl.so
build/Debug/Cygwin-Windows/JNIDemo.o -mno-cygwin -shared gcc: error:
unrecognized command line option ‘-mno-cygwin’
I am only able to build this manually using the following command
gcc -shared -o dist/libJNIDemoCdl.so
build/Debug/Cygwin-Windows/JNIDemo.o -Wall -D_JNI_IMPLEMENTATION_
-Wl,--kill-at
How do I fix this issue with the netbean IDE?
Header file
#include <stdint.h>
#include <jni.h>
/* Header for class jnidemojava_Main */
#ifndef _Included_jnidemojava_Main
#define _Included_jnidemojava_Main
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: jnidemojava_Main
* Method: nativePrint
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_jnidemojava_Main_nativePrint
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
Source
#include <jni.h>
#include <stdio.h>
#include "JNIDemoJava.h"
JNIEXPORT void JNICALL Java_jnidemojava_Main_nativePrint
(JNIEnv *env, jobject obj)
{
printf("\nHello World from C\n");
}
Cygwin and gcc removed the deprecated support of -mno-cygwin flag.
It seems you are using and old version of gcc, update your gcc version to GCC >=4.3
Or follow this guide and remove -mno-cygwin flag manually from your builtin toolchain descriptors
I've looked around on stackoverflow for similar problems, but none of the solutions I've found seem to be working for me. I am on a Linux/Ubuntu machine. I'm just practising JNI but I get this error:
Exception in thread "main" java.lang.UnsatisfiedLinkError: nativetest.sayHello(Ljava/lang/String;)Ljava/lang/String;
at nativetest.sayHello(Native Method)
at nativetest.main(nativetest.java:8)
I have provided my .c, .h, and .java file.
.java file:
public class nativetest
{
System.loadLibrary("nativetest");
public native String sayHello(String s);
public static void main(String[] argv)
{
String retval = null;
nativetest nt = new nativetest();
retval = nt.sayHello("Hi");
System.out.println("Invocation returned " + retval);
}
}
.c file:
#include "nativetest.h"
#include <stdio.h>
#include <jni.h>
#include <stdlib.h>
JNIEXPORT jstring JNICALL Java_nativetest_sayHello(JNIEnv *env, jobject thisobject, jstring js)
{
return js;
}
.h file:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class nativetest */
#ifndef _Included_nativetest
#define _Included_nativetest
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: nativetest
* Method: sayHello
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_nativetest_sayHello
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
I used these commands to generate the .h file, compile/generate the .so file, and run:
javac nativetest.java
javah -jni nativetest
gcc --shared -o libnativetest.so -I/usr/lib/jvm/java-7-openjdk-amd64/include -I/usr/lib/jvm/java-7-openjdk-amd64/include/linux nativetest.c
java -Djava.library.path=. nativetest
I am currently in the directory with the libnativetest.so file and all my .c, .java, and .h files.
Any help would be appreciated.
Okay, turns out the call to
System.loadLibrary("nativetest");
should be static:
static{
System.loadLibrary("nativetest");
}
I was also making mistakes on recompiling using javac. I am quite new to Linux :)
If I try to implement my class on this file I get an error UnsatisfiedLinkError, however if I remove the implementation of the Broker.h Class it goes ok. Why?
Broker.h
#include "XletTable.h"
#ifndef BROKER_H_
#define BROKER_H_
class Broker {
private:
static Broker* brokerSingleton;
static XletTable *table;
// Private constructor for singleton
Broker(JNIEnv *, XletTable *);
// Get XletTable (Hash Table) that contains the...
static XletTable* getTable();
public:
virtual ~Broker();
static Broker* getInstance(JNIEnv *);
jobject callMethod(JNIEnv *, jclass, jstring, jobject, jbyteArray);
};
#endif /* BROKER_H_ */
BrokerJNI.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Broker */
#ifndef _Included_Broker
#define _Included_Broker
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: Broker
* Method: callMethod
* Signature: (Ljava/lang/String;Ljava/lang/reflect/Method;[B)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL Java_Broker_callMethod
(JNIEnv *, jclass, jstring, jobject, jbyteArray);
#ifdef __cplusplus
}
#endif
#endif
Probably your library miss reference to some symbol, or another library. Try make some main.cpp with empty main() function, and link it with your library - g++ main.cpp -o main -lInterAppCC. If you miss something, the linker will give you a detailed error message.
PS. Since your header file already wraps function prototype with extern "C", you don't required to do the same when writing implementation.
You need to use extern "C" around the JNIEXPORT stuff, to avoid c++ name mangling of the JNI functions.
C++ name mangling changes function names (in the obj-files) to include the types of parameters, virtual-ness, etc, to be able to link different overloaded functions with the same name.
So, wrap your JNIEXPORT with extern "C" { ... } (look at the JNI header) and make sure your c++-code isn't wrapped in the same.