Jetty Integration

Turning it around

Usually when you build an application that is going to take advantage of an HTTP server like Jetty or Tomcat or any other HTTP server, you start with that application server as the foundation and build up your own application from there. Zed turns this paradigm on its head and uses Jetty the other way around.

Jetty is really a very flexible and adaptable server, and the folks over at Mortbay and WebTide who maintain Jetty have done a great job providing a standardized HTTP server that is excellently embeddable. We started with the details on Embedding Jetty and expanded on them as we built Zed. This post captures some of the things that we did as we followed this path.

As the Embedding Jetty guide talks about, it's really as easy as simply saying:

Server server = new Server(8080);
server.setHandler(handler);
server.start();
This will start your server listener on port 8080 with the appropriate handler. Allowing for configuration and customization becomes a little bit more involved. We needed two key components in addition to the standard setup: support for optional secure sockets, support for multiple web application contexts.

To implement secure socket listeners with Jetty, simply use their SocketConnector classes like this:

Server server = new Server(); // no port configuration by default
Connector connector;
if(m_use_ssl){
  SslSocketConnector ssc = new SslSocketConnector();
  ssc.setKeystore( keyStorePath );
  ssc.setPassword( keyStorePassword );
  ssc.setKeyPassword( keyPassword );
  connector = ssc;
} else {
  connector = new SocketConnector();
}

connector.setHost( local_ip_addr); // if required
connector.setPort( local_port ); // specify the port to listen on
server.setConnectors( new Connector[]{connector} );

Our configuration file allows the user to turn on/off secure sockets, and also provide the keystore, and passwords, so there are a few more details but this outlines a pretty easy example to follow.

Next we needed support for multiple web application contexts. The Zed Builds And Bugs server allows the administrator to set up a single server that will support multiple development teams, using multiple underlying databases to store each teams' saved data. This includes not only the continuous integration build details and bug data, but also has to include the Wiki documents for each team so that they don't collide.

We use the Jetty WebAppContext class to automatically create as many different Wiki applications as required to support the different team databases defined in the Zed configuration file. To start with, there is always the Zed documentation Wiki, which is set up like this:

WebAppContext wac = new WebAppContext();
wac.setContextPath("/doc_wiki");
wac.setWar("3rdParty/JSPWiki/"); // path to .war file or expanded existing webapp.
TreeMap main_wiki = new TreeMap();
main_wiki.put("jspwiki.propertyfile", "wikiconfig/doc_wiki.properties");
wac.setInitParams(main_wiki);

We then add the wac to the list of handlers for our server context and it is ready to go. We then repeat this process for every Team Wiki that needs to be configured and started based on our database configuration.

Once all of the handlers have been defined, here's how to add them to the server:

HandlerCollection handlers = new HandlerCollection();
handlers.setHandlers( handler_vector.toArray( new Handler[0] ));
server.setHandler( handlers );
And finally start the Jetty server with:
server.start();

Rationale

The reason for doing this with Zed is quite far-reaching. This allows us to create an HTTP listener that is just one among many listeners that the application supports. This means that Zed is capable of receiving not only HTTP request, but JMS messages, and any other type of request in order to invoke activities on the server. The central message processing routines don't care (and shouldn't care) where the original requests come from, they're just responsible for acting on and providing the core capabilities. Once a request has been processed, it is handed back to the proper listener to forward the response back to the caller in the manner that is appropriate for the listener's protocol.

This also allows the core Zed code to be listener independent. If we decide some day to swap Jetty for some other HTTP engine (which I doubt), we won't have to re-architect the entire application, just the listener that exposes Zed to the outside world.

Until that point, we're happy to be part of the Jetty family of users and supporters and wish the team over at WebTide continued success with Jetty.