Related Articles:

Servlet App Event Listeners

Servlet 2.3 API Features Exposed

This article is the third in a series dedicated to examining the new features of the Servlet 2.3 API specification. The first article was a high level introduction to Servlet concepts and the new features of the Servlet 2.3 specification: application lifecycle events and filters. The second article took a more in-depth look at writing and using application lifecycle events. Please read these articles for more information on those topics. This article covers how to write and use filters within your web applications, including examples of using a simple filter, configuring a filter chain, using initial parameters in a filter, parsing the user's request, and logging statistics.

Servlet filters are a new addition to the Servlet 2.3 specification that's in its public final draft stage, released in October, 2000. Filters are Java classes that can intercept requests from a client before they access a resource; manipulate requests from clients; intercept responses from resources before they are sent back to the client; and manipulate responses before they are sent to the client.

Filters have a wide array of uses; the Servlet 2.3 specification suggests the following uses:

Writing a Simple Filter

The first step in learning how to write a filter is to look at a very simple example. A filter is simply a Java class that implements the javax.servlet.Filter interface. The javax.servlet.Filter interface defines three methods:

It is the containers responsibility to create a javax.servlet.FilterConfig object and pass it to the filter during initialization. The javax.servlet.FilterConfig object can be used to

The setFilterConfig() method can be used to capture the object into an attribute of the filter. The doFilter() method is where the filter's work is done, in which you can parse the user's request; log statistics to a file; manipulate the response back to the client; and so on. Listing 1 is an example of a very simple filter which prints a message to the console of the web server while it is filtering the request, calls the Servlet, and then prints another message to the console while it is filtering the response. Figure 1 is a diagram of how the simple filter fits into the servlet's request-response model.

Diagram of the simple filter.
Figure 1. A diagram of how the simple filter fits into the big picture.


Listing 1: An example of a very simple filter (SimpleFilter.java)

package com.filters;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import java.io.IOException;
import javax.servlet.ServletException;

public class SimpleFilter implements Filter
{
  private FilterConfig filterConfig;

  public void doFilter (ServletRequest request,
             ServletResponse response,
             FilterChain chain)
  {

    try
    {
      System.out.print ("Within Simple Filter ... ");
      System.out.println ("Filtering the Request ...");

      chain.doFilter (request, response);

      System.out.print ("Within Simple Filter ... ");
      System.out.println ("Filtering the Response ...");

    } catch (IOException io) {
      System.out.println ("IOException raised in SimpleFilter");
    } catch (ServletException se) {
      System.out.println ("ServletException raised in SimpleFilter");
    }
  }

  public FilterConfig getFilterConfig()
  {
    return this.filterConfig;
  }

  public void setFilterConfig (FilterConfig filterConfig)
  {
    this.filterConfig = filterConfig;
  }
}


As you can see the simple filter example is a Java class named SimpleFilter.java which implements the javax.servlet.Filter interface. It provides implementation for the three methods defined in the javax.servlet.Filter interface.

Notice that doFilter() can be separated into two sections: filtering the request and filtering the response. The two sections are separated by a call from the javax.servlet.FilterChain object to the next object in the chain, which could be the Servlet or another filter.

Now that we have written the filter, it might be nice to deploy it in a web server and see it in action.

Deploying Filters

Apache's Tomcat, version 4.0 beta, supports the Servlet 2.3 specification and all the examples in this article have been tested on that server.

The filters in a web application are defined in the deployment descriptor of the web application, the web.xml file. Filters are defined and then mapped to a URL or Servlet, in much the same was as Servlet is defined and then mapped to a URL pattern. Listing 2 shows the web.xml file that deployed the SimpleFilter.java from Listing 1, mapping it to a Servlet.


Listing 2: The deployment descriptor for the simple filter. (web.xml)

<?xml version = "1.0" encoding = "ISO-8859-1"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/j2ee/dtds/web-app_2_3.dtd">

<web-app>

  <!-- Define the filters within the Web Application -->

  <filter>
    <filter-name>
      Simple Filter Example
    </filter-name>
    <filter-class>
      com.filters.SimpleFilter
    </filter-class>
  </filter>


  <!-- Map the filter to a Servlet or URL -->

  <filter-mapping>
    <filter-name>
      Simple Filter Example
    </filter-name>
    <url-pattern>
      /simple
    </url-pattern>
  </filter-mapping>


  <!-- Define the Servlets within the Web Application -->

  <servlet>
    <servlet-name>
      Simple Servlet
    </servlet-name>
    <servlet-class>
      com.servlets.SimpleServlet
    </servlet-class>
  </servlet>


  <!-- Define Servlet mappings to urls -->

  <servlet-mapping>
    <servlet-name>
      Simple Servlet
    </servlet-name>
    <url-pattern>
      /simple
    </url-pattern>
  </servlet-mapping>

</web-app>

Figure 2 shows the console of the Tomcat server where this web application was running after the servlet had handled a request.

Screen shot.
Figure 2. Console of Tomcat running the simple filter.

Filter Chaining

In the first example only one filter that handled the request and response for the simple servlet. What if you want multiple filters to handle the request and response? Figure 3 shows what a filter chain looks like.

Diagram of filter chaining.
Figure 3. A diagram of filter chaining.

Multiple filters can be written and applied to the same URL pattern. The order of execution is determined by the ordering in the deployment descriptor. Remember that in the doFilter() method you call the next element in the chain with

chain.doFilter(request, response)

Diagram of the filter-chaining example.
Figure 4. Diagram of the filter-chaining example.

Listing 3 shows the code for Filter1.java, which is a simple filter that outputs to the console of the web server a message. The message allows you to track the request through the filters.


Listing 3: A simple filter that outputs a message to the console. (Filter1.java)

package com.filters;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import java.io.IOException;
import javax.servlet.ServletException;

public class Filter1 implements Filter
{
  private FilterConfig filterConfig;

  public void doFilter (ServletRequest request,
              ServletResponse response,
              FilterChain chain)
  {

    try
    {
      System.out.print ("Within First Filter ... ");
      System.out.println ("Filtering the Request ...");

      chain.doFilter (request, response);

      System.out.print ("Within First Filter ... ");
      System.out.println ("Filtering the Response ...");

    } catch (IOException io) {
      System.out.println ("IOException raised in Filter1 Filter");
    } catch (ServletException se) {
      System.out.println ("ServletException raised in Filter1 Filter");
    }
  }

  public FilterConfig getFilterConfig()
  {
    return this.filterConfig;
  }

  public void setFilterConfig (FilterConfig filterConfig)
  {
    this.filterConfig = filterConfig;
  }
}


The second filter prints a message to the console of the web server to help us track the request and response. Listing 4 shows the code for the second filter in the chain.


Listing 4. A simple filter that outputs a message to the console. (Filter2.java)

package com.filters;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import java.io.IOException;
import javax.servlet.ServletException;

public class Filter2 implements Filter
{
  private FilterConfig filterConfig;

  public void doFilter (ServletRequest request,
              ServletResponse response,
              FilterChain chain)
  {

    try
    {
      System.out.print ("Within Second Filter ... ");
      System.out.println ("Filtering the Request ...");

      chain.doFilter (request, response);

      System.out.print ("Within Second Filter ... ");
      System.out.println ("Filtering the Response ...");

    } catch (IOException io) {
      System.out.println ("IOException raised in Filter2 Filter");
    } catch (ServletException se) {
      System.out.println ("ServletException raised in Filter2 Filter");
    }
  }

  public FilterConfig getFilterConfig()
  {
    return this.filterConfig;
  }

  public void setFilterConfig (FilterConfig filterConfig)
  {
    this.filterConfig = filterConfig;
  }
}


After writing the filters, they need to be defined and mapped to a URL pattern in the deployment descriptor. The order in which they are defined matters. The container will execute the filters in the order in which they are defined. Listing 5 shows the web.xml file for deploying Filter1.java and Filter2.java.


Listing 5: The deployment descriptor for the filter chaining. (web.xml)

<?xml version = "1.0" encoding = "ISO-8859-1"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/j2ee/dtds/web-app_2_3.dtd">

<web-app>

  <!-- Define the filters within the Web Application -->

  <filter>
    <filter-name>
      First Filter in Chain
    </filter-name>
    <filter-class>
      com.filters.Filter1
    </filter-class>
  </filter>

  <filter>
    <filter-name>
      Second Filter in Chain
    </filter-name>
    <filter-class>
      com.filters.Filter2
    </filter-class>
  </filter>


  <!-- Map the filter to a Servlet or URL -->

  <filter-mapping>
    <filter-name>
      First Filter in Chain
    </filter-name>
    <url-pattern>
      /simple
    </url-pattern>
  </filter-mapping>

  <filter-mapping>
    <filter-name>
      Second Filter in Chain
    </filter-name>
    <url-pattern>
      /simple
    </url-pattern>
  </filter-mapping>


  <!-- Define the Servlets within the Web Application -->

  <servlet>
    <servlet-name>
      Simple Servlet
    </servlet-name>
    <servlet-class>
      com.servlets.SimpleServlet
    </servlet-class>
  </servlet>


  <!-- Define Servlet mappings to urls -->

  <servlet-mapping>
    <servlet-name>
      Simple Servlet
    </servlet-name>
    <url-pattern>
      /simple
    </url-pattern>
  </servlet-mapping>

</web-app>


The output of the messages in the console is shown in Figure 5. Notice the request went through Filter1 and then Filter2, but the response went through Filter2 and then Filter1.

Screen shot.
Figure 5. Output from the filter chain.

Parsing a User's Request and Using Initial Parameters

So far the examples of the filters have been trivial. Now it's time to do something more realistic. Sometimes you may want to use a filter to verify certain objects pass with the requests, such as elements from an HTML form. You may want to verify the existence and validity of the values. If they don't exist, you send the client's request to one servlet. If they do exist but the values are not valid, then they are sent to a different servlet. Finally, if the elements check out, they are passed to the requested servlet. Figure 6 shows a diagram of this scenario.

Diagram of filtering the request.
Figure 6. Filtering a user's request and verifying HTML form data

Let's take this example one step further. Not only do we want to verify HTML form data, we want to retrieve the names of the HTML form elements from initialization parameters. This means that if the HTML form changes, we do not need to change any code in the filter; we simply change the initialization parameters for the filter.

Initialization parameters can be specified in the deployment descriptor. They are specified when the filter is defined. Listing 6 is the code for the filter that retrieves HTML form element names from initialization parameters, verifies the form data, and sends the request to the appropriate servlet.


Listing 6: A filter that verifies HTML form data. (RequestFilter.java)

package com.filters;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.RequestDispatcher;

import java.util.Enumeration;

import java.io.IOException;
import javax.servlet.ServletException;

public class RequestFilter implements Filter
{
  private FilterConfig filterConfig;

  public void doFilter (ServletRequest request,
              ServletResponse response,
              FilterChain chain)
  {

    RequestDispatcher rd = null;
    boolean emptyform = false;

    try
    {

      Enumeration initParams = filterConfig.getInitParameterNames();

      // no initial parameters so invoke next element in chain
      if (initParams == null)
      {
        System.out.println("No elements to verify");
        chain.doFilter(request, response);
      }

      // grab init param values and get the form elements
      else
      {
        while (initParams.hasMoreElements())
        {
          String name = (String) initParams.nextElement();
          String value = filterConfig.getInitParameter(name);

          String formElement = request.getParameter(value);

          // check to see if element exists (i.e. form was sent)
          if (formElement == null)
          {
            rd = request.getRequestDispatcher("/noform");
            rd.forward(request, response);
          }

          // check to see if elements are empty
          else if (formElement.equals(""))
          {
            emptyform = true;
          }
        }

        // a form element was empty
        if (emptyform)
        {
          rd = request.getRequestDispatcher("/emptyform");
          rd.forward(request, response);
        }

        // form was filled out properly
        else
        {
          chain.doFilter(request, response);
        }
      }
    }

    catch (IOException io)
    {
      System.out.println("IOException raised");
    }
    catch (ServletException se)
    {
      System.out.println("ServletException raised");
    }



  }

  public FilterConfig getFilterConfig()
  {
    return this.filterConfig;
  }

  public void setFilterConfig (FilterConfig filterConfig)
  {
    this.filterConfig = filterConfig;
  }
}


The web.xml to deploy this filter and define initial parameters is shown in Listing 7.


Listing 7: The deployment descriptor that defines initial parameters for a filter. (web.xml)

<?xml version = "1.0" encoding = "ISO-8859-1"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
  "http://java.sun.com/j2ee/dtds/web-app_2_3.dtd">

<web-app>

  <!-- Define the filters within the Web Application -->

  <filter>
    <filter-name>
      Parsing Request Data
    </filter-name>
    <filter-class>
      com.filters.RequestFilter
    </filter-class>

    <init-param>
      <param-name>
        User's Name
      </param-name>
      <param-value>
        myname
      </param-value>
    </init-param>

    <init-param>
      <param-name>
        User's Email
      </param-name>
      <param-value>
        txtemail
      </param-value>
    </init-param>
  </filter>


  <!-- Map the filter to a Servlet or URL -->

  <filter-mapping>
    <filter-name>
      Parsing Request Data
    </filter-name>
    <url-pattern>
      /formprocessor
    </url-pattern>
  </filter-mapping>



  <!-- Define the Servlets within the Web Application -->

  <servlet>
    <servlet-name>
      No Form Submitted
    </servlet-name>
    <servlet-class>
      com.servlets.NoElements
    </servlet-class>
  </servlet>

  <servlet>
    <servlet-name>
      Empty Form Elements
    </servlet-name>
    <servlet-class>
      com.servlets.InvalidEntries
    </servlet-class>
  </servlet>

  <servlet>
    <servlet-name>
      Good Request
    </servlet-name>
    <servlet-class>
      com.servlets.MyServlet
    </servlet-class>
  </servlet>


  <!-- Define Servlet mappings to urls -->

  <servlet-mapping>
    <servlet-name>
      No Form Submitted
    </servlet-name>
    <url-pattern>
      /noform
    </url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>
      Empty Form Elements
    </servlet-name>
    <url-pattern>
      /emptyform
    </url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>
      Good Request
    </servlet-name>
    <url-pattern>
      /formprocessor
    </url-pattern>
  </servlet-mapping>

</web-app>


Summary

The Servlet 2.3 API specification provided important new features. Application lifecycle events provide the developer better control over the ServletContext object and HttpSession objects. Filters provide a way to take control logic out of servlets, placing it in more reusable pieces of code.


Return to ONJava.com.