views:

511

answers:

4

I have written a Java GUI using SWT. I package the application using an ANT script (fragment below).

<jar destfile="./build/jars/swtgui.jar" filesetmanifest="mergewithoutmain">
  <manifest>
    <attribute name="Main-Class" value="org.swtgui.MainGui" />
    <attribute name="Class-Path" value="." />
  </manifest>
  <fileset dir="./build/classes" includes="**/*.class" />
  <zipfileset excludes="META-INF/*.SF" src="lib/org.eclipse.swt.win32.win32.x86_3.5.2.v3557f.jar" />
</jar>

This produces a single jar which on Windows I can just double click to run my GUI. The downside is that I have had to explicitly package the windows SWT package into my jar.

I would like to be able to run my application on other platforms (primarily Linux and OS X). The simplest way to do it would be to create platform specific jars which packaged the appropriate SWT files into separate JARs.

Is there a better way to do this? Is it possible to create a single JAR which would run on multiple platforms?

A: 

Swing runs great everywhere. As for SWT - I did few with Eclipse and no problem at bith Windows and Linux. I don't use manually ANT, maybe if you migrate to some smart ide (eclipse for example) you will not face those kinds of problems anymore.

And few notes:

  • jar files are supposed to run everywhere if they were written good
  • at desktop apps I've written in Java needed some testing on both Linux and Windows. I've always found some bugs...
Xorty
Swing runs great but I like SWT for its use of native widgets. I do use eclipse for writing my code but I don't think it includes any way to produce a single jar cross platform SWT application.
mchr
-1 for providing a useless answer to a specific question recommending doing something completely different althought that wasn't the question at all.
Daniel
A: 

Replace bold selected text in src="lib/org.eclipse.swt.win32.win32.x86_3.5.2.v3557f.jar" with linux-specified swt jar-file

This is the way which I was thinking of but this leaves me with platform specific jars. I'd love to be able to produce a single cross platform jar but I suspect that may not be possible.
mchr
Unfortunately, this is impossible
That's what I suspected. Oh well, I can live with one binary per platform - I still only have to write the code once :) (Although I know I need to test on all platforms)
mchr
+5  A: 

I've just run into the same problem. I haven't tried it yet, but I plan to include versions of swt.jar for all platforms and load the correct one dynamically in the start of the main method.

UPDATE: It worked. build.xml includes all jars:

<zipfileset dir="/home/aromanov/workspace/foo/lib" includes="swt_linux_gtk_x86.jar"/>
<zipfileset dir="/home/aromanov/workspace/foo/lib" includes="swt_macosx_x86.jar"/>
<zipfileset dir="/home/aromanov/workspace/foo/lib" includes="swt_win32_x86.jar"/>
<zipfileset dir="/home/aromanov/workspace/foo/lib" includes="swt_linux_gtk_x64.jar"/>
<zipfileset dir="/home/aromanov/workspace/foo/lib" includes="swt_macosx_x64.jar"/>
<zipfileset dir="/home/aromanov/workspace/foo/lib" includes="swt_win32_x64.jar"/>

and my main method starts with calling this:

private void loadSwtJar() {
    try {
        String osName = System.getProperty("os.name").toLowerCase();
        String osArch = System.getProperty("os.arch").toLowerCase();
        URLClassLoader classLoader = (URLClassLoader) getClass().getClassLoader();
        Method addUrlMethod = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
        addUrlMethod.setAccessible(true);

        String swtFileNameOsPart = 
            osName.contains("win") ? "win32" :
            osName.contains("mac") ? "macosx" :
            osName.contains("linux") || osName.contains("nix") ? "linux_gtk" :
            ""; // throw new RuntimeException("Unknown OS name: "+osName)

        String swtFileNameArchPart = osArch.contains("64") ? "x64" : "x86";
        String swtFileName = "swt_"+swtFileNameOsPart+"_"+swtFileNameArchPart+".jar";
        URL swtFileUrl = new URL("rsrc:"+swtFileName); // I am using Jar-in-Jar class loader which understands this URL; adjust accordingly if you don't
        addUrlMethod.invoke(classLoader, swtFileUrl);
    }
    catch(Exception e) {
        System.out.println("Unable to add the swt jar to the class path: "+swtFileName);
        e.printStackTrace();
    }
}

[EDIT] For those looking for the "jar-in-jar classloader": It's included in Eclipse's JDT (the Java IDE built on Eclipse). Open org.eclipse.jdt.ui_*version_number*.jar with an archiver and you will find a file jar-in-jar-loader.zip inside.

Alexey Romanov
Please let me know if you get this working.
mchr
I did! See the update.
Alexey Romanov
Where can we find the "Jar-in-Jar" classloader?
Aaron Digulla
Here: https://bugs.eclipse.org/bugs/show_bug.cgi?id=219530
Alexey Romanov
Or the latest version in your Eclipse install.
Alexey Romanov
How can I find the JAR in my Eclipse 3.6 install? I tried to figure out the name from the bug report but couldn't find anything that seemed to match from `org.eclipse.jdt.*`
Aaron Digulla
Open `org.eclipse.jdt.ui_*version_number*.jar` with an archiver and it should have the file `jar-in-jar-loader.zip` inside. (I have 3.5.1 installed here, but I expect 3.6 shouldn't be different.)
Alexey Romanov
Cool solution, exactly what I was looking for. However implementing this have proved to be tougher than expected.
posdef
A: 

@Alexey I extracted the jar-in-jar-loader.zip from my Eclipse 3.6 installation and put all the swt jars in my project. What do I do from here? Do you create your own child classloader?

Connor
see http://stackoverflow.com/questions/3959556/problems-with-loading-resources-during-execution
posdef