JNI, загрузка нативных библиотек. Меняем java.library.path на лету
В подмножестве экосистемы 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.
А здесь я опишу сам механизм загрузки, и почему то, что описано по ссылкам, работает. Обычно, нативные библиотеки загружаются через статический инициализатор:
* 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, добавив к ним нужные пути к вашим нативным библиотекам.
What is java.library.path? How to set in Eclipse IDE? Example
java.library.path is a System property, which is used by Java programming language, mostly JVM, to search native libraries, required by a project. Similar to PATH and Classpath envir onment variable, java.library.path also includes a list of directory. When Java code loads a native library (a library or executable written in a language like C, C++, or native code) using System.loadLibrary(«name of library») method, java.library.path is scanned for specified library. If JVM doesn’t found the requested native library, then it throws java.lang.UnsatisfiedLinkError: no native library in java.library.path.
Now someone may argue that, why does Java program should depend on a native library, doesn’t this make a Java application platform-dependent ? Well, he would be right, but there are situations, where most of the code is already written in native languages, and you are just writing some Java code on top of that. In that scenario, instead of rewriting the whole stuff, you tend to use native libraries.
Sometimes a third-party JAR (written in Java) d epends upon some native components as well. If you have been using Tibco RV messaging, then you might know that Java API f or Tibco RV depends upon several DLL files, and until those files are present in java.library.path , you can not run that Java program successfully.
Since most of us, run Java programs from Eclipse IDE, it’s important to know How to set java.library.path in Eclipse. In this Java tutorial, I will show you a couple of ways to set java.library.path in Eclipse IDE, you can follow similar steps in other IDE like Netbeans or IntelliJ to set java.library.path.
3 Ways to set java.library.path in Eclipse IDE
If you are already familiar wit h setting PATH and Classpath in Java, then this shouldn’t be a problem. Since java.library.path is a system property, most common way to set this is by providing as JVM arguments. Since at low level (if you are using start-up scripts), Java program starts with «java» command, you can provide them system property using -Dpropery=value . In order to set java.library.path you can provide -Djava.library.path=C:\Windows to set it in Windows.
By the way, it’s a little different in Eclipse, which we will see in next section, which shows three places to set java.library.path in Eclipse.
- 30 Useful Eclipse Shortcuts for Java Developers (list)
- How to remote debug Java application in Eclipse? (tutorial)
- 10 Eclipse debugging tips Java developer should know? (see here)
- How to attach source code for the JAR file in Eclipse? (guide)
- Eclipse shortcut to print System.out.println statements? (shortcut)
- How to increase the console buffer size in Eclipse? (steps)
- How to use spaces instead of tabs in Eclipse? (guide)
- How to create an executable JAR file from Eclipse? (example)
- 3 Books to Learn Eclipse IDE for Java developers (list)
- How to Increase Heap Size of Java Program running in Eclipse? (guide)
6 comments :
Doesn't by using native library or code, you risk your Java program of making platform dependent? Isn't it against of Java's motive of creating application which is platform independent?
I have never set that java.library.path and never faced any issue. Do you know that is the default value of java.library.path, and why it's not a problem to not set this?
Few things to note about java.library.path system property :
1) java.library.path is used to search native libraries used by your Java application e.g. tibco rv binaries, .dll files in windows and .so files in Linux.
2) If you don't provide explicit value for java.library.path, it is automatically set to PATH by JVM in Windows operating system and to the value of LD_LIBRARY_PATH in UNIX e.g. Solaris or Linux.
3) They only contains location of native binaries not JAR files.
4) If your native libraries are not located on those location then you will get unsatisfied link error.
You can also check value of this system property by using following code :
System.out.println("java.library.path : " + System.getProperty("java.library.path"));
I have performed the steps as mentioned by specifying the path of the DLL under the native library but still the DLL is not found?
Как установить java.library.path из Eclipse
Как установить java.library.path для всего проекта Eclipse? Я использую библиотеку Java, которая опирается на конкретные файлы ОС и должна найти .dll/.so/.jnilib. Но приложение всегда выходит с сообщением об ошибке, что эти файлы не найдены на пути к библиотеке.
Я хотел бы настроить весь этот проект на использование пути к библиотеке. Я попытался добавить путь в качестве аргумента VM для некоторых конфигураций запуска в eclipse, но это не сработало.
15 ответов
Не путайте путь к библиотеке! Eclipse строит его сам!
Вместо этого перейдите в настройки библиотеки для своих проектов, и для каждой банки /etc, которая требует встроенную библиотеку, разверните ее на вкладке «Библиотеки». В древовидной структуре в каждой библиотеке есть элементы для исходных /javadoc и локальных библиотек.
В частности: выберите Project , щелкните правой кнопкой мыши → Свойства/вкладка «Путь/Библиотеки компоновки Java», выберите .jar, разверните его, выберите «Место нативной библиотеки», нажмите «Изменить», откроется диалоговое окно выбора папки)
Взаимодействие с пути библиотеки в командной строке должно быть вашим последним усилием в канаве, потому что вы можете сломать то, что уже правильно установлено eclipse.
How to set the java.library.path from Eclipse
How can I set the java.library.path for a whole Eclipse Project? I’m using a Java library that relies on OS specific files and need to find a .dll/ .so/ .jnilib . But the Application always exits with an error message that those files are not found on the library path.
I would like to configure this whole project to use the library path. I tried to add the path as a VM argument to some run configurations in eclipse but that didn’t work.
![]()
![]()
16 Answers 16
Don’t mess with the library path! Eclipse builds it itself!
Instead, go into the library settings for your projects and, for each jar/etc that requires a native library, expand it in the Libraries tab. In the tree view there, each library has items for source/javadoc and native library locations.
Specifically: select Project , right click -> Properties / Java Build Path / Libraries tab, select a .jar, expand it, select Native library location, click Edit, folder chooser dialog will appear)
Messing with the library path on the command line should be your last ditch effort, because you might break something that is already properly set by eclipse.