ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


What Is Jetty

by Q Ethan McCallum
06/14/2006
Jetty
Jetty is an open source servlet container, which means it serves Java-based web content such as servlets and JSPs. Jetty is written in Java and its API is available as a set of JARs. Developers can instantiate a Jetty container as an object, instantly adding network and web connectivity to a stand-alone Java app.

In This Article:

  1. What's the Point of an Embedded Servlet Container?
  2. Setting up an Embedded Container: The Jetty API
  3. Externalizing the Configuration: XML-driven Config File
  4. The Executable JAR
  5. Conclusion
  6. Resources

Ask someone to name an open source servlet container and they'll likely mention Apache Tomcat in reply. Tomcat isn't alone in this world, though: we also have Jetty. Having another option for a servlet container is a plus, but Jetty's real claim to fame is that it is designed be embedded in other Java code. That is, the development team exposes Jetty as a set of JAR files such that you can instantiate and manipulate a servlet container in your own code, as an object. This opens up new possibilities for servlets and web apps.

Jetty is hardly a new face in the container crowd; it has been around in some form since 1998. It's released under the royalty-free, non-viral Apache 2.0 license, permitting free and commercial apps alike to incorporate it into their infrastructure.

In this article, I'll offer some ideas on why you'd want an embedded servlet container, explain the basics of Jetty's API, and show how to use its XML configuration to trim your Jetty-specific code to a minimum.

The sample code was tested under Jetty 5.1.10 and Sun's JDK 1.5.0_03.

What's the Point of an Embedded Servlet Container?

Before you adopt Jetty, it's reasonable to ask yourself why you would want a servlet container embedded in your application in the first place. What caught my eye was the potential to expose servlet functionality from within another, preexisting app. That presents possibilities for several groups, including Java EE application server vendors, software testers, and vendors of custom software. Most Java developers fall into one of these categories.

First, consider the fringe case of creating your own Java EE application server. Per the spec, a full-blown app server must offer servlets, EJBs, and other functionality. Instead of creating a servlet container from scratch, you could leverage existing, tested components and incorporate Jetty. This is exactly what the teams behind Apache Geronimo, JBoss, and ObjectWeb JOnAS did to create their Java EE app servers.

Software testers can benefit from creating a container on demand, when having several predefined containers is not desirable or possible. For example, a colleague once wanted a means to drive the unit tests for his web services code. Given his scenario -- several developers plus some automated unit tests running in Cruise Control -- I demonstrated how he could use Jetty to instantiate servlet containers on the fly inside his unit test suites. No extra scripts, no leftover files, just code.

For those of you that sell Java EE apps as your products, why give your clients a WAR file, and add container-specific headaches to your support costs? Instead, give them proper applications with their own start, stop, and management functionality. Even hardware vendors can benefit: Jetty's small memory footprint -- 350Kb for a plain HTTP (non-servlet) service -- makes it an option for appliances. You could provide web-based control panels with the full power of Java web apps but without the weight of a separate container component.

Finally, I'd wager the most interesting uses of an embedded servlet container would be from people who aren't writing traditional, browser-based web applications at all. It's possible to leverage a Java EE-HTTP combination as the backbone of a client-server app. Consider an event-driven service such as the (fictitious) Message-Driven Bank that launches from a main() method and waits for incoming requests, similar to a Unix-style daemon. It's only matter of time before someone wants that functionality exposed in a user-based fashion such as a GUI desktop app.

Creating your own infrastructure components -- here the protocol and socket communications code -- is often an unwelcome distraction from the task at hand, not to mention something else to debug later. Instead, you could use an embedded Jetty container to expose that business logic via HTTP, with minimal changes to the existing app. Toss in a Swing, SWT, or XUI GUI that packages requests as HTTP POST operations, REST, or even SOAP to complete the circuit. These may be less performant than a custom, domain-specific protocol; but for the time being you can leverage the existing, road-tested protocols and spare yourself the extra effort.

Setting Up an Embedded Container: The Jetty API

Hopefully those ideas whet your appetite to try an embedded servlet container. The sample program Step1Driver demonstrates a simple Jetty-based service. It instantiates a servlet container, maps a servlet class to a Uniform Resource Identifier (URI), and invokes some URL calls against itself. I have sacrificed production quality for legibility.

The Service object is the Jetty container itself. Instantiating such an object brings the container into existence:

Server service = new Server() ;

Thus far, the Service object is akin to a hotel with no doors: no one can get in to use it, so it's of little value. The following line of code sets up the container to listen on the localhost interface, port 7501.

service.addListener( "localhost:7501" ) ;

To listen on all available interfaces, omit the hostname ("addListener( ":7501" )"). As its name would imply, you can call addListener() multiple times to listen on different interfaces.

Notice that the sample code maintains a reference to the Server object. This is required to shutdown the container later on.

Mapping web applications to the Service is straightforward:

service.addWebApplication(
   "/someContextPath" ,
   "/path/to/some.war"
) ;

This call will process a web app's web.xml deployment descriptor to map its filters and servlets, the same as any other container. The first argument is the name of the context path. All of this web app's servlets and JSPs will be available at URIs relative to this path. The second argument is the web app itself. This can be either a packed WAR file or a web app in exploded-directory format. Call addWebApplication() again to map more web apps.

Note that Jetty doesn't require a full-blown, spec-compliant WAR file to deploy servlets, either. If you have written a custom application protocol that rides HTTP, you can load a single servlet that exposes your app to the network. There's no need to WAR up your otherwise non-web app just for the sake of HTTP-enabling it.

To map such a one-off servlet, create a context on the fly by calling getContext() on the Service object. The sample code does just this to create a context called /embed:

ServletHttpContext ctx = (ServletHttpContext)
   service.getContext( "/embed" ) ;

The call to getContext() creates the context if it doesn't already exist.

Next, call addServlet() to map the servlet class to a URI:

ctx.addServlet(
   "Simple" , // servlet name
   "/TryThis/*" , // URI mapping pattern
   "sample.SimpleServlet" // class name
) ;

The first parameter is a descriptive name for the servlet. The second is the mapped path, equivalent to the <url-pattern> in a web.xml servlet mapping. This mapped path is relative to the context path, here /embed. The "/*" fragment means that this servlet will accept calls for /embed/TryThis as well as anything that begins with that URI, such as /embed/TryThis/123. This mapping style is useful when you have a single servlet that acts as an entry point to a greater system. Struts and Axis are examples of real-world apps that use such a servlet mapping.

Sometimes you want your context to be the root, or "/," to better mimic plain HTTP services. Jetty supports this via the Service.setRootWebapp() method:

service.setRootWebapp(
   "/path/to/another.war"
) ;

The only argument is the path to a web app (WAR or directory).

The container is idle at this point. It hasn't even attempted to bind to its listener socket. Starting the container requires a single call:

service.start() ;

This method returns immediately because Jetty takes care of running the service in a separate thread. main() is free to do whatever it likes while the container keeps running.

The remainder of the code is a set of URL calls to the embedded container. These calls confirm that the container is indeed running and that the servlet functions as expected.

Stopping the container is as straightforward as starting it:

service.stop() ;

Note the catch() clause of the outer try/catch block:

{

   service.start() ;
   // ... URL calls to mapped servlet ...
   service.stop() ;

}catch( Throwable t ){

   System.exit( 1 ) ;

}

The explicit call to System.exit() ensures the container is shutdown in the event of an exception. The container would otherwise continue to run in its own thread and the app would never exit.

Keep in mind that Jetty web apps aren't limited to in-code access. Were I to remove the call to service.stop() the container would run indefinitely and I could invoke the servlet via a browser. For example:

http://localhost:7501/embed/TryThis/SomeExtraInfo

You don't have to take my word for it. The sample code is suitable for running as an Eclipse project. There is also a shell script for running the code from a Unix/Linux command line. In both cases, be sure to adjust the classpath to point to your Jetty install location.

Externalizing the Configuration: XML-driven Config File

While its API is straightforward and clean, the sample's direct calls to the Jetty API leave configuration -- port numbers, context paths, servlet class names -- buried in code. Jetty provides an XML-based configuration as an alternative, such that you can externalize all of this information and keep your code clean.

The XML configuration is based on Java's Reflection API. Classes in the java.lang.reflect represent Java methods and classes, such that you can instantiate objects and invoke their methods based on their names and argument types. Behind the scenes, Jetty's XML config parser translates the XML elements and attributes into Reflection calls.

This excerpt from the Step2Driver sample class is a revamped version of Step1Driver. As far as configuration is concerned, there's just enough Jetty-related code to load the file.

URL serviceConfig = /* load XML file */ ;
   // can use an InputStream or URL

XmlConfiguration serverFactory =
   new XmlConfiguration( serviceConfig ) ;

                        
Server service =
   (Server) serverFactory.newInstance() ;

This admittedly doesn't save much code compared to the trivial example in Step1Driver. Then again, this is as much code as Step2Driver will ever need, even as you add more servlets or web apps. Directly calling methods on Service and context objects is a case of diminishing returns as the configuration grows more complex.

Listing 1 is the XML file loaded by Step2Driver. The toplevel <Configure> element's class attribute (marker 1) specifies which class to instantiate. Here it's a Jetty Server object.

<!-- 1 -->
<Configure class="org.mortbay.jetty.Server">

  <!-- 2 -->
  <Call name="addListener">
    <Arg>
      <!-- 3 -->
      <New
         class="org.mortbay.http.SocketListener">

        <!-- 4 -->
        <Set name="Host">

          <!-- 5 -->
          <SystemProperty
             name="service.listen.host"
             default="localhost"
          />

        </Set>

        <Set name="Port">
          <SystemProperty
             name="service.listen.port"
             default="7501"
          />
        </Set>

      </New>
    </Arg>
  </Call>


  <Call name="getContext">

    <Arg>/embed</Arg>


    <!--
    call methods on the return value of
    Server.getContext()
    -->

    <!-- 6 -->
    <Call name="addServlet">

      <!-- servlet name -->
      <Arg>"Simple"</Arg>

      <!-- URL pattern -->
      <Arg>/TryThis/*</Arg>

      <!-- servlet class -->
      <Arg>sample.SimpleServlet</Arg>

    </Call>

  </Call>

</Configure>

The child <Call> elements represent method invocations on the Server object defined in the parent element. The call to addListener(), at marker (2), itself has child <Arg> elements that specify method arguments. Here I could have passed the string value for the listen address, but addListener() is overloaded to take a SocketListener object. For the sake of demonstration I call the <New> element to instantiate a new SocketListener at market (3). Markers 2 and 3 are the XML equivalent of calling

server.addListener(
   new SocketListener( ... )
) ;

in code.

To configure the SocketListener itself, you could use a <Call> element to invoke its setHost() method. Since this method follows JavaBean naming and signature conventions, the sample code instead uses the <Set> element (4) as a shortcut. Behind the scenes, Jetty prepends "set" to the value of the name attribute to determine the method name to invoke (here, setHost()).

setHost()'s arguments aren't explicitly listed here. Instead, the <SystemProperty> element (5) accepts the name of the system property from which to fetch the values, here service.listen.host and service.listen.port. In case these system properties are not defined, the <SystemProperty> element also lets you specify a default value using the default attribute. Together, markers 4 and 5 are the same as calling:

   socketListener.setHost(
      System.getProperty(
         "service.listen.host" ,
         "localhost"
      )
  ) ;

Finally, note the <Call> elements inside the <Call> to getContext() (6). The inner <Call> is invoked on the value returned by the outer <Call>. Here, then, addServlet() is called on the context object returned by getContext():

server.getContext().addServlet( ... ) ;

Kudos to the Jetty team for taking the XML configuration one step farther: notice that all Jetty-specific calls in Listing 1 are element or attribute values, not names. This means the XML configuration can be used with any classes, even non-Jetty classes. Depending on how your app is written, you could configure it all through Jetty's XML config.

The Executable JAR

If you embrace Jetty's XML to configure your own apps, you could end up with a lot of boilerplate code to load the config file and launch your stand-alone apps. You can use Jetty's executable start.jar to load the file for you, saving you even more code.

For example, you can load the Jetty-based service from Step2Driver using the following command line:

CLASSPATH= ...various Jetty JARs...
java \
   -Djetty.class.path=${CLASSPATH} \
   -jar <jetty install path>/start.jar \
   standalone.xml

Note that this call only loads the XML file to establish the container and listener. It, therefore, doesn't call the rest of the sample code that invokes the test URLs.

Conclusion

An embedded Jetty servlet container lets you web enable a Java app without having to package it as a formal web app. This opens up new possibilities for several disciplines, making Jetty a versatile addition to your toolbox.

There is certainly more to Jetty than I have written here. I encourage you to review the Jetty website for docs and sample code.

Resources

Q Ethan McCallum grew from curious child to curious adult, turning his passion for technology into a career.


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.