001/* 002 * Copyright 2002-2016 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; 022 023import javax.servlet.Filter; 024import javax.servlet.FilterChain; 025import javax.servlet.FilterConfig; 026import javax.servlet.ServletException; 027import javax.servlet.ServletRequest; 028import javax.servlet.ServletResponse; 029 030/** 031 * A generic composite servlet {@link Filter} that just delegates its behavior 032 * to a chain (list) of user-supplied filters, achieving the functionality of a 033 * {@link FilterChain}, but conveniently using only {@link Filter} instances. 034 * 035 * <p>This is useful for filters that require dependency injection, and can 036 * therefore be set up in a Spring application context. Typically, this 037 * composite would be used in conjunction with {@link DelegatingFilterProxy}, 038 * so that it can be declared in Spring but applied to a servlet context. 039 * 040 * @author Dave Syer 041 * @since 3.1 042 */ 043public class CompositeFilter implements Filter { 044 045 private List<? extends Filter> filters = new ArrayList<>(); 046 047 048 public void setFilters(List<? extends Filter> filters) { 049 this.filters = new ArrayList<>(filters); 050 } 051 052 053 /** 054 * Initialize all the filters, calling each one's init method in turn in the order supplied. 055 * @see Filter#init(FilterConfig) 056 */ 057 @Override 058 public void init(FilterConfig config) throws ServletException { 059 for (Filter filter : this.filters) { 060 filter.init(config); 061 } 062 } 063 064 /** 065 * Forms a temporary chain from the list of delegate filters supplied ({@link #setFilters}) 066 * and executes them in order. Each filter delegates to the next one in the list, achieving 067 * the normal behavior of a {@link FilterChain}, despite the fact that this is a {@link Filter}. 068 * @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain) 069 */ 070 @Override 071 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 072 throws IOException, ServletException { 073 074 new VirtualFilterChain(chain, this.filters).doFilter(request, response); 075 } 076 077 /** 078 * Clean up all the filters supplied, calling each one's destroy method in turn, but in reverse order. 079 * @see Filter#init(FilterConfig) 080 */ 081 @Override 082 public void destroy() { 083 for (int i = this.filters.size(); i-- > 0;) { 084 Filter filter = this.filters.get(i); 085 filter.destroy(); 086 } 087 } 088 089 090 private static class VirtualFilterChain implements FilterChain { 091 092 private final FilterChain originalChain; 093 094 private final List<? extends Filter> additionalFilters; 095 096 private int currentPosition = 0; 097 098 public VirtualFilterChain(FilterChain chain, List<? extends Filter> additionalFilters) { 099 this.originalChain = chain; 100 this.additionalFilters = additionalFilters; 101 } 102 103 @Override 104 public void doFilter(final ServletRequest request, final ServletResponse response) 105 throws IOException, ServletException { 106 107 if (this.currentPosition == this.additionalFilters.size()) { 108 this.originalChain.doFilter(request, response); 109 } 110 else { 111 this.currentPosition++; 112 Filter nextFilter = this.additionalFilters.get(this.currentPosition - 1); 113 nextFilter.doFilter(request, response, this); 114 } 115 } 116 } 117 118}