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

前端 未结 4 966
失恋的感觉
失恋的感觉 2020-12-01 18:09

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

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

Now, I de

相关标签:
4条回答
  • 2020-12-01 18:49

    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.

    0 讨论(0)
  • 2020-12-01 18:49

    I got stuck on this exact problem.

    Adding listener in Tomcat (v8.5.58) server.xml file seems to successfully load the dll file (at least the log says so) when Tomcat starts, but when you call the the native method, it fails with java.lang.UnsatisfiedLinkError.

    With or without calling "org.apache.tomcat.jni.Library.loadLibrary("TeighaJavaCore");" in my java code makes no difference, same error remain. I include tomcat-jni dependency in my project to enable the "org.apache.tomcat.jni.Library.loadLibrary("TeighaJavaCore")" call. While, I guess there is no need to call "org.apache.tomcat.jni.Library.loadLibrary("TeighaJavaCore")" in java code (at web application level) as the TeighaJavaCore.dll will be automatically loaded when Tomcat starts (because the listener above is defined for this purpose at Tomcat container level)

    I also check the source code of "org.apache.tomcat.jni.Library.loadLibrary" here, it simply calls "System.loadLibrary(libname)".

    https://github.com/apache/tomcat-native/blob/master/java/org/apache/tomcat/jni/Library.java

    0 讨论(0)
  • 2020-12-01 19:03

    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();
        }
    }
    
    0 讨论(0)
  • 2020-12-01 19:11

    As of javacpp>=1.3 you may also change the cache folder (defined by system property) in your war deployment listener:

    System.setProperty("org.bytedeco.javacpp.cachedir",
                       Files.createTempDirectory( "javacppnew" ).toString());
    

    Note though that native libraries are always unpacked and will be loaded several times (because considered as different libs).

    0 讨论(0)
提交回复
热议问题