Java - загрузка DLL по относительному пути и скрыть их внутри банки
ЧАСТЬ 1
Я разрабатываю приложение Java, которое должно быть выпущено как банку. Эта программа зависит от внешних библиотек С++, называемых JNI. Чтобы загрузить их, я использую метод System.load
с абсолютным путем, и это отлично работает.
Однако я действительно хочу "спрятать" их внутри JAR, поэтому я создал пакет для их сбора. Это заставляет меня загружать относительный путь - путь пакета. Благодаря такому подходу я разрешаю пользователю запускать JAR в любом каталоге, не беспокоясь о необходимости связывать библиотеки DLL или скучать с предыдущим процессом установки.
Это генерирует ожидаемое исключение:
Исключение в потоке "main" java.lang.UnsatisfiedLinkError: ожидающий абсолютный путь библиотеки
Как я могу заставить это работать?
ЧАСТЬ 2
Подход к копированию DLL в папку (объясняется ниже) работает только при запуске в среде eclipse. Запуск экспортированного JAR, двоичные файлы DLL хорошо созданы, но загрузка JNI создает следующее исключение:
Исключение в потоке "main" java.lang.reflect.InvocationTargetException
at org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader.main(JarRsrcLoader.java:56)
Caused by: java.lang.UnsatisfiedLinkError: C:\Users\Supertreta\Desktop\nm files\temp\jniBin.dll: Can't find dependent libraries at java.lang.ClassLoader$NativeLibrary.load(Native Method)
Я запускаю этот метод загрузки:
public static void loadBinaries(){
String os = System.getProperty("os.name").toLowerCase();
if(os.indexOf("win") >= 0){
ArrayList<String> bins = new ArrayList<String>(){{
add("/nm/metadata/bin/dependence1.dll");
add("/nm/metadata/bin/dependence2.dll");
add("/nm/metadata/bin/dependence3.dll");
add("/nm/metadata/bin/dependence4.dll");
add("/nm/metadata/bin/jniBin.dll");
}};
File f = null;
for(String bin : bins){
InputStream in = FileManager.class.getResourceAsStream(bin);
byte[] buffer = new byte[1024];
int read = -1;
try {
String[] temp = bin.split("/");
f = new File(TEMP_FOLDER, temp[temp.length-1]);
FileOutputStream fos = new FileOutputStream(f);
while((read = in.read(buffer)) != -1) {
fos.write(buffer, 0, read);
}
fos.close();
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
System.load(f.getAbsolutePath());
}
}
Я думаю, что это может быть проблема с правами доступа, но не знаю, как ее решить. Как вы думаете?
Ответы
Ответ 1
Я не верю, что вы можете загрузить DLL непосредственно из JAR. Вы должны предпринять промежуточный шаг по копированию DLL из JAR. Следующий код должен сделать это:
public static void loadJarDll(String name) throws IOException {
InputStream in = MyClass.class.getResourceAsStream(name);
byte[] buffer = new byte[1024];
int read = -1;
File temp = File.createTempFile(name, "");
FileOutputStream fos = new FileOutputStream(temp);
while((read = in.read(buffer)) != -1) {
fos.write(buffer, 0, read);
}
fos.close();
in.close();
System.load(temp.getAbsolutePath());
}
Ответ 2
В основном это должно работать. Так как это делает JNA, просто загрузите его и изучите код. У вас даже есть некоторые подсказки, чтобы сделать эту платформу независимой...
ИЗМЕНИТЬ
JNA привносит свой собственный код в банку, распаковывает правильный двоичный файл во время выполнения и загружает его. Это может быть хорошей моделью (если я правильно понял ваш вопрос).
Ответ 3
Этот JarClassLoader, стремящийся решить ту же проблему:
http://www.jdotsoft.com/JarClassLoader.php
Ответ 4
Вам нужно закодировать загрузчик классов с расположением DLL, но его можно загрузить без извлечения из банки. Достаточно простого перед выполнением вызова загрузки. В своем основном классе добавьте:
static {
System.loadLibrary("resource/path/to/foo"); // no .dll or .so extension!
}
В частности, я столкнулся с той же проблемой с обработкой JNA и OSGi, как загружаются DLL.