How to do Everything Wrong with Servlets

April 8th, 2009 · 9 Comments ·

When I interview people I often ask them what an evil developer would do to make his code hard to test. Servlets are a great example of what to do if you want it to be hard to test

Lets start with constructor. Constructor is the first thing I look at since it tells me about my dependencies. In the case of servlets the spec asks for no-argument constructor so that the container can control the instantiation process. This is all fine but how is my Servlet supposed to get a hold of its dependencies such as database connection or any other object which needs to be shared between multiple servlets? There is no good way! The only thing which I can do is to use global state and singletons for inter servlet communication (which we have covered in detailed here, here, and here).

Initialization process is no help here. Yes the container will call a init method, but the only thing I can get a hold of in the initialization process is the strings which I have placed in my XML. What I really want is to share instances of objects between servlets so that I can configure my collaborators. Again I am forced to result to global state and singletons. But there is something worse about this. Mainly the single responsibility principle. Each class is responsible for exactly one thing. But in our case the servlet is responsible for the logic, the wiring of itself to the collaborators and initialization of the collaborators. Now, I like to place my initialization in a single place so that I can control the order of initialization. But servlets make this difficult since the servlet container does not guarantee any order of initialization for your code. This means that each servlet is a potential source of initialization. Which again forces you to the singleton and lazy initialization model.

In Java you have single inheritance hierarchy, (which is a good thing) but Servlets take that away from you by forcing you to inherit from their class in essence giving you no inheritance choice at all. Inheritance should be used when we need to take advantage of polymorphism, but there is no polymorphism going on within servlets. What passible purpose does inheriting from HttpServlet serve which could not be addressed with an interface. Code reuse through inheritance is quite possibly the worst offense in software engineering. Composition over inheritance should always be the goal. The worse thing about this is it sets a precedence which is followed on most projects. Most web apps have deep inheritance hierarchies with Servlet at the top which seem to serve no other purpose than code reuse. Testing collaborators is easy since I can test each collaborator in isolation, but if collaboration is replaced with inheritance now my tests have to test the full inheritance hierarchy at once. Inheritance is all or nothing proposition.

OK, if you managed to survive the constructor, initialization, and inheritance, there are other subtle surprises waiting for you. We are building web apps, therefor the most common object lifetime should be equal to the duration of HTTP request. Instead we have servlets whose lifetime is lifetime of the JVM. This creates a huge problem since all of the HTTP request information/data now has to travel through the stack rather than being a part of the object. In other words we have static code-base and we push the data around through the method arguments. Sounds like procedural code to me. I don’t want to get into debate over OO vs Procedural, but I do want to point out that Java is OO, use it in that way. If you want to write rocedural code, use a procedural language. Play to the strengths of the language no to its weakness.

Surely there is nothing else which servlets do wrong. Ohh, but there is, let me introduce you to the Service Locators anty-pattern. The HttpServletRequest and HttpServletResponse are two huge service locators. They themselves hove nothing of interest, but they do have getter methods which have things which I need. Well actually, I don’t need that either. What I want is a User for example. What I have is request. Let’s see does this look familiar?

String cookie = request.getCookie();
long userId = cookieParser.parse(cookie).getUserId();
User user = userRepository.find(userId);

What I want is user but what I got is worthy of a Sherlock-Holms detective story as I try to piece together the identity of the user. If I had to do this in one location, I would not complain so much but I have to do this in every single servlet. Please, just give me the user, or whatever else which I need to get my work done. The thing is that request has cookie, but it is not the cookie I want, it is the user id which is embedded in the cookie. But it is not the user id, but the user i really want. This Law of Demeter violation is the direct result of two things: (1) service locator nature of the request / response and (2) the wrong lifetime of the servlet.

Now imagine trying to test this thing. First I have to call new, great that is easy as I have no argument constructor, but how do I get all of my mock dependencies into the servelet as collaborators? My only hope is setting some global state. But, wait the servlet is also responsible for initialization which means it is loaded with new operators, and those suck from the testability point of view. (see here) Now I really want to test the home page but the home page servlet inherits from the AuthorizedServlet which talks to the database to authenticate. Great now my every test needs to mock out the authentication even if it is not testing it. So if I change my authentication all my test are broken. Now I really just want to create a new User to test the home page but I have to place my user into a Mock Repository and assign a mock id. I than have to create a mock cookie and place it into a mock http request all the while hoping that I will be able to give you a mock UserRepesitory through some global state.  I wish I was making this up, but Servlets are untestable in their current form.

Let’s see if we can solve these problems. First forget servlets and create your own classes which have your own minimal inheritance with preference for composition with object lifetime which equals that of the HTTP request so that your collaborators can ask for objects such as User directly in the constructor. This way testing your code is easy. Now for the servlet part, Since our code is HTTP servlet, request and response free it is both easy to test and incompatible. Therefor the job of the servlet is to call the new operators to build the object graph of collaborators with the right lifetime and to delegate the execution to our code. In essence the servlet becomes one big factory and object look up. It is still untestable as ever, but at least there is no logic to test there. Since the nature of the factories is that they either work or they blow up spectacularly.

Tags: Uncategorized

9 responses so far ↓

  • Shantanu Kumar // Apr 8, 2009 at 2:24 pm

    Servlets are certainly bad candidates for testing. I generally test them by passing test config through constructor, and launching them through an embedded Jetty container.Today I attended a talk by Neal Ford at JAX 09 at Bangalore, where he showed how we can use MockRunner to test classes that depend on infrastructure.MockRunner URL: http://mockrunner.sourceforge.net/Cool stuff!

  • Sarah Happy // Apr 8, 2009 at 5:39 pm

    Personally I use a ServletContextListener to create an application object, most of which is testable outside of the servlet container, and in the servlets I look up the application object in the context attributes, and pass the request and response down to it, making the servlets very simple wiring. The container provided request and response objects typically do not remain visible past the first call level either.

  • Sakuraba // Apr 9, 2009 at 2:59 am

    I agree with all of the statements, however is this a real issue? Most of us are using something like SpringMVC and dont touch Servlets at all.

  • Przemek // Apr 14, 2009 at 3:02 pm

    A lot of good advice regarding servlet-like frameworks. But I’m not sure, where most of the evil really lies – in API or in particular apps? A very interresting question for me is: how to design those APIs in a better way?But this was not my main reason to comment your article. I was particularly puzzled by the piece about data travelling through the stack – called procedural-style by you. What? Methods with parameters are not OO? I’m a big fan of objects for 9 years and I haven’t expected many more surprises on that field, I have to confess – the credit goes to you, Misko :) I spent a while trying to understand your point and finally I think now I got it :) But please correct me, if I don’t. I think you would design a webapp in a following way:when a request comes in, a web of request-scoped objects are created, with all necessary data taken from HTTP request.Then, a method of a root request object is called and all of real processing happens. Then, results are collected and transformed into HTTP response.I hope I guessed, what you’ve meant. If yes, this could be extrapolated with very interresting consequences for whole OO programming (at least, if you want pure OO). In particular, following rules would apply:use arguments only in constructors and factory methodswhen you need an argument in other method, turn it into method-object (AKA command pattern)use all data you have to build a graph of request objects, then call a single method on it, to let it do its workI’m sure, I’ve still omitted many other interresting consequences, but these already challenge many of mine habits (and popular beliefs).Therefore I start to be curious, what benefits can give me this style of programming, to justify the transition.If I got the point of “Java is OO, use it in that way” correctly, could you enumerate few of the benefits?Or, please, correct me, if I misunderstood you.

  • misko // Apr 15, 2009 at 10:44 am

    @Przemek,

    You are right on the money. I should probably talk more about this in a separate blog, but your conclusion are valid. As far as benefits go, you will find that the code is (1) easier to test (2) modifying the code becomes easier since you con deliver new date directly to the class which needs it without having to pass it through the stack or resolve to Globals.

  • Christian Gruber // Apr 15, 2009 at 6:40 pm

    One of the reasons I’ve been loving Tapestry5 is that I can build “misko-friendly” apps.  My components and pages don’t have to inherit from anything (not even interfaces), pages, components and services are all injectable dependencies and declare their dependencies quite nicely, etc.  I want a reference to another page?  I inject it.  I want a DAO?  I inject it.   It also does really nice things like byte-code manipulate the classes at load-time (for pages and gui components) so that I don’t have to write a lot of boiler plate and then test it – it’s taken care of and the boiler-plate code weaving is well tested, so it’s SEP (somebody else’s problem).  This means that my gui component classes are almost entirely consisting of declarations of dependencies (@Inject private MyFoo myFoo;) declarations of properties with getters and setters taken care of for me if they’re default boiler plate javabean style (@Property private MyLoopContent currentContent;) and actual methods which do something.The above means that I only have to write tests to test that which is unique about the class – Tapestry 5 pages and components follow SRP (well, they can – they’re not forced to break SRP), are easy to read, all abide by consistent convention, etc.Anyway – small plug for T5, but even if you’re not using T5, it’s a great proof that what Misko’s talking about isn’t just fantasy – you really can have frameworks which let you do this, and you can hammer out existing frameworks to be more like this.

  • Rajiv Mordani // Mar 30, 2011 at 3:54 pm

    Hi Misko,
    I am the Servlet spec lead and I will try to address some of the issues that you have listed above –

    For initialization of resources like Database connection you can use CDI / Resource injection to inject the data source for example. Similarly you can use CDI to inject all kinds of managed beans into servlets. So that will take care of initialization issues you list above like Christian noted above.

    Another issue that you list about constructor – In servlet 3.0 we added ways to add an “initialized” servlet during context initialization phase dynamically. It is restricted but there is a way to do it.

    For the single instance of a Servlet in the VM – sessions are useful for storing state. There used to be a SingleThreadedModel which did basically what you are suggesting. We deprecated that because there are a lot of issues with that.

    We can talk more to see what we can improve in 3.1 for “testability” if that’s the main issue you are trying to address or about any other issues. Let me know.

    - Rajiv

  • misko // Mar 31, 2011 at 8:56 am

    @Rajiv,

    Glad to hear from you and happy to offer my thoughts on making servlets more testable. I will start a dialog on email.

  • Java Experience // Jan 11, 2013 at 6:55 am

    some more reasons on why servlets don’t have constructor blogged by me.

Leave a Comment