Java - Loading dlls by a relative path and hide them inside a jar

PART 1

I am developing a Java application that should be release as a jar. This program depends on C++ external libraries called by JNI. To load them, I use the method System.load with an absolute path and this works fine.

However, I really want to "hide" them inside the JAR, so I have created a package to collect them. This forces me to load an relative path - the package path. By this approach, I let the user run the JAR in any directory, without being worried about linking the DLLs or bored with a previous installation process.

This throws the expected exception:

Exception in thread "main" java.lang.UnsatisfiedLinkError: Expecting an absolute path of the library

How can I get this working?

PART 2

The approach of copying the DLLs to a folder (explained below) only works when I run it under the eclipse environment. Running an exported JAR, the DLL binaries are well created but loading the JNI one throws the next exception:

Exception in thread "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)

I run this loading 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());
        }
    }

I think this could be an access privileges issue, but don't know how to solve it. What do you think?

Answers


I don't believe you can load the DLL directly from the JAR. You have to take the intermediary step of copying the DLL out of the JAR. The following code should do it:

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

Basically this should work. As this is the way JNA does it, simply download it and study the code. YOu even have some hints to make this platform independent...

EDIT

JNA brings its native code along in the jar, unpacks the correct binary at runtime und loads it. This may be a good pattern to follow (if i got your question correct).


This JarClassLoader aiming to solve the same problem:

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


You need to prime the classloader with the location of the DLL -- but it can be loaded without extracting it from the jar. Something simple before the load call is executed is sufficient. In your main class add:

static {
    System.loadLibrary("resource/path/to/foo"); // no .dll or .so extension!
}

Notably, I experienced basically the same issue with JNA and OSGi's handling of how the DLLs are loaded.


Need Your Help

What is a multithreaded application?

multithreading

I am looking to learn more about threading and I wanted to know: what is a multithreaded application?

ORMLite reset all tables

android ormlite

I have an application that uses ORMLite. I need to create a function to reset the entire db (basically I need to delete all rows from every db, reset the autoincrement and reset indexes).