The Golden Age of Java on the Mac
Java desktop apps were first-class citizens on OS X before the app store came and spoiled all the fun
I’m going to take a short excursus in this chapter to chronicle the often forgotten golden age of Mac Java development that occurred in the first decade of the millennium, from the initial release of OS X, in 2001, to the launch of the Mac app-store, in 2011. The emergence of HTML5 may have disrupted the Java enterprise landscape, but Java developers who were building Mac desktop apps were largely immune. It wasn’t until Apple unveiled their plans for a Mac app-store, that “things got real”.
Circa 2006, I built a utility app in Java that performed converted PDFs into text using OCR. Originally, I distributed it on Mac only, and promoted it in the Downloads area of Apple’s website. (Side note: The Apple downloads area was a great source of traffic - much better than the Mac Appstore that would eventually replace it). At this point, Macs still shipped with Java, so there was no “size” penalty for creating a Mac app in Java. It looked and felt just like a native app, and importantly, in a time where bandwidth was still limited, the app size was small.
The Windows Market
I had always planned to port the app to Windows, but, since I was a Mac user, it decided to put it on the back-burner while I flushed out all the kinks. Since it was written in Java, the Windows port shouldn’t have been too difficult. I had been using a few Mac native libraries for image enhancement, which I would need to develop windows-friendly replacements for, and I needed to alter a few UI items (e.g. “Exit” instead of “Quit”, and some slight variations in how it handled file associations).
By far, the most difficult part of porting it to Windows was building an installer for it. Initially I used Launch4J to generate a Windows .exe launcher for it. I distributed this as a zip archive, and left it up to the users to manually copy to a desired location on their computer. However, after frequent requests for a “proper” installer, I used Install4J to generate an installer. Since this “installer” was a Java app itself, I used Launch4J to create a launcher for this installer app.
The result on Windows wasn’t quite native, but Windows users are less picky than Mac users, so it was “good enough”.
When I first released the Windows version of my utility, I expected sales to sky rocket, being that the Windows market was so much larger than Mac. I was disappointed to find out that, although the Windows market is larger, it is also more fragmented and saturated. On the Mac, you only need to list your app in the Apple downloads area, and you reach almost all of your audience. Everyone with a Mac looks there when they are looking for an app.
On Windows, however, there was no one place to advertise my app. There were hundreds of Download sites, and each of them was packed with apps that did roughly the same thing as my app. For the first release, I submitted it to dozens of download sites. I may have even paid a submission service that advertised that they would submit your app to hundreds of sites automatically. After the first couple versions, however, this became too much work for too little gain. Windows barely made a blip on my sales. For every Windows sale, I made a hundred Mac sales. So for most releases, I posted the Windows version on CNET downloads (i.e. upload.com and download.com), and left it at that.
For the next few years, I developed a fairly consistent pattern of small updates every two to three weeks. The Mac releases I would post in MacUpdate. The Windows releases, I would post on CNET. Every new release included a few bug fixes and new features, and their announcements would attract a little bit of traffic, to keep the downloads flowing.
App Store: “We don’t serve your kind”
During the time between 2006 and 2010, the biggest developments in the Java desktop world were:
The iPhone. This was big because it was yet another hot new platform that Java didn’t run on.
JavaFX. This was big because it brought a sorely needed injection of youth into Java’s aging UI toolkit.
Neither of these things impacted me or my little utility very much - at least not directly. My app didn’t make much sense on the iPhone, since it was for processing files that you would usually have on your desktop. And the UI was simple enough that I didn’t need any of the flashy new graphics that JavaFX offered. All the same, I followed both of them with great interest, since I had ambitions beyond my humble OCR app, and both modern graphics, and modern platforms were too enticing to ignore.
It was Apple’s 2010 announcement of a Mac app-store that caused the biggest tremor. Apple promised me the world, then ripped it away from me in a single press release. They announced that they would be inviting Mac apps into a new app store. This was the good part. The bad part was that they would be deprecating their Java distribution, and future versions of OS X would no longer be including Java. And, just to put a nail in the coffin, they enshrined into the Appstore guidelines that app-store apps weren’t allowed to use any deprecated libraries.
I wasn’t a lawyer, and I am an optimist by nature, so I held out hope that there was still a way to get my app into the app store. I mean, the announcement didn’t explicitly say that Java apps weren’t allowed in. It just said that Java was now deprecated, and separately, that apps couldn’t use any deprecated libraries. You had to put two and two together to make the conclusion. Kind of like in a movie where you don’t actually see the bad guy die. “Maybe he survived that fall from the 50th floor!”. Unfortunately, Apple support confirmed to me that, indeed, my app would not be eligible, because it depended on Java.
Java’s Future on the Mac in Question
The future of Java on the Mac was legitimately in question for a few months following this press release. Sun (now Oracle) had always maintained Java on Linux and Windows, but Apple had historically maintained and developed the Mac version. Now, Apple was saying that they were no longer going to be doing that. A few months later, Oracle announced that they would be taking over development of the Mac JDK as part of OpenJDK7, but that would take some time, and almost certainly wouldn’t be ready for the Mac app store’s grand opening in January of 2011.
Alternative JVMs
Deep in my gut, I could feel that the Mac app-store was the key to untold riches, and as things stood, I was excluded from that store. I would still be allowed in the Apple Downloads section, but, realistically, how long would Apple keep that running after they had the app store. As I recall, they discontinued the downloads section shortly after the app-store launched.
The way I saw it, I had a three options:
Rewrite my app in Objective-C as a native Mac app.
Wait for the new JDK7 mac port from Oracle, and try to bundle it with my app.
Use an alternative JVM and bundle that with my Mac.
I’m a “leave no stones left unturned” kind of guy, so I essentially attempted all three, but ultimately, it was option 2 (Oracle’s JDK7) that won the day. I just missed out on the first year or so of the Mac app-store hype.
During that year, I spent most of my time experimenting with alternate Java virtual machines. I looked at anything the showed promise, but the three that stick in my memory were GCJ (The GNU Compiler for Java), Avian, and IKVM + Mono. All of these had the same limitation: no Swing support. If I could refactor my code so that the UI was completely modular, then I could potentially compile the business logic in one of these alternative compilers, and pair it with another UI kit such as SWT, QT, or Cocoa.
I found the output of GCJ to be difficult to work with. I don’t recall the specifics - only that I spent a couple of weeks wrestling with it, and came out with a couple of bruises and nothing really workable.
I quite liked working with Avian, but its runtime library did not include all of the standard JavaSE classes so it required too many changes to be practical. (Or maybe I was just too lazy to make the changes). The few tests that I did with Avian using SWT for the UI worked out quite nicely. They were snappy to launch and the executable size was reasonably small, so despite the fact that it wasn’t a good fit for this project, I made a mental note to keep it in mind for the future.
By far my best experience with an alternate toolchain was IKVM+Mono. IKVM was a Java to DotNet compiler, and Mono was the open-source, cross-platform version of DotNet. I was able to factor out my Swing code, and produce a jar with only my app’s business logic, then use IKVM to convert this into a .dll file. The Mono Mac project had Cocoa bindings, so I was able to build a UI in interface builder, and then write some glue code in C# to link it to my app’s business logic.
I never released the Mono version of my app because, by the time it was close to ready, Oracle’s JDK7 was available in early access releases, which would allow me to ship the app mostly unchanged, which would result in far less maintenance in the long run.
Bundling the App for the App Store
With the availability of JDK7, the only remaining obstacle was bundling a native app. The old bundler I was using was tied to Apple’s Java, and didn’t bundle the JRE inside the app - it just linked to the system’s installation of Java. With JDK7, you needed to bundle the full JRE inside your app bundle. This made the app much larger, but it also meant that you were no longer depending on deprecated APIs. The app would be self-contained.
Oracle provided a tool named javafxpackager that was supposed to help you bundle your app as a native app, but it was missing a few key features that were necessary for app-store deployment. I’m working from memory here, but I recall that, among other things, it didn’t work nicely with the new app sandbox. All Mac app-store apps had to run inside their own “sand-box”. They had their own little playground inside the ~/Library/Containers/YOUR_APP_ID
directory, where all of their files would be stored. This just required a little extra care and preparation. (But it was a PITA!)
I was about to write my own bundler, when the open source community came to the rescue. A bundler by some kind developer who’s username was “InfiniteKind” had developed or forked an app bundler that would work with the new JDK7, and included a few tweaks to satisfy Apple’s app-store police. A google search reveals that that app bundler project is still around, and still includes a credit for one of my minor contributions in its README file.
Finally, almost a year after its initial launch, I was allowed to submit my app into the Mac app store. My app was now 50 megabytes larger (compressed), because it included a bundled Java runtime, but that didn’t matter if it meant more sales. As it turned out, it didn’t really affect sales at all. I suspect that people who bought from the App store would have bought it from my website had it not been available, so it really just shifted where my sales were coming from. Every year the share of app-store sales grows a little, and website sales decline. Today, almost all of my sales come through the app store.
It worked out in the end…
The decision to deprecate Java on the Mac was painful for Java developers like me at the time. But looking back, I think it was the right move, and it was inevitable. Had they not pulled the trigger then, they likely would still have had to make the change eventually, and the longer they waited, the more painful it would be.
By bringing the Mac port into the standard OpenJDK, it ensured that Mac users would stay up to date with the latest advancements in Java. They wouldn’t be hindered, waiting for a reluctant third-party steward to update their version any longer.
That said, having to bundle the Java runtime into every app felt unnecessarily heavy at the time, and still does. As Steve Jobs famously quipped, “Java is a huge ball and chain”. With the JRE bundled in every Java app, users were now reminded of this every time they downloaded an app update. Surely there was some way to share this “ball-and-chain” between apps. I have always been frustrated by this. It was one of the primary motivators for developing jDeploy.
Next week, we’ll return to the topic of The Decline and Fall of Java on the Desktop. Specifically, how it responded to the HTML5 juggernaut on the desktop (JavaFX), and how it has quietly positioned itself as the best platform for cross-platform desktop development today. Make sure you subscribe to the newsletter so you won’t miss it.
do you know if Java apps are now dis-allowed in the Apple macOS store? Their guidelines 2.4.5(viii) specifically calling out Java as a deprecated technology.