Internet Programming with Java Course

Writing Servlet Filters

by Kief Morris

Filters are an under-appreciated feature of the Java servlet platform, ideal for writing components that can be added transparently to any web application. A filter is like a lightweight servlet that doesn't generate its own content, instead it plugs into the request handling process and executes in addition to the normal page processing.

Filters might record information about requests, convert content to a different format, or even redirect access to a different page. Filters can be applied to any resources served by a servlet engine, whether it's flat HTML, graphics, a JSP page, servlet , or whatever. They can be added to an existing web application without either the filter or the application being aware of one another. Filters are essentially a server plug-in that works with any servlet container compliant with version 2.3 or later of the servlet specification.

A filter implements the interface javax.servlet.Filter, and configured in the web application web.xml file, where the URL's it will process are defined. For each request, the servlet container decides which filters to apply, and adds those filters to a chain in the same order they appear in web.xml. Each filter has its Filter.doFilter() method called, and triggers the invocation of the next filter in the chain or the loading of the final resource (HTML page, servlet, or whatever).

Writing a simple filter

To write a filter, we create a class implementing the Filter interface, which requires three methods: init(), doFilter(), and destroy(). init() and destroy() are called when the filter is first initialized and when it is destroyed, respectively, to allow configuration and cleanup. For the moment we'll ignore these and focus on doFilter(), which is the meat of the filter.

Filter.doFilter()

The servlet container calls the filter's doFilter() method and passes request and response objects for the current request. These are the same objects as a servlet gets, but since there is no HttpFilter, the parameters are defined as javax.servlet.ServletRequest and javax.servlet.ServletResponse, rather than the javax.servlet.http subclasses. This means that if you want to access methods available only on the HTTP versions of the request and response classes, you will need to cast the objects. To be a Good Programmer, you should first test that they actually are the HTTP versions, in case future generations want to use your filter in a non-HTTP servlet environment. Some of the code later in this article shows examples of this.

The third parameter to doFilter() is a FilterChain object, which is used to invoke the next filter in the chain. This is done by calling FilterChain.doFilter() and passing it the request and response objects. If the current filter is the last filter in the chain, the destination resource will be accessed, that is, the HTML page or other static file will be read in, or else a servlet or JSP page will be invoked. After FilterChain.doFilter() returns, the filter can do more processing if it chooses; at this point, the response object should have more fields populated, than it did beforehand, such as the content-type header having been set.

TimerFilter is a simple filter which does nothing more than time how long it takes to process the request after the filter, based directly on the Apache Foundation's Tomcat 4.0 server's ExampleFilter. If this filter is placed as the last filter in the chain, it times the servlet execution or page access itself. Here is the doFilter() method:

public final class TimerFilter implements Filter 
{
 
    public void doFilter(ServletRequest request, 
                         ServletResponse response,
                         FilterChain chain)
        throws IOException, ServletException 
    {
 
        long startTime = System.currentTimeMillis();
        chain.doFilter(request, response);
        long stopTime = System.currentTimeMillis();
        System.out.println("Time to execute request: " + (stopTime - startTime) + 
            " milliseconds");
 
    }
...

To compile a Filter you will need to link it with the servlet API classes, which include the interface definitions and other classes used by the filter. These classes are almost certainly available with your servlet container; typically named servlet.jar. If you download the sample source code for this article you can use Ant to compile the code; see the README.txt file for help on configuring the build.

Deploying a filter

To add a filter to a web application, you must first put the compiled filter class in the web application's classpath, which is normally done by putting it under WEB-INF/classes or in a jar file in WEB-INF/lib. The filter is then added to the WEB-INF/web.xml configuration file in much the same way a servlet is, in that there are two configuration blocks. The first defines the filter and gives it a name, and the second defines the circumstances in which the filter is invoked.

The <filter> configuration block

The filter class is defined with a <filter> block, which takes the following child elements:

filter-name

The name which will be used to identify the filter elsewhere in the web.xml file.

filter-class

The classname, including package, of the filter. This name will be used by the servlet container to load the filter class.

init-param

Initialization parameters to pass to the filter. We'll discuss these shortly.

description

Long description for the filter, this may be used by configuration tools also.

icon

Optional paths to image files for GUI configuration tools to use to represent the filter.

display-name

Optional descriptive text for the filter, mainly useful for configuration tools.

The only required elements are the name and class; the icon and display-name are pointless unless you're using a tool which uses them. Here is the configuration for the TimerFilter.

    <filter>
        <filter-name>Timer</filter-name>
        <filter-class>com.kief.FilterDemo.TimerFilter</filter-class>
        <description>
            This filter times the execution of the request after
            the filter itself, and prints the execution time to
            the standard output.
        </description>
    </filter>

Note that the same filter class can be defined in multiple <filter> blocks, each with a different name. This creates a separate instance of the class for each <filter> block, each of which can have different configuration parameters.

The <filter-mapping> block

Now we need to tell the servlet container when to apply the filter, which is done with a <filter-mapping> block. The filter-mapping block includes the filter-name, which must match the name given in a <filter> block, and either a URL pattern or a servlet name. Most often you will use a URL pattern, but you can also specify the name of a servlet as defined in a <servlet> block.

URL patterns for filter-mappings are the same as for the <servlet-mapping> block, normally either a subpath such as "/secure/*", or a file ending like "*.gif". Exact paths may also be used, like "/filtered_page.html".

To have our filter handle all requests, we'll use the following filter-mapping block:

    <filter-mapping>
        <filter-name>Timer</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>    

This filter will run for every single request handled by the servlet engine. In most cases this would put an unnecessary load on the server, since few filters really need to execute for every image file as well as flat HTML, JSP, and servlet calls.

Using the FilterConfig object

When the servlet container starts it initializes each filter by calling the init() method, passing in a FilterConfig object. This object has two main uses, obtaining initialization parameters and accessing the container's ServletContext.

Initialization parameters are set in the web.xml file, in the <filter> block, and are accessed by calling FilterConfig.getInitParameter(). The names of all parameters can be retrieved with getInitParameterNames().

The ServletContext is useful for accessing data and services relating to the servlet container and the web application. For instance, it is generally better to send output to the container's log file rather than printing to standard output, as the TimerFilter code earlier in this article does. The original ExampleFilter code from the Tomcat project does this by saving a reference to FilterConfig in a class variable, and then gets the servlet context to call its log() method.

Let's look at the original Tomcat ExampleFilter code to see this in action. Not only does it use ServletContext, it also uses a configuration parameter to create a request attribute that could be retrieved by a servlet, JSP, or another filter. Although not obviously useful here, this is a handy technique for passing data between the components of a web application.

In its init() method the filter saves the FilterConfig object in an object variable, along with the "attribute" initialization parameter.

    private FilterConfig filterConfig = null;
    private String attribute = null;
 
    public void init(FilterConfig filterConfig) throws ServletException {
 
        this.filterConfig = filterConfig;
        this.attribute = filterConfig.getInitParameter("attribute");
 
    }

Then the doFilter() method uses the FilterConfig to grab the ServletContext and use its log() method to write to the log file. It also sets an attribute in the request named according to the init-parameter, whose value is the ExampleFilter object itself. This could be used in a JSP page or servlet, or a later Filter.

    
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain)
        throws IOException, ServletException {
 
        // Store ourselves as a request attribute (if requested)
        if (attribute != null)
            request.setAttribute(attribute, this);
 
        // Time and log the subsequent processing
        long startTime = System.currentTimeMillis();
        chain.doFilter(request, response);
        long stopTime = System.currentTimeMillis();
        filterConfig.getServletContext().log
            (this.toString() + ": " + (stopTime - startTime) +
             " milliseconds");
 
    }

Overriding the request or response

In most cases filters need to affect what happens later in the request process, even if later components or the resource being accessed don't know anything about the filter. A powerful way to do this is to substitute your own request or response object when calling the FilterChain.doFilter() method. The servlet specification offers special wrapper classes for this purpose, which can be subclassed to override their default behavior. The replacement class is instantiated passing in the original request or response object, so methods which aren't overridden can still be used.

This technique is particularly useful for filters that need to process the raw output of the request, for example encrypting, converting, or compressing the response data. Once again the Tomcat folks offer a good example of how to do this with CompressionFilter. Since most web browsers can automatically handle compressed pages, CompressionFilter compresses large pages on the fly. This can be applied not only to static HTML pages, but also HTML (or other format) output generated by servlets or JSP pages.

If CompressionFilter determines that the browser supports compression, it wraps the response with a CompressionServletResponseWrapper, which is a subclass of HttpServletResponseWrapper. Whatever final resource is accessed by the request will be sent to the browser by calling ServletResponse.getOutputStream() or ServletResponse.getWriter() to obtain a writer or stream to feed the output into. So the response wrapper overrides these methods, replacing the stream with a custom CompressionResponseStream that uses java.util.zip.GZIPOutputStream to compress whatever is written to it, or returning a writer which employs that same stream.

Here is the portion of CompressionFilter.doFilter() which actually wraps the response.

    if (response instanceof HttpServletResponse) {
        CompressionServletResponseWrapper wrappedResponse =
            new CompressionServletResponseWrapper((HttpServletResponse)response);
        wrappedResponse.setCompressionThreshold(compressionThreshold);
        if (debug > 0) {
            System.out.println("doFilter gets called with compression");
        }
        try {
            chain.doFilter(request, wrappedResponse);
        } finally {
            wrappedResponse.finishResponse();
        }
        return;
    }

The implementation of the custom output stream is actually more sophisticated than this. It waits to ensure the returned content is large enough to be worth compressing, and then uses the compression stream or a normal stream accordingly.

Redirecting a request

Another use for filters is to restrict access to resources - if a filter doesn't call FilterChain.doFilter(), the resource won't be loaded. Simply returning would send an empty document to the browser, so it's better to redirect or forward the request to a different resource. As with a servlet, there are two ways to do this. HttpServletReponse.sendRedirect() actually sends a response to the browser giving it a URL to fetch instead of the original one, which may be on a different server. The other option is to use a RequestDispatcher to load a different resource on the server instead of the resource which would have been loaded, transparently to the browser.

Unfortunately the Tomcat project doesn't provide any useful examples for this, so I'm reduced to writing my own code. SecureFilter checks the current session to see whether there is a User object; if not, it forwards the request to a login form. While hardly a robust component, you can see how a more useful system could be implemented.

public void doFilter(ServletRequest request, 
                     ServletResponse response, 
                     FilterChain chain)
    throws IOException, ServletException
{
    boolean authorized = false;
    if (request instanceof HttpServletRequest) {
        HttpSession session = ((HttpServletRequest)request).getSession(false);
        if (session != null) {
            User user = (User) session.getAttribute("user");
            if (user != null)
                authorized = true;
        }
    }
            
    if (authorized) {
        chain.doFilter(request, response);
        return;
    } else if (filterConfig != null) {
        String login_page = filterConfig.getInitParameter("login_page");
        if (login_page != null && !"".equals(login_page)) {
            filterConfig.getServletContext().getRequestDispatcher(login_page).
                    forward(request, response);
            return;
        }
    }
    
    throw new ServletException
                    ("Unauthorized access, unable to forward to login page");
    
}

Threading issues

As with servlets, it's important to remember that filters are used in a threaded environment. A single instance of the filter class is used to handle all of the requests it applies to (well, one instance for each <filter> block), so more than one request may be using the object at the same time. Each request will execute methods on the same filter object in different threads, so you need to take care to write your filters in a threadsafe manner.

A common mistake by programmers who aren't familiar with threads is to use object variables to store data that applies only to one thread. If you have data which you are only using for a particular request, create variables for it inside the method so each thread will have its own copy, not outside the methods where all threads will share the same copy.

Here is a contrived example of making this error. This filter counts the number of times it is invoked, storing the number in an integer object variable named hitCount.

    public void doFilter(ServletRequest request, 
                         ServletResponse response, 
                         FilterChain chain)
        throws IOException, ServletException
    {
        hitCount++;
        chain.doFilter(request, response);
        context.log("This filter has been hit = " + hitCount + " times");
    }

The problem is that if two requests hit the server at about the same time, both threads may increment the hitCount before either thread reaches the line that prints the count to the log file, so both threads will print the same value. Combining the increment with the log line is less obviously wrong, but it is still not threadsafe:

        context.log("This filter has been hit = " + (++hitCount) + " times");

Although the incrementation happens on the same line, the line itself is compiled into multiple bytecode instructions which aren't guaranteed to be atomic. That is, the JVM doesn't guarantee to complete the operation all at once, it may switch to another thread in the middle, which might then execute the same operation (or part of the operation).

If you want to demonstrate this, use UnsafeFilter2, which inserts a 5 second pause inside the danger area of the code. Open a page filtered by UnsafeFilter2 in two browser windows and reload them both quickly, then see the standard output (which may be your servlet engine's console, or a log file).

    public void doFilter(ServletRequest request, 
                         ServletResponse response, 
                         FilterChain chain)
        throws IOException, ServletException
    {
        hitCount++;
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
        }
        chain.doFilter(request, response);
        System.out.println("This filter has been hit = " + hitCount + " times");
    }

On my Tomcat server this prints the following:

This filter has been hit = 2 times
This filter has been hit = 2 times

Another issue is with data that actually does need to be shared between threads, for example caching data in a Map. If one thread modifies the shared object while another accesses it the results may not be what you expect; even if the second thread is only retrieving data, if the object being accessed isn't threadsafe its internal data may be in an inconsistent state while it is being updated by the first thread. The key here is to use threadsafe classes, for example Hashtable instead of HashMap, to synchronize appropriate blocks of code, and generally to be aware of threadsafe programming techniques. Doug Lea's book "Concurrent Programming in Java" is an excellent reference.

Conclusion

By now you should have an idea of the kinds of things filters can do, and how to write your own filters. For more information, see the Servlet specifications available at Java.sun.com. Filters were introduced in the 2.3 version of the specification, available at java.sun.com, and so aren't available on older servlet containers. The proposed 2.4 version of the specification, also available from Sun, adds the capability to configure how a filter applies to forwarded requests.