What’s wrong with the Java Servlet API
Before Play, Java web frameworks were based on the Java Servlet API, the part of the Java Enterprise Edition (Java EE) stack that provides the HTTP interface. The Play framework is unusual in that it does not use the Java Servlet API. However, what’s really unusual is that there are not more such Java web frameworks that avoid the Servlet API, considering how terrible it is from a web development perspective.
Java’s design and evolution seems to be based on the idea that the platform itself is more important than other systems that it might be used with. From a Java architect’s perspective, the web is just another external system. As with other APIs, the Servlet API has shielded Java EE from being infected by the web’s own architecture (as if it were bad) by abstracting it away into a more Java-like API. This approach is unfortunate, because the web is more important than Java - the platform and the language - and these days, a useful web framework is one whose architecture embraces the web’s, and whose API embraces HTTP.
First, the API is based on an abstraction style that treats HTTP as just another protocol, so that the Servlet API could potentially support other protocols, as if the web might stop using HTTP next year. This might make sense to a Java developer who wants to encapsulate the protocol, but it makes no sense at all to a web developer who understands how central HTTP is to ‘the web’ as a whole.
The Servlet API’s ‘HTTP denial’ is a core part of its design, and not likely to change.
The second problem with the Servlet API is that the API is simply not rich enough to make it easy to build web applications directly. Instead, the Servlet API provides a minimal interface that is almost always used as the basis for a web framework. It’s as if the technology was a one-off innovation to improve on the 1990’s Common Gateway Interface (CGI), with no subsequent design improvements.
For example, perhaps the most basic thing that a Java HTTP API does is
to route an incoming HTTP request to a Java method invocation. In the
Servlet API, this routing is specified by the URL pattern in a ‘Servlet
mapping’. Unfortunately, this mapping has an extremely limited syntax
that is not flexible enough to allow web application developers to
design ‘clean’ URLs that do not unnecessarily expose implementation
details, because it apparently provides enough of an HTTP interface for
most Java web frameworks to do no more than expose this API. For
example, Struts 1.x applications use a default URL pattern with a
Servlet ‘extension mapping’, as if it were reasonable for the web
framework to dictate the URLs. This isn’t Struts’ fault: the Servlet API
provides this feature even though it has nothing to do with HTTP. HTTP
interfaces and URLs don’t have ‘extensions’; file names do.
The consequence of the Servlet API’s problems is complexity, mostly in the form of too many layers. This is the complexity caused by the API’s own abstraction layers, compounded by the additional layer of a web framework that provides an API that is rich enough to build a web application with.
In the case of URL routing, for example, this means that Java web frameworks typically start with a Servlet API configuration that routes all HTTP requests to the framework’s own controller class, which then has its own routing configuration. With this approach, the Servlet API only adds an additional layer that makes it harder to debug HTTP requests. This may keep the architects happy, but at the cost of developer productivity.
The JSF non-solution
This lack of focus on productive web development is best seen within the Java EE stack itself. The recognition that the Servlet API is insufficient to build a modern web application led to the development of JSF, which focuses on components and server-side state, resulting in unprecedented complexity in a web framework as well as a serious mismatch with HTTP itself.
Java EE frameworks such as JBoss Seam did an excellent job at addressing deficiencies in JSF, but only by adding yet another layer to the application architecture. Since then, Java EE 6 has improved the situation by addressing JSF’s worst shortcomings, but this is certainly too little, too late.
Thin versus rich interfaces
Thin versus rich interfaces represents a commonly faced trade-off in object-oriented design. The trade-off is between the implementers and the clients of an interface. A rich interface has many methods, which make it convenient for the caller. Clients can pick a method that exactly matches the functionality they need. A thin interface, on the other hand, has fewer methods, and thus is easier on the implementers. Clients calling into a thin interface, however, have to write more code. Given the smaller selection of methods to call, they may have to choose a less than perfect match for their needs and write extra code to use it.
Java’s interfaces are more often thin than rich. For example, interface CharSequence, which was introduced in Java 1.4, is a thin interface common to all string-like classes that hold a sequence of characters.
java.lang.CharSequence is a good example, with only four methods for
accessing and manipulating strings, a very common programming task.
However, half a million results for a
Google search for
‘stringutils’ also mean that this is generally solved problem.
javax.servlet.http.HttpServletRequest is more insidious,
because several generations of Java web frameworks have considered its
interface to be sufficient. In practice the interface manages to be
verbose without managing to be rich, expressive or convenient.
A whole generation of professional software developers learned Java at school, have only ever built web applications in Java, and are used to the Servlet API. Take it for granted even. However, you quickly realise that HTTP can be easier to use with the APIs provided by PHP and Ruby on Rails. Or the Play framework.