Monday, August 19, 2013

Configuring Jetty with a Groovy Builder

As a small, fast, embeddable web server and servlet container  Jetty is used by a multitude of both commercial and open source products (see Jetty Powered). We're big fans of Jetty at Maestro and love the direction Jetty has taken over the years. During that time, Jetty has moved homes from MortBay to SourceForge and to eclipse where active development and releases are at version 9.

XML IoC Configuration

Jetty Version 9 brings a several improvements and updates to Jetty. In particular we like the the ability to configure Jetty separately from the sources.  Consider the following configuration snippet in Java:
package org.eclipse.jetty.embedded;
 
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.servlet.ServletContextHandler;
 
public class ExampleServer {
 
    public static void main(String[] args) throws Exception {
        Server server = new Server();
        ServerConnector connector = new ServerConnector(server);
        connector.setPort(8080);
        server.setConnectors(new Connector[] { connector });
        ServletContextHandler context = new ServletContextHandler();
        context.setContextPath("/hello");
        context.addServlet(HelloServlet.class, "/");
        HandlerCollection handlers = new HandlerCollection();
        handlers.setHandlers(new Handler[] { context, new DefaultHandler() });
        server.setHandler(handlers);
        server.start();
        server.join();
    }
}

Jetty's XML configuration mechanism can achieve the same without any Java code.

 
  
    
      
        
          
          8080
        
      
    
  
 
  
    /hello
    
      org.eclipse.jetty.embedded.HelloServlet
      /
    
  
 
  
    
      
        
          
            
          
          
            
          
        
      
    
  

Jetty's XML driven configuration is an Inversion of Control (IoC) framework which allows a user to specify what they want of Jetty and hides the details of instantiation and object graph assembly to underlying IoC container.

Jetty objects form a tree-like object graph (quite possibly a directed acyclic graph or DAG) with a Server object at the root of the hierarchy. Since the framework uses XML, there is a good overlap between Jetty's object hierarchy and the underlying XML. Compared to the purely imperative case of configuring with Java code, the XML version conveys much more information for the same screen real estate (disclaimer: we haven't performed any tests nor are we aware of studies that provide 1D or 2D information density metrics for code/language/content; our information/sq inch metrics are therefore, entirely subjective).

A reader scanning the XML will quickly see that the Jetty server in question has set, an array in-fact, of connectors, comprising a single connector listening on port 8080. Reading on, the reader will note that a ServletHandler is configured for path "/hello"and is handled by the evergreen HelloServlet. Presumably, this servlet is to be included in the subsequent definition of a set of Handlers, an array in-fact, the first element of which is none other than the HelloServlet (the connection made due to the use of the same id in both places). The reader also notes a second hander which is a DefaultHandler.

Syntactic Noise & Mixing Idioms

In using the XML configuration framework we couldn't help feeling bogged yet again by the volume of text needed for the configuration which we distilled down to two issues: a) syntactic noise brought along by XML and b) mixing of idioms: the representation was in part, declarative (e.g. lines 3 - 12; the connectors and again 21 - 35) and in part, imperative (lines 14 - 20; create a new object). Also to our dismay, much of the underlying detail of Jetty objects, their interfaces and the interconnecting plumbing came through. 

Martin Fowler on Syntactic Noise
"By Syntactic Noise, what people mean is extraneous characters that aren't part of what we really need to say, but are there to satisfy the language definition. Noise characters are bad because they obscure the meaning of our program, forcing us to puzzle out what it's doing."

JettyBuilder - A Groovy Builder

We developed a "builder" for Jetty in Groovy to address some of the issues we noted above. Builders are a common idiom in Groovy. Builders handle the busywork of creating complex object hierarchies, such as instantiating children, calling child methods, and attaching these children to their parents in a declarative fashion. As a consequence, builder based code is much more readable and maintainable, while still allowing access to the full range of underlying components. 

Continuing with the above example, JettyBuilder lets us describe the same server in this manner:
jetty.server {
 connector( port: 8080 )
 servletHandler servlets: ["/hello": "org.eclipse.jetty.embedded.HelloServlet"]
 defaultHandler()
}
In scanning this description, our reader will see that the Jetty server (line 1), has a connector on port 8080 (line 2), "/hello" is mapped to HelloServlet. What isn't handled by this servlet is handled by the defaultHandler (line 3).

Further examples and source code for the builder can be found here.