Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

java.lang.UnsatisfiedLinkError: Native Library XXX.so already loaded in another classloader

I have deployed one web-application, which contains following code.

System.loadLibrary(org.opencv.core.Core.NATIVE_LIBRARY_NAME);

Now, I deployed another web-application which also have same code. When it tries to load library, it throwing following error.

Exception in thread "Thread-143" java.lang.UnsatisfiedLinkError: 
Native Library /usr/lib/jni/libopencv_java248.so already loaded in
another classloader

I want to run these both application simultaneously.

Till now what I have tried:

  1. Loaded library in one application and caught above exception into another application
  2. Removed jars from both application and put opencv.jar into Tomcat's classpath(ie in /usr/share/tomcat7/lib).

But none of above worked, any suggestions by which I can do this ?

Edit: for option two,

System.loadLibrary(Core.NATIVE_LIBRARY_NAME);

This line works but gets exception when I am actually going to use that library. That is when I do following

Mat mat = Highgui.imread("/tmp/abc.png");

And I get this exception

java.lang.UnsatisfiedLinkError: org.opencv.highgui.Highgui.imread_1(Ljava/lang/String;)J
    at org.opencv.highgui.Highgui.imread_1(Native Method)
    at org.opencv.highgui.Highgui.imread(Highgui.java:362)
like image 760
Bhushan Avatar asked Apr 29 '16 11:04

Bhushan


2 Answers

The problem is with how OpenCV handles the initialization of the native library.

Usually a class that uses a native library will have a static initializer that loads the library. This way the class and the native library will always be loaded in the same class loader. With OpenCV the application code loads the native library.

Now there's the restriction that a native library can only be loaded in one class loader. Web applications use their own class loader so if one web application has loaded a native library, another web application cannot do the same. Therefore code loading native libraries cannot be put in a webapp directory but must be put in the container's (Tomcat) shared directory. When you have a class written with the usual pattern above (loadLibrary in static initializer of using class) it's enough to put the jar containing the class in the shared directory. With OpenCV and the loadLibrary call in the web application code however, the native library will still be loaded in the "wrong" class loader and you will get the UnsatisfiedLinkError.

To make the "right" class loader load the native library you could create a tiny class with a single static method doing only the loadLibrary. Put this class in an extra jar and put this jar in the shared Tomcat directory. Then in the web applications replace the call to System.loadLibrary with a call to your new static method. This way the class loaders for the OpenCV classes and their native library will match and the native methods can be initialized.

Edit: example as requested by a commenter

instead of

public class WebApplicationClass {
    static {
        System.loadLibrary(org.opencv.core.Core.NATIVE_LIBRARY_NAME);
    }
}

use

public class ToolClassInSeparateJarInSharedDirectory {
    public static void loadNativeLibrary() {
        System.loadLibrary(org.opencv.core.Core.NATIVE_LIBRARY_NAME);
    }
}

public class WebApplicationClass {
    static {
        ToolClassInSeparateJarInSharedDirectory.loadNativeLibrary();
    }
}
like image 83
user2543253 Avatar answered Sep 24 '22 19:09

user2543253


As of Tomcat versions 9.0.13, 8.5.35, and 7.0.92 we have added the following options to address this issue BZ-62830:

1) Use the JniLifecycleListener to load the native library.

e.g. to load the opencv_java343 library, you can use:

<Listener className="org.apache.catalina.core.JniLifecycleListener"
          libraryName="opencv_java343" />

2) Use the load() or loadLibrary() from org.apache.tomcat.jni.Library instead of System.

e.g.

org.apache.tomcat.jni.Library.loadLibrary("opencv_java343");

Using either of those options will use the Common ClassLoader to load the native library, and therefore it will be available to all of the Web Apps.

like image 24
isapir Avatar answered Sep 21 '22 19:09

isapir