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.htmlunit; 018 019import java.util.ArrayList; 020import java.util.List; 021 022import com.gargoylesoftware.htmlunit.WebClient; 023import com.gargoylesoftware.htmlunit.WebConnection; 024 025import org.springframework.test.web.servlet.MockMvc; 026import org.springframework.test.web.servlet.htmlunit.DelegatingWebConnection.DelegateWebConnection; 027import org.springframework.test.web.servlet.setup.MockMvcBuilders; 028import org.springframework.test.web.servlet.setup.MockMvcConfigurer; 029import org.springframework.util.Assert; 030import org.springframework.web.context.WebApplicationContext; 031 032/** 033 * Support class that simplifies the creation of a {@link WebConnection} that 034 * uses {@link MockMvc} and optionally delegates to a real {@link WebConnection} 035 * for specific requests. 036 * 037 * <p>The default is to use {@link MockMvc} for requests to {@code localhost} 038 * and otherwise use a real {@link WebConnection}. 039 * 040 * @author Rob Winch 041 * @author Sam Brannen 042 * @since 4.2 043 */ 044public abstract class MockMvcWebConnectionBuilderSupport<T extends MockMvcWebConnectionBuilderSupport<T>> { 045 046 private final MockMvc mockMvc; 047 048 private final List<WebRequestMatcher> requestMatchers = new ArrayList<WebRequestMatcher>(); 049 050 private String contextPath = ""; 051 052 private boolean alwaysUseMockMvc; 053 054 055 /** 056 * Create a new instance using the supplied {@link MockMvc} instance. 057 * @param mockMvc the {@code MockMvc} instance to use; never {@code null} 058 */ 059 protected MockMvcWebConnectionBuilderSupport(MockMvc mockMvc) { 060 Assert.notNull(mockMvc, "MockMvc must not be null"); 061 this.mockMvc = mockMvc; 062 this.requestMatchers.add(new HostRequestMatcher("localhost")); 063 } 064 065 /** 066 * Create a new instance using the supplied {@link WebApplicationContext}. 067 * @param context the {@code WebApplicationContext} to create a {@code MockMvc} 068 * instance from; never {@code null} 069 */ 070 protected MockMvcWebConnectionBuilderSupport(WebApplicationContext context) { 071 this(MockMvcBuilders.webAppContextSetup(context).build()); 072 } 073 074 /** 075 * Create a new instance using the supplied {@link WebApplicationContext} 076 * and {@link MockMvcConfigurer}. 077 * @param context the {@code WebApplicationContext} to create a {@code MockMvc} 078 * instance from; never {@code null} 079 * @param configurer the MockMvcConfigurer to apply; never {@code null} 080 */ 081 protected MockMvcWebConnectionBuilderSupport(WebApplicationContext context, MockMvcConfigurer configurer) { 082 this(MockMvcBuilders.webAppContextSetup(context).apply(configurer).build()); 083 } 084 085 086 /** 087 * Set the context path to use. 088 * <p>If the supplied value is {@code null} or empty, the first path 089 * segment of the request URL is assumed to be the context path. 090 * <p>Default is {@code ""}. 091 * @param contextPath the context path to use 092 * @return this builder for further customization 093 */ 094 @SuppressWarnings("unchecked") 095 public T contextPath(String contextPath) { 096 this.contextPath = contextPath; 097 return (T) this; 098 } 099 100 /** 101 * Specify that {@link MockMvc} should always be used regardless of 102 * what the request looks like. 103 * @return this builder for further customization 104 */ 105 @SuppressWarnings("unchecked") 106 public T alwaysUseMockMvc() { 107 this.alwaysUseMockMvc = true; 108 return (T) this; 109 } 110 111 /** 112 * Add additional {@link WebRequestMatcher} instances that will ensure 113 * that {@link MockMvc} is used to process the request, if such a matcher 114 * matches against the web request. 115 * @param matchers additional {@code WebRequestMatcher} instances 116 * @return this builder for further customization 117 */ 118 @SuppressWarnings("unchecked") 119 public T useMockMvc(WebRequestMatcher... matchers) { 120 for (WebRequestMatcher matcher : matchers) { 121 this.requestMatchers.add(matcher); 122 } 123 return (T) this; 124 } 125 126 /** 127 * Add additional {@link WebRequestMatcher} instances that return {@code true} 128 * if a supplied host matches — for example, {@code "example.com"} or 129 * {@code "example.com:8080"}. 130 * @param hosts additional hosts that ensure {@code MockMvc} gets invoked 131 * @return this builder for further customization 132 */ 133 @SuppressWarnings("unchecked") 134 public T useMockMvcForHosts(String... hosts) { 135 this.requestMatchers.add(new HostRequestMatcher(hosts)); 136 return (T) this; 137 } 138 139 /** 140 * Create a new {@link WebConnection} that will use a {@link MockMvc} 141 * instance if one of the specified {@link WebRequestMatcher} instances 142 * matches. 143 * @param defaultConnection the default WebConnection to use if none of 144 * the specified {@code WebRequestMatcher} instances matches; never {@code null} 145 * @return a new {@code WebConnection} that will use a {@code MockMvc} 146 * instance if one of the specified {@code WebRequestMatcher} matches 147 * @see #alwaysUseMockMvc() 148 * @see #useMockMvc(WebRequestMatcher...) 149 * @see #useMockMvcForHosts(String...) 150 * @deprecated Use {@link #createConnection(WebClient)} instead 151 */ 152 @Deprecated 153 protected final WebConnection createConnection(WebConnection defaultConnection) { 154 Assert.notNull(defaultConnection, "Default WebConnection must not be null"); 155 return createConnection(new WebClient(), defaultConnection); 156 } 157 158 /** 159 * Create a new {@link WebConnection} that will use a {@link MockMvc} 160 * instance if one of the specified {@link WebRequestMatcher} instances 161 * matches. 162 * @param webClient the WebClient to use if none of the specified 163 * {@code WebRequestMatcher} instances matches (never {@code null}) 164 * @return a new {@code WebConnection} that will use a {@code MockMvc} 165 * instance if one of the specified {@code WebRequestMatcher} matches 166 * @see #alwaysUseMockMvc() 167 * @see #useMockMvc(WebRequestMatcher...) 168 * @see #useMockMvcForHosts(String...) 169 * @since 4.3 170 */ 171 protected final WebConnection createConnection(WebClient webClient) { 172 Assert.notNull(webClient, "WebClient must not be null"); 173 return createConnection(webClient, webClient.getWebConnection()); 174 } 175 176 private WebConnection createConnection(WebClient webClient, WebConnection defaultConnection) { 177 WebConnection connection = new MockMvcWebConnection(this.mockMvc, webClient, this.contextPath); 178 if (this.alwaysUseMockMvc) { 179 return connection; 180 } 181 List<DelegateWebConnection> delegates = new ArrayList<DelegateWebConnection>(this.requestMatchers.size()); 182 for (WebRequestMatcher matcher : this.requestMatchers) { 183 delegates.add(new DelegateWebConnection(matcher, connection)); 184 } 185 return new DelegatingWebConnection(defaultConnection, delegates); 186 } 187 188}