When someone starts to make a path, most people tend to follow the trail. So when Sun introduced the Servlet API, most people were uneasy about the design problems:
- Servlets are singletons (which are evil; this is due to performance problems in Java 1.2 which have been fixed about ten years ago).
- The resulting code smells (feature envy since you call many methods of
HttpServletRequest/Response
, long method since you can’t call subdivide thedoGet()
without passing at least two parameters to each new method, duplicated code since you have to walk instance trees to get at a lot of information, …)
Whenever I see Java servlet code, I always wonder why people don’t solve this. It’s so simple:
public abstract class ServletRequestHandler { protected HttpServlet servlet; protected HttpServletRequest request; protected HttpServletResponse response; protected String contentType = "text/html"; public void init( HttpServlet servlet, HttpServletRequest request, HttpServletResponse response ) { this.servlet = servlet; this.request = request; this.response = response; } public abstract void process(); public PrintWriter out() { if( null == out ) { response.setContentType( contentType ); try { out = response.getWriter(); } catch( IOException e ) { throw new RuntimeException( "Can't get writer", e ); } } return out; } public ServletRequestHandler html( String html ) { out().print( html ); return this; } public ServletRequestHandler attribute( String value ) { out().print( escape( value ) ); return this; } public ServletRequestHandler attribute( String name, String value ) { if( StringUtils.isBlank( value ) ) { return this; } PrintWriter out = out(); out.print( ' ' ); out.print( name ); out.print( "=\"" ); out.print( escape( value ) ); out.print( "\"" ); return this; } public ServletRequestHandler text( String value ) { out().print( escape( value ) ); return this; } // Add more methods to support your favorite // web design patterns }
Now, all your servlets look the same: They accept the request, create a new instance of the right implementation of ServletRequestHandler
, and call init()
plus process()
. With a bit of glue code, you can even use Spring to manage all your servlets for you. No more dabbling with web.xml
!
And the best part: This pattern allows you to test the servlet code from a unit test without any container. Just use Spring or Mockrunner to get mock-ups for the J2EE API classes.