views:

539

answers:

5

SWT comes with a base JAR and one specific JAR per platform (Windows, Linux/32bit, Linux/64bit, Mac, AIX, ...). How can I create an executable JAR that will select the correct platform JAR at runtime?

[EDIT] I was thinking to supply all platform JARs in a subdirectory and in main() would then modify the class loader. Has anyone already tried this?

A: 

Maybe http://one-jar.sourceforge.net/ (Maven plugin at http://code.google.com/p/onejar-maven-plugin/) could help in that direction...

Marcel
+1  A: 

IIUC, you'd still have the problem of specifying the platform-specific JNI library. You might be able to leverage Java Web Start for this, but I haven't tried. Alternatively, some projects build custom installers for supported platforms. For example, Deploying SWT Applications on Mac OS X describes how to construct an SWT Mac application bundle. The approach is used in this example. I've also seen this JarBundler Ant Task used.

Addendum: the article Deploying an SWT application on Java Webstart includes some useful references.

trashgod
I have now tried with an URLClassLoader but there are two problems: If the JAR isn't in the ClassPath in the MANIFEST.MF, then loading the DLLs will fail. This means I have to add *all* SWT JARs to the classpath at the same time. This leads to the problem that the 32bit and 64bit DLLs are visible and loading of either will fail. *sigh* In the end, I'll add all JARs to the class path but copy only a single SWT JAR into the lib directory. This way, only a single JAR will load.
Aaron Digulla
I can see the technical appeal, but I can also see the maintenance difficulties. Facing a similar problem, I added a link to a SO article mentioning JWS.
trashgod
A: 

It will be easier to use different shell scripts for different platforms and specify platform-specific jar in the script.

tulskiy
I could write shell scripts but I was really hoping to avoid that. My current solution (add all SWT JARs to the classpath but copy only the correct one into the lib directory) works. Now, I only need to write an installer :)
Aaron Digulla
+4  A: 

For my current job I needed to supply an executable jar that could load jars inside itself and execute a second main(). Basically a bootstrap main() and an application main().

Step 1. in the manifest "main-class" you put your bootstrap class

Step 2. When your bootstrap class runs it unjar's its own jar and all jars inside it to a temp directory. Use something like the line below to get your own jar.

Main.class.getProtectionDomain().getCodeSource().getLocation().toURI()

Step 3. Your bootstrap class detects the OS via the "os.name" property and loads the appropriate jars from the temp directory with this

private static void loadJarIntoClassloader( URL u ) throws Exception
{
    URLClassLoader sysLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();

    Class<URLClassLoader> sysclass = URLClassLoader.class;
    Method method = sysclass.getDeclaredMethod("addURL", URL.class);
    method.setAccessible(true);
    method.invoke(sysLoader, new Object[]{u});
}

Step 4. Now you should be able to run your application by calling the application main().

NOTE: This little hack depends on your JVM using URLClassLoader as its SystemClassLoader, which is true for Sun JVMs, not for sure on others.

This way you can deliver a single jar only, and it will unpack itself and run with the correct jars.

karoberts
If you want to be independent of the type of the classloader, just use the factory `newInstance(urls, parentClassLoader)` method to wrap it and then install the new classloader with `Thread.currentThread().setContextClassLoader ()`.
Aaron Digulla
+1 Interesting idea to create the classpath in one main and then call another.
Aaron Digulla
@Aaron thanks for the tip, I'll have to try that
karoberts
+1  A: 

Look at this, there is a code sample: Create cross platform java swt application

Matías