I was getting an exception: UnsatisfiedLinkError when I was trying to load a library. I had placed the library file in the right path and added the path to the PATH env variable. But nothing seemed to work. Untill I changed the Tomcat configuration and added -Djava.library.path=C:\Windows\System32 to the java options. One of my colleagues did not have to do this and yet it worked fine on her system, what is it that I am missing? Can anybody throw some light on this pleasE?
One option could be to register the dll
Regsvr32 “path to your dll.dll”.
This will install/register the dll (I am assuming it is a dll)
But I have generally observed that if it is COM dll then you have to register it and put it in System32
In JNI the name of Java native method and the name of corresponding C function are not same. In order to call C function, the name of C function MUST include the prefix "Java_", the class name and method name. The easy way is using the program "javah" in order to generate a header file including all definitions.
Try with following example for Windows:
(remember that the Java class name must be the same that corresponding file name)
Step 1. Create the following Java file (P.java):
class P
{
static
{
// "P" is the name of DLL without ".dll"
System.loadLibrary ("P");
}
public static native void f(int i);
public static void main(String[] args)
{
f(1);
}
}
Step 2. javac P.java
Step 3. javah P
Then, "javah" generates the header file "P.h"
Step 4. Create the file "P.def" including the following two lines (this file defines the exported symbols, in this case the name of C function):
EXPORTS
Java_P_f
Step 5. Create your C file (P.c):
#include "p.h"
JNIEXPORT void JNICALL Java_P_f(JNIEnv *env, jclass c, jint i)
{
printf("%i\n",i);
}
Step 6. Within Visual Studio command promt, define the following variables:
set JAVA_HOME= the path of JDK
set include=%include%;%JAVA_HOME%\include;%JAVA_HOME%\include\win32
Step 7. Generate DLL:
cl /LD P.c P.def
Step 8. Run the Java program:
java P
(Note: P.dll and P.class are located in the same directory)
Related
Goal: Link Java to Swift
Problem: I get an UnsatisfiedLinkError when trying to load a JNI .dylib file that is linked with a Swift .dylib file when calling System#loadLibrary(String).
Expected Behavior: The dependency of the Java .dylib would be automatically loaded or the call to System.loadLibrary("SwiftCode") would load the dependency (the only solution I could come up with).
Note: I am combining this github tutorial and this Medium article to create my JNI .dylib file and this tutorial to create my Swift .dylib file.
Full stacktrace:
Exception in thread "main" java.lang.UnsatisfiedLinkError: /Users/hillmacbookpro/IdeaProjects/JavaToSwift/src/native/libSwiftHelloWorld.dylib: dlopen(/Users/hillmacbookpro/IdeaProjects/JavaToSwift/src/native/libSwiftHelloWorld.dylib, 1): Library not loaded: libSwiftCode.dylib
Referenced from: /Users/hillmacbookpro/IdeaProjects/JavaToSwift/src/native/libSwiftHelloWorld.dylib
Reason: image not found
at java.base/java.lang.ClassLoader$NativeLibrary.load0(Native Method)
at java.base/java.lang.ClassLoader$NativeLibrary.load(ClassLoader.java:2408)
at java.base/java.lang.ClassLoader$NativeLibrary.loadLibrary(ClassLoader.java:2465)
at java.base/java.lang.ClassLoader.loadLibrary0(ClassLoader.java:2662)
at java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2627)
at java.base/java.lang.Runtime.loadLibrary0(Runtime.java:829)
at java.base/java.lang.System.loadLibrary(System.java:1833)
at helloworld.SwiftHelloWorld.<clinit>(SwiftHelloWorld.java:7)
File Structure:
src
- helloworld
- SwiftHelloWorld.java
- native
- libSwiftHelloWorld.dylib
- libSwiftCode.dylib
MCVE:
SwiftHelloWorld.java:
package helloworld;
public class SwiftHelloWorld {
static {
System.loadLibrary("SwiftCode"); // loading SwiftHelloWorld's dependency first
System.loadLibrary("SwiftHelloWorld"); // exception thrown here, can't find dependency?
}
public static native void printHelloWorldImpl();
public static void main(final String[] args) {
printHelloWorldImpl();
}
}
libSwiftHelloWorld.dylib: Created using these two terminal commands:
export JAVA_HOME="$(/usr/libexec/java_home -v 11)"; gcc -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/darwin/" -o libSwiftHelloWorld.dylib -dynamiclib helloworld_SwiftHelloWorld.c libSwiftCode.dylib
and these files:
helloworld_SwiftHelloWorld.c:
#include <jni.h>
#include <stdio.h>
#include "helloworld_SwiftHelloWorld.h"
#include "helloworld_SwiftHelloWorld_swift.h"
JNIEXPORT void JNICALL Java_helloworld_SwiftHelloWorld_printHelloWorldImpl
(JNIEnv *env, jclass clazz) {
int result = swiftHelloWorld(42);
printf("%s%i%s", "Hello World from JNI! ", result, "\n");
}
helloworld_SwiftHelloWorld.h: The auto-generated JNI header from
javac -h native/ helloworld/SwiftHelloWorld.java
helloworld_SwiftHelloWorld_swift.h:
int swiftHelloWorld(int);
libSwiftCode.dylib: Created using swiftc SwiftCode.swift -emit-library -o libSwiftCode.dylib and the SwiftCode.swift file:
import Foundation
// force the function to have a name callable by the c code
#_silgen_name("swiftHelloWorld")
public func swiftHelloWorld(number: Int) -> Int {
print("Hello world from Swift: \(number)")
return 69
}
Related Questions:
Java JNI linking multiple libraries, he/she gets an UnsatifiedLinkError but with the discription undefined symbol: ... (unanswered)
Anything tagged with JNI and containing UnsatifiedLinkError
Other Notes: I am setting the Java virtual machine's options to -Djava.library.path=src/native, the parent directory of both of the .dylib files. I am using macOS.
Edit: I have tried preceding the command to compile SwiftCode.swift with xcrun as seen in this article (under "Compile Swift code") but I still get the same error.
macOS ld builds a library dependency's path into the binary. The loader loading libSwiftHelloWorld.dylib will only find libSwiftCode.dylib if the latter is in the current directory. Loading the dependency in Java doesn't work because for the loader it's a different library.
You can change the built-in path for libSwiftCode.dylib with the -install_name argument (i.e. swiftc ... -Xlinker -install_name -Xlinker <your path>). If you rebuild libSwiftHelloWorld.dylib afterwards it will reference the path that you gave.
I have read and searched all stack overflow .. I also found JPype class not found but it didn't help me although it is solved! I have the same problem ! I am using Mac , python 2.7.6
My both python code and A.java are on desktop. But I keep receiving this error :
Traceback (most recent call last): File
"/Users/jeren/Desktop/aa.py", line 13, in
A = jpype.JClass("A") File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/jpype/_jclass.py",
line 54, in JClass
raise _RUNTIMEEXCEPTION.PYEXC("Class %s not found" % name) java.lang.ExceptionPyRaisable: java.lang.Exception: Class A not found
aa.py :
import jpype
import os
jpype.startJVM(jpype.getDefaultJVMPath(), "-ea", "-Djava.class.path=/Users/jeren/Desktop/")
A = jpype.JClass("A")
a = A()
jpype.shutdownJVM()
A.java :
class A
{
public A()
{
super();
}
public String sayHi()
{
return("Hello");
}
public static void main(String[] argv)
{
System.out.println ("Hello ");
}
public static int add(int a, int b)
{
return(a+b);
}
}
My mac , java and python are all 64bit ! where the problem can be?
everything was ok just needed to add a 'public' to the beginning of class A:
public class A
{
public A()
{
super();
}
public String sayHi()
{
return("Hello");
}
Here are some further nodes on specifying the class path for jpype.
A. Check JDK path
I had several versions of Java JDK installed and getDefaultJVMPath did not yield the expected path. I needed to replace
jpype.getDefaultJVMPath()
with the path to the JDK, that actually has been used to compile the code, e.g
D:/jdk11/bin/server/jvm.dll
B. relative paths
It is possible to use relative paths. If my python file is for example in a package folder "pkg" and my java class file is in a sub folder "foo" of a "bin" folder:
parentFolder
pkg/main.py
bin/foo/Foo.class
jpype.startJVM(jvmPath, '-Djava.class.path=../bin")
link = jpype.JClass('foo.Foo')
For this example, the working directory of the java application will be the pkg folder. With other words, inside a main method of Foo class, you might want to use "../" to access the parentFolder.
C. -cp option does not work
I tried to use -cp option instead of -Djava.class.path, which I would found more induitive. However, the following code does not work:
jpype.startJVM(jvmPath, '-cp', classPath)
D. jars need to be included individually
I tried to include a folder with several jar files.
parentFolder
foo/main.py
lib/foo.jar
Following code does not work:
jpype.startJVM(jvmPath, '-Djava.class.path=../lib/*")
link = jpype.JClass('foo.Foo')
Each jar file needs to be included individually, e.g.:
libOath = '../lib'
libJarPaths = str.join(';', [libPath + '/' + name for name in os.listdir(libPath)])
jpype.startJVM(jvmPath, '-Djava.class.path=../lib/*")
link = jpype.JClass('foo.Foo')
(Solution from JPype (Python): importing folder of jar's )
I'm working on a project where there's the need to call some methods from dll files.
These two dlls are
EasySign.dll
EasySignJNI
EasySignJNI depends on EasySign.
I wrote the class to load EasySignJNI as follows:
package easysign;
class EasySign {
EasySign(){}
public native String EasyHashFile(String filename);
public native int EasySign(String pkcs11_driver,String pin, int type, String file_data, int out_format, String signed_file, String cert_out, int cert_format);
public native int EasyVerify(String cert_user, String file_data, String signed_file, String crl_file, String ca_file, String out_document);
static {
System.loadLibrary("EasySignJNI");
}
}
Now I would call these method from my main method like this:
public class Test {
public static void main(String[] args) throws IOException{
EasySign es = new EasySign();
System.out.println("EasyHashFile : " + es.EasyHashFile("test.txt"));
}
}
What I have to specify in the -Djava.library.path ? Only the path where my EasySignJNI.dll is located? It is possible to call native method in this way?
I'm using NetBeans for completeness.
EDIT:
I have noticed that the third party dll provided to me (the JNI dll in particular) defines method names without any package, so I'm forced to put the class that loads the dll in the default package. Is there any way to change only the dll method names including my own package name?
EDIT 2:
What I mean is that both EasySign.dll and EasySignJNI.dll are provided me as they are and I can't modify them (I have not the source code). The EasySignJNI is the JNI portion but inspecting it I have noticed thath the method sign is in the form: _java_EasySign_MethodName. When I load the dll in Java from my Easysign class (this class must reside in the "mypackage" package), I receive the jni unsatisfiedlinkerror because, if I understood right, I'm calling the "_java_mypackage_EasySign_MethodName" method, i.e the sign is different from the dll's one. So the only way to make it work is to rewrite the JNI part and build it to have the correct sign of the JNI method?
What I have to specify in the -Djava.library.path ? Only the path where my EasySignJNI.dll is located?
Correct, the operating system will locate the dependent EasySign.dll for you as long as it is available where the OS expects it to be.
It is possible to call native method in this way? I'm using NetBeans for completeness.
I read through your edit and you have successfully lost me. What default package are you referring to? (Remember that none of us know what EasySign.dll is) So, I am going to provide some info about how I do what you originally described and hopefully it will help.
First start by compiling EasySign if you have the src. Do not build a DLL or shared object, instead build a static library. If you do not have the src code for EasySign, or a prebuilt static library, you will be stuck with the dll and can continue to the next step.
Now you are ready to compile the jni portion. All of your JNI C code should basically translate your Java input/output to their JVM/Native types and call the appropriate functions in the DLL library. You want to keep this layer and thin and simple as possible because it is incredibly difficult to debug. Your C++ package names shouldn't really matter here and you can use what ever package name you want for your Java classes. You should be able to compile the JNI code and preferably static link to the EasySign.dll file so you don't need to worry about distributing it. If you must dynamically link, make sure EasySign.dll gets installed to a location that is on the DLL PATH / LDPATH because the OS will need to locate and load that file right after the JVM loads the JNI DLL.
At this point you should just be able to point -Djava.library.path at your JNI DLL's path and all should work.
I want to to run java code in Matlab so I can make use of what I have learned in java to enhance my MATLAB codes.
Totally quoting from here
Suppose your Java program is like this:
public class MyClass {
public static void main( String args[] ){}
}
To call this program in MATLAB:
Outside of MATLAB: Compile this class, so you have file MyClass.class
Locate the classpath.txt file for the MATLAB installation. The location of this file can be found by typing the following command in MATLAB command window:
which classpath.txt
Open the 'classpath.txt' with a text editor as Administrator. Add the full path for the directory with the MyClass.class to the end of the 'classpath.txt' file as a single line and save the file.
Restart MATLAB.
In MATLAB: to create an object of class MyClass, type:
o = MyClass
In MATLAB: to execute main() of object o, type:
javaMethod('main', o, '')
Alternately one may also add the directory in which the class files are to the dynamic path. Use the JAVAADDPATH command to add the directory (which contains the MyClass.class file) to JAVA's dynamic classpath. This also obviates the need to restart MATLAB. Once this is done the code can be invoked as follows:
o = MyClass;
javaMethod('main', o);
For a detailed tutorial please see this.
Situation: I have a library of JNI files, the library is comprised of several functions that are called by the main header file in that JNI library (i.e., code1.h). I have a Java file (i.e., code2.java) that I want to pass to and from JNI header file (code1.h). I created a source code for the (code1.h) called (code1.c).
My question is: Does (code1.h), (code1.c), and (code2.java) have to be the same name for the communication between the JNI and the java?
EDIT: So (code1.h), (code1.c), and (code1.java) all have to be the same name in order for the (code1.java) to pass strings to/from (code1.c)/(code1.h)? And it is not possible to have (code2.java) pass strings to/from (code1.c)/(code1.h) because they are not named the same, is this correct?
For instance,
public class code1 { /*this is code2.java, but should the name be changed to (code1.java) to match that of the JNI?*/
static {
System.loadLibrary("myjni");
}
to pass strings to code1.h/code1.c
This will be compiled for android using Linux Debian"Wheezy" and Eclipse with Android SDK and NDK
While Java requires a match between compilation unit name (SomeClass.java being the name and public class SomeClass{ being the declaration, C does not require this.
You may name the C source and header files as you see fit as long as the function names/exported symbol names match the name of the native method on the java side. For example:
//JavaClass.java
public class JavaClass{
public native String getAString(String in);
}
And header would be:
// any name
JNIEXPORT jstring JNICALL
Java_JavaClass_getAString(JNIEnv *, jobject, jstring);
with matching C files. You could name this header catsMakeTheWorldGoRound.h for all Java cares.
Here is an example of what your "JNI object" should look like.
//In my experience, it is better to put the JNI object into a separate package.
package org.example;
public class Code1
{
static
{
// On a Linux system, the actual name of the library
// is prefixed with "lib" and suffixed with ".so"
// -- e.g. "myjni-java.so"
// Windows looks for "myjni-java.dll"
//
// On a Windows system, we also need to load the prequisite
// libraries first. (Linux loaders do this automatically).
//
String osname = System.getProperty("os.name");
if (osname.indexOf("win") > -1 || osname.indexOf("Win") > -1)
{
System.loadLibrary("myjni");
}
System.loadLibrary("myjni-java");
}
// Now we declare the C functions which we will use in our Java code.
public static native void foo(int bar);
public static native int bar(String foo);
//...
}
Given that you have compiled your JNI library correctly, you can then call the C functions from other Java classes like this:
//Again, in my experience, it is better to explicitly give the package name here.
org.example.Code1 Code1= new org.example.Code1();
Code1.foo(123);
int a= Code1.bar("Hello C function from Java function!");
Does this help you with your question? (I am not an expert in JNI, so I might not be able to help further.)