Friday, September 18, 2015

Resolving java.lang.UnsatisfiedLinkError: Native Library already loaded ...

 java.lang.UnsatisfiedLinkError

It was bit of relief when this issue was resolved, but it took a tormented a while before figuring how to resolve this issue. Solution was not much any big science, in fact very simple and outright.

What exactly is this about?

When you want to run 2 separate application in your tomcat container, and suppose both of them had to load a shared native wrapper, you are most likely to encounter this issue. It comes to single line that you would have anywhere in a place the application starts. If web application, a simple context listener class.

System.loadLibrary("excalibur"); 

Here by, you tell the listener to load the native library on application startup, in Windows system this this will load excalibur.dll from path this in "java.library.path", in Linux system this would be file named libexcalibur.so in "java.library.path". In Linux you can configure this path as follows 

export LD_LIBRARY_PATH=/path/to/whereyourlinklibrary

in turn, this path marked and added existing java.library.path.

Behind the scenes you Tomcat class loaders works bring this library to JVM. So, if you the same approach to load the same library for in another web application running in the container, you are in for surprise. Why, Tomcat, uses separate class loaders for each of the web application, and it wont allow you load a same native library more than once to JVM via another class loader. Hence you will get some decent message like this, 

Resolving java.lang.UnsatisfiedLinkError: Native Library already loaded
How exactly Tomcat class loaders works behind the scenes is explained here. So, how can you work this out

Use Tomcat shared class loader

Tomcat shared class loader make this possible, it loads library only once in container and allows that to be shared across all the web application in the container. First, you need to completely remove your wrapper jar, which you have built to invoke methods from libexcalibur.so. Say this wrapper library called knightofjustice.jar. In your maven, if you have marked this as a dependency, remember to mark this scope as provided. so it wont be packaged into your lib folder of your web application. 


<dependency>
 <artifactId>knightofjustice</artifactId>
 <groupId>org.rome.empire</groupId>
 <version>1.0.0</version>
 <scope>provided</scope>
</dependency> 

Then move your precious knightofjustice.jar inside the territory of (folder) Tomcat\lib. This makes tomcat to load the knightofjustice whenever Tomcat container starts.

ok, Now how do our applications get to know if libexcalibur.so link library available in JVM and start using it.

One guy(probably a descendant of King Arthur), has written utility library here, you can download it and build the source code, you get a jar dll-bootstrapper, move this library under same Tomcat\lib along with a property file called dll-bootstrapper.properties, where you are going to tell the tomcat which link libraries to be loaded, like our  libexcalibur.so.

Just add a line in dll-bootstrapper.properties

dll.0=excalibur

Remember, excalibur NOT libexcalibur.so, in Linux based system this will be interpreted correctly as libexcalibur.so

OR,

you can simply add a class with static initialization like this, (with the hard coded library name)

public class DLLBootstrapper {

 static {
  System.loadLibrary("excalibur ");
              }

public static void main(String args[]) {
  System.out.println("Loaded");
 }

}

and then compile the class and simply drop inside the Tomcat\lib folder, all good to go!

Ok, good. Now additionally you also need to reference the Loader class that has loaded your libexcalibur.so.

So, go back to your web application and add this line in a context listener, probably in contextInitialized method


Class.forName("msm.DLLBootstrapper");

//Now, you can call your wrapper library classes to invoke methods from it such as

HolyGrail grail = new HolyGrail();
grail.serve();
Some important things to remember
Never keep your wrapper inside your web application, if so Tomcat may try to load from using application class loader. And you will still get  Native Library already loaded ... message

if you still get java.lang.UnsatisfiedLinkError, check by printing java.library.path and see if you libraryxxx.so resides in any of the path printed. if so adjust it accordingly. or you can explicitly set the System.setProperty("java.library.path","/path/to/foldercontainingthelibrary")

String property = System.getProperty("java.library.path");

StringTokenizer parser = new StringTokenizer(property, ";");

while (parser.hasMoreTokens()) {

System.err.println(parser.nextToken());

}

No comments:

Post a Comment