All About Filters

Filters are java components you can use to intercept and process requests before they are sent to the servlet, or to process responses after the servlet has completed (before the response is sent to the client). They are one of the most powerful tools you can use in a web application and are well-suited to providing functionality that spans many use cases or requests eg. security checks, changing request headers and bodies, url rewriting, request logging, compressing response streams, altering response streams.

Notable points about filters:

  1. Filters are an example of the Intercepting Filter Pattern
  2. There is only one filter interface – Filter – when we talk about ‘request filters’ or ‘response filters’ we are talking about how you use the filter
  3. The Container decides when to invoke your filters based on how you configure them in the deployment descriptor (web.xml)
  4. A web application can have many filters – which ones will run, and in what order, is determined by how they are configured in the deployment descriptor based on a URL pattern
  5. Filters are designed to be self-contained ie. they should not have dependencies on other filters – however they can be chained to run in a particular order by chaining them togethor. Although thay will not have dependencies on each other, the order that filters run, in a sequence, is often important based on the transformations they carry out
  6. Filters, like servlets, live inside the container and have a lifecycle. When a java class implements the Filter interface, it ceases to be a plain old class and becomes a JEE Filter that is intrinsically connected with the container

A Bare-Bones Servlet

import java.io.*;

import javax.servlet.*;                                    // Filter and FilterChain are in javax.servlet

import javax.servlet.http.HttpServletRequest;

public class SomeFilter implements Filter {       // you must implement the Filter interface

public void init() {    // you must implement init

}

public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws

ServletException, IOException  {

// we are pretty sure we can cast the request and response to their HTTP subtypes

HttpServletRequest httpRequest = (HttpServletRequest) req;

// do the something useful your filter was designed for

chain.doFilter(req, resp);      // call the next filter in the chain, or else if at the end then a servlet

}

public void destroy() {       // must implement destroy

// do any cleanup

}

}

Filters must implement 3 methods:

  1. init() – do any required setup here before filter gets called
  2. doFilter() – called by Container whenever it determines the filter should be applied tot he current request, its where your work is done
  3. destroy() – you can do cleanup here

FilterChain Interface

Filters are not aware of other filters. However, if you want to run them in a sequence then something needs to take care of the order. This is what the FilterChain does – it is driven by the Filter declarations in the deployment descriptor (DD). FilterChain has a doFilter() method – this is different than the doFilter() method of the Filter interface – it figures out which Filter’s doFilter() method needs to be called next in the chain. If we are at the end of the chain then it figures out which servlet’s service() method should be called – assuming the Container is able to map the request URL to a servlet or JSP (the service() method to be called could be in a JSP’s generated servlet).

Declaration and Filter Ordering

When you configure filters in the DD you do 3 things:

  1. declare the filter <filter>
  2. map the filter to the web resource you want to filter, either using a <url-pattern> for multiple resources or <servlet-name> for a single resource
  3. arrange these mappings in an order that creates filter invocation sequences

The containers rules for ordering:

  1. all filters matching the URL pattern are placed in the chain – by the container – in the order in which they are declared in the DD
  2. after this, the container then adds filters to the chain that have a matching <servlet-name>

example:

<filter-mapping>

<filter-name>FilterA</filter-name>

<url-pattern>/Surveys/*</url-pattern>

</filter-mapping>

<filter-mapping>

<filter-name>FilterB</filter-name>

<servlet-name>/Surveys/UserSurvey.action</servlet-name>

</filter-mapping>

<filter-mapping>

<filter-name>FilterC</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

Given the request path: /Surveys/UserSurvey.action

The filters will be added to the chain in the following order:

FilterA – most specific URL match

FilterC – next URL match

FilterB – matching <servlet-name> resources added last

Filters are always invoked in the chain before the servlet is called, and are always re-entered after the servlet completes – before the response is sent to the client.

   on  the  way in  —->              |          on   the  way   out  —->

 

+————+    +————+    +————+    +————+    +————+

|            |    |            |    |            |    |            |    |            |

|  FilterX   |—>|  FilterY   |—>|  Servlet   |—>|  FilterY   |—>|  FilterX   |

|            |    |            |    |            |    |            |    |            |

+————+    +————+    +————+    +————+    +————+

If we consider the structure of the Filter.doFilter() method below, we can see that you have a choice to do something to the request on the ‘way in’, or to the response on the ‘way out’, or both:

public void doFilter(ServletRequest req, ServletResponse resp, …  {

this is where we do work with the request on the way in ie. before the next filter/servlet gets called – IN —>

chain.doFilter(req, resp); // call the next filter/servlet in the chain

<— OUT – this is where we do work with the response on the way out ie. after the servlet, before we return to the client

}

Servlet API 2.4

As of the servlet specification 2.4, you can now map filters to dispatchers as well as direct requests from the client ie. from a forwardincluderequest dispatch, or error handler.

<filter-mapping>

<filter-name>SomeFilter</filter-name>

<url-pattern>*.action</url-pattern>

<dispatcher>REQUEST</dispatcher>

AND / OR

<dispatcher>INCLUDE</dispatcher>

AND / OR

<dispatcher>FORWARD</dispatcher>

AND / OR

<dispatcher>ERROR</dispatcher>

</filter-mapping>

Custom HttpServletResponse

There is 1 problem with what we have discussed so far – the container does not actually enforce the servlet to return its response via the filters, since the servlet is in control of the output stream in the response object once it has it. The servlet has no knowledge that the request was filtered before it received it and so it likewise is unaware that there are filters potentially waiting to do something with the response en route back to the client. To address this we can create our own custom implementation of HttpServletResponse in the filter and pass that to the servlet via the FilterChain.doFilter(request, response) method.

NOTE: Filters pass around ServletRequest/ServletResponse objects (parents of HttpServletRequest/HttpServletResponse) – this is because filters are not designed to be used in just web applications (the environment in which the Http obejcts are relevant) – although they mostly are. Because they are only generally used in web applications we consider it ok to create a custom HttpServletRequest object and pass that on to the servlet.

Now the filter has control of the output stream of the custom response it sent to the servlet. This custom response object delegates most of the calls it receives to the real response object.

Servlet API Wrapper Classes

How do you create a custom HttpServletResponse?

Sun have already done it for you by creating a convenience Wrapper class (HttpServletResponseWrapper) that implements the HttpServletResponse interface and delegates all calls to the underlying real response object created by the container. There are several wrappers in the servlet API – ServletRequestWrapper, HttpServletRequestWrapper, ServletResponseWrapper, HttpServletResponseWrapper. They are convenient since they provide implementations of all the interface methods for you – meaning you dont have to, you simply need to extend the wrapper of the request or response object you wish to use and override the method/s you need to do your custom work.

These wrapper classes not only provide interface implementations, they actually hold a reference to an object of the interface type (wrap it) – they need to do this so that they can delegate the method calls to it. For example, when we wish to create our custom response object to pass on to the servlet (in order that we can keep control of the output stream in the filter), we need this custom response object to be able to delegate (pass-on) any calls made to it to the real response object, since its the real response object that will be returning to the browser ie. the information that the servlet writes to the custom response object needs a way to get passed into the real response object otherwise this information will not make it back to the client – which would make our application pretty useless. So the HttpServletResponseWrapper object holds a reference to the real response object in order that it can Decorate it with the data it receives.

Decorator/Wrapper Design Pattern

The servlet API wrapper classes are an example of using the Decorator – sometimes referred to as Wrapper – pattern. This pattern decorates/wraps one kind of object with an ‘enhanced’ implementation ie. adds new capabilities to everything the original object did. The main characteritic is that the wrapper delegates method invocations to the object it wraps rather than replacing it completely.

class CompressionWrapper extends HttpServletResponseWrapper {

CompressionWrapper(HttpServletResponse response) {

super(response);     // we pass ‘real’ response object to Wrapper constructor which performs Decorator responsibility of storing reference to object being decorated

}

}

public class CompresionFilter implements Filter {

public void doFilter(……….) throws ……. {

CompressionWrapper compWrapper = new CompressionWrapper(response); // we pass real response to the wrapper

}

}

Leave a Reply

Your email address will not be published. Required fields are marked *