web-dev-qa-db-ja.com

Java-相対パスでdllをロードしてjar内に隠す

パート1

私はJavaアプリケーションをjarとしてリリースする必要があります。このプログラムは、JNIによって呼び出されるC++外部ライブラリに依存しています。それらをロードするには、メソッドSystem.loadを絶対パスで使用しますパスとこれは正常に動作します。

ただし、私は本当にそれらをJAR内に「隠したい」ので、それらを収集するためのパッケージを作成しました。これにより、相対パス(パッケージパス)を強制的にロードします。このアプローチにより、DLLをリンクしたり、以前のインストールプロセスで退屈したりすることなく、ユーザーは任意のディレクトリでJARを実行できます。

これにより、予期される例外がスローされます。

スレッド「メイン」の例外Java.lang.UnsatisfiedLinkError:ライブラリの絶対パスが必要です

どうすればこれを機能させることができますか?

パート2

DLLをフォルダーにコピーする方法(以下で説明)は、Eclipse環境で実行した場合にのみ機能します。エクスポートされたJARを実行すると、DLLバイナリは適切に作成されますが、JNIをロードすると、次の例外がスローされます。

スレッド「メイン」の例外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());
        }
    }

これはアクセス権限の問題である可能性があると思いますが、それを解決する方法がわかりません。どう思いますか?

20
supertreta

DLLをJARから直接ロードできるとは思いません。JARからDLLをコピーする中間ステップを実行する必要があります。次のコードはそれを行うべきです:

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());
}
26
Kurt Kaylor

同じ問題を解決することを目的としたこのJarClassLoader:

http://www.jdotsoft.com/JarClassLoader.php

2
AlexV

基本的にこれはうまくいくはずです。これはJNAが行う方法なので、ダウンロードしてコードを調べます。このプラットフォームを独立させるためのヒントもいくつかあります...

編集

JNAは、そのネイティブコードをjarに入れ、実行時に正しいバイナリーをアンパックし、ロードします。これは良いパターンかもしれません(もしあなたの質問が正しいなら)。

2
mtraut