I will need to use an external, unknown DLL and build a java wrapper around it. I do not have the DLL nor the header file at the moment, and maybe will not even get the header-file, but I'd like to prepare myself. (I have no experience with C++ )
Following situation:
Let's say this DLL has a function, which contains one or more C++ classes as method signature. Then how could I call this function with JNI, as in my java project those custom classes in the DLL are non-existent? Is there a option to "clone" or "port" the C++ class to java? Is there anything I could do with a tool like Dependency Walker to resolve this problem?
What would be the best / most simple approach to accomplish that?
Here is some code which I already tried, to find out how it behaves:
Java Class with main
public class FourthJNI {
public static native int returnAgeOfHuman(int zuQuadrierendeZahl);
public static void main(String[] args) {
// /* This message will help you determine whether
// LD_LIBRARY_PATH is correctly set
// */
// System.out.println("library: "
// + System.getProperty("java.library.path"));
Human testHuman = new Human("abcde", 23, "M");
/* Call to shared library */
int ageOfHuman = FourthJNI.returnAgeOfHuman(5);
System.out.println(testHuman.toString());
System.out.println("Age: " + ageOfHuman);
}
}
generatd h-file
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class FourthJNI */
#ifndef _Included_FourthJNI
#define _Included_FourthJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: FourthJNI
* Method: returnAgeOfHuman
* Signature: (I)I
*/
JNIEXPORT jint JNICALL Java_FourthJNI_returnAgeOfHuman
(JNIEnv *, jclass, jint);
#ifdef __cplusplus
}
#endif
#endif
The best way here is to use adapter pattern (https://en.wikipedia.org/wiki/Adapter_pattern). Inside your JNI code you have to call DLL by creating all the objects, as expected by C++ API.
You can find sample here: http://jnicookbook.owsiak.org/recipe-No-021/ and here https://github.com/mkowsiak/jnicookbook/tree/master/recipes/recipeNo025
You can also take a look at the code where one shared library (JNI) calls the code from another one: http://jnicookbook.owsiak.org/recipe-No-023/
Basically, what you have to do is to create JNI based wrapper code that translates Java calls to native methods into C++ calls, and vice versa - the code that translates return values into something that is expected by Java.
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 have found other posts of people having this exact error, but not one thus far has had a solution that worked for me. For reference, here are the things I have found:
https://community.oracle.com/tech/developers/discussion/2233828/jni-link-error-a-dynamic-link-library-dll-initialization-routine-failed
JNI UnsatisfiedLinkError: A dynamic link library (DLL) initialization routine failed
https://www.debugcn.com/en/article/5175409.html
https://coderanch.com/t/132356/engineering/java-lang-UnsatisfiedLinkError
Either their solution was not relevant to my particular scenario, or it did not fix the issue for me.
Everything is being compiled on the command line with a Windows 10 computer and using GCC (gcc-5.1.0-tdm64-1-c++) for compiling the C++ portions into a .dll, and JDK 15.0.1's javac tool. There are three relevant files here, one being the header file derived from the java file.
Main.java:
public class Main {
static {
System.load("C:\\Users\\17659\\Documents\\Programming\\C++ & Java - JNI Tests\\library.dll");
//System.loadLibrary("library");
}
public static void main(String[] args) {
new Main().outputText();
}
private native void outputText();
}
Main.h:
/* 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: outputText
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_Main_outputText
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
Library.cpp:
#include <iostream>
#include "Main.h"
JNIEXPORT void JNICALL Java_Main_outputText(JNIEnv * a, jobject b)
{
std::cout << "testing";
}
They are contained all within the folder with the absolute path of C:\Users\17659\Documents\Programming\C++ & Java - JNI Tests. With a command prompt set to that as the current directory, I run the following commands in order:
g++ -c -o Library.o -I"C:\Users\17659\Documents\jdk-15.0.1\include" -I"C:\Users\17659\Documents\jdk-15.0.1\include\win32" Library.cpp
g++ -shared -o library.dll Library.o
javac Main.java
java Main
Despite the multiple things I have tried, I always get this same error:
Exception in thread "main" java.lang.UnsatisfiedLinkError: C:\Users\17659\Documents\Programming\C++ & Java - JNI Tests\library.dll: A dynamic link library (DLL) initialization routine failed
at java.base/jdk.internal.loader.NativeLibraries.load(Native Method)
at java.base/jdk.internal.loader.NativeLibraries$NativeLibraryImpl.open(NativeLibraries.java:383)
at java.base/jdk.internal.loader.NativeLibraries.loadLibrary(NativeLibraries.java:227)
at java.base/jdk.internal.loader.NativeLibraries.loadLibrary(NativeLibraries.java:169)
at java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2407)
at java.base/java.lang.Runtime.load0(Runtime.java:747)
at java.base/java.lang.System.load(System.java:1857)
at Main.<clinit>(Main.java:3)
I have used nm on the resulting .dll to make sure the name of the function is correct, and it does seem to be exactly as it should.
The entire point of this little project of mine is to figure out how JNI works, since I have a plan to write a small portion of a program in C++. The rest of the program though would work best in Java (for me). I do not know what I need to do to get this program to work, I have spent approximately 2 hours of googling and fiddling attempting to get it to function. This is on a 64-bit OS. How can I make this program run and print out the very little amount of text I would like it to print out?
Update: As per #JornVernee removing the line #include <iostream> and replacing the std::cout with a printf() to write to the console did actually work. So my question now becomes this: why does including a standard C++ header cause an error?
Well, #JornVernee effectively nailed the issue right on the head. It was a mismatch between the standard library I had for C++ and the one being loaded. I changed the version of GCC I was using to a more up-to-date version, recompiled the entire project, and the program works now.
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
I am trying to load the JNI library and run the below program but I am getting the below error
Exception in thread "main" java.lang.UnsatisfiedLinkError: no JNIDemo
in java.library.path
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1865)
at java.lang.Runtime.loadLibrary0(Runtime.java:870)
at java.lang.System.loadLibrary(System.java:1122)
at demo.JNIWrapper.<clinit>(JNIWrapper.java:10)
Below are my Java,C,command
Step1:Java Code :Present in a Eclipse Project at Path (/documents/JNIDemoProject/src/main/java/demo)
package demo;
public class JNIWrapper {
static{
//System.load("/home/arpit/Documents/JNI/libJNIDemo.so");
System.loadLibrary("JNIDemo");
}
public native int multiply(int a,int b);
public static void main(String args[]){
try{
JNIWrapper jni=new JNIWrapper();
int result=jni.multiply(7, 8);
System.out.println("Result is "+result);
}catch(Exception e){
e.printStackTrace();
}
}
}
Step 2:A .h file named demo_JNIWrapper was created (Note name is demo_JNIWrapper as I had to run the javah command from /documents/JNIDemoProject/src/main/java)
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class demo_JNIWrapper */
#ifndef _Included_demo_JNIWrapper
#define _Included_demo_JNIWrapper
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: demo_JNIWrapper
* Method: multiply
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_demo_JNIWrapper_multiply
(JNIEnv *, jobject, jint, jint);
#ifdef __cplusplus
}
#endif
#endif
Step 3:
I create a C file
#include<stdio.h>
#include <jni.h>
#include "demo_JNIWrapper.h"
JNIEXPORT jint JNICALL Java_demo_JNIWrapper_multiply
(JNIEnv *env, jobject jobj, jint a, jint b){
int result=a*b;
return result;
}
Step 4:
I create the lib file named libJNIDemo.so
Step 5:
All the three files(libJNIDemo.so,demo_JNIWrapper.h,HelloJNI.c) are located at the folder /users/documents/JNI
Step 6:
I export it into lib path
export LD_LIBRARY_PATH="/users/documents/JNI"
Step7:
When I run the java program it gives me the above error .
POINT TO NOTE:
My program runs fine when I load the libJniDemo.so file directly with the path
static{
System.load("/users/documents/JNI/libJNIDemo.so");
//System.loadLibrary("JNIDemo");
}
Can anyone please suggest
Java uses dlopen() on Linux (and other Unix or Unix-like operating systems) to find native libraries. From the Linux dlopen() man page:
If filename is NULL, then the returned handle is for the main
program. If filename contains a slash ("/"), then it is interpreted
as a (relative or absolute) pathname. Otherwise, the dynamic linker
searches for the object as follows (see ld.so(8) for further
details):
o (ELF only) If the executable file for the calling program
contains a DT_RPATH tag, and does not contain a DT_RUNPATH tag,
then the directories listed in the DT_RPATH tag are searched.
o If, at the time that the program was started, the environment
variable LD_LIBRARY_PATH was defined to contain a colon-separated
list of directories, then these are searched. (As a security
measure, this variable is ignored for set-user-ID and set-group-
ID programs.)
o (ELF only) If the executable file for the calling program
contains a DT_RUNPATH tag, then the directories listed in that
tag are searched.
o The cache file /etc/ld.so.cache (maintained by ldconfig(8)) is
checked to see whether it contains an entry for filename.
o The directories /lib and /usr/lib are searched (in that order).
If you can, run your Java process under strace, trace open calls, and see where the JVM is looking for your native library, and exactly what it's looking for.
Given knowing how dlopen() work, and the information from strace, you should be able to determine exactly what's happening.
One thing to be aware of: you may be creating a 64-bit native library but running a 32-bit JVM.
I have a custom dll that I access from Java using JNA. So far all is working perfect. Now however I would like to create Java classes from my C code. I assume this can't be done with JNA so what I did is to create a JNI method but this leads me to UnsatisfiedLinkError's. So my question is: Can I mix JNA and JNI when accessing the same DLL and if so, how should I go about doing that?
Of course can you mix access to the DLL, since it is only loaded once anyway. The problem is how the linking to your application works:
JNA:
When using JNA you call the native functions of the jna library, which by some kind of reflection bind to the functions in your DLL. This has the advantage that you don't have to worry about the name of the functions in the DLL, they don't have to meet any conventions.
JNI:
Simple works by a mapping from your java classes to method names which are expected in the DLL. If you have a Class com.company.SomeClass containing a function int doStuff(int i, long john) with this signature:
JNIEXPORT jint JNICALL Java_SomeClass_doStuff(JNIEnv *env, jint i, jlong john) {
return ...whatever...
}
If this function is missing, you get the UnsatisfiedLinkException.
Solution:
Since it seems you have written your own DLL, just add the functions required as wrapper functions, and you are done. To get the function signatures, you can create a header file automatically with the javah command.
I recommend reading Advanced Programming for the Java 2 Platform - Chapter 5: JNI Technology.
I want to add one thing to do that. Don't forget extern "C" for each JNIEXPORT and also function for JNA.
As a simple example:
// Example DLL header file MyDLL.dll
#ifdef MYDLL_EXPORTS
#define MYDLL_API __declspec(dllexport)
#else
#define MYDLL_API __declspec(dllimport)
#endif
extern "C" {
MYDLL_API void HelloWorld(void);
}
extern "C" {
JNIEXPORT void JNICALL Java_MyJavaMain_HelloWorld(void);
}
//Example CPP file MyDLL.cpp
#include "MyDLL.h"
#include "stdio.h"
extern "C" declspec(dllexport)
void HelloWorld(void){
printf("Hello World From Dll");
}