Introduction
There are often requirements for a Web application to provide the functionalities that are either for an administrative purpose, for a special group of users, or simply to improve the overall response time for a specific type of response for the application. For a Java-based Web application, we have the facility of using filters and wrappers to serve these functionalities. Filters and wrappers are powerful features provided by servlet API to implement cross-cutting features such as logging, auditing, keeping track of user activities, zipping a large file before sending it to a user, or creating a different response altogether.
In this blog article, I am going to discuss the definition of a filter and how we can use the filter API in an application.
What are Filters?
Filters are Java components available in servlet API that are used to intercept the request and response generated by a servlet. The functionality and implementation of a filter are very similar to that of a servlet.
How to implement a Filter?
There is a single interface called Filter available in the servlet API to implement a filter. This interface declares the lifecycle methods for a filter. There are three lifecycle methods called init(), doFilter() and destroy() that we need to implement in a filter class as demonstrated in the following code snippet.
package test;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class TestFilter implements Filter {
private FilterConfig config;
public void init(FilterConfig config) throws ServletException {
this.config = config;
}
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws ServletException, IOException {
HttpServletRequest req = (HttpServletRequest) request;
…
// Call the next filter in the chain
chain.doFilter(request, response);
// Process the response if required
…
}
public void destroy() {
}
}
As we can see, the implementation of a filter is very similar to that of a servlet, with a minor difference in the execution of a filter. The fact is, we can have multiple filters associated for a given URL pattern or a servlet, and the container then chooses which filter to execute and in which order based on the configuration provided in the deployment descriptor. There are certain rules followed by the container that decides which filter will be executed next.
The call to chain.doFilter() in the code snippet above is an instruction to the container that this filter has finished its work and the request is ready to be passed on to the next filter in the chain. If there is no other filter remaining in the chain, then the container will pass on the request to the actual servlet. Similarly, when the servlet finishes with the response, the container will pass on the response to the last filter called and the execution begins from the statement after the chain.doFilter() call.
We need to register and map filters for a URL pattern or for a servlet in a deployment descriptor as illustrated in the following code snippet. We will map TestFilter declared above with two more filters, Filter1 and Filter2, with the ‘*.do’ URL pattern, and TestFilterServlet to understand how multiple filters are mapped and executed.
<filter> <filter-name>TestFilter</filter-name> <filter-class>test.TestFilter</filter-class> </filter> <filter> <filter-name>Filter1</filter-name> <filter-class>test.Filter1</filter-class> </filter> <filter> <filter-name>Filter2</filter-name> <filter-class>test.Filter2</filter-class> </filter> <filter-mapping> <filter-name>TestFilter</filter-name> <url-pattern>*.do</url-pattern> </filter-mapping> <filter-mapping> <filter-name>Filter2</filter-name> <servlet-name>FilterTestServlet</servlet-name> </filter-mapping> <filter-mapping> <filter-name>Filter1</filter-name> <url-pattern>*.do</url-pattern> </filter-mapping>
Here, we have configured TestFilter and Filter1 for a URL pattern ‘*.do’ and Filter2 specifically for FilterTestServlet. When more than one filter is mapped to a single URL pattern or a servlet, the container executes all URL related filters first in the order specified in the deployment descriptor, followed by all the filters mapped to a specific servlet in the order specified.
In the example above, if the request is for a FilterTestServlet and if it ends with a ‘.do’ extension, then the container executes TestFilter first and calls its doFilter() method. When the execution reaches the chain.doFilter()call in TestFilter, the container executes Filter1, because it too matches the ‘.do’ extension. Similarly, when the execution reaches the chain.doFilter() call in Filter1, the container executes Filter2, because it is declared as a filter for FilterTestServlet. When the container encounters the chain.doFilter() call in Filter2 and it identifies that there are no more filters in the chain to be executed, the request is passed on to the actual servlet TestFilterServlet. When the TestFilterServlet finishes, the container will execute the filters in the LIFO order. In our example, the container will execute Filter2, Filter1, and TestFilter in that order. Remember, this time the execution begins at the statement that follows the chain.doFilter() call in the filter.
Since filters are configured for a URL or a resource through a deployment descriptor, it is very easy to change the execution order, or to add new filters and remove the existing ones without modifying the actual servlet code. Using filters, you can easily add common functionalities such as logging, auditing, security, user tracking, etc. across the application without touching the servlet code.
Conclusion
There is more to the filter API to modify the request and response objects before the actual request is passed onto the servlet or the actual response is passed onto the client. We have wrapper classes called ServletRequestWrapper, HttpServletRequestWrapper, ServletResponseWrapper, and HttpServletResponseWrapper in servlet APIs using which we can provide custom request and response objects. For detailed explanation of filters and wrappers, I suggest reading The Essentials of Filters provided by Oracle.



