the past
the future

Hidden Treasure: AppBundler

When working on big projects I often create little projects to support the larger effort. Sometimes these little projects turn into something great on their own. It's time for me to tell you about one of them: AppBundler. AppBundler is a small tool which packages up Java (client side) apps with a minimum of fuss. From a single app description it can generate Mac OSX .app bundles, Windows .EXE files, JNLPs (Java Web Start), double clickable jars; and as of yesterday evening: webOS apps! I start the project to support Leonardo Sketch but I think it's time for AppBundler to stand on it's own.   Packaging Java apps has historically been an exercise in creative swearing. The JVM provides no packaging mechanism other than double clickable jars, which are limited and feel nothing like native apps. Mac and Windows have their own executable formats that involve native code, and Sun has never provided tools to support them. Java Web Start was supposed to solve this, but it never took off the way the creators hoped and has it's own idiosyncrasies. Long term we will have more a more environments with Java available but with different native package systems. Add in native libs, file extension registration, and other metadata; and now you've got a real problem. After hacking Ant files for years to deal with the issue I decided it was finally time to encode my build scripts and Java lore into a new tool that will solve the issue once and for all. Thus AppBundler was born.  

How it works

You create a simple XML descriptor file for your application. It lists the jars that make up your app along with some metadata like the App name and main class. It can optionally include icons, file extensions, and links to native libraries.
<?xml version="1.0" encoding="UTF-8"?> <app name="Amino Particles"> <jar name="Amino2.jar"/> <jar name="amino_sdl.jar"/> <jar name="examples.jar" main-class="com.joshondesign.amino.examples.Particles"/> <property name="com.joshondesign.amino.impl" value="sdl"/> <native name="sdl"/> </app> 
  Then you run AppBundler on this file from the command line along with a list of directories where the jars can be found. In most apps you have a single directory with all of your jars, plus the app jar itself, so you usually only need to list two directories. You also specify which output format you want or --all for all of them. Here's what it looks like called from an ant script (command line would be the same).
 <java classpath="lib/AppBundler.jar;lib/XMLLib.jar" classname="com.joshondesign.appbundler.Bundler" fork="true"> <arg value="--file=myapp.xml"/> <arg value="--target=mac"/> <arg value="--outdir=dist/"/> <arg value="--jardir=lib/"/> <arg value="--jardir=build/jars/"/> </java> 
AppBundler will then generate the executable for each output format.

What it does

For Mac it will create a .APP bundle containing your jars, then include a copy of the JavaApplicationStub and generate the correct Info.plist files (Mac specific metadata files), and finally zip up the bundle. For Windows it uses JSmooth to generate a .EXE file with an icon and class path. For Java WebStart it will generate the JNLP file and copy over the jars. For double click jar files it will actually squish all of your jars together into a single jar with the right META-INF files. And all of the above works with native libraries like JOGL too. For each target it will set the correct library paths and do tricky things like decompress native libs into temp directories. Registering for file extensions and requesting JREs mostly works.

What about webOS?

All of the platforms except webOS ship with a JVM or one can be auto-installed on demand (the Windows EXEs do this). There is no such option for webOS, however. webOS has a high level HTML 5 based SDK and a low level C based PDK. To run Java on webOS you'd have to provide your own JVM and class libraries, so that's exactly what I've done. The full OpenJDK would be too big to run on a lightweight tablet, and a port would take a team of people months to do. Instead I cross compiled the amazingĀ open source JVM Avian to ARM. Avian was meant to be embedded and already has an ARM port, so compiling it was a snap. Avian can use the full OpenJDK runtime, but it also comes with it's own minimal classpath.jar that provides the bare minimum needed to run Java code. Using the smaller runtime meant we wouldn't have a GUI like Swing, but using Swing would require months of AWT porting anyway, which I wasn't interested in doing. Instead I created a new set of Java bindings to SDL (Simple DirectMedia Layer), a low level graphics API available on pretty much every platform. Then I created a port of Amino (my 2D scene graph library) to run on top of SDL. It sounds complicated (and it was, actually), but the scripts hide the complexity. The end result is a set of tools to let you write graphical apps with Java on webOS. Amino already has ports to Java2D and HTML 5 Canvas (and OpenGL is in the works), so you can easily create cross platform graphics apps. And now with AppBundler you can easily package them as well. Interestingly, Avian runs on desktops nicely, so putting Java apps into the Mac App Store might now be possible. There's already some enterprising developers trying to get Avian working on iOS.

How you can help.

While functional, I consider AppBundler to be in an alpha state. There's lots of things that need work. In particular it needs Linux support (generate rpms or debs?) and a real Ant task instead of the Java exec commands you see above. I would also like to have it be included in Maven and any other useful repo. And as a final request (as long as I have you here), I need some servers to run builds tests on. I have already have Hudson running on a Linux server. I'd love it if someone could run a Hudson slave for me on their Windows or Mac server. And of course we need lots of testing and bug fixes. If you are interested please join the mailing list.

Client Java Freedom

AppBundler is another facet of my efforts to let help Java provide a good user experience everywhere. Apps should always feel native, and that includes the installation and start experience. I've used AppBundler to provide native installs of Leonardo Sketch on every desktop platform. I hope AppBundler will help you do the same. Enjoy! -Josh  

References:

 

the past
the future