001/* 002 * Copyright 2002-2014 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.web.filter; 018 019import java.io.IOException; 020import java.util.ArrayList; 021import java.util.List; 022import javax.servlet.Filter; 023import javax.servlet.FilterChain; 024import javax.servlet.FilterConfig; 025import javax.servlet.ServletException; 026import javax.servlet.ServletRequest; 027import javax.servlet.ServletResponse; 028 029/** 030 * A generic composite servlet {@link Filter} that just delegates its behavior 031 * to a chain (list) of user-supplied filters, achieving the functionality of a 032 * {@link FilterChain}, but conveniently using only {@link Filter} instances. 033 * 034 * <p>This is useful for filters that require dependency injection, and can 035 * therefore be set up in a Spring application context. Typically, this 036 * composite would be used in conjunction with {@link DelegatingFilterProxy}, 037 * so that it can be declared in Spring but applied to a servlet context. 038 * 039 * @author Dave Syer 040 * @since 3.1 041 */ 042public class CompositeFilter implements Filter { 043 044 private List<? extends Filter> filters = new ArrayList<Filter>(); 045 046 047 public void setFilters(List<? extends Filter> filters) { 048 this.filters = new ArrayList<Filter>(filters); 049 } 050 051 052 /** 053 * Initialize all the filters, calling each one's init method in turn in the order supplied. 054 * @see Filter#init(FilterConfig) 055 */ 056 @Override 057 public void init(FilterConfig config) throws ServletException { 058 for (Filter filter : this.filters) { 059 filter.init(config); 060 } 061 } 062 063 /** 064 * Forms a temporary chain from the list of delegate filters supplied ({@link #setFilters}) 065 * and executes them in order. Each filter delegates to the next one in the list, achieving 066 * the normal behavior of a {@link FilterChain}, despite the fact that this is a {@link Filter}. 067 * @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain) 068 */ 069 @Override 070 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 071 throws IOException, ServletException { 072 073 new VirtualFilterChain(chain, this.filters).doFilter(request, response); 074 } 075 076 /** 077 * Clean up all the filters supplied, calling each one's destroy method in turn, but in reverse order. 078 * @see Filter#init(FilterConfig) 079 */ 080 @Override 081 public void destroy() { 082 for (int i = this.filters.size(); i-- > 0;) { 083 Filter filter = this.filters.get(i); 084 filter.destroy(); 085 } 086 } 087 088 089 private static class VirtualFilterChain implements FilterChain { 090 091 private final FilterChain originalChain; 092 093 private final List<? extends Filter> additionalFilters; 094 095 private int currentPosition = 0; 096 097 public VirtualFilterChain(FilterChain chain, List<? extends Filter> additionalFilters) { 098 this.originalChain = chain; 099 this.additionalFilters = additionalFilters; 100 } 101 102 @Override 103 public void doFilter(final ServletRequest request, final ServletResponse response) 104 throws IOException, ServletException { 105 106 if (this.currentPosition == this.additionalFilters.size()) { 107 this.originalChain.doFilter(request, response); 108 } 109 else { 110 this.currentPosition++; 111 Filter nextFilter = this.additionalFilters.get(this.currentPosition - 1); 112 nextFilter.doFilter(request, response, this); 113 } 114 } 115 } 116 117}