Java System.load(libName) looks for wrong lib name - java

I've run into a strange behaviour on one of our customers machine when trying to load a JNI library at runtime.
When trying to load a library with
System.load("libtestlibrary.so")
I get a
liblibtestlibrary.so.so: cannot open shared object file: No such file or directory
It seems like the given library name is (wrongly) prefixed with "lib" and suffixed with ".so" and thus our library cannot be found.
This only happens on one machine (AIX 6). I was unable to reproduce this behaviour on our own AIX 6 box using the exact same JRE, so I'm guessing this is caused by some OS settings on the customer machine.
Does anybody have an idea how to turn this off?

You can use either:
System.loadLibrary("libraryname");
It will be properly expanded to system specific notation. Or you can use:
System.load("you_can_have_anything_you_like_here")
In second case, all you need to do is to point to proper location of file. If you are looking for cross system development, you might be interested in packaging your native code inside JAR. Take a look here for a sample code where library is taken from JAR file
https://github.com/mkowsiak/jnicookbook/tree/master/recipes/recipeNo031
Also, make sure that other libraries are available in LD_LIBRARY_PATH. Note that java.library.path is only valid for your "first" native lib. If your code uses some other shared libs, you have to either compile with rpath or make sure libraries are visible system wide.
Yet another approach is to go via stub: http://jnicookbook.owsiak.org/recipe-No-018/
In that case, you can load library as you like and do whatever you like (e.g. change native code on the fly).

Related

Installing Java API for GPIB devices in eclipse

This is probably a trivial question, but I am having trouble installing a Java API called JPIB_Windows into eclipse which should allow me to control external devices connected via GPIB.
This is the contents of the folder that I downloaded.
I created a new project in Eclipse, right clicked the project and went to build path -> configure build path.
I then clicked on add external libraries and added the JPIB.jar file. The file was added, but I am still not able to use the classes in the API.
Is there something else that I need to do to be able to use the API? Is there a better way of importing this API into my project?
You jar file is installed correctly. JPIB (and many other Java libraries) is just wrapper for low-level routines. So it is mandatory to load corresponding native libraries before using Java classes.
In Eclipse go to the Run > Run Configurations... > Arguments tab, select configuration for, perhaps, main method. Then specify in VM arguments field:
-Djava.library.path=C:\path\to\jpib\dll
Then add at the beginning of main() the following line:
System.loadLibrary("jpib_32");
Then run already edited configuration. Everything should be OK. But in case of failure you can examine path to DLL:
System.out.println("Libary path: " + System.getProperty("java.library.path"));
Also working directory can be specified in the same tab, avoiding absolute path, but for the first time absolute path is simple and less error-prone.
Also note that Java may not recognize Windows-specific issues (missing drivers, insufficient user privileges, wrong DLL version) and will report about general error.

How does a JNI DLL search for its dependent native DLL?

Say I have JNI.dll. It depends on native.dll. Now my Java application calls System.loadLibrary("JNI").
Will the following folder layout work?
MainFolder
|--main.exe
|--SubFolder
|--JNI.dll
|--native.dll
My guess is, there are 2 levels of dependency resolution.
[Level 1]:
System.loadLibrary("JNI") uses JVM property java.library.path to locate JNI.dll.
[Level 2]:
JNI.dll relies on Windows system mechanism to locate native.dll.
Is this correct?
If I set %_JAVA_OPTIONS% as -Djava.library.path=MainFolder\SubFolder, I think it can cover the search for JNI.dll. But will it cover the search for native.dll, too?
ADD 1
It seems my guess of 2 levels got confirmed from here: How to add native library to "java.library.path" with Eclipse launch (instead of overriding it)
See comment by kevin cline. But the mentioned solution with LD_LIBRARY_PATH environment variable only applies to Linux.
ADD 2
I think I didn't make my question clear. Let me put it this way.
My confusion is: the JNI.dll depends on native.dll. Both of them are not in the main.exe's current working directory. Actually they are in a sub folder of CWD.
If I run main.exe directly, I just need to set the java.library.path = <other path>\MainFolder\SubFolder. Both DLL are found correctly.
But if I run my project from Eclipse, besides setting the java.library.path, I have to put the "\MainFolder\SubFolder" in the %PATH% environment variable.
I just don't know why Eclipse is so different.
1)It searches in the current Directory.
2)System folder typically C:\Windows\System32 (use CSIDL_SYSTEM with shgetspecialfolderpath() to get the rigth folder on the given system)
3)Windows folder (C:\Windows )(CSIDL_WINDOWS with shgetspecialfolderpath() to get the right folder on the given system)
4)All the folders listed under PAth environmnet variable
A similar link: Must I place all dependent DLLs into the JDK's bin folder?
Inspired by this thread:System versus user PATH environmental variable...winmerge works only if I add the path to the user PATH
I start to wonder maybe my User %Path% is too long. So I moved the folder path containing my dependent DLL from the end of User %PATH% to the beginning. It works now!
At first, I conclude that one who implemented the Windows' DLL lookup algorithm has some truncation issue. And I almost consider it as another annoying Windows Bug.
But I wrote another Windows application which has similar DLL dependencies to confirm my guess. That application works fine! So I have to review my conclusion.
I checked my User %PATH% entry one by one, and place the folder to each possible location. And finally, I find the root cause.
I have a C:\MinGW\bin entry in User %PATH%, which happens to contain a
libstdc++-6.dll (977KB) but unfortunately, which isn't compatible
with the one I need (825KB). It only works if I place my folder before MinGW.
Now this issue seems resolved. But another one comes up, do I need to switch back and forth if I want to use both my DLL and the MinGW?

UnsatisfiedLinkError - Unable to load library - Native library not found in resource path

I have the following error at runtime, while trying to run Tess4J:
Exception in thread "main" java.lang.UnsatisfiedLinkError: Unable to load library 'libtesseract302': Native library (win32-x86-64/libtesseract302.dll) not found in resource path ([myproject/target/classes/, ...some jars...])
My questions are:
1) What exactly it tries to find and where?
2) Why is it apparently searches for myproject/target/classes/ directory? I set it nowhere.
3) Why is it ignores "native directory path" I set for tess4j.jar in user library descripto in Eclipse? My DLLs are there. If it didn't ignore the path, it would find DLLs.
4) Why is it apparently prepending DLL name with win32-x86-64/? I set this nowhere. Is this standard prefix of some API?
5) What is "resource path"? How to set it?
Like the error says, it's looking for win32-x86-64/libtesseract302.dll in java.class.path. Part of your classpath apparently includes myproject/target/classes.
The prefix represents the platform and architecture of the shared library to be loaded, which allows shared libraries for different targets to be included in the same archive. If JNA cannot find the requested library name in the system load path, then it attempts to find it within your resource path (extracting it, if necessary). So if you put the DLL in a jar file, you'll need to give it the win32-x86-64 prefix in order for it to load.
The "resource path" is nominally your class path; basically anywhere reachable by ClassLoader.getResource().
The error stems from your trying to load 32-bit DLLs in 64-bit JVM. The possible solution is switch to 32-bit JVM; alternatively, use 64-bit Tesseract and Leptonica DLLs.
Had the same issue, sorted with the following lines
System.load("/usr/local/lib/liblept.so.5")
System.loadLibrary("tesseract")
For your case, it might be different libraries but in the end is pretty much the same: just load the libraries that you need manually.
Why don't you use JNA API http://www.java2s.com/Code/Jar/j/Downloadjna351jar.htm to load native library? Once you putted into your project classpath, you add this code
NativeLibrary.addSearchPath("libtesseract302", "your native lib path"); make sure you have this libtesseract302.dll file, normally it is located at windows32 folder.
For example, if your libtesseract302.dll file in somewhere c:/abcv/aaa/libtesseract302.dll then you just set the path like this NativeLibrary.addSearchPath("libtesseract302", "c:/abcv/aaa");
I don't know how windows path look like either c:/abcv/aaa or c:\\abcv\\aaa\\
if you want easier way, just put all your necessary dll file into your windows32 folder, JVM will take care of it.
Another issue might be you were not installing the application correctly or the application version is unmatch with your jar version. try to install the latest application and download the latest jar to try again. Hope it helps :)
I had the same problem and found that this "resource path" is not set by "native directory path" .
You can however add new folders to it by using "Add External Class Folder" in the Library tab, even if this folder does not contain any class file but native library files(like DLL on Windows)
A few days ago I ran into the same error message when trying to load a C++ DLL with JNA. It turned out that the cause was a missing DLL that my DLL depended on.
In my case it was the MS Visual Studio 2012 redistributable, which I then downloaded and installed on the machine and the problem was gone. Try using Dependency Walker to find any missing libraries and install them.
I think an easier way to get around this error would be to revert to an earlier version where you were not getting this error. Right click on the project folder and navigate to local history to revert to an earlier version. I verified this workaround on the android studio installed on Mac OS Big sur.

Can't load a .dll with System.load(path);

I made a Java OCR program using the AspriseOCR.
It requires a .dll called AspriseOCR.dll, I copied the dll to C:/Windows/System32/
But when I use
System.load("C:/Windows/System32/AspriseOCR.dll");
I still get a UnsatisfiedLinkError.
I've spent the last 2 days searching for a solution to my problem, but I couldn't find anything that works.
Okay everyone, it works now. Turns out I also had to make a 32-bit version!
If anyone ever needs help with the OCR Engine from Asprise, pm and I'll try to help you!
To load libraries, such as DLLs, you should use
System.loadLibrary("libname");
Where "libname" is the name of the library. You do not include the extension of the file it is stored in, or the full path to the file. For your case, you would probably call
System.loadLibrary("AspriseOCR");
to load the library you are using.
Because loadLibrary takes a library name for an argument, not a file, you must be careful where you place the .dll. Normally, you could include it in the working directory of the application, or in a native folder such as System32. If you must put it somewhere else, be sure to appropriately set java.libary.path. For example, if the .dll is in the folder "libraries", you should launch java with the argument
-Djava.library.path=libraries

Running Eclipse Equinox and an Application from a single Jar using no other files

I have an application that uses the Eclipse runtime. Unfortunately, due to restrictions in Equinox (the Eclispe OSGi implementation), it's not possible to start the Eclipse runtime with all of the plugins on the classpath, so it must actually be located on the disk. (There is code that insists to look for the OSGi framework bundle using a "file:" URL protocol).
I need to have my application (with Eclipse) be in a single JAR file and have no dependencies on external environment variable settings. You should be able to call my app's API like any other and it should just work. How I have implemented this is to create a temporary directory using the Java temp file support (i.e. File.createTempFile()), and then I read all of the plugins and some other stuff from the classpath and write it to the disk so there is the expected Eclipse installation. This is about 10MB worth of stuff and takes IIRC less than a second. However, I would like to avoid even this if possible by checking to see that it has been done before and not doing it again unless there is a problem.
One way that occurs to me is to write some other small file in a known location (like in the user's home directory) that contains the version number of my code and a pointer to the temporary location. Another alternative is to locate the installation in a known place (instead of using the Java temp file support). But then this gets messy with platform specific considerations.
Anyone have a better idea for how to solve this?

Categories

Resources