I'm trying to work build my very first JNI application, following this tutorial: https://www3.ntu.edu.sg/home/ehchua/programming/java/JavaNativeInterface.html
Problem Summary: While running my application, I get an java.lang.UnsatisfiedLinkError error.
First I wrote the Class HelloJNI.java:
public class HelloJNI {
static {
System.loadLibrary("hello"); // Load native library at runtime
// hello.dll (Windows) or libhello.so (Unixes)
}
// Declare a native method sayHello() that receives nothing and returns void
private native void sayHello();
// Test Driver
public static void main(String[] args) {
new HelloJNI().sayHello(); // invoke the native method
}
}
This class I compiled with:
javac HelloJNI.java
Next I ran javah HelloJNI
This produced the following file HelloJNI.h:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloJNI */
#ifndef _Included_HelloJNI
#define _Included_HelloJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: HelloJNI
* Method: sayHello
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_HelloJNI_sayHello
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
Next I implemented HelloJNI.c:
#include <jni.h>
#include <stdio.h>
#include "HelloJNI.h"
// Implementation of native method sayHello() of HelloJNI class
JNIEXPORT void JNICALL Java_HelloJNI_sayHello(JNIEnv *env, jobject thisObj) {
printf("Hello World!\n");
return;
}
Finally I compiled the c class:
gcc -I"/usr/lib/jvm/java-8-oracle/include" -I"/usr/lib/jvm/java-8-oracle/include/linux" -c -Wall -Werror -fpic HelloJNI.c
gcc -shared -o hello.so HelloJNI.o
This produces the files hello.so and HelloJNI.o. Next I try to run the code:
java -Djava.library.path=. HelloJNI
This produces the error:
Exception in thread "main" java.lang.UnsatisfiedLinkError: no hello in java.library.path
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1864)
at java.lang.Runtime.loadLibrary0(Runtime.java:870)
at java.lang.System.loadLibrary(System.java:1122)
at HelloJNI.(HelloJNI.java:3)
This seems to be the most common JNI error on the internet... My method names seem to be correct. I also ran:
nm hello.so | grep say
This gives me: 00000000000006b0 T Java_HelloJNI_sayHello which seems to be correct, i.e. the compiler didn't add additional characters. I simply ran out of ideas of things I could try. Any suggestions?
My OS: Linux Mint 13, GCC version 4.7.3, java version 1.8.0_60
==========UPDATE===============
When I replace System.loadLibrary("hello"); by System.load("/usr0/home/jkinable/workspace/javaIDEA/jnitest/hello.so"); my HelloWorld example works! However, I don't want to use an absolute path so I'm still looking for a way to use System.loadLibrary("hello"); instead? Any suggestions? I've also tried running on a different linux system, but I get the same issue.
It turns out that the problem is due to some naming convention on unix/linux platforms! When using:
System.loadLibrary("hello");
the file should not be named hello.so! Instead, the name should be libhello.so. On Windows, use hello.dll. I'm surprised that this issue is not mentioned in IBM's JNI tutorial: http://www.ibm.com/developerworks/java/tutorials/j-jni/j-jni.html
I'm not sure what the rationality behind this issue is. Why would you load a library "hello" which should be named libhello.so on your filesystem (instead of hello.so)?
Related
I am trying to use some c++'s libraries in Java (IDE is NetBeans). I have created the shared library, which is called "pylontesting.so". The following code is what it was used to create it:
g++ -fPIC -lbxapi -lbxapi-6.3.0 -lGCBase_gcc_v3_1_Basler_pylon -lGenApi_gcc_v3_1_Basler_pylon -lgxapi -lgxapi-6.3.0 -llog4cpp_gcc_v3_1_Basler_pylon -lLog_gcc_v3_1_Basler_pylon -lMathParser_gcc_v3_1_Basler_pylon -lNodeMapData_gcc_v3_1_Basler_pylon -lpylonbase -lpylonbase-6.3.0 -lpylonc -lpylonc-6.3.0 -lpylon_TL_bcon -lpylon_TL_bcon-6.3.0 -lpylon_TL_camemu -lpylon_TL_camemu-6.3.0 -lpylon_TL_gige -lpylon_TL_gige-6.3.0 -lpylon_TL_gtc -lpylon_TL_gtc-6.3.0 -lpylon_TL_usb -lpylon_TL_usb-6.3.0 -lpylonutility -lpylonutility-6.3.0 -luxapi -luxapi-6.3.0 -lXmlParser_gcc_v3_1_Basler_pylon -L/opt/pylon/lib -I/opt/pylon/include -I/home/fra/NetBeansProjects/TestPylon/src/main/java/Pylon -I/lib/jvm/java-1.8.0-openjdk-amd64/include -I/lib/jvm/java-1.8.0-openjdk-amd64/include/linux -shared -o libpylontesting.so Pylon_NativeTest.cpp
This might look a mess, but basically I'm linking every libraries I need through the command "-l" and tell the linker where the libraries are using "-L" and then using "-I" for the headers into the c++ code. The c++ code that I'm using is the following:
#include <jni.h>
#include <stdio.h>
#include "Pylon_NativeTest.h"
#include <pylon/PylonIncludes.h>
#ifdef PYLON_WIN_BUILD
# include <pylon/PylonGUI.h>
#endif
using namespace Pylon;
JNIEXPORT void JNICALL Java_Pylon_NativeTest_Initialize (JNIEnv *, jobject){
PylonInitialize();
}
Its purpose is to export this simple library function called "PylonInitialize()". Then, I also have created my java file:
package Pylon;
/**
*
* #author
*/
public class NativeTest {
static {
System.loadLibrary("pylontesting");
}
private native void Initialize();
public static void main(String args[]){
new NativeTest().Initialize();
}
}
Which should call the function "PylonInitialize()" from the shared library created and called "pylontesting". But when I run the java main, it throws me the error:
usr/lib/jvm/java-11-openjdk-amd64/bin/java: symbol lookup error: /usr/lib/libpylontesting.so: undefined symbol: _ZN5Pylon15PylonInitializeEv
Command execution failed.
I'm using Maven as a compiler, and working under Ubuntu 21.10. Any ideas?
I'm trying to call a c function from Java.
When loading the library (in Test.java) 2 things happen randomly:
"Load Lib" gets printed, and the jvm just exits without any errors
"Load Lib" gets printed, and the jvm gets stuck in a loop
The weird thing is that 'sometimes' "Lib loaded" gets printed too. Which means the library got loaded...
My question is that how can I fix this? The real problem is that I don't know what I'm doing wrong.
Dll compilation steps:
gcc -fpic -I "C:\Program Files\Java\jdk-15\include" -I "C:\Program Files\Java\jdk-15\include\win32" -c BindLib.c BindLib.h
gcc -fpic -s -shared -o BindLib.dll BindLib.o
System info:
Windows 10 64 bit, version 1909
Java 15
Main file:
package degubi;
public final class Main {
public static void main(String[] args) {
Test.enable();
}
}
Library file:
package degubi;
public class Test {
static {
System.out.println("Load lib");
System.loadLibrary("BindLib");
System.out.println("Lib loaded");
}
public static native void enable();
}
Source file:
#include "windows.h"
#include "BindLib.h"
JNIEXPORT void JNICALL Java_degubi_Test_enable(JNIEnv* env, jclass clazz) {
}
Header file:
#define __int64 long long
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class degubi_Test */
#ifndef _Included_degubi_Test
#define _Included_degubi_Test
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: degubi_Test
* Method: enable
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_degubi_Test_enable(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif
Which shell and C compiler were you using?
The problem might come from the incompatible between C compiler & the shell environment.
For example, if you use Cygwin compiler to compile the DLL, then execute the binary on Windows CMD, the program might not work (dependencies on cygwin.dll).
I ended up creating a project in Visual Studio and building it from there... worked perfectly. Still don't know what caused the issue.
I'm trying to get a small/sample Java application, which invokes native C++ code using JNI calls, running on Linux.
I use Eclipse to build, configure the environment and run the application.
I have divided the "overall" project in 2 separate Eclipse projects: 1 Java project and 1 C++ project, containing the native code.
Eclipse project view
The Java part consists of:
1: a "main" class, from which an "adapter" class is invoked to load the .so library and call the native interface/C++ methods
2: an "adapter" class, containing the native method declarations
public class Java_Main_For_So_Kickoff {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("Hello from Java main!");
Native_Cpp_Adapter adapter = new Native_Cpp_Adapter();
adapter.locSetProperty();
adapter.locLoadLib();
adapter.sayHello();
adapter.test_Kickoff_So_For_Print();
}
}
public class Native_Cpp_Adapter {
public native void test_Kickoff_So_For_Print();
public void locSetProperty() {
"/home/adminuser/workspace_Unit_Test_Java_Cpp/Unit_Test_Cpp/Debug");
String libPathProp = System.getProperty("java.library.path");
System.out.println("lib path set:" + libPathProp);
}
public void locLoadLib() {
System.setProperty("java.library.path",
"//home//adminuser//workspace_Unit_Test_Java_Cpp//Unit_Test_Cpp//Debug//");
String libPathProp = System.getProperty("java.library.path");
String soLibName = "libUnit_Test_Cpp.so";
String soLibWithPath = libPathProp.concat(soLibName);
System.out.println("lib path set for loading:" + libPathProp);
System.load(soLibWithPath);
System.out.println("library loaded");
}
public void sayHello() {
System.out.println("Hello from JNI Adapter (Java part)");
}
}
The C++ part consists of:
1: a *.h file, generated with javah, containing the native method definition
2: a *.cpp file, containing the C++ implementation of the native methods
Both very rudimentary, only meant to sanity-test the JNI environment setup. Added this, omitted that in the original post for this issue.
.h file: Native_Cpp_adapter.h
/*
* Native_Cpp_Adapter.h
*
* Created on: Aug 23, 2017
* Author: adminuser
*/
#ifndef SRC_NATIVE_CPP_ADAPTER_H_
#define SRC_NATIVE_CPP_ADAPTER_H_
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Native_Cpp_Adapter */
#ifndef _Included_Native_Cpp_Adapter
#define _Included_Native_Cpp_Adapter
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT void JNICALL Java_Native_Cpp_Adapter_test_Kickoff_So_For_Print
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
#endif /* SRC_NATIVE_CPP_ADAPTER_H_ */
.cpp file: Native_Cpp_Adapter.cpp
#include "jni.h"
#include <iostream>
using namespace std;
JNIEXPORT void JNICALL Java_Native_Cpp_Adapter_test_Kickoff_So_For_Print
(JNIEnv *, jobject)
{
cout << "Correct kickoff of Native JNI method nr. 1";
return;
}
From the C++ project an .so is built, including jni.h and jni_md.h. for the Java project, the resulting .so is included as external library in the Java build path and added as VM argument "java.library.path" in the run configuration (for the applicable Java project/main class).
First, the .so is built from the C++ project: this results in an .so binary in the C++ project folder within the common workspace.
The the Java build is performed. Thereafter the Java program is run invoking the project's main class.
Result: the .so appears to be loaded, but thereafter the process is aborted with an (very persistent) "UnsatisfiedLinkError".
For what I understand, this error is thrown if the Java Virtual Machine cannot find an appropriate native-language definition of a method declared native.
This sounds like the native method definition (C++ side) is not compliant with the native method declaration on the Java-side.
But the C++ definitions are as far as I see/know entirely compliant with the native method declaration in Java, and with the applicable native method naming conventions.
For me, it appears that all building/configuration options are exhausted - does anyone have a suggestion what might be the cause, and to solve this?
It looks like name of your native method is not valid. Make sure to properly escape _ . _ is used to separate packages in native methods. You need to follow naming convention:
https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#resolving_native_method_names
If you have "_" in method name, you need to escape it with "_1" in native code.
Example. For method in Java:
public static native void display_Message();
you need:
JNIEXPORT void JNICALL Java_recipeNo001_HelloWorld_display_1Message
(JNIEnv *, jclass);
Note "_1" that is between "display" and "Message".
Source taken (and slightly modified) from here: http://jnicookbook.owsiak.org/recipe-No-001/
Update
Where should you pay attention:
If everything else fails, make sure to set LD_LIBRARY_PATH before running Eclipse. This will give you some insight whether Eclipse plays nasty or something else is broken
Make sure to pass java.library.path using "-D" while starting your code. You can set it in Debug configuration as JVM arguments
Sometimes, it might be tricky, and it may turn out that your lib doesn't contain symbol at all. You can check it using nm
nm libSomeLibFile.so
You can also set native code location inside project's Properties configuration in Eclipse
Update. I have slightly simplified your code to make it easier to check what is wrong. I suggest you to remove "_" from class names as well as they are again mixed up in native code.
Take a look here:
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("Hello from Java main!");
NativeCppAdapter adapter = new NativeCppAdapter();
adapter.locLoadLib();
adapter.testKickoffSoForPrint();
}
}
Native Adapter class
public class NativeCppAdapter {
public native void testKickoffSoForPrint();
public void locLoadLib() {
String soLibName = "/tmp/libNativeCppAdapter.so";
System.load(soLibName);
}
}
C++ code (pay attention to C export - it has impact on function name !)
#include "jni.h"
#include <iostream>
using namespace std;
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: NativeCppAdapter
* Method: testKickoffSoForPrint
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_NativeCppAdapter_testKickoffSoForPrint
(JNIEnv *, jobject) {
cout << "Correct kickoff of Native JNI method nr. 1";
return;
}
#ifdef __cplusplus
}
#endif
Compile code and Java
> javac *.java
> c++ -g -shared -fpic -I${JAVA_HOME}/include \
-I${JAVA_HOME}/include/darwin \
NativeCppAdapter.cpp \
-o /tmp/libNativeCppAdapter.so
> java -cp . Main
Hello from Java main!
Correct kickoff of Native JNI method nr. 1
Just few more remarks
If you compile code without
#ifdef __cplusplus
extern "C" {
#endif
...
#ifdef __cplusplus
}
#endif
your symbols inside library will be incorrect (not what JVM expects).
Another thing is. If you use "loadLibrary" you have to make sure that file name is in the form: libSomeName.so and you load file via System.loadLibrary("SomeName"); - and you have to make sure that either java.library.path or LD_LIBRARY_PATH point to the file. If you use System.load, on the other hand, you don't have to make any assumptions regarding name. Just make sure you provide full path to file. For example: /tmp/someFileWithMyLib
Firstly, my example has the following directory structure:
Sample.c
lib/
mypackage/
--Sample.java
Sample.java in mypackage looks like this:
package mypackage;
public class Sample {
public static native int sampleMethod(int x);
public static void main(String[] args) {
System.loadLibrary("Sample");
System.out.println("sampleMethod result: " + sampleMethod(5));
}
}
I run javac mypackage/Sample.java to compile the java file and javah mypackage.Sample to generate the JNI headers. Then I compile the library using the following command:
clang -I"${JAVA_HOME}/include" -I"${JAVA_HOME}/include/darwin" -o lib/libSample.so -shared Sample.c
At this point the directory structure looks like this:
Sample.c
mypackage_Sample.h
lib/
--libSample.so
mypackage/
--Sample.java
--Sample.class
Now when I try to run the example using java -Djava.library.path=./lib/ mypackage.Sample I get the following error:
Exception in thread "main" java.lang.UnsatisfiedLinkError: no Sample in java.library.path
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1867)
at java.lang.Runtime.loadLibrary0(Runtime.java:870)
at java.lang.System.loadLibrary(System.java:1122)
at mypackage.Sample.main(Sample.java:7)
I tried specifying the full path to lib/, but I get the same error.
I am not sure if the code for the header and the implementation matter, but I will post them anyway.
mypackage_Sample.h:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class mypackage_Sample */
#ifndef _Included_mypackage_Sample
#define _Included_mypackage_Sample
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: mypackage_Sample
* Method: sampleMethod
* Signature: (I)I
*/
JNIEXPORT jint JNICALL Java_mypackage_Sample_sampleMethod
(JNIEnv *, jclass, jint);
#ifdef __cplusplus
}
#endif
#endif
Sample.c:
#include "mypackage_Sample.h"
#include <stdio.h>
JNIEXPORT jint JNICALL Java_mypackage_Sample_sampleMethod
(JNIEnv * env, jclass obj, jint num) {
return num * num;
}
I run this on OS X Yosemite 10.10.5 using clang 7.0.2 and java 1.8.0_101.
It looks like you have the wrong name from your library file (libSample.so).
If you use:
System.loadLibrary("Sample");
The JVM will map this name to a platform specific file name to try and load. On Linux that is libSample.so, on Windows that is Sample.dll, but on OS X it's something else.
You can find out which name your library file should have by looking at the output of:
System.mapLibraryName("Sample");
Called on the target platform.
After that, you can use that as the name of your library file.
I want to call a C subroutine from Java. I'm using JNI. I have created the .java, .c, and .h files, and compiled a DLL. All the files are in the same folder. But when I run the program, it shows an unsatisfiedlinkError. Where am I going wrong...?
As I am learning JNI, the source code I used is the one from: http://www.ibm.com/developerworks/java/tutorials/j-jni/section2.html and things I have already tried:
Create a dll using Code::Blocks(ide) and GCC as the compiler
Create dll using GCC from command line (Ref. http://sig9.com/node/35 )
I am using Win7 32 bit, and I guess all the methods above generate 32-bit DLLs
All the solutions I found for creating a DLL (shared library) are for MS VC/VCPP and I don't have that on my machine right now.
Where is the problem? DLL files are being generated without any exception, but when I run the Java code, it throws the exception.
PS: If there is any theoretical examples that explains how JNI works and what actually it does, then kindly share the link...
The message or exception being thrown:
c:\myjava1>java Sample1
Exception in thread "main" java.lang.UnsatisfiedLinkError: Sample1.intMethod(I)I
at Sample1.intMethod(Native Method)
at Sample1.main(Sample1.java:11)
At the end of the day after creating dll file lots of times,I am pretty sure that there is possibly no issue with it ,something is wrong with the path ...I have changed the loadlibrary method with load method ,but still no luck,.....
as suggested by a MOD:
I have been discussing about this question on the post:JNI error while calling a C subroutine ,I am posting all the codes here as the comments have limited characters... Sample1.c
#include "jni.h"
#include"Sample_Sample1.h"
JNIEXPORT jint JNICALL Java_Sample_Sample1_test(JNIEnv *env, jobject obj){
return(1);
}
void main(){}
Sample1.java
package Sample;
public class Sample1
{
public native int test();
static{
System.loadLibrary("Sample1");
}
public static void main(String[] args)
{
Sample1 sample = new Sample1();
System.out.println(sample.test());
}
}
Sample_Sample1.h(generated using javah -jni command)
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class Sample_Sample1 */
#ifndef _Included_Sample_Sample1
#define _Included_Sample_Sample1
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: Sample_Sample1
* Method: test
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_Sample_Sample1_test
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
Can anyone guide me where the issue is?In earlier post I mentioned that the code I am using is the one from tutorials ,but to simplyfy the things I have changed the codes ...While using **java Sample.Sample1" i am getting:
c:\myjava1>java Sample.Sample1
Exception in thread "main" java.lang.UnsatisfiedLinkError: Sample.Sample1.test()I
at Sample.Sample1.test(Native Method)
at Sample.Sample1.main(Sample1.java:12)
You need to have your library explicitly set on your path.
It may be the case that the flags you are using there aren't quite right. Try this:
gcc -Wall -D_JNI_IMPLEMENTATION_ -Wl,--kill-at \
-I[Java_HOME]/include -I[Java_HOME]/include/win32 \
-shared -o Sample1.dll Sample1.c
From MinGW GCC site.
Have you checked your DLL by calling it from a stub C++ application? Pay particular attention to the exact name (including capitalisation) of the method(s) you are calling.
The Javadoc for UnsatisfiedLinkError says "Thrown if the Java Virtual Machine cannot find an appropriate native-language definition of a method declared native." That probably means either that the name is misspelled or your DLL is not where the JVM is expecting to find it.
Finally solved the problem using System.load() method, System.loadLibrary() Still doesn't work for me...it keeps on giving the same exception, And the I think the issue was with the .dll Thanks to all of you who supported and responded...
After spending 2hrs and analysing the code. Problem is with .dll/.so compilation. Below command works for me:
g++ -dynamiclib HelloJni.cpp -I /usr/include/ -I /usr/lib/jvm/java-1.8.0-openjdk-amd64/include/ -I /usr/lib/jvm/java-1.8.0-openjdk-amd64/include/linux/ -shared -o libHelloJni.so
or
g++ -dynamiclib HelloJni.cpp -I /usr/include/ -I $JAVA_HOME/include/ -I $JAVA_HOME/include/linux/ -shared -o libHelloJni.so
Above command is for linux. In case of Windows, change
[Java_HOME]/include/linux/ ---> [Java_HOME]/include/win/
Mac:
$Java_HOME/include/linux/ ---> $Java_HOME/include/darwin/