Java: загрузка общих библиотек с зависимостями - PullRequest
7 голосов
/ 24 марта 2011

Я обертываю разделяемую библиотеку (написанную на C) с помощью Java, используя JNA. Общая библиотека написана внутри, но эта библиотека использует функции из другой внешней библиотеки, которая снова зависит от другой внешней библиотеки. Так что ситуация примерно такая:

ext1 <- ext2 <- внутренний </p>

т.е. внутренняя использует внешнюю библиотеку ext2, которая снова использует внешнюю библиотеку ext1. Я попробовал:

System.loadLibrary("ext1");
System.loadLibrary("ext2");
NativeLIbrary.loadLibrary("internal",xxx.class);  

Этот подход не работает с «UnresolvedException» при загрузке библиотеки «ext2»; компоновщик жалуется на символы, которые действительно присутствуют в библиотеке "ext1". Итак, кажется, что функция System.loadLibrary () не делает символы из «ext1» глобально доступными? При использовании функции stdlib dlopen () как:

handle = dlopen( lib_name , RTLD_GLOBAL );

Все символы, найденные в @lib_name, будут доступны для разрешения символов при последующих загрузках; Я думаю, что я хотел бы, чтобы было что-то похожее на Java System.loadLibrary ()?

С уважением - Йоаким Хоув

Ответы [ 5 ]

4 голосов
/ 13 апреля 2012

Это старый вопрос, но я нашел приемлемое решение, которое также должно быть переносимым, и я решил опубликовать ответ. Решение состоит в том, чтобы использовать JNA NativeLibrary#getInstance(), потому что в Linux это будет передаваться RTLD_GLOBAL в dlopen() (а в Windows это не нужно).

Теперь, если вы используете эту библиотеку для реализации метода Java native, вам также потребуется вызвать System.load() (или Sysem.loadLibrary()) в той же библиотеке после вызова NativeLibrary#getInstance().

Сначала ссылка на ошибку JNA: JNA-61

В комментарии там говорится, что в основном нужно загружать зависимости, прежде чем фактическая библиотека будет использовать изнутри JNA, а не стандартным способом Java. Я просто скопирую и вставлю свой код, это типичный сценарий :

String libPath =
        "/path/to/my/lib:" + // My library file
        "/usr/local/lib:" +  // Libraries lept and tesseract
        System.getProperty("java.library.path");

System.setProperty("jna.library.path", libPath);

NativeLibrary.getInstance("lept");
NativeLibrary.getInstance("tesseract");
OcrTesseractInterf ocrInstance = (OcrTesseractInterf)
        Native.loadLibrary(OcrTesseractInterf.JNA_LIBRARY_NAME, OcrTesseractInterf.class);

Я написал небольшую библиотеку для обеспечения возможности распознавания текста моим приложением Java с использованием Tesseract. Тессеракт зависит от Leptonica, поэтому для использования моей библиотеки мне нужно сначала загрузить библиотеки lept и tesseract . Загрузка библиотек стандартными средствами (System.load () и System.loadLibrary ()) не помогает, как и установка свойств jna.library.path или java.library. путь . Очевидно, JNA любит загружать библиотеки по-своему.

Это работает для меня в Linux, я думаю, если установить правильный путь к библиотеке, это должно работать и в других ОС.

2 голосов
/ 25 мая 2017

Для этого есть еще одно решение. Вы можете добавить непосредственно в код JNI, например:

void loadLibrary() {
  if(handle == NULL) {
    handle = dlopen("libname.so", RTLD_LAZY | RTLD_GLOBAL);
    if (!handle) {
      fprintf(stderr, "%s\n", dlerror());
      exit(EXIT_FAILURE);
    }
  }
}

...
...

loadLibrary();

Таким образом, вы откроете библиотеку с RTLD_GLOBAL.

Подробное описание вы можете найти здесь: http://www.owsiak.org/?p=3640

2 голосов
/ 25 марта 2011

OK;

В конце концов я нашел приемлемое решение, но не без значительного количества обручей. Что я делаю, это

  1. Используйте обычный механизм JNA для сопоставления функции dlopen () из библиотеки динамической компоновки (libdl.so).
  2. Используйте функцию dlopen (), сопоставленную с JNA, для загрузки внешних библиотек "ext1" и "ext2" с установленным параметром RTLD_GLOBAL.

На самом деле, похоже, работает: -)

0 голосов
/ 05 августа 2018

Как описано в http://www.owsiak.org/?p=3640,, простое, но грубое решение в Linux заключается в использовании LD_PRELOAD.

Если это неприемлемо, то я бы порекомендовал ответ Oo.oO: dlopen библиотека с RTLD_GLOBAL в коде JNI.

0 голосов
/ 25 марта 2011

Попробуйте, добавьте эту функцию в свой код. Позвоните, прежде чем загружать свои DLL. Для параметра используйте местоположение ваших dll.


    public boolean addDllLocationToPath(String dllLocation)
    {
        try
        {
            System.setProperty("java.library.path", System.getProperty("java.library.path") + ";" + dllLocation);
            Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
            fieldSysPath.setAccessible(true);
            fieldSysPath.set(null, null);
        }
        catch (Exception e)
        {
            System.err.println("Could not modify path");
            return false;
        }
        return true;
    }
}

...