Introduction to Servlet 3.0
Earlier in 2011 the Apache group released Tomcat v7 on the waiting Servlet world. With this came featured support for Servlet 3.0. In some discussions it seems that Servlet 3.0 was supposedly supported by later versions of Tomcat v6, but I can’t find anything more than a few scattered mentions; mostly wishful thinking, it seems. With Tomcat v7, we can now get Servlet 3.0 support! It also supports JSP v2.2 and EL v2.2, but for now I just want to poke at a few niceties provided with Servlet 3.0.
Annotations
One very nice thing Servlet 3.0 brings us is a set of annotations to declare Filters and Servlets and Listeners. With these annotations, the configuration of applications is removed from the web.xml file and put into the application’s JARs and WEB-INF/classes. This is an exciting feature because it also allows you to package your Servlets, Filters, and Listeners into JAR files and include them in your web application simply by including the JAR file in the WEB-INF/lib folder of the application. Packaging and fragmenting an application is a topic for another day; let’s just focus on the annotations for the moment.
For those familiar with annotated frameworks such as Spring, it shouldn’t be too difficult to see the benefit of annotating code instead of adding configuration to the web.xml file. There’s the easy spot of “ease,” as it simply doesn’t need to be done. There’s a simple case for “error-proofing” as the annotation should be close enough to the code that it alleviates some of those errors.
Filters
Typically Filters are declared in the web.xml file in the following fashion:
<filter> <filter-name>site-filter</filter-name> <filter-class>foo.SiteFilter</filter-class> </filter> <filter-mapping> <filter-name>site-filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
It’s a bit of XML just to relate a URL pattern (or few) to a class. Of course, additional information can be provided, including initialization parameters for the filter class. The filter class needs to then implement the javax.servlet.Filter interface, and contain a few methods to satisfy the Filter interface. This doesn’t change much in the Servlet 3.0 annotation scheme.
To use a Servlet 3.0 annotated filter, the class must still implement the Filter interface and contain the methods to satisfy the interface, but rather than all of that XML configuration, the annotation @WebFilter() can be added to the class definition, and its parameters added as attributes to the Filter. Here’s what it takes to replace that block of XML:
@WebFilter( "/*" ) public class SiteFilter implements Filter { @Override public void destroy() { } @Override public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException { // Whatever you need to do filterChain.doFilter(servletRequest, servletResponse); } @Override public void init(final FilterConfig filterConfig) throws ServletException { } }
Of course, as with the XML, additional values can be applied.
@WebFilter(urlPatterns = { "/specific","/url","/patterns.ext" }, initParams={@WebInitParam(name="something",value="cool")})
The sample above allows our Filter only to match three specific URL patterns (since there’s no wildcard), and adds a cool value to the initialization values.
Including Other Filters
This can also be applied to older Filters that are not annotated. For example, I frequently use the Tuckey UrlRewriteFilter to ensure my application doesn’t throw itself for a loop by adding the session ID to the returned URL. It’s a simple Filter, expressed in some XML included with the application (taken from their examples):
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE urlrewrite> <!-- PUBLIC "-//tuckey.org//DTD UrlRewrite 3.2//EN" "http://tuckey.org/res/dtds/urlrewrite3.2.dtd"--> <urlrewrite> <outbound-rule encodefirst="true"> <name>Strip URL Session ID's</name> <from>^(.*?)(?:;jsessionid=[^?#]*)?(?[^#]*)?(#.*)?$</from> <to>$1$2$3</to> </outbound-rule> </urlrewrite>
This strips the jsessionid parameter from any outbound data returned to the browser; this stops the URL from changing to /somepath.ext?jsessionid=breakmenow which can cause relative URLs in the application to end up wrong, like /somepath.ext?jsessionid=breakmenow/images/reallywanted.jpg. To implement this the old way, you’d specify the Filter in the web.xml, as the following (from their documentation) demonstrates:
<filter> <filter-name>UrlRewriteFilter</filter-name> <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class> </filter> <filter-mapping> <filter-name>UrlRewriteFilter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> </filter-mapping>
To implement this annotation-unaware Filter into your annotated application, though, simply make a Filter that overrides the UrlRewriteFilter, and pass it the URLs to filter:
@WebFilter(urlPatterns = { "/*" }, dispatcherTypes = { DispatcherType.REQUEST, DispatcherType.FORWARD }) public class RewriteFilter extends UrlRewriteFilter { }
No other content in the class is required, and the now annotated Filter is added without any inclusion in the web.xml file.
Listener
Listeners are defined much the same way. As before, the old way of defining a Listener was to declare it in the web.xml file
<listener> <listener-class>foo.SiteListener</listener-class> </listener>
The listeners define classes that the web app container will find and start. As before, the class must implement an appropriate interface; one of the javax.servlet.Servlet*Listeners. And, of course, it must implement the methods to satisfy the interface.
To use an annotated Listener, implement the desired Listener, satisfy it with the required methods, and annotate it with the @WebListener annotation.
@WebListener() public class SiteListener implements ServletRequestListener { @Override public void requestDestroyed(ServletRequestEvent servletRequestEvent) { } @Override public void requestInitialized(ServletRequestEvent servletRequestEvent) { } }
The annotation doesn’t require any parameters. The only parameter it allows is an optional description of the Listener.
Including Other Listeners
As with Filters, it’s easy to include Listeners that aren’t annotated. For example, to include Apache Tiles in your web.xml, you can define the Listener using the XML (from their documentation):
<listener> <listener-class>org.apache.tiles.web.startup.simple.SimpleTilesListener</listener-class> </listener>
Or using annotations, simply extend the Tiles Listener you want to use, and annotate it appropriately.
@WebListener public class TilesListener extends SimpleTilesListener { }
This way you can contain the configuration entirely in software.
Servlet
Servlets are mapped much the same way Filters are mapped. Traditionally, the web.xml will define a Servlet and Servlet mappings to tie URLs to the correct Servlet.
<servlet> <servlet-name>SiteServlet</servlet-name> <servlet-class>foo.SiteServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>SiteServlet</servlet-name> <url-pattern>/mapping</url-pattern> </servlet-mapping>
The Servlet then needs to extend the class javax.servlet.http.HttpServlet. No methods are required, but generally either or both of the doGet() or doPost() methods are implemented to handle the request action.
To use the Servlet 3.0 annotation for a Servlet, the HttpServlet class must still be implemented, and the annotation @WebServlet needs to be applied. The @WebServlet annotation requires only one attribute as it needs to know at least one URL to which the Servlet will be mapped.
@WebServlet("/mapping") public class SiteServlet extends HttpServlet { @Override protected void doPost(final HttpServletRequest httpServletRequest, final HttpServletResponse httpServletResponse) throws ServletException, IOException { // Do your stuff } }
Of course, mutliple URLs can be added, as with the Filter, by specifying the urlPatterns parameter.
Using Other Servlets
With many frameworks, like Spring MVC or Apache Struts, there’s only one Servlet. That Servlet handles all of the processing of requests, routing them to the appropriate Action or Controller as necessary. As with the Filter and Listener annotations, a simple annotation can bring that third-party Servlet into the application with @WebServlet annotations. From the Spring documentation, here’s how to set up a basic DispatcherServlet in the web.xml file.
<servlet> <servlet-name>example</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>example</servlet-name> <url-pattern>*.form</url-pattern> </servlet-mapping>
Here’s how to do the same using Servlet 3.0 annotations.
@WebServlet(urlPatterns={"/*.form"}, loadOnStartup=1) public class SpringDispatcherServlet extends DispatcherServlet { }
With this, the Spring DispatcherServlet is mapped to accept all URLs ending in .form, and it will be loaded when the application starts (instead of waiting for the first request). As with @WebFilter annotations, the @WebServlet also accepts init parameters, so those can also be added to help configure the application.
Some Limitations
One thing that some of the other frameworks like Spring bring is an ability to map the URL to methods with their annotations. While Servlet 3.0 annotations allow mapping more than one URL to a Servlet class, it is still up to the developer to decipher the URL in the Servlet, as necessary. This is no different than “usual” Servlet development where more than one URL is mapped to a Servlet; inside the Servlet the request path or URI or other mechanism can be used to determine by which the visitor arrived to the Servlet.
Additionally, Servlet 3.0 doesn’t bring any tools to map elements such as GET or POST parameters to method parameters. The same Servlet digestion of parameters occurs, and they’re all Strings until converted. It’s up to the developer to handle all of the parameter transformation.