I currently am doing some spare-time work for Naxos (the music label), customising my Java-based, open-source eMusic.com downloader, eMusic/J, to work with their Classicsonline download service. This means taking what was a Linux-only application and making it work on Windows and Mac OS X also. Getting the Linux version was obviously easy, I’d already done that; making a Windows version took a little more time, but most of that was learning how to make EXE files and installers (I’m using Launch4j and NSIS for that). However, making it work on Mac OS X was quite a trial, because this platform decides to do some things totally differently to the others.
The fundamental issue was an incompatibility between SWT, the GUI toolkit I’m using, and the Apple event system. Basically, to make SWT work you need to pass a special option to the JVM: StartOnMainThread
. However, as soon as you do this, you lose the ability to catch events for things like “someone double-clicked one of my documents”. So I had the option of having a broken GUI, or no events. Neither of which is a possibility, as opening a particular file is the major purpose of this program. My theorising about what the actual issue is is in this message.
However, Azureus manages to do this, so I knew it was possible. With some help from the Azureus devs on IRC, and staring at undocumented code, I finally have a solution which I’m putting here in the hope that other people don’t spend hours of fruitless searching like I did.
It is possible, with some JNI hooks into the Carbon layer of OS X, to catch the events there and have them passed through to the application. Basically, you include this JNI code, create some callbacks using SWT, sacrifice a chicken and bleed a goat, and all of a sudden your code gets called when someone opens one of your files.
The code that I use is here. If you look at one of the ...Mac.java
files, you can see the open doc handler being loaded, and inside that (OpenDocHandler.java
), at the bottom of the target
object, you have the bit that sends the files to the rest of the program. I don’t understand most of what it does, but it works. If I get around to it, I plan to refactor this and create a fairly stand-alone bit of code that means you can just register your own callback, and get the events without having to modify the handler. That would be pretty easy to do, I just didn’t want to touch it once I got it working. This method can probably be adapted to handle the other events, but I don’t need them so I stripped those parts out. Have a look in OSXAccess.java
in the Azureus 2 code if you want to know how to do that.
Once you have this, and someone double-clicks a document type you have registered, you get told about it. Conveniently, you also get an event when your application is opened because someone opened your document.
Note: most of the code there is GPLed as I ripped it from Azureus (and eMusic/J is GPL too), but I think the actual C bit is probably something more liberal, as it was done by IBM and I expect they intend for it to be under something like the Apache licence. But I don’t know for sure, as the original file doesn’t have licence details.
So, hopefully very soon, Classicsonline will have a cross-platform, open-source download manager for its users.