I just released the Master Class of "Learn Spring Security":
1. Overview
In this tutorial, we’re going to introduce the Intercepting Filter Pattern presentation-tier Core J2EE Pattern.
This is the second tutorial in our Pattern Series and a follow-up to the Front Controller Pattern guide which can be found here.
Intercepting Filters are filters that trigger actions before or after an incoming request is processed by a handler.
Intercepting filters represents centralized components in a web application, common to all requests and extensible without affecting existing handlers.
2. Use Cases
Let’s extend the example from the previous guide and implement an authentication mechanism, request logging, and a visitor counter. In addition, we want the ability to deliver our pages in various different encoding.
All these are use cases for intercepting filters because they are common to all requests and should be independent of the handlers.
3. Filter Strategies
Let us introduce different filter strategies and exemplary use cases. To run the code with Jetty Servlet container, simply execute:
$> mvn install jetty:run
3.1. Custom Filter Strategy
The custom filter strategy is used in every use case that requires an ordered processing of requests, in the meaning of one filter is based on the results of a previous filter in an execution chain.
These chains will be created by implementing the FilterChain interface and registering various Filter classes with it.
When using multiple filter chains with different concerns, you can join them together in a filter manager:
In our example, the visitor counter is working by counting unique usernames from logged-in users, which means it’s based on the result of the authentication filter, therefore, both filters have to be chained.
Let’s implement this filter chain.
First, we’ll create an authentication filter which checks if the session exists for a set ‘username’ attribute and issue a login procedure if not:
public class AuthenticationFilter implements Filter { ... @Override public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain) { HttpServletRequest httpServletRequest = (HttpServletRequest) request; HttpServletResponse httpServletResponse = (HttpServletResponse) response; HttpSession session = httpServletRequest.getSession(false); if (session == null || session.getAttribute("username") == null) { FrontCommand command = new LoginCommand(); command.init(httpServletRequest, httpServletResponse); command.process(); } else { chain.doFilter(request, response); } } ... }
Now let’s create the visitor counter. This filter maintains a HashSet of unique usernames and adds a ‘counter’ attribute to the request:
public class VisitorCounterFilter implements Filter { private static Set<String> users = new HashSet<>(); ... @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { HttpSession session = ((HttpServletRequest) request).getSession(false); Optional.ofNullable(session.getAttribute("username")) .map(Object::toString) .ifPresent(users::add); request.setAttribute("counter", users.size()); chain.doFilter(request, response); } ... }
Next, we’ll implement a FilterChain that iterates registered filters and executes doFilter method:
public class FilterChainImpl implements FilterChain { private Iterator<Filter> filters; public FilterChainImpl(Filter... filters) { this.filters = Arrays.asList(filters).iterator(); } @Override public void doFilter(ServletRequest request, ServletResponse response) { if (filters.hasNext()) { Filter filter = filters.next(); filter.doFilter(request, response, this); } } }
To wire our components together, let’s create a simple static manager which is responsible for instantiating filter chains, registering its filters, and initiate it:
public class FilterManager { public static void process(HttpServletRequest request, HttpServletResponse response, OnIntercept callback) { FilterChain filterChain = new FilterChainImpl( new AuthenticationFilter(callback), new VisitorCounterFilter()); filterChain.doFilter(request, response); } }
As the last step we’ll have to call our FilterManager as common part of the request processing sequence from within our FrontCommand:
public abstract class FrontCommand { ... public void process() { FilterManager.process(request, response); } ... }
3.2. Base Filter Strategy
In this section, we’ll present the Base Filter Strategy, with which a common superclass is used for all implemented filters.
This strategy plays nicely together with the custom strategy from the previous section or with the Standard Filter Strategy that we’ll introduce in the next section.
The abstract base class can be used to apply custom behavior that belongs to a filter chain. We’ll use it in our example to reduce boilerplate code related to filter configuration and debug logging:
public abstract class BaseFilter implements Filter { private Logger log = LoggerFactory.getLogger(BaseFilter.class); protected FilterConfig filterConfig; @Override public void init(FilterConfig filterConfig) throws ServletException { log.info("Initialize filter: {}", getClass().getSimpleName()); this.filterConfig = filterConfig; } @Override public void destroy() { log.info("Destroy filter: {}", getClass().getSimpleName()); } }
Let’s extend this base class to create a request logging filter, which will be integrated into the next section:
public class LoggingFilter extends BaseFilter { private static final Logger log = LoggerFactory.getLogger(LoggingFilter.class); @Override public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain) { chain.doFilter(request, response); HttpServletRequest httpServletRequest = (HttpServletRequest) request; String username = Optional .ofNullable(httpServletRequest.getAttribute("username")) .map(Object::toString) .orElse("guest"); log.info( "Request from '{}@{}': {}?{}", username, request.getRemoteAddr(), httpServletRequest.getRequestURI(), request.getParameterMap()); } }
3.3. Standard Filter Strategy
A more flexible way of applying filters is to implement the Standard Filter Strategy. This can be done by declaring filters in a deployment descriptor or, since Servlet specification 3.0, by annotation.
The standard filter strategy allows to plug-in new filters into a default chain without having an explicitly defined filter manager:
Note that the order, in which the filters get applied, cannot be specified via annotation. If you need an ordered execution, you have to stick with a deployment descriptor or implement a custom filter strategy.
Let’s implement an annotation driven encoding filter that also uses the base filter strategy:
@WebFilter(servletNames = {"intercepting-filter"}, initParams = {@WebInitParam(name = "encoding", value = "UTF-8")}) public class EncodingFilter extends BaseFilter { private String encoding; @Override public void init(FilterConfig filterConfig) throws ServletException { super.init(filterConfig); this.encoding = filterConfig.getInitParameter("encoding"); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { String encoding = Optional .ofNullable(request.getParameter("encoding")) .orElse(this.encoding); response.setCharacterEncoding(encoding); chain.doFilter(request, response); } }
In a Servlet scenario with having a deployment descriptor, our web.xml would contain these extra declarations:
<filter> <filter-name>encoding-filter</filter-name> <filter-class> com.baeldung.patterns.intercepting.filter.filters.EncodingFilter </filter-class> </filter> <filter-mapping> <filter-name>encoding-filter</filter-name> <servlet-name>intercepting-filter</servlet-name> </filter-mapping>
Let’s pick-up our logging filter and annotate it too, in order to get used by the Servlet:
@WebFilter(servletNames = "intercepting-filter") public class LoggingFilter extends BaseFilter { ... }
3.4. Template Filter Strategy
The Template Filter Strategy is pretty much the same as the base filter strategy, except that it uses template methods declared in the base class that must be overridden in implementations:
Let’s create a base filter class with two abstract filter methods that get called before and after further processing.
Since this strategy is less common and we don’t use it in our example, a concrete implementation and use case is up to your imagination:
public abstract class TemplateFilter extends BaseFilter { protected abstract void preFilter(HttpServletRequest request, HttpServletResponse response); protected abstract void postFilter(HttpServletRequest request, HttpServletResponse response); @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { HttpServletRequest httpServletRequest = (HttpServletRequest) request; HttpServletResponse httpServletResponse = (HttpServletResponse) response; preFilter(httpServletRequest, httpServletResponse); chain.doFilter(request, response); postFilter(httpServletRequest, httpServletResponse); } }
4. Conclusion
The Intercepting Filter Pattern captures cross-cutting concerns that can evolve independently of the business logic. From the perspective of business operations, filters are executed as a chain of pre or post actions.
As we’ve seen so far, the Intercepting Filter Pattern can be implemented using different strategies. In a ‘real world’ applications these different approaches can be combined.
As usual, you’ll find the sources on GitHub.