In larger projects there is always the desire to split up parts of the project into separate modules (e.g. Maven modules). Within WAR web applications you may want to move web resources to a separate module. WAR overlays were the way to go, but as of Servlet 3.0 there is another (possibly better) way to deal with external web resources. However, it took some doing to get it to work with Jetty.

WAR Overlays

For years I did this using WAR overlays: the reusable resources go into a module that itself looks like a WAR, which is laid over the final WAR during the packaging phase of Maven. Now, this approach is thus bound to the packaging phase, it is exclusively a tric of the Maven WAR Plugin, and the WAR overlay is thus not available during unit tests, nor is it available when using the Tomcat Maven Plugin (tomcat7:run). Also, this is a build time-only approach, so once your final WAR is packaged, the overlay is included and cannot be changed anymore (by, say, changing the version number of the module that contains the WAR overlay).

Servlet 3.0

In comes the Servlet 3.0 specification, which allows to include web resources in a JAR dependency. Everything in the JAR dependency's META-INF/resources will become available at the root of the web application. For example: META-INF/resources/css/stylesheet.css will become available at http://host/application/css/stylesheet.css.

So much for the good news. Let's look at the different execution modes in my project:

Execution mode WAR overlays Servlet 3.0 external resources
Unit test running Jetty Unavailable Available, but hard to do
tomcat7:run Unavailable Available
WAR deployed to Tomcat Available (resources built into final WAR) Available (resources in JAR dependency)

Jetty Issues

So, the Servlet 3.0 approach leads to happiness, but only after doing some hard work to make Jetty happy. Note that this is Jetty being instantiated programmatically from within a unit test (as opposed to using the Jetty Maven Plugin). I am using Jetty 8.1.9 (not the latest version at the time, being forced to use an earlier version to resolve a dependency conflict).

I am moving the following web resources:

  1. css/stylesheet.css
  2. bootstrap/**
  3. WEB-INF/jsp/** // JSP pages
  4. WEB-INF/tags/** // JSP tags

Moving 1. and 2. is effortless: these are static resources to Jetty, and it serves them happily.

Moving 3. and 4. is problematic: Jetty wasn't able to find the JSP pages anymore:

ERROR: PWC6117: File "...\src\main\webapp\WEB-INF\jsp\authentication\login.jsp" not found

It appears that Jetty needs these external web resources defined specifically. This can be done as follows:

server = new Server(serverPort);
WebAppContext webAppContext = new WebAppContext();
setBaseResources(webAppContext);
webAppContext.setWar("src/main/webapp");
webAppContext.setServer(server);
...
server.setHandler(webAppContext);

And:

import org.springframework.core.io.ClassPathResource;

private static void setBaseResources(WebAppContext webAppContext) {
    try {
        ClassPathResource classPathResource = new ClassPathResource("META-INF/resources");
        String externalResource = classPathResource.getURI().toString();
        String[] resources = new String[] {"src/main/webapp", externalResource };
        webAppContext.setBaseResource(new ResourceCollection(resources));
    }
    catch (IOException exception) {
        fail("problem getting base resources (jsp, tags, etc.): " + exception);
    }
}