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. 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:
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.
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.
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:
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.
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: