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}