The Source for Java Technology Collaboration
User: Password:



   

Make Your Swing App Go Native, Part 2 Make Your Swing App Go Native, Part 2

by Joshua Marinacci
01/05/2004

Welcome back to this series on making Swing applications behave more like native applications. In the previous article, we created a simple chat program with OS-specific menus and alerts. It sort of feels native, but we still have to start it from the command line. It would be nice if there were a double-clickable icon on the desktop to start our program.

In this installment, we will create native executables for Mac OS X and Windows, then add another native feature: file type associations. In the third installment we will add custom icons and finishing polish.

Automation

As we add icons and file associations, we need the process to be repeatable and automatable. Using GUI tools and wizards is fine for while you are learning, but in production, the last thing you want to do is open Photoshop and scale icons for every build. Everything we do here will be run from Ant, the popular Java build tool, so all work needs to be done by custom Ant tasks or scriptable utilities.

If you haven't used Ant before, you should really check it out. Ant is similar to the old Unix make, in that it uses a project description file to build portions, or the entirety, of programs. Ant is different in that it uses XML files instead of plain text, and all of its functions are implemented as cross-platform Java classes instead of shell scripts that may not be present on your computer. Though it's only been around for a few years, it has quickly proven to be the tool of choice for building Java applications.

Packaging Options

The ideal Java application package is one that behaves just like a native app. For desktop systems, this means that the user can double click on an icon to launch the program. The icon is custom for the program, and the windows and taskbar/dock all have matching icons. This is the ideal scenario. Now, how to get there? To package up an application. we have a couple of options:

  • Double-clickable .jar files
  • Shell scripts
  • .apps and .exes

First we could just make a double-clickable .jar. This is where the user receives a .jar file and can double click on it to start the program. The .jar has extra information in the manifest to tell the JVM where the startup class is and what other .jars need to be included in the classpath. In this case, double clicking on the .jar would be equivalent to doing a java -jar myprogram.jar command. This is a great way to keep it simple, but it doesn't feel quite right. There's no custom icon, as Figure 1 illustrates, and the application doesn't take advantage of native features. In fact, its icon and description make it look like a document, not an application. Still, it does have the ability to launch the program without a command line. It's pretty simple and shouldn't be overlooked when you are sending out betas.

Figure 1
Figure 1. Double clickable .jar on Mac OS X

Our second option is using a batch file or Mac .command script to launch the program. This may be somewhat better, since we can probably get a custom icon and the user no longer has to know which of the many .jars you sent is the one he or she needs to double click, but it still doesn't take advantage of native hooks.

It looks like we need something better: real .apps and .exes.

Building a Double-Clickable OS X Application

On the Mac, we can create a real application bundle, which gives the program access to the same features as a native application. An application bundle (commonly called a .app) is similar to a .exe under Windows in that it is a double-clickable application, but instead of being a simple binary, it is actually a folder with a .app extension. The extension is usually hidden, but a listing from the command line will show it. One of the really cool things about .apps is that they contain an entire directory structure with standard places for resources (icons and text), and XML config files for setting every conceivable option. In particular, we will set the startup classfile and display options. So as not to rehash what's been written elsewhere, including Apple's documentation, I won't explain all of the available features. Instead, we'll focus on automating it with an Ant script.

To build an application bundle, we first need to create the directory structure. Here's part of a dist-mac target from an Ant build.xml file to do that:

<target name="dist-mac" depends="jar">
    <property name="appdir"
        value="${dist-mac}/${app-name}.app"/>
    <mkdir dir="${appdir}"/>
    <mkdir dir="${appdir}/Contents"/>
    <mkdir dir="${appdir}/Contents/MacOS"/>
    <mkdir dir="${appdir}/Contents/Resources"/>
    <mkdir dir="${appdir}/Contents/Resources/Java"/>

This assumes that the variables ${dist-mac} and ${app-name} have been previously defined. Such an arrangement lets us decide later where to put the files and makes the task more reusable.

Next, we need to get a copy of the application stub, which is the binary code that actually starts your Java program. This stub is helpfully provided by Apple in the developer toolkit, in /System/Library/Frameworks/JavaVM.framework/ Versions/Current/Resources/MacOS/. Since we need to use the 1.3 JVM to support our JDirect calls to bounce the dock icon (see the previous article), I have copied the older one to src/packaging so it won't conflict with the normal dev tools.

<copy file="${packaging}/JavaApplicationStub"
  todir="${appdir}/Contents/MacOS"/>
<exec command="chmod 755
 ${appdir}/Contents/MacOS/JavaApplicationStub"/>

A quick note here: I've added the chmod command here because Ant will not preserve the executable bit. Unfortunately, Java has no concept of Unix file permissions, only the ability to read and write. So we use Ant's exec task to call out the shell command chmod. Hopefully a future version of Java (or a standard extension) will address this.

Next we need to copy in the Info.plist file.

<copy file="${packaging}/Info.plist"
    todir="${dist-mac}/${app-name}.app/Contents"/>

The Info.plist file is an XML configuration file that completely defines an application. It tells OS X the name of the application, how to start it, which icons to use, which files it understands, and virtually every other config parameter you can imagine. To create it, we will start with a sample file provided by Apple and make a few changes. We will set the main class to our Startup class and include the joshy-common.jar support lib. Below is a portion of the file:

<key>CFBundleIconFile</key>
<string>GenericJavaApp.icns</string>
<key>Java</key>
<dict>
    <key>MainClass</key>
    <string>org.joshy.oreilly.swingnative.Startup</string>
    <key>JVMVersion</key>
    <string>1.3+</string>
    <key>ClassPath</key>
      <array>
      <string>$JAVAROOT/joshy-common.jar</string>
      </array>
    <key>Properties</key>
    <dict>
      <key>com.apple.mrj.application.apple.menu.about.name</key>
      <string>MadChatter</string>
      <key>com.apple.macos.useScreenMenuBar</key>
      <string>true</string>
    </dict>

Notice that in the <dict> element, we've also included the command line parameters discussed in the previous article to set the application name and use the main Mac menu bar.

Now, back to our Ant file. Next we copy the application itself, which consists of our code .jars and any support library .jars.

<copy file="${build}/${app-name}.jar"
  todir="${appdir}/Contents/Resources/Java"/>
<copy todir="${appdir}/Contents/Resources/Java">
    <fileset dir="lib">
        <include name="*.jar"/>
    </fileset>
</copy>

The last thing we need to do is tell OS X that this directory isn't just a directory, but a real live application. We do this with another exec call to a command-line tool, setfile.

<exec command="/Developer/Tools/SetFile -a B ${appdir}"/>

Run the osx.app target and we get a double-clickable application, as seen in Figure 2:

Figure 2
Figure 2. Mac OS X Java application bundle

Pages: 1, 2, 3

Next Page » 

View all java.net Articles.

 Feed java.net RSS Feeds