Thinking Considered Harmful

The Technical Musings of Aaron Meriwether

Embedded Jetty Servlets and JSP

Java

Thursday, November 03, 2011


Recently I had been using Tomcat as a Java Servlet container for a project at work. This works well in the context of a tightly-integrated set of servlets and JSP pages like a typical website, but the project I was working on is intended to be a self-contained module which presents an HTTP API and should be easily deployable without worrying too much about shared settings and shared libraries on the target box. I also wanted the ability to profile, debug, and run jUnit tests on the component from within Eclipse, without requiring an additional, separate deployment to a Tomcat server (even if it is a local Tomcat server on my dev machine.

So I decided to switch to a design involving embedding a Jetty container into a plain Java app. This would give me the ability to have a clean deployable artifact with no external dependencies, and also to launch the servlet container and the HTTP tests against it from the same jUnit script. Sadly, the documentation for embedding Jetty is not the most comprehensive.

In order to keep things as tight and straightforward as possible, I opted to instantiate the individual parts of Jetty directly rather than just using the WebApp context as shown in the examples. (using the WebApp context would involve additional dependencies and overhead of dealing with WAR files and web.xml, etc.) Getting a basic servlet context running and serving static content was fairly simple, but I couldn’t find any good documentation or example on setting up the JSP servlet without using the WebApp wrapper. After some source-reading and a lot of experimentation, I got it working, but I ran into quite a few issues along the way.

After fighting through all of these issues, I finally arrived at the clean, programmatically-configured embedded Jetty solution, which I present to you here:

import org.apache.jasper.servlet.JspServlet;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;

class RunServer {
    public static void main(String args[]) {

        System.out.println("Initializing server...");
        final ServletContextHandler context =
          new ServletContextHandler(ServletContextHandler.SESSIONS);
        context.setContextPath("/");
        context.setResourceBase("webapp");

        context.setClassLoader(
            Thread.currentThread().getContextClassLoader()
        );

        context.addServlet(DefaultServlet.class, "/");

        final ServletHolder jsp =
          context.addServlet(JspServlet.class, "*.jsp");
        jsp.setInitParameter("classpath", context.getClassPath());

        // add your own additional servlets like this:
        // context.addServlet(JSONServlet.class, "/json");

        final Server server = new Server(8080);
        server.setHandler(context);

        System.out.println("Starting server...");
        try {
            server.start();
        } catch(Exception e) {
            System.out.println("Failed to start server!");
            return;
        }

        System.out.println("Server running...");
        while(true) {
            try {
                server.join();
            } catch(InterruptedException e) {
                System.out.println("Server interrupted!");
            }
        }
    }
}