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