July 2005

« June 2005 | Main Index | Archives | August 2005 »

28
Jul

Carlton and United Breweries unwittingly discriminate against a third of the population:

To enter carltondraft.com.au, you must first enter your birthdate. Enter a single-digit day, and you get a Javascript popup reminding you that ‘Day must be 2 numerals long’

On the other hand, they do make some really neat TV ads:

Lesson one:

10.3.2 301 Moved Permanently The requested resource has been assigned a new permanent URI and any future references to this resource SHOULD use one of the returned URIs. Clients with link editing capabilities ought to automatically re-link references to the Request-URI to one or more of the new references returned by the server, where possible. This response is cacheable unless indicated otherwise. (RFC2616: Hypertext Transfer Protocol -- HTTP/1.1.)

It's pretty obvious, you'd think. If a webserver sends a "Moved Permanently" response, an HTTP client ought to rename bookmarks to point to the new address, since the old one is no longer valid and will never be valid again. RSS readers tend to follow this rule religiously. Mark Pilgrim's feed parser documentation states:

If you are polling a feed on a regular basis, it is very important to check the status code (d.status) every time you download. If the feed has been permanently redirected, you should update your database or configuration file with the new address (d.url). Repeatedly requesting the original address of a feed that has been permanently redirected is very rude, and may get you banned from the server. (HTTP Redirects [Universal Feed Parser])

(Most of the time it's a good rule. Javablogs follows it.)

On a recent trip to the USA, Scott stayed in a hotel with a 'net connection. The connection was one of those with an arbitrary web-based registration that made sure you'd agreed to their ass-covering terms of service. Until you had agreed, the hotel's proxy would redirect any HTTP connection to the registration page... using the 301 status code.

Within a few seconds of plugging in the ethernet cable, all of Scott's RSS subscriptions had been silently replaced with the hotel's registration page URL. All the original feed URLs were lost.

Lesson Two:

The Servlet 2.3 specification defines HttpSessionListener#sessionDestroyed thus:

Notification that a session was invalidated.

If you follow the specification to the letter, as many servlet container implementors did, you end up with a totally useless method. Sure, it gets called when a user's session is destroyed or times out. But since it's only called after the session is invalidated, you can't do anything with it.

Want to know what was in the session? Can't, it's invalid. Want to do some cleanup on objects in the session before it is dumped? Can't, it's invalid. Want to know who the session belonged to? Can't, it's invalid.

Thankfully, for the 2.4 spec the wording was fixed to read:

Notification that a session is about to be invalidated

One of the truly fun things about writing an application that runs against multiple databases is the heady joy of having to test said application against every database it claims to work with.

For the last day or two, a single test in our nightly build was failing against Oracle. Only against Oracle. The test in question creates three blog posts and checks that the "previous" and "next" links work between them. When running the test against Oracle, you could traverse forwards, but the link to go back wasn't there.

I ran the setup portion of the test manually, and somehow each post's "back" link pointed to the post you were viewing rather than the previous post. "Curious", I thought, and restarted the test server so I could attach a remote debugger.

Sod's law in full effect, the act of restarting the server in debug mode made the problem go away. All the links had magically reverted to pointing where they were supposed to point.

The query to find the previous post was mind-numbingly simple. You take the creation date of a post, and find the first post with a lesser date. We'd had problems with this sort of thing before - Hibernate maps dates to Oracle's DATE type (apparently changed to TIMESTAMP for Hibernate3) which only has a one-second granularity, but it's not a practical problem and the tests make sure the posts are spaced out sufficiently.

And anyway, if it was the data, why would restarting the application server make the problem go away? It must be something we're cache...

Aha.

Just before we store a blog post, we set its creation date. Like any Java date, this is captured with millisecond precision. However, Oracle isn't storing our dates with the same precision, and zeroes the last few digits. So java.util.Date thinks the time is 1121235471964, but Oracle records it as 1121235471000.

Hibernate then shoves the original BlogPost object in the second-level cache. When you look up the post in future requests, you're getting back the original, millisecond-precise date from the cache. You pass that date back to the database to look for the previous blog post. Oracle is told to look for the first post before 1121235471964, a position for which 1121235471000 obviously qualifies. You then get back the post you started with.

When you restart the server the cached date goes away and you're left with only Oracle's version of things, so all your queries are back to normal.

The lesson, of course, is not that Hibernate sucks, or that Oracle sucks, or that Charles can't code his way out of a paper bag (although the fundamental truth is that all software sucks, and by corollary, all programmers suck). In the end, it's just a long-winded way to agree with Joel about leaky abstractions.

Code generation tools which pretend to abstract out something, like all abstractions, leak, and the only way to deal with the leaks competently is to learn about how the abstractions work and what they are abstracting. So the abstractions save us time working, but they don't save us time learning.