Java development on OS X is similar to Java development on any platform,
particularly any Unix platform. The differences are in how your code
integrates with the platform. Java lacks a cohesive platform integration
strategy, so running a Java application usually doesn't have the same feel
as running a native one.
In contrast, Java on OS X is a first-class citizen. You can integrate
your app so well that users probably won't even know they're using a Java
application. You can package your app so it has one of those lovely 128 x
128 icons and can be launched with a double-click; it can even be bound to
particular file types so it's launched when the documents are
double-clicked. Swing apps also get the luscious Aqua user interface free,
and with a couple of lines of code you can also tell the runtime to let your
app use the system menu bar and the hardware acceleration that you won't
find on any other platform.
OS X also comes bundled with a great set of developer tools, all at no
cost. I'll be using one of them, the PackageMaker, in this article.
Creating a Native Installer
This article shows how to package your Java application into a native OS
X installer. I'll use one of the many free development tools that come with
the platform, so to follow along, you should be running OS X and have the
developer tools installed. Depending on when you bought your system, the
developer tools installer may be on a separate CD or preloaded on your
system under /Applications/Installers/Developer Tools/.
If you can't find the installer, you can
always download it from the Apple developer Web site. You'll have to join
the Apple Developer Connection, but the minimum membership is free and
provides access to the tools. (You should do this anyway if you plan on
doing any amount of development for the Macintosh, as it provides access to
lots of good information.)
If you haven't already done so, run the installer by double-clicking it;
you'll need an administrator password to complete the install.
The installer we'll be creating looks like the one you used to install
the developer tools. (They eat their own dog food at Apple, so we're using
the same tool they used to create their installer.) Figure 1 shows the
installer I built for Ant 1.5, and Figure 2 shows the icon. It all looks
very official.
There are two important versions of the package installer. We're
documenting version 1.0, which came with OS X 10.0 and 10.1; OS X 10.2, code
named Jaguar, which shipped on August 24, comes with version 1.1. Although
this tool is much nicer that 1.0, I've opted to cover 1.0 for one important
reason: installers created with 1.1 may not be backward compatible with OS X
10.1 and earlier. (This article was written before the final release was
available, so this may be addressed by the time they ship.) The extra pain
of using 1.0 is minimal, so you may as well make your Java apps available to
the widest possible installed base.
The installer we create contains three groups of files; we want to
install these files on the user's machine, which PackageMaker packages into
an archive file. There are scripts, such as preinstall and postinstall,
which are executed at various points during the install process, and a set
of files that contains documentation presented to the user when the
installer is run. We'll look at each of these in turn.
The files to install are the application, whether a double-clickable JAR
file or a fully packaged .app bundle, and any other files you may wish to
distribute with your app. Good examples are documentation files,
configuration files, external data files, and the like. We'll cover how to
build a fully packaged, double-clickable .app bundle, complete with its own
icon and everything else in a subsequent article. For now we'll assume that
the app is a double-clickable JAR file. (Any JAR file that includes a
Main-Class: attribute will automatically be launched when you double-click
it in the Finder on OS X.)
You'll need to create a new directory that contains the files you want
to install, with the files laid out the way you want in the directory the
app is installed in. You'll typically put a folder named for your
application in this directory as well as all the files you want to include.
When you build the installer, you can specify whether the path contained
here should be absolute or relative to a directory that the user chooses.
For most apps, the latter is the right choice. Of course, for the Ant
installer, I wanted to make sure that the files were installed in
/usr/local/ant, and that symbolic links were created so that Ant would be
included on the command-search path. Which one is appropriate depends on the
kind of application you're working with, but if you can let the user have a
choice about where the app lives, you should.
You'll then need to create a second directory for the installer
resources at the same level as your first directory hierarchy. Be sure to
name it something you'll be able to distinguish from the other directory
when you run the installer. Place your scripts and documentation files into
this directory.
The install scripts are scripts that are run before and after the
archive containing the app is unpacked. There are several optional scripts
you can include, each corresponding to a different phase of the install
process. The scripts correspond to six phases: preflight, preinstall,
preupgrade, postinstall, postupgrade, and postflight. This lets you check out the environment before the
installer runs, move old files out of the way, do postinstall configuration,
and clean up when you're done. The package installer runtime will execute
each script at the appropriate time. They can be written in just about any
scripting language, from AppleScript to PERL to a simple shell script. Many
simple installers don't even need any scripts. Let's look at the preinstall
script from the Ant 1.5 installer to see what it's all about.
#!/bin/sh
if [ -e /usr/local/ant ] ; then
mv /usr/local/ant /usr/local/ant-pre-1.5
fi
My Ant installer preinstall script checks if there's a previously
installed version of Ant, and if so, moves it out of the way before the
installer unpacks the archive. It's nice not to clobber software someone may
have installed by hand! The postinstaller then runs through, looking for any
previously installed Ant extensions and copies them into its extensions
directory. What you do in these scripts depends entirely on the app you're
installing.
The scripts must be named according to naming conventions that the
package runtime uses to determine which file to use when. In the 1.0 version
of the package tool, the correct name for the preinstall script is
<package-name>.pre_install, where <package-name> is replaced by the name of
the installer you're creating (without the .pkg or .mpkg extension that all
installers have). As of this writing, it looks like 1.1 will remove the
underscore character. They do need to be named correctly in order for the
package runtime engine to run them, so check the help files for the correct
naming convention for the version you're using. They should have the read
and execute bits turned on. (chmod a+rx *_install in your resources
directory will do this.)
Once your scripts are written, you can add more files that describe the
app so that the user is presented with information when installing it. There
are three main files to be concerned with here: Welcome, ReadMe, and
License. These can be in any of four file formats: HTML, RTF, RTFD, or plain
text. They must have the appropriate extension for the file type: .html,
.rtf, .rtfd, or .txt, respectively, and again must be named correctly in
order for the installer runtime to know when to display them.
You may also include images in your HTML files, and they must also be
included in the resources directory when you build your installer.
These files are presented to the user in the order specified earlier. If
you include a license file, the user will also be prompted as to whether he
or she agrees to your license terms before being allowed to continue the
installation process. These files can even be easily globalized, so the
single installer will present information in the language of the current
locale. You'll have to write the localized content yourself though.
Last, you can even include a background image. This can be a .jpg,
.tiff, .gif, .pict, .eps, or .pdf file. (Between Quicktime and Quartz, it
must have been too easy to include so many.)
Running PackageMaker
Once your files are all prepared, it's time to launch PackageMaker.
You'll find it in /Developer/Applications/. It will present you with a
single window (see Figure 3), but without any of the fields filled in.
In the Package Root Directory box, enter the full path to the first
directory you created, the one that contains the actual files to install.
You can either browse to it by clicking the Change Root button or enter the
path if you know it.
In the Package Resources Directory box, specify the resources directory,
the second directory you created.
In the Package Information box, fill out the various text fields:
Package Title, Package Version, Package Description, with text that's
appropriate for your app.
The Default Location field is important and works with the Relocatable
checkbox. It specifies which path your app should be installed in.
If your installer is for a regular application, the default location
should be /Applications and Relocatable should be checked.
However, if you need to install in a particular directory, specify it
here. Remember, any subdirectories inside the Package Root Directory will be
installed as subdirectories of the directory specified here. Most likely
you'll also need to define that the specified path needs to be on the root
volume. I'll show how to do that in a moment.
Depending on where your app needs to be installed and what your install
scripts need to do, you may need administrator privileges to complete your
install. Avoid requiring this if you can, but if you're installing in system
directories or creating new users (as I do in my forthcoming Tomcat
installer), you'll need your scripts to run as root for the install to work.
PackageMaker handles this for you. By checking the Needs Authorization
box, you can instruct the package you create to request an administrator
password. The installer then executes the scripts with the appropriate
privileges. The Developer Tools installer worked this way.
The Required checkbox is only relevant if you're going to bundle this
package into a Meta Package, a construct that allows users to select
optional subpackages, as the Developer Tools package did. I'll cover how to
do that another time.
The other checkboxes are fairly self-explanatory and can typically be
left unchecked when installing a Java application. Try to avoid requiring a
reboot. Now that Mac OS is based on Unix, it can easily stay up for months
at a time. Don't make your users sit through a reboot, unless it can't be
avoided.
When you're happy with the settings, click the Create Package button and
save out your installer.
The last little trick is the one I alluded to earlier. If your package
needs to be installed on the root volume, you'll need to resort to using a
text editor to tweak a file in the package you just created. (The 1.1
version has a checkbox for Root Volume Only.) Control-click or right-click
on the package you just created and select Show Package Contents from the
context menu that pops up. Then drill down to Contents
/Resources/English.lproj to find the <package-name>.info file. This contains
several of the settings we created using the graphical user interface. You
can open it by dragging it onto TextEdit or your favorite text editor.
You'll need to add a package flag:
rootVolumeOnly YES
Insert this line above the other package flags and save.
Now all you have to do to share your application with the world is to
archive it and post it somewhere. The safest way to do this is with gnutar:
gnutar -czvf <package-name>.tgz <package-name>.pkg. If you're distributing
your app this way, make sure you don't have any filenames longer than 32
characters, since StuffIt Expander still doesn't maintain long filenames
correctly, and, by default, browsers will open downloaded archives using it.
Author Bio
Ian McFarland is the president of Neo Ventures Ltd., a software consulting
company in San Francisco, and author of Mastering Tomcat Development,
available in September from Wiley; he also maintains www.javaosx.com. He's
been a Java developer since release 1.0 alpha 2.
ian@neo.com