Executive summary: We’re launching complete support for OS X, Windows and Linux at the end of our current Sprint in a few weeks, and dropping JNLP in favour of an in-house engineered solution, Jinn, due to deployment issues. We’re also open-sourcing Jinn.
One thing you can say about import•io is that it is not a simple piece of software. The servers that run the queries have to deal with an ever changing web, scaling, balancing and generally being awesome. However, servers are reasonably simple to deploy, because you control the environment that you are deploying to, and you can control it, especially with great tools that we use such as CloudFront. The client applications are a different matter though.
Websites are a known problem, and we just deal with the fact we have to test in 10+ different environments. No one likes it, but we all just accept it now. There’s no getting around it (although a lot of tools make it easier).
However what about other client interfaces? The first of these that we are releasing is the Live Web Connector Builder. With this tool, you can interact with a website that you want to query to get data from live, e.g. a website that has a search box, and by interacting with the tool, you train a connector. As you can imagine, the algorithms that run in the background that collect information from your interactions and analyze it are complex, and logic heavy.
We needed a solution that would let us:
- Run heavy proprietary algorithms efficiently with a level of IP protection
- Support the same tooling as the server – advanced regular expressions, XPath 2.0 etc.
- Provide a launch of the application from the web site
- Be as multi-platform as possible, to allow us to minimize any platform specific code (because we all love upkeeping lots of different versions of the same code!)
- Be browser agnostic
- Operate totally in the user space, not requiring admin rights
- Be able to reuse code as much as possible (DRY and all that)
The performance considerations of Javascript meant that it was ruled out. Further, there’s no native XPath 2.0, the only implementation is Saxon CE, and the regular expressions support is very limited in Javascript. As we didn’t want to have to maintain different versions of the product for different browsers, it would have meant using something like XULRunner to develop our application. All in all, we agreed an XULRunner app was not the way to go.
Chrome’s Native Client was out of the window, as we would be abandoning Firefox and IE users, and tying ourselves to Chrome.
We could have gone down the Spotify/iTunes route, and used a C variant with a cross platform library such as QT or GTK. However, hiring C++ programmers is a challenge in itself, and I personally have very limited C++ experience. This did not seem an attractive route.
So that brought us to Java. Java can be decompiled, but solutions such as ProGuard, Sandmark and Jode make this a practical nightmare. JavaFX has a web pane, but with very limited interoperation. However, we could utilize the excellent cross-platform Mozilla XULRunner library to provide our browser. Further, our server is written in JVM languages, so we could re-use data structure code etc. from that. Java is well supported in browsers (or so we assumed), fast, multi-platform, consistent (unlike HTML!), and by signing an applet, we can run like a normal application in the user space. It would steel feel “webby” when building a connector, as you never leave the browser. We can bootstrap the applet, and then download and cache dependencies such as XULRunner. The best Java library for interfacing with XULRunner is SWT, but applets run within the Java SE GUI framework AWT; however the SWT_AWT bridge makes that not an issue. Ok, great, deal done! Hmm, hold on, wait a minute…..
We had issues with general stability, and also keyboard events being swallowed somewhere in between the applet and XULRunner – both of which proved impossible to debug. So we swapped from using an applet, to using JNLP. JNLP (a.k.a. Java Web Start) is a mechanism for launching Java programs from a manifest file over HTTP. The hope was that things would improve by taking out a layer or two: the browser and the applet container.
And improve they did. The application was stable. Keyboard strokes still got lost sometimes, but we had a workaround that wasn’t outrageous. Now we just had to test it on other platforms. What could go wrong?
BOOM. On OS X SWT does not work with JNLP unless you have code that is signed with a valid certificate (always trust this self-signed certificate is apparently not enough!), because in order for SWT to start, you have to run the GUI on the main native Cocoa thread, and to do this it requires you to use specific Apple libraries to run the GUI code on the Java main thread, and JNLP exits the main thread before dispatching any actual application code, so you need the JNLP JVM to spawn a new JVM to run your application. (Gasp…) Which requires that you pass all the security checks, including no self-signed certs. (To find this out, you only need to read the source :/ )
Ok, so let’s buy a code signing certificate from Comodo. Ok, in order to do this you need a Yellow pages listing or be in the phone book. Errr… hello? Is this the 80s? We’re a technology startup in a co-working space. Do we look like we have our own phone line or an advert in the Shoreditch Yellow Pages? Unlikely. Fortunately, yell.com and 118188.com will list you for free. With no security checks, apart from calling you ON THE SAME NUMBER YOU GAVE TO COMODO. Security through obscurity? Yes please! *Sigh* This drawn out process took over two weeks, and unfortunately, in that time we had to launch the builder to be used at an event. Which meant putting off OS X support. Which stung. A lot.
OK, given that OS X isn’t happening, let’s test on the platforms we know it works on: Windows and OS X.
Let’s test this thing! (a.k.a. What could go wrong?)
“Doesn’t work on win64.” “Works for me dot JPG”. “No, it really doesn’t.” “Hold on, what Java version are you running?”
It turns out that as Mozilla did not fully support 64bit on Windows (and have since chosen not to support 64bit on Windows indefinitely), even though we had a 64 bit XULRunner, the SWT team didn’t build the 64bit bridging DLL between XULRunner and SWT. Not documented anywhere I could find, apart from Bugzilla. Ah. Building the DLL was one option, but since Mozilla have hit the big red pause button on 64bit, it seemed unwise to support it ourselves. It wasn’t possible to detect what architecture the JVM was through Javascript, so we couldn’t let users know ahead of time whether or not their platform was supported without loading an applet. This wasn’t a problem before because the default JRE on windows was 32 bit, as the 64 bit had no web start or applet support. Unfortunately (for us) now, it does. So the default is 64 bit. So the appl
ication can’t load up XULRunner. Damn!
“Fails STILL in OS X”
Ok, so we’ve got our certificate, we’re ready to roll. It’s got to work now, surely…. BOOM! Unfortunately, even when you get this far, on OS X, AWT is really really broken. JNLP spawns a new JVM, only for it to unexpectedly exit advising you to raise a bug with OpenJDK, just so you know it’s really bad. (This is the first time I have seen this particular error output, and I hope, the last.) Guess what, there looks like a patch. In Java 8. Woot! So we had to fast track ripping out any AWT references from the source from when it was an applet, and do a pure JNLP SWT application. (Amazing how priorities change…)
“Doesn’t work in Chrome on OS X”.
What do you mean Chrome doesn’t support Java?!?! In what can be only described as amusing twist of fate given the hoohaa over Mozilla dropping win64, Chrome on OS X is a 32 bit browser, and there is no 32 bit Java 7 build for OS X. Game over. A 32 bit browser cannot load a 64 bit plugin.
“Fails with OpenJDK.”
We all develop with the Oracle JDK. Someday I hope the OpenJDK will be as reliable and not segfault as much, but for now we develop against Oracle’s. However, most peeps out there in the real world that are Linux users use OpenJDK. While JNLP is a great piece of technology, we found it decidedly buggy in OpenJDK, as well as actually being in a separate package that often people do not have installed. Further this package on Ubuntu doesn’t work with some browsers. Waaa, this is too much!
“Couldn’t use at a hackathon.”
We’re a modern company, and of course we use the latest Java 7. However, some institutions aren’t keeping up (as we found out on some hackdays recently where computer labs still had Java 6 installed!), and it may be impossible for them to upgrade it if they don’t have admin rights on the computer they are using: breaking point 6 above. Ah. Ok that’s a real issue. We want people to be able to run our software!
Conclusion
- Never ever assume anything when there would be a large impact. Even if what you are assuming is common knowledge that it “just works”. A proof of concept is always a good investment.
- JNLP is like many things. Great in theory, a headache in practice.
Jinn-based-import•io-Connector-Builder
So, what to do? Well, the only thing to do is to go the Spotify/iTunes route. No, I don’t mean rewriting it in C++! I mean having an install process that installs a browser plugin so the website knows if the app is available, and a custom URL protocol handler so you can launch a native application from a browser, like when you click an iTunes itms:// link.
So we need to build a browser plugin, multi-platform. No problem, Firebreath rocks.
And we need a protocol handler. Ok, different on every platform, but do-able.
And we need a multi-platform open source solution for replacing JNLP, that is a native application, that uses a web-based JSON manifest file, like a JNLP file, which can show licensing info, install dependencies such as a JRE, libraries and XULRunner, and then version manage that installation, keep it up to date, and self-update. And have as small a download size as possible. And do it all in the user area. So there’s omaha from Google, but that’s just for Windows and boy is it complicated. Surely there must be something that’s cross platform, and open source. (Some comparable closed source solutions are $1600/dev!)
Well, no there’s not. But there will be. Because we are writing it, and it’s called Jinn. On realizing that there was no other option but to drop JNLP, we had a two day internal hackathon to specify how our installer would work and build it, and this was some of the outcome. It’s a set of scripts written in python that can be compiled using pyinstaller to a native binary. You specify a manifest file location when building the application, and the application bootstraps from that. We’re working on moving the docs over from our JIRA to github at the moment, so expect the wiki to start to take shape. Jinn isn’t just for Java, it’s for any application. I hope to see more people using it in the future, and contributing to it.
So, there you have it. We’re launching support for all platforms at the end of our current Sprint in a few weeks.
I love Java. It’s a great language, and the JVM is a great platform. But deploying a Java app was a pain. Was. 🙂
by Matthew Painter, CTO