I guess from my background, I'm somewhat of a computer language polyglot, like most developers need to be these days. I believe I have what it takes to understand most programming languages and paradigms, and have never been afraid of taking on new challenges. During autumn 2010, some friends and former colleagues of mine had started a new company ("Friendco"), and were looking for people. I was already in the process with another company ("Otherco"), but since that process had not completed yet, we agreed that I would work with the Friendco my friends had started at least until the process I had initiated with Otherco completed.
As faith would have it I ended up taking a position at Otherco, but I worked with Friendco for a couple of months, and this would be my first "professional" encounter with the Java programming language and environment. I've quoted "professional", considering I only got to spend a couple of months doing it. Two of my colleagues, one of which I knew from before, had been using Java professionally in their former jobs, so I guess they would qualify as Java professionals (without quotes). Needless to say, I also depended on their support to bring me up to speed.
My friends also qualify as polyglots, and their familiarity with Java and their growth plans, meant that they had selected Java as the platform for building the software platform for the company. So if I was going to contribute, I had to develop in Java. This blog post is more about my biased opinions related to my relatively brief introduction to Java, and should not be misinterpreted as critique to my friends in Friendco or the choices they've made.
The first obstacle was the development platform. I've developed on Windows, OSX and Linux, but I prefer Linux over the others. Of the two friends I would be working with, one was developing on OSX and the other on Windows (don't bash him for it; he does server side development on Linux as well, he just prefers Windows on the client and is a strong believer of dogfooding, i.e. using the same platforms as his "customers"). Regardless of the development platform, the final software is deployed on Linux servers. Despite being a fairly small company, their setup seems pretty modern, using modern tools like the Atlassian sweet of tools, together with Hudson for continous integration and deployment, together with various automated testing tools (CheckStyle, FindBugs and more).
Eclipse
Their development platform was Eclipse, the java-based IDE for java development. As programming languages have come and gone, so have IDEs, and like many "grown-up" developers, I've found my comfort zone using Emacs for editing and development, and various command-line based build tools (most often already integrated with Emacs). One of the devs said he had tried using Emacs as well when he started out with Java, but had ended up with Eclipse anyway since he was more effective with it. Specifically mentioned were automatic code reading and code-completion inside the editor, and the fact that Eclipse more or less was able to build the project while he was working on it, picking up individual file changes and recompiling automatically.
Apache Maven
Considering there was already two devs using Eclipse with success in the project, I started out working with Eclipse as well. After a few days, a lot of the "automagic" things Eclipse did started to freak me out. I was never quite sure what was built or not, and sometimes I had to invoke some magic commands in Eclipse to force a full rebuild. I also learned that the other devs experiences similar issues from time to time. I do not like having a build enviroment I do not fully understand, so I slowly but surely started digging into how the build stuff worked.
The codebase we worked on was already quite big, because source code had been licensed from another company when Friendco started up, and the project was split into separate modules/sub-projects with various dependencies. So while my frustration with Eclipse grew, I started to dig into the build process. The project used Apache Maven as a "build tool", and it seems the Eclipse magic used the Maven project files together with it's own pixie dust for automating the builds. Fortunately, Maven also has a command line utility for building Java projects, which I started using together with Emacs for editing source code. I was finally able to ditch Eclipse for Emacs and Maven. Sure, some "handholding" tools like documentation was a bit harder than inside Eclipse, but at least I knew exactly when and how things were built. Emacs it seems had similar tools in various alpha-stage add-ons, but nothing I found worked very well (I did not spend a lot of time on getting it set up either). Another thing I would probably miss at one time or the other would be the integrated debugger built into Eclipse, but as you will see later in this article I never really needed the debugger for the kind of work I was doing. Getting intimate with Maven also meant that I was able to help out in cleaning up some problems in the maven project files as well, so the whole build and continous integration process was improved overall on all platforms.
Cross-platform Java mostly works
Considering we were working on three different operating system platforms, using a multitude of tools and stand-alone software that ran more or less across all three platforms, I am actually impressed that it worked as well as it did. At least initially.
Firefox/Mozilla integration
Part of the software we were working on included a web spider that would index web pages, and based on former projects and current ambitions, my dev colleagues had decided that we would continue to actually integrate with the Firefox browser from Java and use this for the project. The main reasons were to "see" the web as users would see it (same layout, similar plugins etc), our spider would more or less "see" the web as users would be seeing it inside their own browser (assuming they used Firefox I guess...). Mozilla offers their XPCOM layer as a cross-platform integration layer. They even have a Java version, JavaXPCOM. Supposedly, XPCOM is supposed to be some kind of cross-platform version of Microsoft's own Component Object Model that enable system components to communicate. Microsoft's own COM model is known to have it's own set of challenges, and ufortunately it seems similar issues exist with XPCOM.
JavaXPCOM
The idea of integrating using JavaXPCOM supposedly is that it would be very easy to do callbacks from our own Java software into Firefox, and the reverse (Java-based callbacks from events inside Firefox). Sounds great in theory. In practice however, it is very difficult. The documentation for both interface and usage is "challenging" at the very least, and unfortunately mostly inaccurate or obsolete. There's also a whole range of issues related to Firefox versions (3.5, 3.6) and the Mozilla library versions (1.9.1 and 1.9.2); I figured it out eventually, but really, it shouldn't really be that hard to figure out. And finally, the interface itself. The lack of docs and/or working programming examples leads to lots of trial&error and cut&paste programming, finding tidbits here and there and trying to pin down what works and what does not, and why if at all possible.
But there is one place where JavaXPCOM seems to work fine; inside Eclipse. Lots of the things we struggled with getting working seems to work fine with the browser inside Eclipse. And as all real programmers do eventually, you start digging into the source code to figure out how it does it's magic. Magic is certainly the right word for it. It seems XPCOM is implemented using some kind of binary communication protocol with lots of hardcoded integer constants. Imagine the fun I had when I had to replace the constants used with symbols instead to make "Checkstyle" tools happy, without any real insight to what the constant actually did ("final const int MAGIC_INT_6 = 6").
Furthermore, I had to use Java run-time reflection APIs to figure out which XPCOM interfaces was available. Sometimes signatures using "ints" were available, other times they used "longs".
Cross platform Eclipse with XPCOM; consider it evil
Ripping through the Eclipse sources, I eventually managed to get the XPCOM integration working by more or less depending on modules provided by the Eclipse APIs. Although I did get the spider working, it wasn't elegant, and I hadn't nailed the root cause as to why things differed so much on the various platforms we used. Which is about when I discovered how Eclipse uses source code manipulation to effectively implement platform dependent macros. Those int versus longs challenges I was having earlier was due to Eclipse, as part of being compiled or built, effectively replace function signatures prior to compilation, replacing ints with longs or the other way around, depending on whether the target platform is 32-bit or 64-bit. I can't remember the specific signatures used, but they looked something like this: int /*long*/ somefunc(int /*long*/ par1 ...). So obviously, if the platform changed to 64-bit, some software would replace all those signatures in the source code to long /*int*/ somefunc(long /*int*/ par1 ...).
Firefox spiders
During my efforts, I found various alternative methods of automating Firefox for spider web pages or similar, where most of them used the add-ons mechanism in Firefox together with Javascript to provide the necessary integration. Based on my efforts, I believe that is a much better solution going forwards, and it is probably also easier to extend to other browsers as well when/if that is necessary (google "firefox automation").
Other Java observations
-
Signed bytes. While working with Friendco, I also implemented a SOCKS4 proxy in Java which would be used for IP traffic accounting. Worked perfectly. The proxy, like most other low-level software, works on bytes inside IP packages. It's always a challenge when you know exactly the simple operations that needs to be done at the byte level, but the tool at hand (Java) seems to be more or less deisgned to make it very hard. Signed bytes in Java, together with converting to shorts, ints and similar, really made for an interesting few days... ;-)
-
Constants inlining. We also had some interesting times where the Java compiler allows one class to inline constants from another class, so if you changed the other class it would be recompiled, but the class that had inlined the constants would not pick up the changes. In big projects, this gets really fun. There are workaround by not using constants directly (think "constant functions returning the constants" instead), but they again may not play well with the style verification tools (Checkstyle, Findbugs etc). Personally, I believe this kind of behaviour from Java compilers should be discontinued asap; if something changes in a dependency, all classes using the dependency should be forced to recompile.
In summary
This was my first real Java development in an enterprise setting (I've done some tinkering on mobiles earlier). Up until this project, I've more or less consciously decided against diving into Java, but I've also had the luxury to make the calls on most projects I've participated in so far. This experience with Java confirms what has been my beliefs so far; use Java if you must, or if you plan on hiring lots of developers fast (you need some middle ground that everybody can understand). But there are certainly better tools for getting most jobs done more efficiently (both in development time and run-time), and it'll most often be less painful than Java itself.
It's also worth pointing out that the stuff that I actually did in Java (run-time reflection, Firefox XPCOM integration and writing a SOCKS4 proxy server) probably are not good examples on how Java makes cross-platform software development easy, and I doubt Java can really be blamed for the challenges I had with XPCOM. I guess Mozilla deserves the blame for XPCOM, or Eclipse with how they implemented it. On the other hand if professional Java software like Eclipse uses "source code modifications" to implement "platform dependent macros", I would say that definitively is a step back compared to languages and systems before Java. If that's the way "the professionals" do it, it certainly does not smell good.
There are interesting developments happening on the JVM itself (the target that Java compiles to run on), including Clojure, Scala and more, but I'll save that for another blog post.