Saturday, April 2, 2016

Without doubt one of the most important Servlet implementations is done by Tomcat. Tomcat serves, or has served, as the base for Servlet functionality in a number of Java EE application servers and is one of the most frequently used standalone Servlet containers.

Contrary to what is often thought, Tomcat is not the reference implementation for Servlet; the build-in Servlet container of GlassFish is. Even though important parts of that GlassFish Servlet implementation are in fact based on Tomcat it's a different code base. Normally GlassFish being the RI implements new spec level functionality first, but unfortunately during the Java EE 8/Servlet 4.0 cycle the GlassFish team is working on other assignments and hasn't worked out a schedule to incorporate this new functionality.

This API allows Servlet users to find out via which mapping a Servlet was being called. E.g. a Servlet can be mapped by a user to both "/foo/*" and "*.bar" among others. Especially for frameworks it can be important to know what the mapping was, since not rarely something (typically a template file) has to be loaded based on what the * from the above example was. And not only that, if links have to be generated they often need to use the same mapping that was used to call the Servlet.

The current way to do this is a little hairy and therefor quite error prone; it requires checking against the many components of the request URI. The new proposed API greatly simplifies this via the new HttpServletRequest#getMapping() method:

public default Mapping getMapping() {
// ...
}

The new Mapping type that can be seen in the signature of the above method looks as follows:

/**
* Represents how the request from which this object was obtained was mapped to
* the associated servlet.
*
* @since 4.0
*/
public interface Mapping {
/**
* @return The value that was matched or the empty String if not known.
*/
String getMatchValue();
/**
* @return The {@code url-pattern} that matched this request or the empty
* String if not known.
*/
String getPattern();
/**
* @return The type of match ({@link MappingMatch#UNKNOWN} if not known)
*/
MappingMatch getMatchType();
}

The MappingMatch is an enumeration of the different types of possible mappings as shown below:

As can be seen this Servlet is mapped via a path mapping ("/path/*"), extension mapping ("*.ext"), context root mapping (""), default mapping ("/") and a single exact mapping ("/exact"). We deployed this Servlet via an application called "servlet4" to a Tomcat 9.0 M4 instance and subjected it to a couple of requests via a browser. The results are shown below:

Path mappinghttp://localhost:8080/servlet4/path/foo

Mapping match:PATH
Match value:/foo
Pattern:/path/*

Extension mappinghttp://localhost:8080/servlet4/foo.ext

Mapping match:EXTENSION
Match value:/foo
Pattern:*.ext

Context root mappinghttp://localhost:8080/servlet4

Mapping match:CONTEXT_ROOT
Match value:
Pattern:

Default (fallback) mappinghttp://localhost:8080/servlet4/doesnotexist

Mapping match:DEFAULT
Match value:/
Pattern:/

Exact mappinghttp://localhost:8080/servlet4/exact

Mapping match:EXACT
Match value:/exact
Pattern:/exact

To test the implicit mapping (as per Servlet spec 12.2.1), the following JSP file was used:

Implicit mappings are a little bit difficult to represent. Are they a mapping themselves, or is it just extra information? I.e. can you speak of an "implicit extension mapping" and an "implicit path mapping"?

Conclusion

At the moment it's unfortunately difficult to commit code to the Servlet RI (GlassFish), but Tomcat 9.0 M4 can be used to get an early glimpse of one of the new Servlet APIs. As the examples have shown, the new Mapping API now makes it trivial to find out via which mapping a Servlet was selected. The "implicit" mapping type however may still need some discussion.