Как стать автором
Обновить

JNI, загрузка нативных библиотек. Меняем java.library.path на лету

Время на прочтение3 мин
Количество просмотров38K
В подмножестве экосистемы Java, относящейся в основном к JNI (без которого никуда не деться, если приходиться интегрироваться с каким-то legacy или просто редким и специфическим кодом, написанном на С или каком-то другом языке), есть такое понятие, как java.library.path. Вкратце, это в некотором роде аналог classpath, только не для Java классов и *.jar файлов, а для нативных библиотек — системное свойство, которое указывает JVM, где искать эти самые нативные библиотеки (.dll в винде или .so под юниксами).

Свойство это устанавливается один раз, перед запуском JVM, через глобальные system properties, или как ключ -Dname=value для JVM, и после этого оно становится read-only. Точнее, менять-то его можно, но никакого эффекта на работу программы это не окажет, т.к. после того как вы обновите это свойство, JVM не перечитает его и не будет использовать новое значение.

Под катом — про то, как все таки поменять это свойство в рантайме, и немного о том, как собственно работает загрузка нативных библиотек в Java.



Однако, возможность менять java.library.path на лету была бы очень кстати — тогда бы не пришлись много раз генерить, переписывать и перезаписывать скрипты для запуска JBoss-a, например, чтобы отразить в них все нужные пути ДО старта аппсервера.

И такая возможность, изменять эти пути, по которым JVM ищет нативные библиотеки, на самом деле есть. Конкретные приемы показаны тут — blog.cedarsoft.com/2010/11/setting-java-library-path-programmatically и еще вот тут — nicklothian.com/blog/2008/11/19/modify-javalibrarypath-at-runtime.

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

static {
 try {
  System.loadLibrary(«dp-integ»);
 }
}


* This source code was highlighted with Source Code Highlighter.


Который выглядит так:

public static void loadLibrary(String libname) {
 Runtime.getRuntime().loadLibrary0(getCallerClass(), libname);
}


* This source code was highlighted with Source Code Highlighter.


И далее в классе Runtime:

synchronized void loadLibrary0(Class fromClass, String libname) {
 // Проверяем, разрешено ли загружать данную конкретную библиотеку
 SecurityManager security = System.getSecurityManager();
 if (security != null) {
  security.checkLink(libname);
 }
 if (libname.indexOf((int)File.separatorChar) != -1) {
  throw new UnsatisfiedLinkError("Directory separator" +
   "should not appear in library name: " + libname);
 }
 ClassLoader.loadLibrary(fromClass, libname, false);
}


* This source code was highlighted with Source Code Highlighter.


Т.е. в итоге, нативные библиотеки загружаются, так же как и обычные классы, через ClassLoader. У класса ClassLoader есть два свойства, в которых кешируются проинициализированные пути поиска.

// The paths searched for libraries
static private String usr_paths[];
static private String sys_paths[];


* This source code was highlighted with Source Code Highlighter.


Код метода ClassLoader.loadLibrary(fromClass, libname, false), довольно длинный, и загроможденный многочисленными проверками, в сокращенном виде выглядит это так.

// Invoked in the java.lang.Runtime class to implement load and loadLibrary.
static void loadLibrary(Class fromClass, String name,
    boolean isAbsolute) {
  
 ClassLoader loader = (fromClass == null) ? null : fromClass.getClassLoader();
 if (sys_paths == null) {
  // это то, что нам нужно
  usr_paths = initializePath("java.library.path");
  
  // а это для тех библиотек, которые загружаются из классов,
  // загруженных из boot classpath.
  sys_paths = initializePath("sun.boot.library.path");
 }

 // Дальше попытка загрузить библиотеку, и дальше,
 // если найти ее так и не удалось, то - 
 // Oops, it failed
 throw new UnsatisfiedLinkError("no " + name + " in java.library.path");
}


* This source code was highlighted with Source Code Highlighter.


Таким образом, теперь механизм загрузки нативной библиотеки стал более понятен.

Вы можете либо выставить в null свойство sys_paths у класслоадера, либо просто поменять свойства sys_paths / usr_paths, добавив к ним нужные пути к вашим нативным библиотекам.
Теги:
Хабы:
Всего голосов 28: ↑26 и ↓2+24
Комментарии14

Публикации