Writing Servlet Filters | Redirecting a Request

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.

Download the source code from this tutorial.

 1   
About | Sitemap | Contact