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.

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.

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:

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.