Home TOC |
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:
- Modify the request headers and data by providing a customized version of the request.
- Modify the response headers data by providing a customized version of the response.
- Interact with external resources.
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:
- Examine the request headers
- Wrap the request object with a customized implementation of ServletRequest or HttpServletRequest if it wishes to modify request headers or data.
- Wrap the response object with a customized implementation of ServletResponse or HttpServletResponse if it wishes to modify response headers or data.
- Invoke the next entity in the filter chain. If the current filter is the last filter in the chain that ends with the target web component or static resource, the next entity is the resource at the end of the chain; otherwise, it is the next filter that was configured in the WAR. It invokes the next entity by calling the doFilter method on the chain object (passing in the request and response it was called with, or the wrapped versions it may have created). Alternatively, it can choose to block the request by not making the call to invoke the next entity. In the latter case, the filter is responsible for filling out the response.
- Examine response headers after it has invoked the next filter in the chain.
- Throw an exception to indicate an error in processing.
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.
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 |
Search
Feedback |