Подтвердить что ты не робот

Java.lang.UnsatisfiedLinkError: Собственная библиотека XXX.so уже загружена в другой загрузчик классов

Я развернул одно веб-приложение, содержащее следующий код.

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

Теперь я развернул другое веб-приложение, которое также имеет тот же код. Когда он пытается загрузить библиотеку, она бросает следующую ошибку.

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

Я хочу запустить оба этих приложения одновременно.

До сих пор я пробовал:

  • Загруженная библиотека в одном приложении и исключенная выше исключение в другое приложение
  • Удалены банки из обоих приложений и поместите opencv.jar в путь Tomcat classpath (то есть в /usr/share/tomcat 7/lib).

Но никто из вышеперечисленных не работал, никаких предложений, по которым я могу это сделать?

Изменить: для второй опции

System.loadLibrary(Core.NATIVE_LIBRARY_NAME);

Эта строка работает, но получает исключение, когда я собираюсь использовать эту библиотеку. То есть, когда я выполняю

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

И я получаю это исключение

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)
4b9b3361

Ответ 1

Проблема заключается в том, как OpenCV обрабатывает инициализацию родной библиотеки.

Обычно класс, который использует собственную библиотеку, будет иметь статический инициализатор, который загружает библиотеку. Таким образом, класс и встроенная библиотека всегда будут загружаться в один и тот же загрузчик классов. С OpenCV код приложения загружает собственную библиотеку.

Теперь существует ограничение того, что встроенная библиотека может быть загружена только в один загрузчик классов. Веб-приложения используют свой собственный загрузчик классов, поэтому, если одно веб-приложение загрузило собственную библиотеку, другое веб-приложение не может сделать то же самое. Поэтому загрузка исходных библиотек кода не может быть помещена в каталог webapp, но должна быть помещена в общий каталог контейнера (Tomcat). Когда у вас есть класс, написанный с обычным шаблоном выше (loadLibrary в статическом инициализаторе использования класса), достаточно разместить банку, содержащую класс в общей папке. Однако при вызове OpenCV и loadLibrary в коде веб-приложения собственная библиотека все равно будет загружена в "неправильный" загрузчик классов, и вы получите UnsatisfiedLinkError.

Чтобы "правый" загрузчик классов загрузил собственную библиотеку, вы могли бы создать крошечный класс с помощью одного статического метода, выполняющего только loadLibrary. Поместите этот класс в дополнительную банку и поместите эту банку в общий каталог Tomcat. Затем в веб-приложениях замените вызов System.loadLibrary вызовом нового статического метода. Таким образом, загрузчики классов для классов OpenCV и их родной библиотеки будут соответствовать, а собственные методы могут быть инициализированы.

Изменить: пример, запрошенный комментатором

вместо

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

использование

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

public class WebApplicationClass {
    static {
        ToolClassInSeparateJarInSharedDirectory.loadNativeLibrary();
    }
}

Ответ 2

По версии Tomcat 9.0.13, 8.5.35 и 7.0.92 мы добавили следующие параметры для решения этого вопроса BZ-62830:

1) Используйте JniLifecycleListener для загрузки собственной библиотеки.

Например, чтобы загрузить библиотеку opencv_java343, вы можете использовать:

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

2) Используйте load() или loadLibrary() из org.apache.tomcat.jni.Library вместо System.

например

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

Использование любого из этих параметров будет использовать Common ClassLoader для загрузки собственной библиотеки, и, следовательно, она будет доступна для всех веб-приложений.

Ответ 3

Как и в javacpp >= 1.3, вы также можете изменить папку кэша (определяемую системным свойством) в своем прослушивателе развертывания войны:

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

Обратите внимание, что встроенные библиотеки всегда распаковываются и будут загружаться несколько раз (потому что считаются разными libs).