ContextSystem handles web requests. Request processing involves handling a variable number of system services. ProblemPre-processing and post-processing of a client web request and response are required. Forces
SolutionCreate pluggable filters to process common services in a standard manner without requiring changes to core request processing code. The filters intercept incoming requests and outgoing responses, allowing pre and post-processing. We are able to add and remove these filters unobtrusively, without requiring changes to our existing code. We are able, in effect, to "decorate" our main processing with a variety of common services, such as security, logging, debugging, and so forth. These filters are components that are independent of the main application code, and they may be added or removed declaratively. For example, a deployment configuration file may be modified in order to set up a chain of filters. The same configuration file might include a mapping of specific URLs to this filter chain. When a client requests a resource that matches this configured URL mapping, the filters in the chain are each processed in order before the requested target resource is invoked. StructureThe following class diagram represents the Decorating Filter pattern:
Participants & ResponsibilitiesThe following sequence diagram represents the Decorating Filter pattern: click to enlargeStrategiesDynamic Filter StrategyFilter is implemented via a custom strategy defined by the developer. This is less flexible and powerful than the 'Declared Filter Strategy', which is preferred. The developer would use the Decorator [GoF] pattern to programmatically wrap filters around the core request processing logic. There may be a debugging filter which wraps an authentication filter. Here is an example of how one might create this mechanism programmatically: public class DebuggingFilter implements Processor private Processor target; public void execute(ServletRequest req, } public class CoreProcessing implements Processor private Processor target; public CoreProcessing (Processor myTarget) public void execute(ServletRequest req, } In the Servlet controller we might delegate to a method called 'processRequest' in order to handle requests: public void processRequest(ServletRequest req, Processor processors = new DebuggingFilter( processors.execute(req, res); //Then dispatch to next resource, dispatcher.dispatch(req, res); } Assuming that each processing component writes to stdout when it is executed, possible execution output is as follows: Debugging filter pre-processing completed at this point... A chain of processors is executed in order. Each processor except for the last one in the chain is considered a filter. The final processor component is where we encapsulate the core processing we want to complete for each request. Given this design, we will need to change the code in the CoreProcessing class, as well as in any filter classes when we want to modify how we handle requests. This strategy does not allow us to create filters that are as flexible or as powerful as we would like. For one, filters are added and removed programmatically. More importantly, we have no way to wrap the request and response objects. The preferred strategy, the 'Declared Filter Strategy' provides solutions to these issues.
Declared Filter StrategyFilters are controlled declaratively, using a property file or a descriptor, such as that supported by the Servlet specification version 2.3. This is the simplest and most elegant strategy, and is supported in version 2.3 of the Servlet specification. Vendor support for this version of the specification should be widely available by approximately late 2001. One could write portions of the infrastructure to support this strategy oneself, but it would not provide the same power and flexibility and would be nonstandard. Such an approach is described in the 'Dynamic Filter Strategy'. The Servlet specification, version 2.3 will include a standard mechanism to build, add, and remove filters unobtrusively. A filter is built based on specified interfaces, and added/removed in a declarative manner, by modifying the deployment descriptor for a web application. Here is an example of a possible filter, based on the final draft of the Servlet Specification, version 2.3: public final class DebuggingFilter implements Filter { public void doFilter(ServletRequest req, } Here is an excerpt of a possible filter configuration from a deployment descriptor for a web application: <filter-mapping> In this case, our Debugging Filter will intercept control when a client makes a request to the ControllerServlet. When a client attempts to invoke the ControllerServlet, the container checks to see what filters are configured to wrap the target resource, finds the Debugging Filter, and vectors control to this filter by invoking its doFilter() method. After completing its processing, the filter passes along control to the next resource in the chain. Once again the container checks to see what filters are configured to wrap the target resource. If there was another filter configured in the deployment descriptor, this filter would receive control at this point. Since there is only one filter in the chain, the next resource to receive control will be the actual target resource, in this case the ControllerServlet. Filters, as supported in version 2.3 of the Servlet specification also support wrapping the request and response objects. This feature provides for a much more powerful mechanism than can be built using the custom implementation suggested by the 'Dynamic Filter Strategy'. Of course, a hybrid approach combining the two strategies could be custom built, as well, but would still lack the power of the Declared filter strategy as supported by the Servlet specification version 2.3.
Consequences
Sharing information between filters can be inefficient, since by definition each filter is loosely coupled. If large amounts of information must be shared between filters, this approach may prove costly.
Related Patterns
The Front Controller solves some similar problems, but is often more powerful when used in combination with DecoratingFilter. The DecoratingFilter pattern is related to the decorator pattern, which provides for dynamically pluggable wrappers. The DecoratingFilter pattern is related to the pipes and filters pattern. (c) 2000-2001 Sun Microsystems, Inc. All Rights Reserved. Version 1.0 Beta | |||
|
| ||||||||||||