/*
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.web.servlet.setup;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.Filter;
import javax.servlet.ServletContext;
import org.springframework.mock.web.MockServletConfig;
import org.springframework.test.web.servlet.DispatcherServletCustomizer;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MockMvcBuilderSupport;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.test.web.servlet.ResultHandler;
import org.springframework.test.web.servlet.ResultMatcher;
import org.springframework.test.web.servlet.request.ConfigurableSmartRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.request.RequestPostProcessor;
import org.springframework.util.Assert;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.test.web.servlet.MockMvcBuilder;
/**
* Abstract implementation of {@link MockMvcBuilder} with common methods for
* configuring filters, default request properties, global expectations and
* global result actions.
*
* <p>Sub-classes can use different strategies to prepare the Spring
* {@code WebApplicationContext} that will be passed to the
* {@code DispatcherServlet}.
*
* @author Rossen Stoyanchev
* @author Stephane Nicoll
* @since 4.0
*/
public abstract class AbstractMockMvcBuilder<B extends AbstractMockMvcBuilder<B>>
extends MockMvcBuilderSupport implements ConfigurableMockMvcBuilder<B> {
private List<Filter> filters = new ArrayList<>();
private RequestBuilder defaultRequestBuilder;
private final List<ResultMatcher> globalResultMatchers = new ArrayList<>();
private final List<ResultHandler> globalResultHandlers = new ArrayList<>();
private final List<DispatcherServletCustomizer> dispatcherServletCustomizers = new ArrayList<>();
private final List<MockMvcConfigurer> configurers = new ArrayList<>(4);
public final <T extends B> T addFilters(Filter... filters) {
Assert.notNull(filters, "filters cannot be null");
for (Filter f : filters) {
Assert.notNull(f, "filters cannot contain null values");
this.filters.add(f);
}
return self();
}
public final <T extends B> T addFilter(Filter filter, String... urlPatterns) {
Assert.notNull(filter, "filter cannot be null");
Assert.notNull(urlPatterns, "urlPatterns cannot be null");
if (urlPatterns.length > 0) {
filter = new PatternMappingFilterProxy(filter, urlPatterns);
}
this.filters.add(filter);
return self();
}
public final <T extends B> T defaultRequest(RequestBuilder requestBuilder) {
this.defaultRequestBuilder = requestBuilder;
return self();
}
public final <T extends B> T alwaysExpect(ResultMatcher resultMatcher) {
this.globalResultMatchers.add(resultMatcher);
return self();
}
public final <T extends B> T alwaysDo(ResultHandler resultHandler) {
this.globalResultHandlers.add(resultHandler);
return self();
}
public final <T extends B> T addDispatcherServletCustomizer(DispatcherServletCustomizer customizer) {
this.dispatcherServletCustomizers.add(customizer);
return self();
}
public final <T extends B> T dispatchOptions(boolean dispatchOptions) {
return addDispatcherServletCustomizer(
dispatcherServlet -> dispatcherServlet.setDispatchOptionsRequest(dispatchOptions));
}
public final <T extends B> T apply(MockMvcConfigurer configurer) {
configurer.afterConfigurerAdded(this);
this.configurers.add(configurer);
return self();
}
@SuppressWarnings("unchecked")
protected <T extends B> T self() {
return (T) this;
}
/**
* Build a {@link org.springframework.test.web.servlet.MockMvc} instance.
*/
@Override
@SuppressWarnings("rawtypes")
public final MockMvc build() {
WebApplicationContext wac = initWebAppContext();
ServletContext servletContext = wac.getServletContext();
MockServletConfig mockServletConfig = new MockServletConfig(servletContext);
for (MockMvcConfigurer configurer : this.configurers) {
RequestPostProcessor processor = configurer.beforeMockMvcCreated(this, wac);
if (processor != null) {
if (this.defaultRequestBuilder == null) {
this.defaultRequestBuilder = MockMvcRequestBuilders.get("/");
}
if (this.defaultRequestBuilder instanceof ConfigurableSmartRequestBuilder) {
((ConfigurableSmartRequestBuilder) this.defaultRequestBuilder).with(processor);
}
}
}
Filter[] filterArray = this.filters.toArray(new Filter[this.filters.size()]);
return super.createMockMvc(filterArray, mockServletConfig, wac, this.defaultRequestBuilder,
this.globalResultMatchers, this.globalResultHandlers, this.dispatcherServletCustomizers);
}
/**
* A method to obtain the {@code WebApplicationContext} to be passed to the
* {@code DispatcherServlet}. Invoked from {@link #build()} before the
* {@link MockMvc} instance is created.
*/
protected abstract WebApplicationContext initWebAppContext();
}