From 1452d3cd51579d1f4e15dff705f3a44db517395b Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 22 Nov 2016 10:58:30 -0800 Subject: [PATCH 1/5] Ensure withBasicAuth keeps error handler Fix `TestRestTemplate` so that any custom `ErrorHandler` isn't lost when calling `withBasicAuth`. Fixes gh-7441 --- .../test/web/client/TestRestTemplate.java | 8 +++-- .../web/client/TestRestTemplateTests.java | 36 +++++++++++-------- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/web/client/TestRestTemplate.java b/spring-boot-test/src/main/java/org/springframework/boot/test/web/client/TestRestTemplate.java index 2441537483..b3ab49bfd8 100644 --- a/spring-boot-test/src/main/java/org/springframework/boot/test/web/client/TestRestTemplate.java +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/web/client/TestRestTemplate.java @@ -916,14 +916,16 @@ public class TestRestTemplate { */ public TestRestTemplate withBasicAuth(String username, String password) { RestTemplate restTemplate = new RestTemplate(); - restTemplate.setErrorHandler(getRestTemplate().getErrorHandler()); restTemplate.setMessageConverters(getRestTemplate().getMessageConverters()); restTemplate.setInterceptors( removeBasicAuthInterceptorIfPresent(getRestTemplate().getInterceptors())); restTemplate.setRequestFactory(getRestTemplate().getRequestFactory()); restTemplate.setUriTemplateHandler(getRestTemplate().getUriTemplateHandler()); - return new TestRestTemplate(restTemplate, username, password, - this.httpClientOptions); + TestRestTemplate testRestTemplate = new TestRestTemplate(restTemplate, username, + password, this.httpClientOptions); + testRestTemplate.getRestTemplate() + .setErrorHandler(getRestTemplate().getErrorHandler()); + return testRestTemplate; } private List removeBasicAuthInterceptorIfPresent( diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/web/client/TestRestTemplateTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/web/client/TestRestTemplateTests.java index 68169d005b..81796e2963 100644 --- a/spring-boot-test/src/test/java/org/springframework/boot/test/web/client/TestRestTemplateTests.java +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/web/client/TestRestTemplateTests.java @@ -34,6 +34,7 @@ import org.springframework.http.client.support.BasicAuthorizationInterceptor; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils.MethodCallback; +import org.springframework.web.client.ResponseErrorHandler; import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; @@ -157,25 +158,32 @@ public class TestRestTemplateTests { @Test public void withBasicAuthReplacesBasicAuthInterceptorWhenAlreadyPresent() { - TestRestTemplate originalTemplate = new TestRestTemplate("foo", "bar"); - TestRestTemplate basicAuthTemplate = originalTemplate.withBasicAuth("user", - "password"); - assertThat(basicAuthTemplate.getRestTemplate().getMessageConverters()) + TestRestTemplate original = new TestRestTemplate("foo", "bar"); + TestRestTemplate basicAuth = original.withBasicAuth("user", "password"); + assertThat(basicAuth.getRestTemplate().getMessageConverters()) .containsExactlyElementsOf( - originalTemplate.getRestTemplate().getMessageConverters()); - assertThat(basicAuthTemplate.getRestTemplate().getRequestFactory()) + original.getRestTemplate().getMessageConverters()); + assertThat(basicAuth.getRestTemplate().getRequestFactory()) .isInstanceOf(InterceptingClientHttpRequestFactory.class); assertThat(ReflectionTestUtils.getField( - basicAuthTemplate.getRestTemplate().getRequestFactory(), - "requestFactory")) + basicAuth.getRestTemplate().getRequestFactory(), "requestFactory")) .isInstanceOf(CustomHttpComponentsClientHttpRequestFactory.class); - assertThat(basicAuthTemplate.getRestTemplate().getUriTemplateHandler()) - .isSameAs(originalTemplate.getRestTemplate().getUriTemplateHandler()); - assertThat(basicAuthTemplate.getRestTemplate().getInterceptors()) - .containsExactlyElementsOf( - originalTemplate.getRestTemplate().getInterceptors()); - assertBasicAuthorizationInterceptorCredentials(basicAuthTemplate, "user", + assertThat(basicAuth.getRestTemplate().getUriTemplateHandler()) + .isSameAs(original.getRestTemplate().getUriTemplateHandler()); + assertThat(basicAuth.getRestTemplate().getInterceptors()) + .containsExactlyElementsOf(original.getRestTemplate().getInterceptors()); + assertBasicAuthorizationInterceptorCredentials(basicAuth, "user", "password"); + } + + @Test + public void withBasicAuthDoesNotResetErrorHandler() throws Exception { + TestRestTemplate originalTemplate = new TestRestTemplate("foo", "bar"); + ResponseErrorHandler errorHandler = mock(ResponseErrorHandler.class); + originalTemplate.getRestTemplate().setErrorHandler(errorHandler); + TestRestTemplate basicAuthTemplate = originalTemplate.withBasicAuth("user", "password"); + assertThat(basicAuthTemplate.getRestTemplate().getErrorHandler()) + .isSameAs(errorHandler); } private void assertBasicAuthorizationInterceptorCredentials( From a3b79be6b30abf54beb60f43345e849b1114d52c Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 22 Nov 2016 13:49:28 -0800 Subject: [PATCH 2/5] Support @MockBean on FactoryBeans Update @MockBean support so that FactoryBean classes can be mocked. Fixes gh-7439 --- .../mock/mockito/MockitoPostProcessor.java | 9 +- ...ockBeanForBeanFactoryIntegrationTests.java | 102 ++++++++++++++++++ 2 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanForBeanFactoryIntegrationTests.java diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoPostProcessor.java b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoPostProcessor.java index a95be45f54..94337fe101 100644 --- a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoPostProcessor.java +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoPostProcessor.java @@ -184,14 +184,15 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda BeanDefinitionRegistry registry, MockDefinition definition, Field field) { RootBeanDefinition beanDefinition = createBeanDefinition(definition); String beanName = getBeanName(beanFactory, registry, definition, beanDefinition); + String transformedBeanName = BeanFactoryUtils.transformedBeanName(beanName); beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(1, beanName); - if (registry.containsBeanDefinition(beanName)) { - registry.removeBeanDefinition(beanName); + if (registry.containsBeanDefinition(transformedBeanName)) { + registry.removeBeanDefinition(transformedBeanName); } - registry.registerBeanDefinition(beanName, beanDefinition); + registry.registerBeanDefinition(transformedBeanName, beanDefinition); Object mock = createMock(definition, beanName); - beanFactory.registerSingleton(beanName, mock); + beanFactory.registerSingleton(transformedBeanName, mock); this.mockitoBeans.add(mock); this.beanNameRegistry.put(definition, beanName); if (field != null) { diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanForBeanFactoryIntegrationTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanForBeanFactoryIntegrationTests.java new file mode 100644 index 0000000000..c0783da167 --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanForBeanFactoryIntegrationTests.java @@ -0,0 +1,102 @@ +/* + * Copyright 2012-2016 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.boot.test.mock.mockito; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +/** + * Test {@link MockBean} for a factory bean. + * + * @author Phillip Webb + */ +@RunWith(SpringRunner.class) +public class MockBeanForBeanFactoryIntegrationTests { + + // gh-7439 + + @MockBean + private TestFactoryBean testFactoryBean; + + @Autowired + private ApplicationContext applicationContext; + + @Test + @SuppressWarnings({ "unchecked", "rawtypes" }) + public void testName() throws Exception { + TestBean testBean = mock(TestBean.class); + given(testBean.hello()).willReturn("amock"); + given(this.testFactoryBean.getObjectType()).willReturn((Class) TestBean.class); + given(this.testFactoryBean.getObject()).willReturn(testBean); + TestBean bean = this.applicationContext.getBean(TestBean.class); + assertThat(bean.hello()).isEqualTo("amock"); + } + + @Configuration + static class Config { + + @Bean + public TestFactoryBean testFactoryBean() { + return new TestFactoryBean(); + } + + } + + static class TestFactoryBean implements FactoryBean { + + @Override + public TestBean getObject() throws Exception { + return new TestBean() { + + @Override + public String hello() { + return "normal"; + } + + }; + } + + @Override + public Class getObjectType() { + return TestBean.class; + } + + @Override + public boolean isSingleton() { + return false; + } + + } + + interface TestBean { + + String hello(); + + } + +} From 1ea829e00300d6124d26405d2951ecca78877d46 Mon Sep 17 00:00:00 2001 From: Grigory Fadeev Date: Sat, 19 Nov 2016 19:42:16 +0200 Subject: [PATCH 3/5] Fix connector used to configure connection timeout Fix Tomcat customization so that the main connection is configured with any timeout. Closes gh-7425 --- .../autoconfigure/web/ServerProperties.java | 20 ++++++++++++------- .../web/ServerPropertiesTests.java | 8 ++++++++ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java index ae4a553491..4579f32d04 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java @@ -809,14 +809,20 @@ public class ServerProperties } private void customizeConnectionTimeout( - TomcatEmbeddedServletContainerFactory factory, int connectionTimeout) { - for (Connector connector : factory.getAdditionalTomcatConnectors()) { - if (connector.getProtocolHandler() instanceof AbstractProtocol) { - AbstractProtocol handler = (AbstractProtocol) connector - .getProtocolHandler(); - handler.setConnectionTimeout(connectionTimeout); + TomcatEmbeddedServletContainerFactory factory, + final int connectionTimeout) { + factory.addConnectorCustomizers(new TomcatConnectorCustomizer() { + + @Override + public void customize(Connector connector) { + ProtocolHandler handler = connector.getProtocolHandler(); + if (handler instanceof AbstractProtocol) { + AbstractProtocol protocol = (AbstractProtocol) handler; + protocol.setConnectionTimeout(connectionTimeout); + } } - } + + }); } private void customizeRemoteIpValve(ServerProperties properties, diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java index 7a76eaffea..e581c42e7a 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java @@ -111,6 +111,14 @@ public class ServerPropertiesTests { assertThat(this.properties.getServerHeader()).isEqualTo("Custom Server"); } + @Test + public void testConnectionTimeout() throws Exception { + Map map = new HashMap(); + map.put("server.connection-timeout", "60000"); + bindProperties(map); + assertThat(this.properties.getConnectionTimeout()).isEqualTo(60000); + } + @Test public void testServletPathAsMapping() throws Exception { RelaxedDataBinder binder = new RelaxedDataBinder(this.properties, "server"); From ce58e16860f476c75119d88b571769221e7a41fb Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 22 Nov 2016 14:30:36 -0800 Subject: [PATCH 4/5] Add additional Tomcat timeout test Update the Tomcat sample to also test that the connection timeout is set. See gh-7425 --- .../src/main/resources/application.properties | 1 + .../tomcat/SampleTomcatApplicationTests.java | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/spring-boot-samples/spring-boot-sample-tomcat/src/main/resources/application.properties b/spring-boot-samples/spring-boot-sample-tomcat/src/main/resources/application.properties index 09ab26becc..4c0dadec6d 100644 --- a/spring-boot-samples/spring-boot-sample-tomcat/src/main/resources/application.properties +++ b/spring-boot-samples/spring-boot-sample-tomcat/src/main/resources/application.properties @@ -1,2 +1,3 @@ server.compression.enabled: true server.compression.min-response-size: 1 +server.connection-timeout=5000 diff --git a/spring-boot-samples/spring-boot-sample-tomcat/src/test/java/sample/tomcat/SampleTomcatApplicationTests.java b/spring-boot-samples/spring-boot-sample-tomcat/src/test/java/sample/tomcat/SampleTomcatApplicationTests.java index bc397cff32..3383a91b36 100644 --- a/spring-boot-samples/spring-boot-sample-tomcat/src/test/java/sample/tomcat/SampleTomcatApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-tomcat/src/test/java/sample/tomcat/SampleTomcatApplicationTests.java @@ -20,13 +20,18 @@ import java.io.ByteArrayInputStream; import java.nio.charset.Charset; import java.util.zip.GZIPInputStream; +import org.apache.coyote.AbstractProtocol; +import org.apache.coyote.ProtocolHandler; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.embedded.EmbeddedWebApplicationContext; +import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.context.ApplicationContext; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; @@ -52,6 +57,9 @@ public class SampleTomcatApplicationTests { @Autowired private TestRestTemplate restTemplate; + @Autowired + private ApplicationContext applicationContext; + @Test public void testHome() throws Exception { ResponseEntity entity = this.restTemplate.getForEntity("/", String.class); @@ -78,4 +86,15 @@ public class SampleTomcatApplicationTests { } } + @Test + public void testTimeout() throws Exception { + EmbeddedWebApplicationContext context = (EmbeddedWebApplicationContext) this.applicationContext; + TomcatEmbeddedServletContainer embeddedServletContainer = (TomcatEmbeddedServletContainer) context + .getEmbeddedServletContainer(); + ProtocolHandler protocolHandler = embeddedServletContainer.getTomcat() + .getConnector().getProtocolHandler(); + int timeout = ((AbstractProtocol) protocolHandler).getConnectionTimeout(); + assertThat(timeout).isEqualTo(5000); + } + } From 7e2d0fd1c010f4a7485682e99ef9760d6536c9c0 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 22 Nov 2016 14:32:38 -0800 Subject: [PATCH 5/5] Check for Spring Data before configuring Couchbase Add guard to `CouchbaseAutoConfiguration` so ensure that Spring Data Couchbase is on the classpath. Fixes gh-7453 --- .../autoconfigure/couchbase/CouchbaseAutoConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/couchbase/CouchbaseAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/couchbase/CouchbaseAutoConfiguration.java index 4eb6a72c32..9f5a7dcd5f 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/couchbase/CouchbaseAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/couchbase/CouchbaseAutoConfiguration.java @@ -44,7 +44,7 @@ import org.springframework.data.couchbase.config.CouchbaseConfigurer; * @since 1.4.0 */ @Configuration -@ConditionalOnClass({ CouchbaseBucket.class, Cluster.class }) +@ConditionalOnClass({ CouchbaseBucket.class, Cluster.class, CouchbaseConfigurer.class }) @Conditional(CouchbaseAutoConfiguration.CouchbaseCondition.class) @EnableConfigurationProperties(CouchbaseProperties.class) public class CouchbaseAutoConfiguration {