Embedded Jetty

01/19/2011

One of the coolest things about Jetty is the ability to run it in embedded mode.  This means that you can write a Java app with a main method that will launch a Jetty server.  This has some really nice benefits.  For example, it makes it really easy to deploy a server to EC2 because all you need to do is transfer your jar file there and hit run – no need to setup a servlet container, etc.  Also, it makes it possible to dynamically set servlet parameters such as Struts 2 devMode.  You can set a flag on the command line, parse it using a library such as JCommander, and then run in development mode depending upon what was requested by the flag passed in.

You first need to get the relevant jars included in your project as dependencies. All of the dependencies are available in Maven repositories. I use Gradle, so you may not be familiar with the syntax below, but you can take the parts between the colons and enter them as the groupId, artifactId, and version if you’re using Maven.

  compile 'org.eclipse.jetty:jetty-io:8.0.0.M3'
  compile 'org.eclipse.jetty:jetty-server:8.0.0.M3'
  compile 'org.eclipse.jetty:jetty-servlet:8.0.0.M3'
  compile 'org.eclipse.jetty:jetty-util:8.0.0.M3'
  compile 'org.eclipse.jetty:jetty-webapp:8.0.0.M3'
  compile 'org.mortbay.jetty:jsp-2.1-glassfish:2.1.v20100127'

Here’s an example of running a web app in Jetty’s embedded mode:

package com.benmccann.example.server;

import java.net.URL;

import org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ErrorHandler;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.FilterMapping;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.webapp.WebAppContext;

import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.servlet.GuiceFilter;

/**
 * @author benmccann.com
 */
public class WebServer {

  private final Server server;

  public WebServer() {
    this.server = createNewServer();
  }

  private Server createNewServer() {
    Server server = new Server();

    SelectChannelConnector connector = new SelectChannelConnector();
    connector.setPort(8080);
    server.addConnector(connector);

    WebAppContext webApp = new WebAppContext(getBaseUrl(), "/");
    webApp.addEventListener(new GuiceListener());

    ServletHandler handler = createServletHandler();
    webApp.setServletHandler(handler);
    webApp.setErrorHandler(createErrorHandler());
    server.setHandler(webApp);

    return server;
  }

  private ErrorHandler createErrorHandler() {
    ErrorPageErrorHandler errorHandler = new ErrorPageErrorHandler();
    errorHandler.addErrorPage(500, "/error.html");
    return errorHandler;
  }

  private ServletHandler createServletHandler() {
    ServletHandler servletHandler = new ServletHandler();

    FilterHolder guiceFilterHolder = createGuiceFilterHolder();
    servletHandler.addFilter(guiceFilterHolder,
        createFilterMapping("/*", guiceFilterHolder));

    FilterHolder strutsFilterHolder = createStrutsFilterHolder();
    servletHandler.addFilter(strutsFilterHolder,
        createFilterMapping("/*", strutsFilterHolder));    

    return servletHandler;
  }

  private FilterHolder createGuiceFilterHolder() {
    FilterHolder filterHolder = new FilterHolder(GuiceFilter.class);
    filterHolder.setName("guice");
    return filterHolder;
  }

  private FilterHolder createStrutsFilterHolder() {
    FilterHolder filterHolder
        = new FilterHolder(StrutsPrepareAndExecuteFilter.class);
    filterHolder.setName("struts2");
    filterHolder.setInitParameter("struts.devMode", "true");
    return filterHolder;
  }

  private FilterMapping createFilterMapping(
      String pathSpec, FilterHolder filterHolder) {
    FilterMapping filterMapping = new FilterMapping();
    filterMapping.setPathSpec(pathSpec);
    filterMapping.setFilterName(filterHolder.getName());
    return filterMapping;
  }

  public void run() throws Exception {
    server.start();
    server.join();
  }

  private String getBaseUrl() {
    URL webInfUrl = WebServer.class.getClassLoader().getResource("WEB-INF");
    String webInfUrlString = webInfUrl.toExternalForm();
    return webInfUrlString.substring(0, webInfUrlString.lastIndexOf('/') + 1);
  }

  public static void main(String[] args) throws Exception {
    WebServer server = new WebServer();
    server.run();
  }

}

This web app used the Struts 2 filter and the Guice filter.  However, we specified them in code instead of in the web.xml.  We hardcoded struts.devMode to true, but you can easily see how we could make it true or false at runtime whereas that is impossible using a web.xml file.

You can also run Tomcat in embedded mode.