Java's Poor Support for MIME Content Types

Java is the language in which I program the most, and there's a lot to be said for it. But over the years it has wandered away from its roots as a simple language for Internet applications. Those in charge of Java have bloated the libraries with redundancies, all the while ignoring some of the most funamental concepts needed for a modern Internet programming language. A case in point is Java's poor support of MIME content type, one of the fundamental types used by all platforms to communcate on the Internet.

A MIME content type, originally defined in RFC 2046, is the fundamental type negotiation unit on the Internet for protocols such as HTTP, SMTP, and IMAP. It would not be exaggerating to say that, were it not for content types, the world wide web and email would not function. Does Java have support for MIME types? Well, yes, yes, and no. There are in fact two Java classes for handling MIME types. There is javax.mail.internet.ContentType and javax.activation.MimeType, both of which are inadequate.

A quick glance at these two classes will reveal that their APIs are virtually identical. There's a problem: the javax.mail package is not included in the default JVM distribution, so any application that uses it must also distribute the javax.mail library. The latter, though, was recently incorporated into the standard distribution, so that any JRE version 6 or later will include the javax.activation libraries.

But whichever of these classes you use, there are some major stumbling blocks. First, whoever designed these classes didn't receive the memo of the benefits of immutable classes for identifiers. A MIME type is for the most part an identifier, and shouldn't be modified. Imagine if you ask for a resource's MIME type and you give that identifier to another method—and that method promptly changes the subtype, for example. Yes, these MIME types allow anyone at any time to change the primary time, the subtype, and the parameters! Not only would making these types immutable make applications safer, it could have saved memory by a singleton system with a factory method, like the number classes (e.g. Long), as there a few MIME types used far more often than others.

But the worst transgression of these classes is that they do not provide appropriate hashCode() and equals() methods, preventing them from being used in sets and maps! There are numerous use cases where one would want to use instances of a MIME type class in a hash map, a hash set, or in some other lookup scenario

Consider the following use cases:

All of these use cases necessitate being able to associate MIME content types with other objects, but because the Java MIME type classes do not support being stored in hash tables, one is required either to create custom classes (in which case frameworks cannot work with each other) or to convert to and from string representations of classes (which severely reduced object oriented architecture).

In 2005 I brought up this issue with Bill Shannon, a Distinguished Engineer at Sun Microsystems and one of the architects of J2EE. Besides "[not] see[ing] a lot of use cases of using a ContentType as a key in a map", Bill tried to argue that the idea of "equality" for MIME content types is so contentious that it wouldn't be useful. Bill's examples centered around content type parameters, but in reality, the most common use of content types in a lookup scenario uses content types in their "base type" form.

However, not providing any equals() and hashCode() is not a correct response to there being various use cases for comparing ContentType objects, for the following reasons:

That's really just two ways to say the same thing: because there were several equally good choices, a bad choice was decided upon. Here's an example:

new javax.mail.internet.ContentType("text/plain")
new javax.mail.internet.ContentType("text", "plain", null)
new javax.mail.internet.ContentType("text", "plain", new ParameterList())

It just so happens that all use cases I can think of would consider these objects equal. The current content type implementations determine that these objects are not equal, though, which all use cases would consider this a bad result.

In order to provide consistent semantics for equals() and hashCode(), the most appropriate (i.e. least bad) decision might be to compare everything—including parameters—and return values consistent with equality and hash codes for generated strings for the two types. This option would provide no less functionality than the present functionality, but it would provide expected results for what is perhaps the most common use case: comparing primary types and subtypes. Still, two content type objects with identical parameter lists would also function as expected.

Bill didn't dismiss all of my concerns, but instead recommended I talk to Mark Reinhold, the spec lead for the Java SE expert group. Mark acknowledged the issue and suggested the possibility of adding yet another Java class that would work as expected regarding equality. Alan Bateman joined the discussion and said that "Dolphin [would be] the right release to add this support." That was in 2006. In 2008, Dolphin, aka Java 7.0, is (perhaps not rapidly, but still) approaching, and still no sign of workable content type support.

So like most other fundamental parts of Java, I've been forced to simply write it myself. (And the Google representative who interviewed me last year, after looking over my resume, asked, with a smidgen of condescension, why in the world I rewrite so many libraries when such things already exist!) I've created a MIME content class which works for the most common cases of equality, is compatible with the MimeType present in Java 6.0, and adds other useful features. There's room for improvement (e.g. forcing immutability, returning singletons for common MIME types, etc.), but my content type class is abundantly more useful than the default MIME type support available from Sun. It's open source, and it's available via Subversion as You'll probably be interested in getting the whole com.globalmentor package while you're there.