Help is at hand for everyone who has ever cursed at having to add entries to their web.xml just because they wanted to use a third party framework.

With JSR 315, web.xml is no longer a single monolithic entity, but instead can be assembled from multiple pieces (called web fragments) into a single whole.

For instance, here’s a project I rolled earlier, that is packaged as jSwengsol.jar. This external JAR file would be deployed into your web application’s WEB-INF/lib folder, and as usual, is able to contribute web components (such as servlets, filters, or listeners). In my JAR, these components live within the com.swengsol package.

Note the presence of two special entries within this JAR file, the META-INF/web-fragment.xml file and the resources directory.

The web fragment entry allows an external JAR to package the configuration of its contained web components. This fragment will be merged into your own application’s web.xml when your application is deployed into the servlet container.

The optional resources folder can contain static resources (such as HTML, CSS, and JS files) as well as dynamic resources (JSPs). Resources provided within this folder are publicly available. Note that these resources live within your web application’s WEB-INF folder,  and yet are visible to the external world. This means that frameworks no longer have to implement workarounds (such as filters mapped to URL paths) to serve up resources that are required by that framework’s components.

Note that jSwengsol.jar is deployed into our main web application’s WEB-INF/lib.

This JAR file will now be described.

The IDEA configuration for generating the JAR file is as shown:

To build this artifact, I simply use Build > Build ‘jSwengsol’ artifact:

Squawker.java is a standard helper class that is used by our main web application. It is a very simple helper that takes a message and generates the HTML scaffolding around it. An important point of note is that it retrieves the stylesheet straight out of the web application’s WEB-INF/lib/jSwengsol.jar!resources/styles.css.

package com.swengsol;
/**
* User: Damodar Chetty
* Date: Jul 23, 2010
* Time: 8:13:26 AM
* An introduction to Servlet 3.0 and Java 6 (TC JUG)
* (c) Software Engineering Solutions, Inc.
*/
public class Squawker {
	private String title;
	private String body;
	public Squawker(String body) {
		this("Introducing Servlet 3.0", body);
	}
	public Squawker(String title, String body) {
		this.title = title;
		this.body = body;
	}
	public String getHTML() {
		String html = "<html><head>";
		if (null == title || title.length() ==0)
			title = "JSR315: Servlet 3.0";
		html += "<title>" + title + "</title>";
		html +=
		"<link rel=stylesheet type=\"text/css\"  href=\"styles.css\">";
		html += "</head> <body><p>" ;
		html += body;
		html += "</p>";
		html += "<p class=\"copyright\">Damodar Chetty,”;
		html += “ Software Engineering Solutions, Inc.</p>";
		html += "</body></html>";
		return html;
	}
}

The stylesheet, styles.css, is also a very straightforward affair:

.mainpara {
	font-size: 15pt;
	font-family:'trebuchet ms';
	padding:20;
	border-color:#736AFF;
	position:absolute;
	top:50;
	left: 200;
	border-style: dotted;
}
.copyright {
	width:375px;
	font-size:12pt;
	font-weight:bold;
	font-family:serif;
	background-color:black;
	color:white;
	position: absolute;
	top:300;
	left:500;
	padding:5;
}
body {background-color:#E0FFFF; color:#7F5217;}

In addition, you can also directly request the static resource, jSwengsol.html (listed below) as if it were directly within the root of our web application context, http://localhost:8008/jsr315/jSwengsol.html, where jsr315 is the context path for the web application that includes this JAR file.

<html>
<head>
<title>Introducing Servlet 3.0</title>
<link rel=stylesheet type="text/css"  href="styles.css">
</head>
<body>
<p>This is a static resource file served up from jSwengsol.jar</p>
<p>Damodar Chetty, Software Engineering Solutions, Inc.</p>
</body>
</html>

Likewise, you can access the JSP file as http://localhost:8008/jsr315/jSwengsol.jsp:

<%@ page import="java.util.Date" %>
<%--
  Created by IntelliJ IDEA.
  User: Monsty
  Date: Jul 22, 2010
  Time: 9:24:48 PM
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head><title>Introducing Servlet 3.0</title>
  <link rel=stylesheet type="text/css"  href="styles.css">
  </head>
  <body>
    <p class="mainpara">
      jSwengsol.jar!jSwengsol.jsp says the time is now <%=new Date()%>
   </p>
    <p class="copyright">
      Damodar Chetty, Software Engineering Solutions, Inc.
    </p>
  </body>
</html>

Now, let’s take a look at the web fragment, META-INF/web-fragment.xml:

<web-fragment>
  <name>jSwengsolFragment</name>
  <listener>
    <listener-class>com.swengsol.listeners.MyServletRequestListener</listener-class>
  </listener>
</web-fragment>

Firstly, note that a fragment can be named. This comes in handy when you are trying to specify ordering rules that define how multiple fragments can be combined into the application’s web.xml. Second, note that as its name suggests, this fragment is not a complete descriptor. Here it contains only the listener’s declaration within this fragment is automatically rolled into the main web application’s web.xml.

package com.swengsol.listeners;

import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.logging.Logger;

//1. No annotation - uses web-fragment.xml
public class MyServletRequestListener implements ServletRequestListener {
    private static final Logger logger = Logger.getLogger("com.swengsol");
    // Public constructor is required by servlet spec
    public MyServletRequestListener() {
    }

    public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
        logger.info("RequestListener > request destroyed - " + new Date());
    }

    public void requestInitialized(ServletRequestEvent servletRequestEvent) {
        HttpServletRequest req =
		(HttpServletRequest)servletRequestEvent.getServletRequest();
        String uri = req.getRequestURL().toString();
        logger.info("RequestListener > request initialized - " + uri + " - "
		 + new Date());
    }
}

There’s nothing noteworthy about this listener, except to point out that I specifically avoided using an annotation here to show how a web fragment functions.

To show how annotations could have been used instead, check out the JARServlet servlet:

package com.swengsol.servlets;

import com.swengsol.Squawker;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import java.util.logging.Logger;

/**
 * User: Damodar Chetty
 * Date: Jul 23, 2010
 * Time: 1:12:21 PM
 * An introduction to Servlet 3.0 and Java 6 (TC JUG)
 * (c) Software Engineering Solutions, Inc.
 */
@WebServlet("/JarServlet")
public class JARServlet extends HttpServlet {
    private static final Logger logger = Logger.getLogger("com.swengsol");

    protected void doPost(HttpServletRequest request,
			HttpServletResponse response)
        throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request,
			HttpServletResponse response)
        throws ServletException, IOException {
        logger.info("Within JARServlet request>>>" + new Date());

        Squawker squawker =
		new Squawker("Hello from jSwengsol.jar!JARServlet: " + new Date());
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println(squawker.getHTML());
    }
}

In this case, the servlet annotation is sufficient to register this servlet with the main web application, so no fragment entry is necessary.

That’s about it for now. As you can tell, there’s legs to this story – and I can imagine it being very useful as we head off into the future.