The J2EETM Tutorial
Home
TOC
PREV TOC NEXT Search
Feedback

Filtering Requests and Responses

A filter is an object that can transform the header and/or content of a request or response. Filters differ from web components in that they usually do not themselves create a response. In particular, a filter should not have any dependencies on a web resource for which it is acting as a filter so that it can be composable with more than one type of web resource. The main tasks that a filter can perform are:

Applications of filters include authentication, logging, image conversion, data compression, encryption, tokenizing streams, XML transformations, and so on.

You can configure web components and static resources to be filtered by zero, one, or more filters in a specific order. Thus, to use filters you must:

Defining the Filter Class

You define a filter class by implementing the Filter interface. The most important method in this interface is the doFilter method, which is passed request, response, and filter chain objects. This method can perform the following actions:

The Duke's Bookstore application uses the filters HitCounterFilter and OrderFilter to increment and log the value of a counter when the entry and receipt servlets are accessed.

In the doFilter method, both filters retrieve the servlet context from the filter configuration object so that they can access the counters stored as context attributes. When you write the filter, you must implement the [set|get]FilterConfig methods. The setFilterConfig method is called by the container when the filter is instantiated. After the filters have completed application-specific processing, they invoke doFilter on the filter chain object passed into the original doFilter method.

In addition to logging the value of the hit counter, HitCounterFilter also inserts the value of the counter into the response. It does this by wrapping the response in an object CharResponseWrapper which extends HttpServletResponseWrapper. The wrapped response is passed to the next object in the filter chain, which is BookStoreServlet. When BookStoreServlet calls getWriter on the wrapper, it receives a stand-in print writer implemented by FilterPrintWriter. This layering of print writers is necessary because normally a servlet closes a print writer when it completes and then the filter would not be able to print to that writer. BookStoreServlet writes its response into the FilterPrintWriter. When chain.doFilter returns, HitCounterFilter retrieves the servlet's response from FilterPrintWriter with the getData method, and writes it to the original PrintWriter. The filter then inserts the value of the counter into the original PrintWriter.

public final class OrderFilter implements Filter {
	private FilterConfig filterConfig = null;

	public void doFilter(ServletRequest request,
		ServletResponse response, FilterChain chain) 
		throws IOException, ServletException {
		if (filterConfig == null)
			return;

		StringWriter sw = new StringWriter();
		PrintWriter writer = new PrintWriter(sw);
		Counter counter = (Counter)filterConfig.
			getServletContext().
			getAttribute("hitCounter");
		writer.println();
		writer.println("===============");
		writer.println("The number of hits is: " +
			counter.incCounter());
		writer.println("===============");

		// Log the resulting string
		writer.flush();
		filterConfig.getServletContext().
			log(sw.getBuffer().toString());
				
		PrintWriter out = response.getWriter();
		CharResponseWrapper wrapper = new CharResponseWrapper(
			(HttpServletResponse)response); 
		chain.doFilter(request, wrapper);
		out.write(wrapper.getData());
		out.println("<center>You are visitor number 
			<font color='red'>" + counter.getCounter() +
			"</font></center>");
	}
	public FilterConfig getFilterConfig() {
		return (this.filterConfig);
	}
	public void setFilterConfig(FilterConfig filterConfig) {
		this.filterConfig = filterConfig;
	}
}

public class CharResponseWrapper extends
	HttpServletResponseWrapper {
	private CharArrayWriter output;
	public char[] getData() {
		return output.toCharArray();
	}
	public CharResponseWrapper(HttpServletResponse response){
		super(response);
		output = new CharArrayWriter();
	}
	public PrintWriter getWriter(){
		return new FilterPrintWriter(output);
	}
}

class FilterPrintWriter extends PrintWriter {
	private PrintWriter writer;
	public FilterPrintWriter(Writer output) {
		super(output);
		writer = new PrintWriter(output);
	}
	public void println(String s) {
		writer.println(s);
	}
} 

Figure 14 shows the entry page for Duke's Bookstore with the hit counter.

Figure 14 Duke's Bookstore

Specifying Filter Mappings

A web container uses filter mappings to decide how to apply filters to web resources. A filter mapping matches a filter to a web component by name or to web components and static resources by URL pattern. The filters are invoked in the order that filter mappings appear in the filter mapping list of a WAR. You specify a filter mapping list for a WAR in the deploytool Filter Mapping inspector (see Filter Mapping).

Table 15 contains the filter mapping list for the Duke's Bookstore application. The filters are matched by URL mapping and each filter chain contains only one filter.

Table 15 Duke's Bookstore Filter Mapping List 
URL Pattern
Filter
/enter
HitCounterFilter
/receipt
OrderFilter

Home
TOC
PREV TOC NEXT Search
Feedback