A clean NetBeans Platform integration with Mac OS X (part I)

Even though Steve Jobs didn't love Java™ at all, Mac OS X happens to have one of the cleaner, if not the cleanest, Java integration in the operating system. The application bundle mechanism, together with other facilities, allows to deploy an application made of many files laid out in a complex directory tree, as often happens with Java, while the end user still sees it as a single icon, that can be double-clicked to launch the application and dragged and dropped around; installation is typically just a matter of drag and drop (unless you need something more complex), a much simpler approach than preparing an installer, as for instance Windows needs. This thing was also obviously facilitated by the fact that Mac OS X used to come with a pre-installed Java runtime. Following this approach provides other advantages, for instance your application will be integrated in the operating system in the way Apple wants - and you should know how picky Apple users are whenever they see something that departs from the standard.

Now, things have been substantially changed in the past months:

  1. Apple is no longer pre-installing the new Java 7 with Mac OS X - it can be installed manually, but Apple is promoting the idea of embedding a runtime within the application.
  2. Apple won't any longer support the co-existence of multiple JDK/JRE versions.
  3. Java 7 for Mac OS X is based on Oracle codebase (so it is updated in sync with other operating systems) and there's an OpenJDK 7 port that can be used instead.

Points #1 and #2 triggered some controversy as some think that it doesn't make sense to add a few dozens of megabytes of runtime (JDK 7 can be quite large especially when you add the JavaFX runtime) to each Java application, wasting space that could be optimized with a single installation. On the other hand, it's Apple who decides for Mac OS X, I clearly see for them an advantage of this approach (now the responsibility of updating an eventually security-flawed Java runtime is on the developers' shoulders, not Apple's) and in any case if you want to distribute your application to the Apple Store there aren't alternatives (applications in the store can't rely on external frameworks).

There's an advantage for developers, once you accept you have to pay some more complexity, as you are now in total control of which Java runtime supports your application; in other words, it won't never happen that an Apple system-wide update to the Java runtime breaks your already installed applications. Please read the details of this topic on Scott Kovatch's blog; at the end of the day it makes a lot of sense to use the new “launcher stub” (which in the meantime has been released) so it provides the default behaviour designed by Apple.

NetBeans, and applications based on the NetBeans Platform, have historically had a somewhat more complex approach with Mac OS X, which derives from the architecture of the platform. In fact, they need to be booted by a script (or an equivalent binary launcher for Windows) that does quite a number of things, such as discovering modules, setting up the system proxy and other minor stuff, but above all applying updates that might have been downloaded from an update centre. This latter task is not easy as expected and involves the reboot of the application, a thing that is performed by the script itself. While it's still possible to integrate a NetBeans Platform application with Mac OS X so you can install by drag and drop, even with an embedded JRE, I find this solution not completely satisfactory, as everything that departs from a specific context best practices: in fact, the existence of an alternate launch script means that you can't use the Apple made Java launcher.

Three years ago I consulted for a NetBeans Platform adopter who needed some customizations of the boot process. I can't exactly remember the details, anyway - for instance - there was the need of having multiple installations all in sync with updates, so updates, when present, should have been forced before the application started. We find complex to provide an alternate shell script (also because the client ran Windows), so we opted for a Java-based solution. The idea was to replace the standard script / binary launcher with a small plain Java application, that could be customized. The customer agreed to put this small piece of code in the open source, and so I created CapeCanaveral. Then the consultancy ended, I planned to integrate it with my other applications, but didn't find the time so far. A few days ago CapeCanaveral proved to be useful for another customer that is developing a prototype (he needs his application to start “embedded” in another Java process).

I took the opportunity for making a new release of CapeCanaveral, booting a website where I'll put some documentation as soon as possible (sources are available at BitBucket), and using it for giving to some of my simpler applications a perfectly compliant integration with Mac OS X. I have integrated this capability into my current Maven workflow.

While the thing is perfectly working, please be aware of some limitations and workarounds:

  • I recall that three years ago CapeCanaveral was supporting update centres - but as the behaviour of the official NetBeans Platform launcher often changes, it probably doesn't today (I'll have to patch it). So, the current solution is ok only for simpler applications.
  • appbundler, the official FLOSS tool for creating a Mac OS X Java application, needed some patches for a few bugs/RFEs (APPBUNDLER-7 and APPBUNDLER-8), so I'm presently using a patched version (I'm submitting patches to the developers so they will be hopefully included in a future release).
  • Furthermore appbundler is not present yet in a Maven repository, so I had to temporarily include its jar as a committed resource in my projects. 

If you look at the sources of SolidBlue, blueShades and ForceTen, or if you try the binaries, you'll be able to see the thing working - in particular, the build process creates two versions of the Mac OS X application, one without an embedded Java runtime and one with OpenJRE 1.7.0u7 embedded. All the stuff is implemented in my SuperPOM, in two distinct profiles named it.tidalwave-netbeans-platform-appbundle-v2 and it.tidalwave-netbeans-platform-appbundle-embedded-jre-v2. Using a trick that I explained here a few time ago, they are automatically activated if in your Maven module there are the (empty) files:

src/config/activate-it.tidalwave-netbeans-platform-appbundle-v2-profile
src/config/activate-it.tidalwave-netbeans-platform-appbundle-embedded-jre-v2-profile

I'll soon post a further post with more technical details - perhaps in the meantime I'll be able to write some documentation and smooth a pair of rough corners. In the meantime, feel free to ask for questions here.

comments powered by Disqus