Статьи

JNI в среде Java Web Start / Applet

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