Использование Java native library на серверах приложений

Java native library (JNL) представляет собой JAR-архив, содержащий в себе JNI-код и объекты, которые операционная система может загрузить в качестве разделяемых библиотек. Это позволяет вызывать из Java-приложения функции, реализованные платформо-зависимыми методами. Способы создания JNL — это тема отдельной большой статьи, поэтому считаем, что у вас уже есть JNL и вы хотите ею воспользоваться в своем приложении. Об особенностях использования JNL в приложениях, работающих под управлением сервера приложений, и будет эта статья.

Использование JNL приводит к следующим негативным последствиям:
  • код в JNL выполняется вне Java-машины, что может привести к проблемам с безопасностью;
  • сама JNL, скорее всего, имеет ограниченное количество поддерживаемых платформ (комбинации «операционная система»-«процессор»). Соответственно, попытка запуска приложения, использующего JNL на платформе, которая JNL не поддерживается, закончится неудачно.

Однако в некоторых случаях без JNL обойтись невозможно. В качестве примеров можно назвать:
  • необходимость напрямую использовать API операционной системы из Java-приложения (например, для реализации работы с последовательными портами ввода-вывода);
  • необходимость реализации тяжеловесных алгоритмов (таких, как перекодирование медиа).

Использование JNL в standalone-приложениях практически ничем не отличается от использования обычных Java-библиотек. Т.е. необходимым условием является расположение JNL в месте, известном загрузчику классов (обычно достаточно разместить библиотеку где-нибудь в classpath). Однако иногда бывает необходимо разместить JNL в каталоге, непосредственно доступном и системному загрузчику ОС.

А вот если приложение предназначено для работы на каком-либо сервере приложений, то просто так засунуть JNL WAR/EAR файл и задеплоить его вместе с самим приложением на сервер не получится. Причина очевидна: возможная дыра в безопасности. Приложение с JNL получает доступ к операционной системе с привилегиями сервера приложений (это как минимум). Сервер приложений имеет свою собственную систему безопасности, и обходить ее пользовательскому приложению будет как-то не по пацански.

Таким образом, если вы засунете JNL непосредственно в EAR или WAR, то приложение, конечно же, задеплоится. Но вот при попытке вызова JNI-кода вы получите исключение (скорее всего это будет java.lang.UnsatisfiedLinkError с диагностикой Native Library already loaded in another classloader).

Так что же, использование JNL на сервере приложений абсолютно невозможно? На самом деле, это не так. Просто необходимо объяснить самому серверу приложений, что JNL используется на законных основаниях (и, наверное, ее использование для самого сервера приложений будет более-менее безопасно). Ну и далее начинаются тонкости, зависящие от конкретного сервера приложений.

В любом случае, при компиляции Java-кода необходимо указать пути к используемой JNL (чтобы иметь возможность ссылаться на содержащиеся в ней Java-классы), но вот упаковывать ее внутрь EAR/WAR или JAR файла, предназначенного для деплоймента, не надо. Дальнейшие действия зависят от целевого сервера приложений.

Glassfish 3.x, 4.x

Необходимо скопировать JNL в domain-dir/lib или domain-dir/lib/ext (при этом JNL будет доступна для всех приложений). В конфигурации Glassfish и в пользовательском приложении никаких дополнительных изменений делать не требуется.

WildFly, Jboss

Здесь все немного сложнее. Последовательность действий следующая:
  • создаем внутри $JBOSS_ROOT/modules набор каталогов вида:
    $JBOSS_ROOT/modules/путь/к/моему/модулю/main
  • копируем требуемый JNL в каталог:
    $JBOSS_ROOT/modules/путь/к/моему/модулю/main
  • там же создаем текстовой файл с именем module.xml и следующим содержимым:
    <?xml version="1.0" encoding="UTF-8"?>
    <module xmlns="urn:jboss:module:1.3" name="желаемое.имя.моего.модуля">
    <resources>
    <resource-root path="моя_JNL.jar"/>
    </resources>
    </module>
    

  • в приложении, которое хочет использовать JNL, в файле MANIFEST.MF (расположенном в /META-INF) добавляем строчку:
    Dependencies: желаемое.имя.моего.модуля export

    Параметр «export» добавляем в том случае, если мы хотим сделать наш JNL доступным не только для текущего модуля, но и для других модулей, которые в нем содержатся (например внутри EAR).
Поделиться публикацией
Комментарии 2
    0
    А как быть с Tomcat?

    Кстати, по моему опыту, UnsatisfiedLinkError выбрасывается при редеплое приложения. При попытке повторно загрузить .so JVM обнаруживает, что он связан с другим классом, отсюда ошибка. Пока что мне эту проблему решить не удалось.

    Точнее, решаю перезапуском контейнера.
      –1
      Ну ещё в таких случаях ресурсы текут часто. И треды-то лучше не создавать, если используется сервлет-контейнер или аппсервер, а с jni связываться — тем более. Часто можно отделить этот «кусок» в отдельный микросервис и спать спокойно.

    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

    Самое читаемое