12

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?

Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
  • 1
    Why don't you distribute several executables for each platform (a la Eclipse)? – Pascal Thivent Jan 10 '10 at 23:30
  • Because SWT takes just a small part of the app: The whole thing is currently 30MB. So I can either ask people to download 32MB for each platform or download a single 40MB (for six platforms) file which runs everywhere. – Aaron Digulla Jan 11 '10 at 08:16
  • In the eclipse case, we have 10+ downloads, each >100MB and the only difference between them is the SWT jar. I either want a single download or one big main download and a small download per platform which gets downloaded automatically when I run the app the first time. – Aaron Digulla Jan 11 '10 at 08:29
  • What you describe is more an issue for the application provider than for the users. As a user, I prefer to download a 32MB exec. But I understood that you don't want to do this :) – Pascal Thivent Jan 12 '10 at 00:18
  • What I want is to make the install less painful for the user. I want to give them a single file that works on any supported platform. If they want to take the app along to the next computer/OS (for example, a 64bit Windows or the new Linux box), it should be possible to just copy the app and be done with it. – Aaron Digulla Jan 12 '10 at 09:14

5 Answers5

6

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
  • 9,828
  • 4
  • 39
  • 39
  • 1
    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 Jan 11 '10 at 08:19
  • +1 Interesting idea to create the classpath in one main and then call another. – Aaron Digulla Jan 11 '10 at 08:20
5

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

Community
  • 1
  • 1
Tute
  • 6,943
  • 12
  • 51
  • 61
1

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.

Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • 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 Jan 10 '10 at 22:22
  • 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 Jan 10 '10 at 23:18
0

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

Marcel Stör
  • 22,695
  • 19
  • 92
  • 198
0

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

Denis Tulskiy
  • 19,012
  • 6
  • 50
  • 68
  • 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 Jan 10 '10 at 22:23