Y эс! Я тоже ! Я потратил огромное количество времени на решение проблем с использованием JNI в Java Web Start или в среде апплетов. И, выполнив поиск в моем друге Google, я обнаружил, что я не единственный разработчик Java в мире Java, который сталкивался с подобными проблемами …
Иногда это работает довольно хорошо, особенно если вы используете Java Web Start для переноса ваше приложение (Swing / Eclipse RCP или Applet), вам просто нужно выполнить довольно простую процедуру, которая может быть автоматизирована с помощью простого скрипта Ant (для получения более подробной информации о Java Web Start, посмотрите довольно неплохой веб-сайт Lopica ):
1) Jar родной библиотеки:
<jar destfile="lib/nativewin.jar" basedir="native/win/" includes="some.dll"> </jar>
2) Подпишите банки в вашем заявлении:
<target name="genkey"> <delete file="${keystore}"> <genkey alias="alias" keystore="${keystore}" storepass="storepass" validity="900"> <dname> <param name="CN" value="CN"> <param name="OU" value="OU"> <param name="O" value="O"> <param name="C" value="C"> </dname> </genkey> </delete></target> <target name="signjar" depends="genkey"> <signjar alias="alias" keystore="${keystore}" storepass="storepass"> <fileset dir="${dest.dir}/"> <include name="**/*.jar"> </include></fileset> </signjar> </target>
3) Добавьте хороший тег в свой JNLP:
<resources os="Windows" arch="x86"> <nativelib href="lib/nativewin.jar"> </nativelib></resources>
И все хорошо. Да, иногда все в порядке, но иногда, по некоторым темным причинам, которые я не совсем понимаю, с некоторыми родными библиотекарями, а не с другими, особенно когда у вас нет времени тратить на эту проблему, и всегда на производственная среда, потому что при тестировании в вашей среде разработки вы забыли удалить нативный lib вашей PATH… Так что иногда приложение вылетает со знаменитым:
java.lang.UnsatisfiedLinkError: нет foobar в java.library.path
Мы здесь. Вы проверяете свой JNLP: кажется, хорошо. Вы проверяете банку с исходной библиотекой lib: hmm OK. Сейчас 10 вечера, а вы все еще в офисе: «Должно быть, это какая-то глупая вещь, которую я забыл. Завтра еще раз проверю, что, вероятно, найду решение за 10 минут ».
Наивный из вас …
На следующий день вы проводите все утро, проверяя все эти мелочи, 5 раз перезагружая свой компьютер, но он все еще здесь: UnsatisfiedLinkErrorOfTheDeath …
Первый обходной путь, конечно, состоит в том, чтобы скопировать нативные библиотеки в папку, которая ранее была в вашем PATH. Копирование может быть выполнено с помощью некоторого установочного кода программы, например, с помощью кода, который будет выглядеть следующим образом (нативный код находится в Jar, поэтому нам нужно получить к нему доступ в виде потока):
InputStream src = SomeClass.class.getResourceAsStream("/pathToTheLib/" + libName); File libDestFile = new File(installLibFilePath); FileOutputStream out = null; try { out = new FileOutputStream(libDestFile); byte[] buffer = new byte[256]; int read = 0; while ((read = src.read(buffer)) > 0) { out.write(buffer, 0, read); read = src.read(buffer); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { // close try { if (out != null) { out.close(); } } catch (IOException e) { e.printStackTrace(); } }
Здесь у нас есть две возможности: либо наша программа загружает нативный lib, в этом случае мы заменяем инструкцию:
java.lang.System.load(libName);
По:
java.lang.System.load(libDestFile.getAbsolutePath());
Но если это библиотека (еще один Jar), которая загружает нативную библиотеку, мы застряли. В этом случае нам нужно вручную установить какое-либо свойство env PATH, чтобы ClassLoader нашел родную библиотеку, и ваша душа-разработчик Java сильно плачет, думая о том, чтобы сделать такую позорную вещь.
Итак, как нам сделать такой трюк в Java… Ну, как говорится в сообщении об исключении, нам нужно добавить библиотеку в свойство java.library.path .
Что это за собственность? Он используется, когда java.lang.ClassLoader загрузит первую нативную библиотеку, чтобы инициализировать ее поле usr_paths, которое является массивом String. Действительно, в методе loadLibrary мы можем увидеть этот код:
if (sys_paths == null) { usr_paths = initializePath("java.library.path"); sys_paths = initializePath("sun.boot.library.path"); }
Итак, увидев этот код, мы понимаем, что переопределять это свойство бесполезно, добавив папку, в которую мы будем копировать нашу библиотеку, после запуска JVM, так как мы будем слишком поздно, поле sys_paths будет инициализировано, поэтому пути usr_path не будут обновлены.
И, конечно же, к этому полю нельзя получить прямой доступ, поэтому мы в крайнем случае должны использовать отражение :
Field usrPathFiled = ClassLoader.class.getDeclaredField("usr_paths"); usrPathFiled.setAccessible(true); String[] usrPath = (String[]) usrPathFiled.get(null); String[] newUsrPath = new String[usrPath.length + 1]; System.arraycopy(usrPath, 0, newUsrPath, 0, usrPath.length); newUsrPath[usrPath.length] = destFile.getParentFile().getAbsolutePath(); usrPathFiled.set(null, newUsrPath);
Выполняя этот код перед использованием jar, который загрузит нативную библиотеку, мы имеем программный 100% обходной путь Java для нашей проблемы.
Это определенно не самый красивый способ кодирования в Java, некоторые из вас могут сказать: «Если в JDK он закрыт, есть веская причина, сынок!»
В любом случае, должна ли эта статья избегать того, чтобы другие люди тратили время на решение этой проблемы.
От http://lewisleo.blogspot.jp/2012/08/unsatisfiedlinkerror-in-javawebstart.html