The presentation-tier request handling mechanism receives many different types of requests, which require varied types of processing. Some requests are simply forwarded to the appropriate handler component, while other requests must be modified, audited, or uncompressed before being further processed.
One of the best example for Intercepting Filter Pattern is Spring Security's DelegatingFilterProxy , which will intercept the HTTP request and do the authentication check. Spring security build on chain of filters.
Let's see how the Intercepting Filter Pattern solve the problem with examples. This pattern is divided into a number of sections for simplicity like problem, forces,solution,implementation etc.
Table of contents |
---|
Problem |
Forces |
Solution |
Explanation |
Structure - Class Diagram, Sequence Diagram |
Participants and Responsibilities |
Implementation |
Consequences |
Applicability |
Real world examples |
References |
Problem
(Problem section describes the design issues faced by the developer)
Pre-processing and post-processing of a client Web request and response are required. When a request enters a Web application, it often must pass several entrance tests prior to the main processing stage. For example,
- Has the client been authenticated?
- Does the client have a valid session?
- Is the client's IP address from a trusted network?
- Does the request path violate any constraints?
- What encoding does the client use to send the data?
- Do we support the browser type of the client? Some of these checks are tests, resulting in a yes or no answer that determines whether processing will continue. Other checks manipulate the incoming data stream into a form suitable for processing. You want to intercept and manipulate a request and a response before and after the request is processed.
Forces
(This section describes Lists the reasons and motivations that affect the problem and the solution. The list of forces highlights the reasons why one might choose to use the pattern and provides a justification for using the pattern)
- You want centralized, common processing across requests, such as checking the data-encoding scheme of each request, logging information about each request, or compressing an outgoing response.
- You want pre and postprocessing components loosely coupled with core request-handling services to facilitate unobtrusive addition and removal.
- You want pre and postprocessing components independent of each other and self contained to facilitate reuse.
Solution
(Here solution section describes the solution approach briefly and the solution elements in detail)
Use an Intercepting Filter as a pluggable filter to pre and postprocess requests and responses. A filter manager combines loosely coupled filters in a chain, delegating control to the appropriate filter. In this way, you can add, remove, and combine these filters in various ways without changing existing code.
Structure
Class Diagram
Sequence Diagram
Participants
Filter - Filter which will performs certain task prior or after execution of request by request handler.
Filter Chain - Filter Chain carries multiple filters and help to execute them in defined order on target.
Target - Target object is the request handler
Filter Manager - Filter Manager manages the filters and Filter Chain.
Client - Client is the object who sends request to the Target object.
Implementation
Step 1 : Create Filter interface.
public interface Filter {
public void execute(String request);
}
Step 2 : Create concrete filters - AuthenticationFilter, DebugFilter.
public class AuthenticationFilter implements Filter {
public void execute(String request){
System.out.println("Authenticating request: " + request);
}
}
public class DebugFilter implements Filter {
public void execute(String request){
System.out.println("request log: " + request);
}
}
Step 3 : Create Target.
public class Target {
public void execute(String request){
System.out.println("Executing request: " + request);
}
}
Step 4 : Create FilterChain.
import java.util.ArrayList;
import java.util.List;
public class FilterChain {
private List<Filter> filters = new ArrayList<Filter>();
private Target target;
public void addFilter(Filter filter){
filters.add(filter);
}
public void execute(String request){
for (Filter filter : filters) {
filter.execute(request);
}
target.execute(request);
}
public void setTarget(Target target){
this.target = target;
}
}
Step 5 : Create FilterManager.
public class FilterManager {
FilterChain filterChain;
public FilterManager(Target target){
filterChain = new FilterChain();
filterChain.setTarget(target);
}
public void setFilter(Filter filter){
filterChain.addFilter(filter);
}
public void filterRequest(String request){
filterChain.execute(request);
}
}
Step 6 : Create Client.
public class Client {
FilterManager filterManager;
public void setFilterManager(FilterManager filterManager){
this.filterManager = filterManager;
}
public void sendRequest(String request){
filterManager.filterRequest(request);
}
}
Step 7 : Use the Client to demonstrate Intercepting Filter Design Pattern.
public class InterceptingFilterDemo {
public static void main(String[] args) {
FilterManager filterManager = new FilterManager(new Target());
filterManager.setFilter(new AuthenticationFilter());
filterManager.setFilter(new DebugFilter());
Client client = new Client();
client.setFilterManager(filterManager);
client.sendRequest("HOME");
}
}
Step 8 : Verify the output.
Authenticating request: HOME
request log: HOME
Executing request: HOME
Standard Filter Strategy
The servlet 2.3 specification includes a standard mechanism for building filter chains and unobtrusively adding and removing filters from those chains.
Filters are built around interfaces, and added or removed in a declarative manner by modifying the deployment descriptor for a Web application.
javax.servlet.Filter(interface)
A filter is an object that performs filtering tasks on either the request to a resource (a servlet or static content), or on the response from a resource, or both. Filters perform filtering in the doFilter method.
Every Filter has access to a FilterConfig object from which it can obtain its initialization parameters, a reference to the ServletContext which it can use, for example, to load resources needed for filtering tasks.
Filters are configured in the deployment descriptor of a web application.
Examples that have been identified for this design are
- Authentication Filters
- Logging and Auditing Filters
- Image conversion Filters
- Data compression Filters
- Encryption Filters
- Tokenizing Filters
- Filters that trigger resource access events
- XSL/T filters
- Mime-type chain Filter
javax.servlet.FilterChain(interface)
A FilterChain is an object provided by the servlet container to the developer giving a view into the invocation chain of a filtered request for a resource.
Filters use the FilterChain to invoke the next filter in the chain, or if the calling filter is the last filter in the chain, to invoke the resource at the end of the chain.
Filters use the FilterChain to invoke the next filter in the chain, or if the calling filter is the last filter in the chain, to invoke the resource at the end of the chain.
Examples
Our example for this strategy will be to create a filter that preprocesses requests of any encoding type such that each request may be handled similarly in our core request handling code. Why might this be necessary? HTML forms that include a file upload use a different encoding type than that of most forms.
Thus, form data that accompanies the upload is not available via simplegetParameter()invocations. So, we create two filters that preprocess requests, translating all encoding types into a single consistent format. The format we choose is to have all form data available as request attributes.
Thus, form data that accompanies the upload is not available via simplegetParameter()invocations. So, we create two filters that preprocess requests, translating all encoding types into a single consistent format. The format we choose is to have all form data available as request attributes.
Example 1 - Base Filter - Standard Filter Strategy
public class BaseEncodeFilter implements
javax.servlet.Filter {
private javax.servlet.FilterConfig myFilterConfig;
public BaseEncodeFilter() { }
public void doFilter(
javax.servlet.ServletRequest servletRequest,
javax.servlet.ServletResponse servletResponse,
javax.servlet.FilterChain filterChain)
throws java.io.IOException,
javax.servlet.ServletException {
filterChain.doFilter(servletRequest,
servletResponse);
}
public javax.servlet.FilterConfig getFilterConfig()
{
return myFilterConfig;
}
public void setFilterConfig(
javax.servlet.FilterConfig filterConfig) {
myFilterConfig = filterConfig;
}
}
public class StandardEncodeFilter
extends BaseEncodeFilter {
// Creates new StandardEncodeFilter
public StandardEncodeFilter() { }
public void doFilter(javax.servlet.ServletRequest
servletRequest,javax.servlet.ServletResponse
servletResponse,javax.servlet.FilterChain
filterChain)
throws java.io.IOException,
javax.servlet.ServletException {
String contentType =
servletRequest.getContentType();
if ((contentType == null) ||
contentType.equalsIgnoreCase(
"application/x-www-form-urlencoded")) {
translateParamsToAttributes(servletRequest,
servletResponse);
}
filterChain.doFilter(servletRequest,
servletResponse);
}
private void translateParamsToAttributes(
ServletRequest request, ServletResponse response)
{
Enumeration paramNames =
request.getParameterNames();
while (paramNames.hasMoreElements()) {
String paramName = (String)
paramNames.nextElement();
String [] values;
values = request.getParameterValues(paramName);
System.err.println("paramName = " + paramName);
if (values.length == 1)
request.setAttribute(paramName, values[0]);
else
request.setAttribute(paramName, values);
}
}
}
Example 2- MultipartEncodeFilter - Standard Filter Strategy
public class MultipartEncodeFilter extends
BaseEncodeFilter {
public MultipartEncodeFilter() { }
public void doFilter(javax.servlet.ServletRequest
servletRequest, javax.servlet.ServletResponse
servletResponse,javax.servlet.FilterChain
filterChain)
throws java.io.IOException,
javax.servlet.ServletException {
String contentType =
servletRequest.getContentType();
// Only filter this request if it is multipart
// encoding
if (contentType.startsWith(
"multipart/form-data")){
try {
String uploadFolder =
getFilterConfig().getInitParameter(
"UploadFolder");
if (uploadFolder == null) uploadFolder = ".";
/** The MultipartRequest class is:
* Copyright (C) 2001 by Jason Hunter
* <jhunter@servlets.com>. All rights reserved.
**/
MultipartRequest multi = new
MultipartRequest(servletRequest,
uploadFolder,
1 * 1024 * 1024 );
Enumeration params =
multi.getParameterNames();
while (params.hasMoreElements()) {
String name = (String)params.nextElement();
String value = multi.getParameter(name);
servletRequest.setAttribute(name, value);
}
Enumeration files = multi.getFileNames();
while (files.hasMoreElements()) {
String name = (String)files.nextElement();
String filename = multi.getFilesystemName(name);
String type = multi.getContentType(name);
File f = multi.getFile(name);
// At this point, do something with the
// file, as necessary
}
}
catch (IOException e)
{
LogManager.logMessage(
"error reading or saving file"+ e);
}
} // end if
filterChain.doFilter(servletRequest,
servletResponse);
} // end method doFilter()
}
Deployment Descriptor - Standard Filter Strategy
<filter>
<filter-name>StandardEncodeFilter</filter-name>
<display-name>StandardEncodeFilter</display-name>
<description></description>
<filter-class> corepatterns.filters.encodefilter.
StandardEncodeFilter</filter-class>
</filter>
<filter>
<filter-name>MultipartEncodeFilter</filter-name>
<display-name>MultipartEncodeFilter</display-name>
<description></description>
<filter-class>corepatterns.filters.encodefilter.
MultipartEncodeFilter</filter-class>
<init-param>
<param-name>UploadFolder</param-name>
<param-value>/home/files</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>StandardEncodeFilter</filter-name>
<url-pattern>/EncodeTestServlet</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>MultipartEncodeFilter</filter-name>
<url-pattern>/EncodeTestServlet</url-pattern>
</filter-mapping>
The source code of Intercepting Filter Design Pattern is available on GitHub
Consequences
- Centralizes control with loosely coupled handlers
- Improves reusability
- Declarative and flexible configuration
- Information sharing is inefficient
Applicability
Use the Intercepting Filter pattern when
- A system uses pre-processing or post-processing requests
- A system should do the authentication/ authorization/ logging or tracking of request and then pass the requests to corresponding handlers
- You want a modular approach to configuring pre-processing and post-processing schemes
Real world examples
- javax.servlet.FilterChain and javax.servlet.Filter
- Spring - HandlerInterceptor
- DelegatingFilterProxy
References
- J2EE Design Patterns
- Core J2EE Patterns: Best Practices and Design Strategies
- http://www.corej2eepatterns.com
Related Presentation Tier Posts
- Intercepting Filter Design Pattern in Java
- Front Controller Design Pattern in Java
- Application Controller Design Pattern in Java
- View Helper Design Pattern in Java
- Composite View Design Pattern in Java
- Context Object Design Pattern in Java
Comments
Post a Comment
Leave Comment