001/*
002 * Copyright 2002-2016 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.test.web.servlet.setup;
018
019import java.util.ArrayList;
020import java.util.List;
021import javax.servlet.Filter;
022import javax.servlet.ServletContext;
023
024import org.springframework.mock.web.MockServletConfig;
025import org.springframework.test.web.servlet.DispatcherServletCustomizer;
026import org.springframework.test.web.servlet.MockMvc;
027import org.springframework.test.web.servlet.MockMvcBuilderSupport;
028import org.springframework.test.web.servlet.RequestBuilder;
029import org.springframework.test.web.servlet.ResultHandler;
030import org.springframework.test.web.servlet.ResultMatcher;
031import org.springframework.test.web.servlet.request.ConfigurableSmartRequestBuilder;
032import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
033import org.springframework.test.web.servlet.request.RequestPostProcessor;
034import org.springframework.util.Assert;
035import org.springframework.web.context.WebApplicationContext;
036import org.springframework.web.servlet.DispatcherServlet;
037
038/**
039 * An abstract implementation of {@link org.springframework.test.web.servlet.MockMvcBuilder}
040 * with common methods for configuring filters, default request properties, global
041 * expectations and global result actions.
042 * <p>
043 * Sub-classes can use different strategies to prepare a WebApplicationContext to
044 * pass to the DispatcherServlet.
045 *
046 * @author Rossen Stoyanchev
047 * @author Stephane Nicoll
048 * @since 4.0
049 */
050public abstract class AbstractMockMvcBuilder<B extends AbstractMockMvcBuilder<B>>
051                extends MockMvcBuilderSupport implements ConfigurableMockMvcBuilder<B> {
052
053        private List<Filter> filters = new ArrayList<Filter>();
054
055        private RequestBuilder defaultRequestBuilder;
056
057        private final List<ResultMatcher> globalResultMatchers = new ArrayList<ResultMatcher>();
058
059        private final List<ResultHandler> globalResultHandlers = new ArrayList<ResultHandler>();
060
061        private final List<DispatcherServletCustomizer> dispatcherServletCustomizers = new ArrayList<DispatcherServletCustomizer>();
062
063        private final List<MockMvcConfigurer> configurers = new ArrayList<MockMvcConfigurer>(4);
064
065
066        @SuppressWarnings("unchecked")
067        public final <T extends B> T addFilters(Filter... filters) {
068                Assert.notNull(filters, "filters cannot be null");
069
070                for (Filter f : filters) {
071                        Assert.notNull(f, "filters cannot contain null values");
072                        this.filters.add(f);
073                }
074                return (T) this;
075        }
076
077        @SuppressWarnings("unchecked")
078        public final <T extends B> T addFilter(Filter filter, String... urlPatterns) {
079
080                Assert.notNull(filter, "filter cannot be null");
081                Assert.notNull(urlPatterns, "urlPatterns cannot be null");
082
083                if (urlPatterns.length > 0) {
084                        filter = new PatternMappingFilterProxy(filter, urlPatterns);
085                }
086
087                this.filters.add(filter);
088                return (T) this;
089        }
090
091        @SuppressWarnings("unchecked")
092        public final <T extends B> T defaultRequest(RequestBuilder requestBuilder) {
093                this.defaultRequestBuilder = requestBuilder;
094                return (T) this;
095        }
096
097        @SuppressWarnings("unchecked")
098        public final <T extends B> T alwaysExpect(ResultMatcher resultMatcher) {
099                this.globalResultMatchers.add(resultMatcher);
100                return (T) this;
101        }
102
103        @SuppressWarnings("unchecked")
104        public final <T extends B> T alwaysDo(ResultHandler resultHandler) {
105                this.globalResultHandlers.add(resultHandler);
106                return (T) this;
107        }
108
109        @SuppressWarnings("unchecked")
110        public final <T extends B> T addDispatcherServletCustomizer(DispatcherServletCustomizer customizer) {
111                this.dispatcherServletCustomizers.add(customizer);
112                return (T) this;
113        }
114
115        public final <T extends B> T dispatchOptions(final boolean dispatchOptions) {
116                return addDispatcherServletCustomizer(new DispatcherServletCustomizer() {
117                        @Override
118                        public void customize(DispatcherServlet dispatcherServlet) {
119                                dispatcherServlet.setDispatchOptionsRequest(dispatchOptions);
120                        }
121                });
122        }
123
124        @SuppressWarnings("unchecked")
125        public final <T extends B> T apply(MockMvcConfigurer configurer) {
126                configurer.afterConfigurerAdded(this);
127                this.configurers.add(configurer);
128                return (T) this;
129        }
130
131
132        /**
133         * Build a {@link org.springframework.test.web.servlet.MockMvc} instance.
134         */
135        @Override
136        @SuppressWarnings("rawtypes")
137        public final MockMvc build() {
138
139                WebApplicationContext wac = initWebAppContext();
140
141                ServletContext servletContext = wac.getServletContext();
142                MockServletConfig mockServletConfig = new MockServletConfig(servletContext);
143
144                for (MockMvcConfigurer configurer : this.configurers) {
145                        RequestPostProcessor processor = configurer.beforeMockMvcCreated(this, wac);
146                        if (processor != null) {
147                                if (this.defaultRequestBuilder == null) {
148                                        this.defaultRequestBuilder = MockMvcRequestBuilders.get("/");
149                                }
150                                if (this.defaultRequestBuilder instanceof ConfigurableSmartRequestBuilder) {
151                                        ((ConfigurableSmartRequestBuilder) this.defaultRequestBuilder).with(processor);
152                                }
153                        }
154                }
155
156                Filter[] filterArray = this.filters.toArray(new Filter[this.filters.size()]);
157
158                return super.createMockMvc(filterArray, mockServletConfig, wac, this.defaultRequestBuilder,
159                                this.globalResultMatchers, this.globalResultHandlers, this.dispatcherServletCustomizers);
160        }
161
162        /**
163         * A method to obtain the WebApplicationContext to be passed to the DispatcherServlet.
164         * Invoked from {@link #build()} before the
165         * {@link org.springframework.test.web.servlet.MockMvc} instance is created.
166         */
167        protected abstract WebApplicationContext initWebAppContext();
168
169}