001/*
002 * Copyright 2002-2017 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.mock.web;
018
019import java.io.IOException;
020import java.util.Arrays;
021import java.util.Collections;
022import java.util.Iterator;
023import java.util.List;
024import javax.servlet.Filter;
025import javax.servlet.FilterChain;
026import javax.servlet.FilterConfig;
027import javax.servlet.Servlet;
028import javax.servlet.ServletException;
029import javax.servlet.ServletRequest;
030import javax.servlet.ServletResponse;
031
032import org.springframework.util.Assert;
033import org.springframework.util.ObjectUtils;
034
035/**
036 * Mock implementation of the {@link javax.servlet.FilterChain} interface.
037 *
038 * <p>A {@link MockFilterChain} can be configured with one or more filters and a
039 * Servlet to invoke. The first time the chain is called, it invokes all filters
040 * and the Servlet, and saves the request and response. Subsequent invocations
041 * raise an {@link IllegalStateException} unless {@link #reset()} is called.
042 *
043 * @author Juergen Hoeller
044 * @author Rob Winch
045 * @author Rossen Stoyanchev
046 *
047 * @since 2.0.3
048 * @see MockFilterConfig
049 * @see PassThroughFilterChain
050 */
051public class MockFilterChain implements FilterChain {
052
053        private ServletRequest request;
054
055        private ServletResponse response;
056
057        private final List<Filter> filters;
058
059        private Iterator<Filter> iterator;
060
061
062        /**
063         * Register a single do-nothing {@link Filter} implementation. The first
064         * invocation saves the request and response. Subsequent invocations raise
065         * an {@link IllegalStateException} unless {@link #reset()} is called.
066         */
067        public MockFilterChain() {
068                this.filters = Collections.emptyList();
069        }
070
071        /**
072         * Create a FilterChain with a Servlet.
073         * @param servlet the Servlet to invoke
074         * @since 3.2
075         */
076        public MockFilterChain(Servlet servlet) {
077                this.filters = initFilterList(servlet);
078        }
079
080        /**
081         * Create a {@code FilterChain} with Filter's and a Servlet.
082         * @param servlet the {@link Servlet} to invoke in this {@link FilterChain}
083         * @param filters the {@link Filter}'s to invoke in this {@link FilterChain}
084         * @since 3.2
085         */
086        public MockFilterChain(Servlet servlet, Filter... filters) {
087                Assert.notNull(filters, "filters cannot be null");
088                Assert.noNullElements(filters, "filters cannot contain null values");
089                this.filters = initFilterList(servlet, filters);
090        }
091
092        private static List<Filter> initFilterList(Servlet servlet, Filter... filters) {
093                Filter[] allFilters = ObjectUtils.addObjectToArray(filters, new ServletFilterProxy(servlet));
094                return Arrays.asList(allFilters);
095        }
096
097        /**
098         * Return the request that {@link #doFilter} has been called with.
099         */
100        public ServletRequest getRequest() {
101                return this.request;
102        }
103
104        /**
105         * Return the response that {@link #doFilter} has been called with.
106         */
107        public ServletResponse getResponse() {
108                return this.response;
109        }
110
111        /**
112         * Invoke registered {@link Filter}s and/or {@link Servlet} also saving the
113         * request and response.
114         */
115        @Override
116        public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
117                Assert.notNull(request, "Request must not be null");
118                Assert.notNull(response, "Response must not be null");
119                Assert.state(this.request == null, "This FilterChain has already been called!");
120
121                if (this.iterator == null) {
122                        this.iterator = this.filters.iterator();
123                }
124
125                if (this.iterator.hasNext()) {
126                        Filter nextFilter = this.iterator.next();
127                        nextFilter.doFilter(request, response, this);
128                }
129
130                this.request = request;
131                this.response = response;
132        }
133
134        /**
135         * Reset the {@link MockFilterChain} allowing it to be invoked again.
136         */
137        public void reset() {
138                this.request = null;
139                this.response = null;
140                this.iterator = null;
141        }
142
143
144        /**
145         * A filter that simply delegates to a Servlet.
146         */
147        private static class ServletFilterProxy implements Filter {
148
149                private final Servlet delegateServlet;
150
151                private ServletFilterProxy(Servlet servlet) {
152                        Assert.notNull(servlet, "servlet cannot be null");
153                        this.delegateServlet = servlet;
154                }
155
156                @Override
157                public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
158                                throws IOException, ServletException {
159
160                        this.delegateServlet.service(request, response);
161                }
162
163                @Override
164                public void init(FilterConfig filterConfig) throws ServletException {
165                }
166
167                @Override
168                public void destroy() {
169                }
170
171                @Override
172                public String toString() {
173                        return this.delegateServlet.toString();
174                }
175        }
176
177}