Vending Machines

The vending machine at work in England offers 16 items. After months of staring at the US vending machine with its 50 billion items, this seems shockingly sparse. "Is that all?" was my first thought on seeing it again.

I've just realized, though, that the number of items I'd actually consider eating is higher in England. Not just the proportion; the absolute number.

I also notice that the other lane of traffic seems way too close. I've only made one vehicular journey so far, but I've become a nervous passenger. I had to look away as we made our way out of the multi-storey car park. I couldn't believe it was possible to get out of there without hitting another vehicle or some part of the structure.

The first thing that stood out on re-entry was that the characters on license plates are so legible. Great big thick black things; works of beauty to rival the road signs. Certainly better than a scruffy red geriatric scrawling of "California" above too many characters with no break to aid the memory, using a font that's too narrow for its height.

There are no TV channels here. I'd never felt it that way round before. I'd always felt there were too many elsewhere. But now I click through eight (to the nearest power of two) and am back where I started, and it seems weird.

What TV there is seemed mostly to be US-made, when I flicked through. Raymond (who "Everybody Loves" but surely Nobody Finds At All Amusing) was being told off by Debra for drinking from the carton, and I noticed myself thinking "that's my fridge!", when I stopped thinking "Debra's hot".

The periods of my life spent in other European countries never gave me this much chance to see England as a foreigner might.


"Core Java 2: Volume II-Advanced Features"

I haven't bought a Java book in some time. Why bother, when you have the source, the JavaDoc, and Sun's online tutorial?

Recently, amazon.com offered Cay Horstmann's "Core Java 2: Volume II-Advanced Features" with about $20 off, just as I was thinking it was about time I made an effort to have a look at the new stuff in 1.5; help myself avoid the trap of using familiar old stuff when there are better modern alternatives. I read a sample chapter on the web, and it look okay.

The book is fat. The kind of 1,000-page tome that you might associate with world-recognized morons like Herbert Schildt. But Anguish et al's Cocoa Programming is fat too, and that's a good enough book that I feel bad for mentioning it in the same breath as Schildt.

The first chapter is "Multithreading", which was one of my main interests in terms of 1.5 features: the new concurrency package is something I'm really going to be able to use. I was pleased to find that we get past the "what are threads?" quickly, and move on to various common tasks, what your choices are, and how to discriminate between them.

The author is good at explaining how things used to be in addition to how they are now (and why you'd want to go with the modern way). This is going to be increasingly useful as people come to Java who don't remember the old days; they're going to see plenty of crufty code and not know when they're looking at bogus magic. A strange exception is that when ThreadGroup is discussed, there's no mention of the fact that there's no longer any reason to use it.

(As an aside, I like TimeUnit. That's a great solution to the problem of, well, time units. I explained it to someone I work with, and they were unimpressed, saying it's "obvious". Which is exactly why it's so good. And surprising that neither of us has seen it anywhere else.)

I didn't like the way that, when mentioning fairness, the author kept saying "significant performance penalty". What does that mean? Without a specific application in mind, no-one can say whether a performance penalty is significant or not. If he meant "fairness shouldn't be your default choice because it isn't free", he should have said so.

There's some mention of Swing and threading issues, covering both invokeLater/invokeAndWait and SwingWorker. There's no mention of Foxtrot, which is a pity because from what little I've seen it looks like the nicest way to write that kind of thing — write it as if it were synchronous. I'd like someone to go there for me and find out what works and what doesn't, and come back and tell me if I should use it too.

Next up was "Collections", the chapter I'd read on the web. I liked this chapter. Even if you know the collections classes well, this is a good chapter. I was mostly interested in the new stuff, but I learned a few things about the old stuff too.

The chapter on "Networking" had good but brief coverage of socket timeouts and interruptible socket I/O. I was disappointed in the example network server. ThreadedEchoServer would have been a good place to show thread pools in action. And also a good place to demonstrate java.nio (annoyingly covered in volume I).

Tie-ins between chapters (and good examples) are a common problem with books. It's hard to come up with example code that does something interesting, illustrates a point or two, and is small enough to be understood in a book.

Which brings me to my problem with this book: the unconventional interpretation of "Advanced". Why is NIO not "advanced"? What's "advanced" at all in the very weak "Advanced Swing" chapter? Its coverage of text especially is anything but advanced. The chapter after that on "Advanced AWT" is better, with good coverage of the clipboard, but it tries to do too much and falls way short of its title. "Advanced AWT" needs a book, not a chapter. These chapters come nowhere near the standard set by the book's first two chapters. Sun's Java tutorial has more advanced coverage of AWT and Swing.

I guess that in the context of this book, "advanced" doesn't mean anything more than "not in volume I".

(Kim Topley's book "Core Swing: Advanced Programming", which seems like a permutation of this book's title, is the best book I've seen on Swing's text components. I like Sun's tutorial and the source well enough for the rest. I haven't come across a book on Graphics2D that I really liked, and I tend to read the source when I need to know more about AWT. There's some weird and interesting stuff in there.)

The chapter on "JavaBeans Components" almost got skipped. I mean, who cares? They're so 1996. It turns out to contain an interesting section about XML serialization. (The chapter doesn't explain why java.beans uses a hack instead of using the transient modifier like normal serialization. It probably didn't hurt me to think about it and realize that the answer is that the hack has to work on methods rather than fields, but whenever I do something that's obviously a hack, I feel obliged to explain the motivation and why I don't believe one can do a better/simpler job.)

The chapters on "Security", "Database Programming", "Distributed Objects", and "Internationalization" didn't interest me much. "Native Methods" would have sucked for me because the major example is Windows-only. Sun's JNI tutorial is pretty good anyway, and I have the JNI book too.

The "XML" chapter was good. It covered the variety of choices more clearly than usual, and was refreshingly free of XML hype. All the choice look like they're still way more work than they should be, but there's some interesting stuff coming in 1.5 (XPath and XSLT). This is the one chapter that I've actually used in anger; I replaced some code that read and wrote plain text files with code to read/write XML.

The book ends with "Annotations". It's strange that the emphasis should be on user-defined annotations rather than the standard annotations. Especially when annotations are such unexplored territory. I need to know more about them myself before I can really judge this chapter, but there was nothing there that made me want to rush out and try something.

Overall, the plus points are good overviews of concurrency, collections, and XML. Negatives include most of the other chapters. Can anything that's treated in Sun's Java Tutorial to a similar or greater depth be reasonably described as "advanced"?

I'd have put the supposedly "advanced" AWT and Swing chapters in the first volume, and saved material on subjects like NIO and reflection for the second volume. If you were new to Java and read both books, I guess you'd be happy enough with them as they are, but you'd be misguided if you ended up thinking you had advanced AWT or Swing knowledge.

Despite how negative this might have sounded, I'd still say this is a book worth having around for the three chapters I praised.


Integrating Mac OS in a NIS/NFS environment

[Last updated 2007-04-23.]

This week I acquired an unused Mac at work. It had been doing duty as a plant-pot holder, and I'd assumed it was something like a 400MHz G3. I was only there to lighten it of its USB keyboard and mouse, but I thought I'd have a quick look inside. The size of the disk made me realize it probably wasn't as old as I'd imagined, and indeed it turned out to be a dual 800MHz G4 with a decent amount of disk and memory.

Cool! (Though I now have to think of a use for my Mac mini when it arrives.)

I found and pressed the PRAM reset button so it would power up, booted from the 10.3 CD, reformatted the disk, and re-installed the OS. Any machine that's been used as a plant-pot holder so long it won't even power up probably isn't something you want to use without starting from scratch!

My first mistake was letting the Mac OS installer create me an "elliotth" account, which is the same name I have in NIS. I should probably have used a temporary name, but I wasn't certain I was going to (or going to be able to) integrate the Mac thoroughly.

To enable NIS, I followed the instructions in http://www.bresink.de/osx/nis.html. Note that the pictures correspond to 10.2, but the text tells you what's different in 10.3 (the other way round would make more sense, I think).

This was pretty easy, and I could ypcat(1) to my heart's content, but no-one could log in because their home directories weren't available. I was going to need NFS.

Experiments with mount(1) on the command-line (and Ethereal via Apple's X11.app) suggested that I was going to need a name in DNS or Linux's NFS server would refuse my attempts to mount with EPERM. I couldn't find any support for dynamic DNS in Mac OS, and Google seems to think it doesn't exist. So I changed to "Using DHCP with manual address" in System Preferences' network configuration, and added a DNS entry for a newly-allocated IP address.

Linux wasn't impressed. Ethereal showed that the DNS lookup was successful now, but I was still getting EPERM. It turns out that without the "insecure" export option, Linux won't accept mounts from ports >= 1024. Silly in these days of hand-held machines capable of running Unix (which you've installed yourself and so know the root password for), but what can you do? What you can do is use the "-P" option to mount.

I also had to use the NetInfo GUI to edit the local "elliotth" to have the same uid and gid as the user in NIS, to stop things getting too confused. While I was there, I chose "Enable Root User" just in case. (Interestingly, I found out later that even if you're logged in as root, the NetInfo GUI makes you authenticate with an administrator password before making changes.)

So, I knew that I could manually mount an exported home directory. What I needed now was to tell the automounter to make them all available, on demand.

Unfortunately, Mac OS has its own automounter, different from amd(1) and autofs(1) (see here). So I couldn't just use the local auto.home configuration.

Google offered an O'Reilly Mac OS X hack "Accessing NFS" showing how to manually configure one entry without using the NetInfo GUI. It was pretty easy given that (with the exception of working out the quoting!) to knock up a script to translate auto.home into NetInfo automounter configuration.

#!/usr/bin/ruby -w


def sudo(command)
puts("sudo #{command}")
system("sudo #{command}")

def restart_automount()
sudo("kill -HUP `cat /var/run/automount.pid`")

def remove_old_configuration()
sudo("niutil -destroy . /mounts && rm /home/*")

def add_entry(username, path)
# Based on information in O'Reilly's "Accessing NFS" Mac OS X hack.
# http://hacks.oreilly.com/pub/h/341
sudo("niutil -create . /mounts/temp#{$temp}")
sudo("niutil -createprop . /mounts/temp#{$temp} type 'nfs'")
sudo("niutil -createprop . /mounts/temp#{$temp} dir '/home/#{username}'")
sudo("niutil -createprop . /mounts/temp#{$temp} name '#{path}'")
sudo("nicl . create '/mounts/#{path.gsub('/', "\x5c\x5c/")}' opts \"-P\"")
$temp = $temp + 1

def configure_automount_from_NIS()

sudo("niutil -create . /mounts")

`ypcat -k auto.home`.split("\n").each() {
# "monkeyboy monkeyhouse.zoo.com:/tree/branch/&"
# We expand '&' ourselves.
if line =~ /^(\S+) \s+ (\S+:\S+)\/&$/x
username = $1
path = $2
add_entry(username, "#{path}/#{username}/")



You can check that it looks right with the NetInfo GUI, and you should be able to cd /home/monkeyboy (or whatever makes sense given your NIS users).

The final step was to log out, log in as root (I didn't have another local account; this was the mistake I referred to back at the beginning), move the useful stuff from /Users/elliotth into /home/elliotth (I was actually su(1)ed to elliotth to do this; my reason for logging out was so that nothing important would change in my home directory), remove /Users/elliotth, and use the NetInfo GUI to rename the local user.

Surprisingly, I could log back in as (NIS) elliotth with no problems.

Well, one problem: an Xcode project I'd moved failed to open, with no error message. The command-line xcodebuild tool suggested it was trying to access directories in /private/automounter/home/elliotth instead of /private/automounter/static/home/elliotth, but grep(1) doesn't think that's stored in the project. This may be indicative of some lurking problem with my automounter work-around. Or it might just be that Xcode is broken. It was a good excuse to convert the Xcode project into a normal make(1) project, anyway, and stick it under Subversion's control. "Why I can't stand Xcode" is a topic for another day...

Anyway, don't blame me if this leaves your Mac no more useful than a plant-pot holder, but having this post available would have saved me time, Google strain, and head-scratching this week. I imagine that the arrival of the Mac mini will mean a lot of curious Unix users who couldn't previously justify a Mac will now be facing exactly these problems. It's worth it, but that too is a topic for another day...

There's more information about NFS/NIS on Mac OS at sial.org's Using automount on Mac OS X.



The first time I was impressed by on-line mapping was when multimap.co.uk added a feature where they'd alpha-blend street maps onto aerial photos. You could pan a rectangle around the aerial photo, and within the rectangle you'd see the map composited. For civilians like me, it was a great improvement on aerial photos, and more interesting than a plain old street map.

I had two complaints, though.

One complaint was that I couldn't pan the map; if I hit the edges, it didn't start to auto-scroll. It just clipped my compositing rectangle. If I wanted to pan the map, I had to click an edge, and watch the page reload! Not just the images in the page; the whole page! Really distracting, and an awful way to follow a street.

My other complaint was that it was using about 500 square pixels of my 23" display. No matter how big I made the browser window, it filled the same area with map data. It didn't even use the excess to show me more ads; it just left it blank. [Admittedly, since then they've added a "Bigger" button, but it only doubles the width to give you something like 700x400.]

I knew this would all be fixed one day, but I didn't realize how soon. And I didn't realize it would be done with just a little JavaScript. But Google have done it, and it's great: maps.google.com.

Google don't let you blend with aerial photos yet, but I'm sure they will. They have the data, after all. And that feature always seemed more interesting than strictly useful. The worst problem is that Google's maps only cover North America. A shame, because the British Ordnance Survey makes really nice maps (and not just for the purposes of moving artillery).

Oh, and it doesn't work with Safari yet. Camino works fine.

I'm still waiting for somewhere that will let me blend, say, Bracknell (Berkshire, GB) and San Jose (California, US) to get an idea of their relative sizes. It's not hard if you've got the data.


Birthday Calendar: Address Book/iCal Integration

I don't know why, but it turns out that most of my friends' birthdays are in December. It those few scattered about the rest of the year I have trouble remembering, and I can't count on my Macs' support: although I diligently add everyone's birthday to Address Book, iCal doesn't bother to incorporate them into my calendar.

This lack of integration – otherwise an Apple strong-point – annoyed me initially, but I forgot about it until the end of 2003. The GNOME project (the beginnings of a desktop for non-Mac Unixes) offered cash prizes ('bounties') for people who solved a variety of problems with their applications, including automatically adding address-book birthday entries into the calendar.

Rather than switch to Linux, where the only decent application is Mozilla Firefox, I thought I'd knock up something similar for Mac OS. What I didn't know was that iCal doesn't have any kind of API.

Talking to iCal
If you look inside ~/Library/Calendars, you'll see a file for each of your iCal calendars. If a new file is written to that directory, iCal will notice next time it starts, and add it to its list of calendars.

You can open these .ics files with any text editor; do so, and you'll see that they're in a simple documented IETF standard format [http://www.imc.org/ietf-calendar/index.html].

The macdevcenter.com article "Transforming iCal Calendars with Java" made me want to use Ruby, my scripting language of choice, because Java – though my heavy-duty programming language of choice – seemed such an ill fit for this kind of work. What I wanted was a simple little script that would read my Address Book database, and write out a calendar file for iCal.

Talking to Address Book
If the annoying thing about iCal was its lack of an API, the annoying thing about Address Book is that it has an API instead of using a documented text-based format for its database.

A macdevcenter.com article from 2002, "A Look Inside Address Book" showed how to use the AddressBook framework from Objective C to iterate through all the people in your address book. The only odd omission from the API is a way to get an ABPerson's full name in the preferred form. For my purposes, I made do with the concatenation of the kABFirstNameProperty and the kABLastNameProperty.

Oh no, not Objective C!
I'm deeply ambivalent about Objective C. I really like Cocoa: it's the best application-building framework I've ever used, by some margin. I really like Objective C's method invocation syntax, something which seems to be a sticking point for many. But the thought of going back to manual memory management in the 21st century is almost too much to bear. That, and Objective C's tendency to degenerate into very K&R-like C when Cocoa doesn't just do it all for you tends to put me off. In actual fact, Objective C isn't as bad as some of its users would mislead you into believing. You can declare variables mid-block, for example. You can use enum instead of #define. But it's still quite primitive.

As for Objective C++, you've probably heard of it, but it's not something you see every day, and it seems pretty much undocumented. As a C++ programmer by day, Objective C++ sounded much more tempting than Objective C, so to make up for not being able to use Ruby, I decided to give Objective C++ a go.

Using Objective C++ turns out to be as simple as naming your source file with a .mm suffix instead of Objective C's .m, and making sure you use g++ rather than gcc for the link step. If you use gcc, it won't link with the C++ library, and you'll get this link error:

ld: Undefined symbols:

This program doesn't make heavy use of C++ features. I use std::ostream for output, in case some day I want to write output directly to a file, say. I overload operator<< for NSDate* and NSString* so that I only have to say once what format I want them output in. And that's more-or-less it.

Here's the code:

#import <AddressBook/AddressBook.h>
#import <Foundation/Foundation.h>
#import <iostream>

void emitHeader(std::ostream& os) {
<< "VERSION:2.0\n"
<< "X-WR-CALNAME:Birthdays\n"

void emitFooter(std::ostream& os) {
os << "END:VCALENDAR\n";

NSDate* dayAfter(NSDate* initialDay) {
static const NSTimeInterval SECONDS_PER_DAY = 60 * 60 * 24;
NSTimeInterval initial = [initialDay timeIntervalSince1970];
NSTimeInterval interval = initial + SECONDS_PER_DAY;
return [NSDate dateWithTimeIntervalSince1970: interval];

void emitAlarm(std::ostream& os) {
os << "BEGIN:VALARM\n"
<< "TRIGGER:-P3D\n"
<< "DESCRIPTION:Event reminder\n"
<< "END:VALARM\n";

std::ostream& operator<<(std::ostream& os, NSString* string) {
return os << [string UTF8String];

std::ostream& operator<<(std::ostream& os, NSDate* date) {
NSString* string = [date descriptionWithCalendarFormat: @"%Y%m%d"
timeZone: nil
locale: nil];
return os << string;

void emitSummary(std::ostream& os, ABPerson* person) {
NSString* firstName = [person valueForProperty: kABFirstNameProperty];
NSString* lastName = [person valueForProperty: kABLastNameProperty];
os << "SUMMARY:" << firstName << " " << lastName << "'s Birthday\n";

void emitBirthdayEvent(std::ostream& os, ABPerson* person) {
NSDate* birthday = [person valueForProperty: kABBirthdayProperty];
if (birthday == nil) {

os << "BEGIN:VEVENT\n"
<< "DTSTART;VALUE=DATE:" << birthday << "\n"
<< "DTEND;VALUE=DATE:" << dayAfter(birthday) << "\n";
emitSummary(os, person);
os << "END:VEVENT\n";

void emitBirthdayEvents(std::ostream& os) {
NSArray* people = [[ABAddressBook sharedAddressBook] people];
for (size_t i = 0; i < [people count]; ++i) {
emitBirthdayEvent(os, [people objectAtIndex: i]);

int main(int /*argc*/, char* /*argv*/[]) {
id pool = [[NSAutoreleasePool alloc] init];
[pool release];
return 0;

You can run this from cron(1), but you need to quit iCal before writing the output to ~/Library/Calendars/Birthdays.ics if you want to be sure iCal won't overwrite it. You'll also probably need to click on the calendar in the Calendars list before the events appear on the Day/Week/Month view. iCal's actually got worse in this respect since I started using it.

Objective C++
As it turns out, there are some unfortunate limits to what you can do with Objective C++. You can't have non-POD members in an Objective C class, for example. If you try, the compiler will warn that the constructor and destructor won't get called. So if you thought you could use std::string to avoid writing getters and setters for NSString* ivars, think again. You can forget trying to encapsulate the recommended boilerplate for getters and setters in a template class, too, because not having your constructor and destructor called throw quite a spanner in the works. Hopefully this is a restriction that will be lifted some day. Destructors are easily C++'s best feature.

What's the cost to using Objective C++ instead of Objective C? Why doesn't everyone, if C++ is a superset of C? The main cost I've noticed is the large increase in compile time. If you've ever compiled a project consisting of both C and C++ parts, you'll surely have noticed that the directories full of C are whipped through at a much faster rate than those full of C++.

Would a Ruby script have been simpler?
It's rare for me to use anything other than Ruby these days if I need to write a program that takes an input file of some sort and produces an output file containing some other representation of the same information. Here, my hands were tied by the fact that I needed to use the AddressBook framework.

Or were they? There is actually a way for Ruby to have full access to native OS X APIs: RubyCocoa. I downloaded it, patched it to work on 10.3 (it doesn't work out of the box), patched it to include the AddressBook framework (trivial, but again, it doesn't work out of the box), and wrote a Ruby script equivalent to my little Objective C++ program. To my surprise, they turned out roughly the same length, with neither obviously much clearer than the other. If I had to pick a winner, I'd go with the Objective C++ program because it will run on anybody's Mac.

On reflection, I guess I'm so used to regular expressions being fundamental to this kind of translation task that I tacitly assumed not having them in Objective C++ (without third-party frameworks) would be a handicap. My gut feeling for what would be better/easier/shorter written as a script is in need of some revision.

I got a useful little program out of this, and I learned a bit about Objective C++, even if some of what I learned was disappointing.

What I really want to do now is display the weather forecast in my calendar, but I've not worked out how to do that. Anyone know how to do something along the lines of the GNOME "weather calendar" bounty?

How about having Mail understand MS Outlook meeting requests, and forwarding them to iCal?

If only Apple would give us the source...


A better Subversion post-commit hook than commit-email.pl

I finally got round to automating sending out commit emails for our Subversion repositories. I tried the supplied post-commit.tmpl and commit-email.pl, but they suck. Six hundred lines of Perl and no effort made to color the patch or format the check-in comment?

I knocked up something that isn't going to win any beauty contents unless it's only up against commit-email.pl, in which case it should be a walk-over.

Here's the script; as usual, the latest version will be in salma-hayek:

#!/usr/bin/ruby -w

# A Subversion post-commit hook. Edit the configurable stuff below, and
# copy into your repository's hooks/ directory as "post-commit". Don't
# forget to "chmod a+x post-commit".

# ------------------------------------------------------------------------

# You *will* need to change these.


# ------------------------------------------------------------------------

require 'cgi'

# Subversion's commit-email.pl suggests that svnlook might create files.

# What revision in what repository?
repo = ARGV.shift()
rev = ARGV.shift()

# Get the overview information.
info=`#{svnlook} info #{repo} -r #{rev}`

body = ""

# Output the overview.
body << "<p><b>#{author}</b> #{date}</p>"
body << "<p>"
comment.each() {
body << CGI.escapeHTML(line)
body << "<br>\n"
body << "</p>"
body << "<hr noshade>"

# Get and output the patch.
changes=`#{svnlook} diff #{repo} -r #{rev}`
body << "<pre>"
changes.split('\n').each() {
color = "black"
if line =~ /^Modified: / || line =~ /^=+$/ || line =~ /^@@ /
color = "gray"
elsif line =~ /^-/
color = "red"
elsif line =~ /^\+/
color = "blue"

body << "<font color=\"#{color}\">#{CGI.escapeHTML(line)}</font>\n"
body << "</pre>"

# Write the header.
header = ""
header << "To: #{address}\n"
header << "From: #{address}\n"
header << "Subject: #{repo} revision #{rev}\n"
header << "Reply-to: #{address}\n"
header << "MIME-Version: 1.0\n"
header << "Content-Type: text/html; charset=UTF-8\n"
header << "Content-Transfer-Encoding: 8bit\n"
header << "\n"

# Send the mail.
fd = open("|#{sendmail} #{address}", "w")

# We're done.

[Update: without the MIME-Version field in the header, the Content-Type field isn't valid, and this matters to some programs.]

Mac mini

My Linux build server at work died this week. It's still not clear whether it was memory or disk. For the time being, I'm working on a headless machine and using VNC on Windows.

I'd forgotten what an unsatisfactory hack VNC is. It's visibly slower than using a machine directly, it's ugly (with -depth 24 I have to start a particular Java application before the desktop gets the right color), and random little things don't work (alt-tab, the alt key in general, and the clipboard being the most annoying). [Update: using version 4 of the VNC server fixes the keyboard problems. Stupidly, on Debian you need to install vnc4server rather than just upgrading vncserver.]

Thanks to all this frustration, I've ordered a Mac mini to act as head. With Unix locally, I will probably be better off running most applications locally, and can continue to build remotely.

And I'll have a decent mailer at last! I've been disappointed to find that Thunderbird is no better at handling 15 MiB HTML mails than MS Outlook. I don't know how Mail will fare, but at least I'll have check-as-you-type spelling checking and the ability to search my mail in finite time.

3-4 weeks...


Java's multiple personality disorder

I've always felt that Sun's regular Java renamings/renumberings were pointless and stupid, but I've never noticed before how harmful they are. As a Java programmer, I know the terms Java, JDK, JRE, J2EE, and J2SE. I know that they're more or less interchangeable, modulo the lack of a compiler in the JRE. I know that the 2 in J2SE is bogus, and I know that the version numbers went 1.0, 1.1, 1.2, 1.3, 1.4, 5.0. I also know that in most contexts, 5.0 is still known as 1.5.

All this is unfortunate because it helps non-Java developers feel justified in their ignorant opinions that Java is just marketing-driven bullshit. Because, hey, look: some of it is!

But I now realize that it's a worse problem than just that: I have some idea of just how confusing it is to outsiders.

The two-tone lilac exterior of the "Popular Downloads" box on java.sun.com hides a dark and pernicious heart. In the past couple of weeks, I've had to explain to a C++ developer that "J2SE 5.0" is the link if you want the 1.5.0 SDK. And I've had to explain to testers – people who use a wide range of operating systems on a daily basis and who may well be able to tell you the latest stable and development Linux kernel version numbers – that the prominent "J2SE 1.4.2 SDK" link doesn't mean that 1.4.2 is the latest version of the JDK.

These are people – computer people, if not Java people – who've gone looking for Java 1.5.0, and failed to find it. So they've stuck with 1.4.2. And if I hadn't called them all sorts of names (and explained that J2SE isn't "Server Edition", and the 2 doesn't mean anything, and, yes, that is actually the link you want for JDK 1.5.0), they probably wouldn't have looked again until 1.6.0.

At which point they'd doubtless be confused by the offer of J3XE 2007, or some other new marketing nonsense.

I'm also told that if you have JRE 1.4.2 installed, it'll prod you to download new updates to 1.4.2, but won't mention that there is a 1.5.0 available. I can perhaps understand why you'd have such an option as a sop to corporate control freaks, but it seems like the wrong default.

Either Sun don't really care about making it easy for new people to join in, or they too have been slowly boiled alive in this acronym soup, and don't realize just how daunting and unintelligible it can be.

[My friend Chris Reece reminds me that this is a long-standing Sun tradition. Is it Solaris 9, Solaris 2.9, or SunOS 5.9?]