When does a shared library have to be on the Java classpath? - java

I'm trying to work with a third-party SDK on RHEL. Running this vendor's sample code (using their own provided run.sh) throws what I'll call Error 1: java.lang.UnsatisfiedLinkError: Unable to load library 'redacted': Native library (linux-x86-64/libredacted.so) not found in resource path. I add the library location to the class path, and that error disappears.
It is replaced with Error 2: Java.lang.UnsatisfiedLinkError: libodbc.so: cannot open shared object file: No such file or directory, where I obviously am missing a dependency. I install the appropriate MySQL package (which puts the .so in /lib64) and it's happy.
Now both .so files are in /lib64, which is on the Java library path, so I would assume it should find them the same. Even pointing LD_LIBRARY_PATH to the library (which the vendor had done in their script) doesn't resolve Error 1.
Why is the first library required to be on the class path when Java uses java.library.path to find shared objects?
It's worth noting that the vendor's script didn't run correctly out of the box, which makes me wonder if it's an issue with my particular RHEL installation.

Related

still experiencing linker error despite linking the java jar file in visual studio code

I am trying to build a small personal project with opencv. I included the path to the opencv jar file in visual studio using the java dependencies then referenced libraries but i get the linker error: Exception in thread "main" java.lang.UnsatisfiedLinkError: no opencv_java430 in java.library.path. I have no idea what else i could do
UnsatisfiedLinkError isn't about jar files.
It's about 'native' files. These are generally stored as a .jnilib file on mac, as a .DLL on windows, and as a .so file on on most unixen.
They cannot be in jar files.
Some libraries will put in some effort and ship a whole bevy of them (as each architecture and OS has a unique dll/jnilib/so file that is needed) inside the jar, will find the 'right' one for your arch/os combo, unpack it someplace, and try to load it live.
This is either not working, or this library isn't doing that. Presumably the opencv site contains a tutorial on how to get it running; as native files are required, it's a bit more involved than 'just download, add to classpath, and voila'. I suggest you follow it precisely.
If you do have something that seems suitable (probably called opencv_java430.dll or whatnot), start java with java -Djava.library.path=/directory/containing/that/file the.rest.of.your.java.args - that should help.

OpenCV 4.3.0 java.lang.UnsatisfiedLinkError in Eclipse

I'm trying to do some template matching with the Java binding of OpenCV 4.3.0 in Eclipse, but attempting to load the template image always results in this error:
Exception in thread "main" java.lang.UnsatisfiedLinkError: org.opencv.imgcodecs.Imgcodecs.imread_0(Ljava/lang/String;I)J
The line of code where this exception is thrown is this:
flowerTemplate = Imgcodecs.imread("/templates/flowerpot_white.png", Imgcodecs.IMREAD_COLOR);
I have tried a number of solutions suggested on similar questions on StackOverflow and elsewhere on the internet, including:
Pointing at the native library folder with the "Native library location" variable in the user library definition in Eclipse.
Adding the native library folder location to my PATH variable.
Adding the native library .dll location to my PATH variable.
Setting up the Eclipse run configuration to add the native library folder & .dll locations to the PATH and CLASSPATH variables.
Loading the library with the appropriate Java code, in each of the three ways I saw it suggested, in three different places which all run before the code that throws the exception.
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
System.load(<path_to_the_dll>);
File opencvLibrary = new File(System.mapLibraryName(Core.NATIVE_LIBRARY_NAME));
System.load(opencvLibrary.getAbsolutePath());
Placing the .dll in question into my source folder and every subfolder. I am running it from within Eclipse, so this is also the program's working directory.
UnsatisfiedLinkError is a runtime exception that happens when running your Java program. So placing your file in the source folder will not work.
You need it to be available in a place that your program can find it.
See this article for example:
https://www.javaworld.com/article/2077520/java-tip-23--write-native-methods.html
In it they place the library in Linux's library path. In windows you'd similarly place it in the current directory (where you're running from) or in some shared location.
This article explains Window's dll search order: https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order
You shouldn't need to explicitly call System.loadLibrary() yourself. That's the library's responsibility.
Your problem is that OpenCV is improperly installed on your machine or inaccessible from Eclipse.
For instructions on how to make in work in Eclipse see:
Add .dll to java.library.path in Eclipse/PyDev Jython project
After removing every load method and then adding them back one-by-one, I determined that the issue was most likely caused by Eclipse loading the native library folder twice.

Issues loading .so file with JNA in Spring Boot on Linux

I have an application written in Java with Spring Boot. This application needs to load .so files that are bundled with the project, I am using JNA to accomplish this. However, the JNA loader doesn't seem to be able to pick up the .so files and the application fails to start.
I've tried running the application from within IntelliJ and by running the packaged .jar with "java -jar". The .so files are kept in src/main/resources/linux-x86-64 for now. I've tried keeping them in a different directory, e.g src/main/resources/libs/linux-x86-64 and set the property "jna.library.path", but JNA still fails to find the files.
This is the debug log for JNA:
Trying (via loadLibrary) jnidispatch
Looking in classpath from sun.misc.Launcher$AppClassLoader#18b4aac2 for /com/sun/jna/linux-x86-64/libjnidispatch.so
Found library resource at jar:file:/home/dalivi/.m2/repository/net/java/dev/jna/jna/4.5.0/jna-4.5.0.jar!/com/sun/jna/linux-x86-64/libjnidispatch.so
Trying /tmp/jna--1339148563/jna4246531844315283838.tmp
Found jnidispatch at /tmp/jna--1339148563/jna4246531844315283838.tmp
Looking for library 'GTransTF'
Adding paths from jna.library.path: null
Trying libGTransTF.so
Adding system paths: [/usr/lib/x86_64-linux-gnu, /lib/x86_64-linux-gnu, /lib64, /usr/lib, /lib, /lib/i386-linux-gnu, /usr/lib/i386-linux-gnu, /usr/lib/x86_64-linux-gnu/libfakeroot]
Trying libGTransTF.so
Looking for version variants
Looking in classpath from sun.misc.Launcher$AppClassLoader#18b4aac2 for GTransTF
Found library resource at file:/home/dalivi/Workspace/java/geotransboot/target/classes/linux-x86-64/libGTransTF.so
Looking in /home/dalivi/Workspace/java/geotransboot/target/classes/linux-x86-64/libGTransTF.so
2019-04-25 12:43:38.032 ERROR 25897 --- [o-auto-1-exec-1] s.l.g.c.TransformationRestController : Handler dispatch failed; nested exception is java.lang.UnsatisfiedLinkError: libCoreGTrans.so: cannot open shared object file: No such file or directory
I does seem to find one of the files in the directory: libGTransTF.so, but then immediately fails when trying to find the file libCoreGTrans.so which is present in the same directory as the previous file.
I should mention, on Windows, this works just fine. JNA finds the corresponding dll files in the directory specified with "jna.library.path".
The windows behaviour is to search for dependent libraries in the directory that the .dll comes from, so when jna loads the library into memory the dependent library is loaded from there as well.
If you fire up a terminal window and cd to the directory that the .so exists in and run the command:
ldd ./libGTransTF.so
and it indicates that it's unable to find the library libCoreGTrans.so then you can see that the search order won't find this location.
The run-time link-loader (ld.so) uses a set of decisions as to where to find libraries. The default behaviour doesn't include the directory that the library was found.
You can add an option to the library when building to search in specific locations to find libraries. When you're building the library, you can say to search in the directory that the .so comes from at run time by adding the line:
-Wl,-rpath,'$ORIGIN'
to the link line. It needs to populate with the constant value $ORIGIN or else this doesn't work, so this can be a bit tricky to get right in a makefile. This is a value that gets resolved at run-time.
This is all very fine and well if you're building the library yourself, but if you're getting libraries from somewhere else, or you've already built them and don't want to rebuild them, you can use a tool such as patchelf to edit the search path for an .so to add it's origin location:
patchelf --set-rpath '$ORIGIN' libGTransTF.so
Then when you run:
ldd ./libGTransTF.so
it should be able to successfully find the libCoreGTrans.so library.

How to use a JNI lib, with associated libs, at an arbitrary location?

We have an app that sometimes is installed with an associated app. Both may be installed separately, at different times, and neither is usually in the OS Path environment setting.
IF both apps are installed, the one I'm working on needs to use a JNI library from the other app. This library uses a dozen or so other native libs. While I can FIND the JNI lib, I can't seem to find a way to use it without requiring the user to change their system setup.
I've found the (hacky) technique to add the JNI lib to the java.library.path, I've been unable to find any way of updating the native Path so the JNI lib can find it's associated libs.
The only things that have worked so far are to:
Add the folder that the JNI and associated files are in to the OS path before launching our app.
Launch our app so the Current Working Directory is the JNI lib folder.
Neither of which makes for a hassle-free user experience.
So, is there any way for a Java app to modify it's own environment Path so the JNI lib can find it's associated native libs? (currently testing on Win7, will also need to support OS/X)
This can not be done with an unknown location at run time. According to jni documentation
"To load your shared native library module in Java, simply use Java's System.loadLibrary method in a Java class:"
as well as
"Another common reason for the native library not loading is because it is not in your path. On Windows make sure the path environment variable contains the path to the native library. On Unix make sure that your LD_LIBRARY_PATH contains the path to the native library. Adding paths to LD_LIBRARY_PATH can slow down other programs on your system so you may want to consider alternative approaches. For example you could recompile your native library with extra path information using -rpath if you're using GNU, see the GNU linker documentation (ld man page). You could use a command such as ldconfig (Linux) or crle (Solaris) to add additional search paths to the default system configuration (this requires root access and you will need to read the man pages)."
So thus you need to know the location and must be in a path to be able to load the file. If the location is not known it can not be done due to how jni works and the jvm works.
One way to load a JNI lib with dependent libraries is to load each of the dependents then load the JNI library.
For example, if bar.so is dependent on foo.so and both libraries exist in /some/dir, do the following:
System.load("/some/dir/foo.so");
System.load("/some/dir/bar.so");
Use System.load() instead of System.loadLibrary() so you can specify the absolute path to the library.
You'll have to load all dependents of all loaded libraries unless they can be found in the java.library.path path.
Hope this helps.

UnsatisfiedLinkError, but library is in path

I'm a little confused by this error. I'm new to Java and the error seems pretty self explanatory, but I've checked my paths (even defined my own) and it still fails to find this library. Is there something I'm doing wrong? See directory screenshot and error screenshot below:
UPDATE
UPDATE 2
If I create a new NetBeans project this works fine. Same code and everything. If I create new IntelliJ project I get this error above. I must be missing something in IntelliJ
UPDATE 3
Found the solution. Adding dependencies is a bit different in IntelliJ. Thanks all for the help.
http://www.jetbrains.com/idea/webhelp/configuring-module-dependencies-and-libraries.html
Obidisc4j is a .jar file. You just dont see its extension in the explorer.
Regular jar files are not loaded by System.loadLibrary. They are automatically loaded by the JVM's classLoader.
You are using a native library. It doen't matter if that library is in the classpath. There are 4 ways you can make the Java runtime load your shared library at runtime:
Call System.load to load the .so from an explicitly specified absolute path.
Copy the shared library to one of the paths already listed in java.library.path
Modify the LD_LIBRARY_PATH environment variable to include the directory where the shared library is located.
Specify the java.library.path on the command line by using the -D option.
Seems like your application is trying to find out a obidisc4j.dll (for Windows) or obidisc4j.so (for Linux). The file must be present on the PATH (but not the classpath). If you are not sure what PATH is your Java application searching in, you can write the following statement, before the point where the exception takes place, to find out the PATH.
System.out.println(System.getProperty("java.library.path"));
This will tell you about the paths where your DLL or SO file should be placed. You just need to place the file in ONE of those N-paths.

Categories