How to make a JAR file that includes DLL files?
Asked Answered
A

2

77

I bought a third-party Java library which includes a JAR file and two DLL files. I wrote my own Java program which invoke the third-party JAR file. Now my question is how can I package all my code into a single JAR file which include all my code and the third-party JAR and DLLs?

I know SWT is such a case. The swt.jar includes dll files, but I don't know how to do this and how to make it work properly.

Acidhead answered 23/10, 2009 at 4:20 Comment(0)
F
90

Just package it anywhere in the jar. One thing you have to keep in mind though - before you can use the DLLs you need to actually extract these from the JAR and dump these on the hard disk somewhere otherwise you won't be able to load these

So basically - I did JNI project for the client where I will use such jar packaged within the war. However - before running any native methods I would get the DLL as a resource and write it to the disc into temp directory. Then I would run regular initialization code where my DLL is set to the same location I just wrote DLL to

Oh, and just in case: there's nothing special about packaging dll or any other file into jar. It's just like packaging stuff into zip

Here's some code I just digged out

public class Foo {
private static final String LIB_BIN = "/lib-bin/";
private final static Log logger = LogFactory.getLog(ACWrapper.class);
private final static String ACWRAPPER = "acwrapper";
private final static String AAMAPI = "aamapi51";
private final static String LIBEAU = "libeay32";

static {
    logger.info("Loading DLL");
    try {
        System.loadLibrary(ACWRAPPER);
        logger.info("DLL is loaded from memory");
    } catch (UnsatisfiedLinkError e) {
        loadFromJar();
    }
}

/**
 * When packaged into JAR extracts DLLs, places these into
 */
private static void loadFromJar() {
    // we need to put both DLLs to temp dir
    String path = "AC_" + new Date().getTime();
    loadLib(path, ACWRAPPER);
    loadLib(path, AAMAPI);
    loadLib(path, LIBEAU);
}

/**
 * Puts library to temp dir and loads to memory
 */
private static void loadLib(String path, String name) {
    name = name + ".dll";
    try {
        // have to use a stream
        InputStream in = ACWrapper.class.getResourceAsStream(LIB_BIN + name);
        // always write to different location
        File fileOut = new File(System.getProperty("java.io.tmpdir") + "/" + path + LIB_BIN + name);
        logger.info("Writing dll to: " + fileOut.getAbsolutePath());
        OutputStream out = FileUtils.openOutputStream(fileOut);
        IOUtils.copy(in, out);
        in.close();
        out.close();
        System.load(fileOut.toString());
    } catch (Exception e) {
        throw new ACCoreException("Failed to load required DLL", e);
    }
}
    // blah-blah - more stuff
}
Forgiveness answered 23/10, 2009 at 4:27 Comment(9)
A word of caution on this approach - be sure you clean up the temp files. If you re-use the same path each time, consider what happens if multiple applications use your JAR (one will fail if the other already has a lock on the temp file). Just be careful - sometimes it's easier to deploy the JAR and DLLs separately.Lach
Code can be changed to overwrite previously installed file. In my case - this was a web app which will not be frequently recycled, bu indeed - if you just copy the code "as is" you will get new copy of DLLs each time you execute itForgiveness
One thing to keep in mind: I was using JNI DLLs that were dependent on other DLLs. I included all DLLs inside a jar and used the code above to unpack. But I was getting java.lang.UnsatisfiedLinkError exceptions saying "Can't find dependent libraries". The problem is that you must call System.load() on the DLLs in order such that dependent DLLs are loaded first.Elianaelianora
Can you tell me in the method loadLib(). you have mentioned ACWrapper.class. Can you tell me what class is this. I mean related to your project? As i need to know how to extract the DLL from the war.Megaton
Just a class in the same directory as the DLL. The class you use determines where getResourceAsStream checks for the resource.Cockrell
LGPL license requires dynamically-linking to libraries. Your first suggestion of packaging the dll in the jar, wouldn't that contradict the dynamic-link requirement, since users can no longer freely swap in a different dll version? I know this is an old thread, but relevant question. Thanks.Soso
A more robust loading method would be System.load(fileOut.getAbsolutePath());Entrepreneur
There is File.createTempFile method. This will take care about cleaning up and non colliding names. javadocsUrgency
... before you can use the DLLs you need to actually extract these from the JAR and dump these on the hard disk somewhere otherwise you won't be able to load these Why? Perhaps my question should be a separate SO question?Rentfree
C
7

Use http://www.jdotsoft.com/JarClassLoader.php which could load DLLs and JARs from another JAR with unlimited nesting. For example, DLL could be in JAR which is in another root JAR. All DLLs and JARs are loaded like they are in a classpath or library path.

Cyclopean answered 9/3, 2010 at 15:19 Comment(3)
Note to others: License is GPLv3, which substantially limits applicability.Pride
Also note: This class loader unpacks all nested jars into a temporary directory. So it works like the first answer. The native libs are unpacked only if needed.Redoubt
Commercial license available.Redoubt

© 2022 - 2024 — McMap. All rights reserved.