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}