Servlet 3.0 – A spaghetti API?

26 07 2010

The introduction in Servlet 3.0 of “web fragments” and both annotation-based and programmatic mechanisms for introducing components into a web-application are all very welcome.

However, combined with all the other new features, their configuration facilities, the relevant class/jar-finding mechanisms, and the interactions between everything, the overall complexity of the Servlet API seems to have increased horrendously.

To my mind, an awful lot of it is starting to look like a tangled mess of spaghetti – the API equivalent of spaghetti code.

Here’s just one relatively minor example (but please, please, please put me straight if I’ve missed the meaning of this and it’s all really simple and elegant).

Here goes…

The Javadoc of every “since 3.0” method in javax.servlet.ServletContext (for example, getEffectiveMajorVersion) includes a “throws” clause that says:

Throws: java.lang.UnsupportedOperationException – if this ServletContext was passed to the ServletContextListener#contextInitialized method of a ServletContextListener that was neither declared in web.xml or web-fragment.xml, nor annotated with WebListener

So the behaviour of a ServletContext, including things like whether or not you can determine which Servlet version it needs, thus depends on whether it “was passed to” a ServletContextListener to notify that listener of the context’s initialization – depending on which of various ways were used to create the listener.

For now let’s just gloss over the various minor questions and issues raised by this, such as:

  • What does “was passed to” actually mean? Has been passed to, at any time previously? Is currently being processed within a call to? Both? Something else?
  • Does or doesn’t this apply if the ServletContext “was passed to” multiple listeners of which some are of the specified type and some are not?
  • What is the actual purpose of this rule (i.e. why should being passed to a particular type of listener prevent the ServletContext from processing any of its “since 3.0” methods)?

Quite apart from all that, and far more fundamentally, isn’t it rather perverse for an object’s methods to depend directly on what other objects it “was passed to”? Especially where there doesn’t seem to be any immediately obvious reason for such a dependency?

And doesn’t it seem even more wrong that an object’s behaviour should depend on which other objects are “listening” for events on it? Isn’t that the tail wagging the dog?

Even assuming there’s some reasonable reason for this, and that there’s some sense in which it makes some kind of sense, is this really the kind of thing we want to see in an API?

Just in case this still seems too simple for you, the ServletContext also now includes a createListener method for creating listeners, and a number of overloaded addListener methods for adding listeners to itself (but only provided it has not already been initialized). The method for creating listeners does allow the creation of ServletContextListeners, but the methods for adding listeners only supports the addition of a ServletContextListener “If this ServletContext was passed to ServletContainerInitializer#onStartup” (which I’ll come to later).

Now both of these methods are subject to various conditions, including the “throws” clause described above. Listeners created and added in this way are, presumably, precisely the sort of listeners that such “throws” clauses are referring to (that is, not defined in web.xml or web-fragment.xml and potentially not annotated with WebListener). But what does it mean for methods that create and add such listeners to also have this “throws” clause themselves? Especially when they also require the ServletContext to have not yet have been initialized, in which case it presumably can’t have been passed to any ServletContextListeners yet anyway?

Is anyone else getting confused yet?

If even that still seems simple enough, ServletContextListeners are also no longer the only things listening for the application and/or context’s initialization. There is also now a ServletContainerInitializer interface, for classes that want to handle the application’s start-up (or does it really mean the container’s start-up, as its name would seem to imply?). Clearly, this is another route through which ServletContextListeners can be programmatically created and introduced, in particular by having the ServletContainerInitializer use the ServletContext’s “createListener” and/or “addListener” methods – with the “addListener” methods making specific allowance for this as described above, and requiring the ServletContext to know whether or not it “was passed to” a ServletContainerInitializer.

Of course, this ServletContainerInitializer interface has its own complexities and quirks. I won’t go into full detail on these here, but just to give a flavour:

  • It specifies naming conventions and mechanisms for how its implementing classes are found (and these mechanisms have their own quirks and ambiguities, for example the naming convention appears to require classes to be placed in the “javax.servlet” package, in violation of the usual rues and licence terms, and the class-level javadoc says that implementations must be within the application’s /WEB-INF/services directory but the relevant method’s javadoc talks about different behaviour depending on whether it is within /WEB-INF/lib or elsewhere);
  • It uses an annotation to specify what types of classes are to be passed to its sole method as arguments, together with rules for how the relevant classes are to be found, with this in turn including a requirement for the container to provide “a configuration option” to control whether failure to find such classes should be logged;
  • Its javadoc includes the quite wonderful statement “In either case, ServletContainerInitializer services from web fragment JAR files excluded from an absolute ordering must be ignored, and the order in which these services are discovered must follow the application’s classloading delegation model.”.

Am I alone in thinking this is all getting way out of hand? How many features like these (with their accompanying restrictions, exclusions and interactions) does it take before the API as a whole becomes incomprehensible?

At this point I was going to sarcastically sugguest some incredibly complex and convoluted fictional requirement for things I’d like to see added into the next version of the API. But I’m too afraid that someone might treat it as a serious feature request, and in any case it’s not easy to come up with anything that’s more convoluted than the existing features (at least, not without sounding completely silly).

So instead I’ll just say that, personally, I fear that the Servlet API may have already jumped the shark.



5 responses

27 07 2010
Frederick Polgardy

I haven’t been following the spec at all, but my first instinct is that you may be reading too much into the “passed into” stuff. The important bit I get out of the error message is not that it was passed to something incorrectly, but that you tried to hook it up to a ServletContextListener that wasn’t properly configured. The JVM wouldn’t have any easy way of knowing who passed what to whom anyway. I could be completely wrong, but take that for what it’s worth.

27 07 2010

Thanks Frederick, but I’m not actually trying to do anything! I’m not hitting problems with any actual code, not seeing an error message, not using some kind of incorrect ServletContextListener, nor anything like that.

Rather, I’m just commenting on something that is part of the ServletContext’s definition. In particular, that some of its methods are defined as “unsupported” if the ServletContext “was passed to” a ServletContextListener, depending on which of various entirely-valid mechanisms gave rise to that listener. And following this into various other things that are related to it and have their own complications. Purely as an example of how complex and intertwined the API has become.

As for the mechanics of tracking “was passed to”, it’s clearly up to the servlet container to implement this. One can easily envisage how it can be done by a servlet container, but if one found this in “normal” code one would probably regard it as a poor design and/or code smell.

28 07 2010

Just re-read the spec. I’ve got a standard initialization framework that I use that allows me to order initialization tasks which I use for memory management (classic leak scenarios), logging, performance monitoring, Spring, etc. After looking into the spec and the ordering section of the web-fragment.xml to the ServletContainerInitializer stuff (no onShutdown even though I emailed that into the expert group), I’m very afraid that this spec is worthless and that alot of “features” will just be skipped.

After reading this spec and the JAX-RS spec, the JCP/Sun/Oracle really needs to have teachers / technical writers in charge of documentation. These are some of the worst specifications that I’ve ever read.

28 07 2010

Brian, that’s very interesting… though I’m not sure whether I’m relieved to hear it’s not just me, or alarmed to hear that it’s a wider problem than just Servlets!

I like your idea of teachers / technical writers in charge of documentation. Maybe it needs more professional proofreading too. As well as its growing complexity, the Servlet javadoc has always seemed prone to typos, poor grammar, mistakenly copied/pasted statements, incorrect or ambiguous “and”s and “or”s etc, and it seems non-trivial to get these fixed after the event.

Obviously there would be higher up-front costs for getting it written more carefully, but surely such key specifications and their javadocs are important enough to warrant it, and it could save a lot of work in the long run.

13 02 2011
Servlet 3.0 - A spaghetti API?

[…] of "spaghetti code". Here's just one relatively minor example (but please, please, please put me… [full post] closingbraces Closing Braces javaservlets 0 0 0 0 0 […]

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

<span>%d</span> bloggers like this: