From 084b2889475398117199ac3f8286735257088afe Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Sat, 9 Apr 2016 21:41:17 -0700 Subject: [PATCH 1/5] Polish --- .../embedded/jetty/JettyEmbeddedServletContainerFactory.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainerFactory.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainerFactory.java index eef31f7d817..f2c274b16cd 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainerFactory.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainerFactory.java @@ -407,11 +407,13 @@ public class JettyEmbeddedServletContainerFactory */ private Configuration getErrorPageConfiguration() { return new AbstractConfiguration() { + @Override public void configure(WebAppContext context) throws Exception { ErrorHandler errorHandler = context.getErrorHandler(); addJettyErrorPages(errorHandler, getErrorPages()); } + }; } @@ -421,6 +423,7 @@ public class JettyEmbeddedServletContainerFactory */ private Configuration getMimeTypeConfiguration() { return new AbstractConfiguration() { + @Override public void configure(WebAppContext context) throws Exception { MimeTypes mimeTypes = context.getMimeTypes(); @@ -429,6 +432,7 @@ public class JettyEmbeddedServletContainerFactory mapping.getMimeType()); } } + }; } From 02764b8ff3f530d8b1ecefcd0cd1a6a05e9cfa2e Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Sat, 9 Apr 2016 21:41:31 -0700 Subject: [PATCH 2/5] Support Jetty error page handling of PUT requests Update JettyEmbeddedServletContainerFactory so that requests other than just GET, POST and HEAD are handled by the ErrorHandler. Fixes gh-5367 --- .../jetty/JettyEmbeddedErrorHandler.java | 80 +++++++++++++++++++ .../JettyEmbeddedServletContainerFactory.java | 1 + ...tEmbeddedServletContainerFactoryTests.java | 55 ++++++++++--- 3 files changed, 124 insertions(+), 12 deletions(-) create mode 100644 spring-boot/src/main/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedErrorHandler.java diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedErrorHandler.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedErrorHandler.java new file mode 100644 index 00000000000..42f4ffafb56 --- /dev/null +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedErrorHandler.java @@ -0,0 +1,80 @@ +/* + * 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.context.embedded.jetty; + +import java.io.IOException; + +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.handler.ErrorHandler; + +/** + * Variation of Jetty's {@link ErrorHandler} that supports all {@link HttpMethod + * HttpMethods} rather than just {@code GET}, {@code POST} and {@code HEAD}. Jetty + * intentionally only + * supports a limited set of HTTP methods for error pages, however, Spring Boot + * prefers Tomcat, Jetty and Undertow to all behave in the same way. + * + * @author Phillip Webb + */ +class JettyEmbeddedErrorHandler extends ErrorHandler { + + private final ErrorHandler delegate; + + JettyEmbeddedErrorHandler(ErrorHandler delegate) { + this.delegate = delegate; + } + + @Override + public void handle(String target, Request baseRequest, HttpServletRequest request, + HttpServletResponse response) throws IOException { + String method = request.getMethod(); + if (!HttpMethod.GET.is(method) && !HttpMethod.POST.is(method) + && !HttpMethod.HEAD.is(method)) { + request = new ErrorHttpServletRequest(request); + } + this.delegate.handle(target, baseRequest, request, response); + } + + private static class ErrorHttpServletRequest extends HttpServletRequestWrapper { + + private boolean simulateGetMethod = true; + + ErrorHttpServletRequest(HttpServletRequest request) { + super(request); + } + + @Override + public String getMethod() { + return (this.simulateGetMethod ? HttpMethod.GET.toString() + : super.getMethod()); + } + + @Override + public ServletContext getServletContext() { + this.simulateGetMethod = false; + return super.getServletContext(); + } + + } + +} diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainerFactory.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainerFactory.java index f2c274b16cd..ea756cd3756 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainerFactory.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainerFactory.java @@ -411,6 +411,7 @@ public class JettyEmbeddedServletContainerFactory @Override public void configure(WebAppContext context) throws Exception { ErrorHandler errorHandler = context.getErrorHandler(); + context.setErrorHandler(new JettyEmbeddedErrorHandler(errorHandler)); addJettyErrorPages(errorHandler, getErrorPages()); } diff --git a/spring-boot/src/test/java/org/springframework/boot/context/embedded/AbstractEmbeddedServletContainerFactoryTests.java b/spring-boot/src/test/java/org/springframework/boot/context/embedded/AbstractEmbeddedServletContainerFactoryTests.java index ac997d36043..9b60faefc3a 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/embedded/AbstractEmbeddedServletContainerFactoryTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/embedded/AbstractEmbeddedServletContainerFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * 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. @@ -343,6 +343,19 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests { assertThat(getResponse(getLocalUrl("/bang")), equalTo("Hello World")); } + @Test + public void errorPageFromPutRequest() throws Exception { + AbstractEmbeddedServletContainerFactory factory = getFactory(); + factory.addErrorPages(new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/hello")); + this.container = factory.getEmbeddedServletContainer(exampleServletRegistration(), + errorServletRegistration()); + this.container.start(); + assertThat(getResponse(getLocalUrl("/hello"), HttpMethod.PUT), + equalTo("Hello World")); + assertThat(getResponse(getLocalUrl("/bang"), HttpMethod.PUT), + equalTo("Hello World")); + } + @Test public void basicSslFromClassPath() throws Exception { testBasicSslWithKeyStore("classpath:test.jks"); @@ -792,7 +805,12 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests { protected String getResponse(String url, String... headers) throws IOException, URISyntaxException { - ClientHttpResponse response = getClientResponse(url, headers); + return getResponse(url, HttpMethod.GET, headers); + } + + protected String getResponse(String url, HttpMethod method, String... headers) + throws IOException, URISyntaxException { + ClientHttpResponse response = getClientResponse(url, method, headers); try { return StreamUtils.copyToString(response.getBody(), Charset.forName("UTF-8")); } @@ -804,7 +822,14 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests { protected String getResponse(String url, HttpComponentsClientHttpRequestFactory requestFactory, String... headers) throws IOException, URISyntaxException { - ClientHttpResponse response = getClientResponse(url, requestFactory, headers); + return getResponse(url, HttpMethod.GET, requestFactory, headers); + } + + protected String getResponse(String url, HttpMethod method, + HttpComponentsClientHttpRequestFactory requestFactory, String... headers) + throws IOException, URISyntaxException { + ClientHttpResponse response = getClientResponse(url, method, requestFactory, + headers); try { return StreamUtils.copyToString(response.getBody(), Charset.forName("UTF-8")); } @@ -815,21 +840,27 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests { protected ClientHttpResponse getClientResponse(String url, String... headers) throws IOException, URISyntaxException { - return getClientResponse(url, new HttpComponentsClientHttpRequestFactory() { + return getClientResponse(url, HttpMethod.GET, headers); + } - @Override - protected HttpContext createHttpContext(HttpMethod httpMethod, URI uri) { - return AbstractEmbeddedServletContainerFactoryTests.this.httpClientContext; - } + protected ClientHttpResponse getClientResponse(String url, HttpMethod method, + String... headers) throws IOException, URISyntaxException { + return getClientResponse(url, method, + new HttpComponentsClientHttpRequestFactory() { + + @Override + protected HttpContext createHttpContext(HttpMethod httpMethod, + URI uri) { + return AbstractEmbeddedServletContainerFactoryTests.this.httpClientContext; + } - }, headers); + }, headers); } - protected ClientHttpResponse getClientResponse(String url, + protected ClientHttpResponse getClientResponse(String url, HttpMethod method, HttpComponentsClientHttpRequestFactory requestFactory, String... headers) throws IOException, URISyntaxException { - ClientHttpRequest request = requestFactory.createRequest(new URI(url), - HttpMethod.GET); + ClientHttpRequest request = requestFactory.createRequest(new URI(url), method); request.getHeaders().add("Cookie", "JSESSIONID=" + "123"); for (String header : headers) { String[] parts = header.split(":"); From 3ca365cff061f32f27513092382230cae00512b1 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Sat, 9 Apr 2016 22:04:58 -0700 Subject: [PATCH 3/5] Export MessageChannel metric writer Update the auto-configured MessageChannelMetricWriter with @ExportMetricWriter so that metrics are actually exported. Fixes gh-5517 --- .../autoconfigure/MetricsChannelAutoConfiguration.java | 3 ++- .../autoconfigure/MetricExportAutoConfigurationTests.java | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/MetricsChannelAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/MetricsChannelAutoConfiguration.java index fd44d6b104b..61b856cad9d 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/MetricsChannelAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/MetricsChannelAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * 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. @@ -41,6 +41,7 @@ import org.springframework.messaging.MessageChannel; public class MetricsChannelAutoConfiguration { @Bean + @ExportMetricWriter @ConditionalOnMissingBean public MessageChannelMetricWriter messageChannelMetricWriter( @Qualifier("metricsChannel") MessageChannel channel) { diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/MetricExportAutoConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/MetricExportAutoConfigurationTests.java index 288d9bf1a11..170258fd6b9 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/MetricExportAutoConfigurationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/MetricExportAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * 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. @@ -45,6 +45,7 @@ import org.springframework.messaging.SubscribableChannel; import static org.hamcrest.Matchers.notNullValue; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; /** * Tests for {@link MetricExportAutoConfiguration}. @@ -77,6 +78,7 @@ public class MetricExportAutoConfigurationTests { PropertyPlaceholderAutoConfiguration.class); MetricExporters exporter = this.context.getBean(MetricExporters.class); assertNotNull(exporter); + assertTrue(exporter.getExporters().containsKey("messageChannelMetricWriter")); } @Test From d7e56abdf33c5b5923ca844477327229f403cc88 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Sat, 9 Apr 2016 22:19:25 -0700 Subject: [PATCH 4/5] Don't apply `null` ServerProperties from customize Update ServerProperties so that `null` values are not applied when customizing the EmbeddedServletContainerFactory. Primarily changed to stop `server.undertow.accesslog.enabled` from being blindly applied. Fixes gh-5515 --- .../autoconfigure/web/ServerProperties.java | 42 +++++++++++++------ .../web/ServerPropertiesTests.java | 11 ++++- 2 files changed, 39 insertions(+), 14 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 6c8b2fcc972..1dfb9548424 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * 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. @@ -1019,7 +1019,7 @@ public class ServerProperties @Deprecated @DeprecatedConfigurationProperty(replacement = "server.undertow.accesslog.enabled") public boolean isAccessLogEnabled() { - return this.accesslog.isEnabled(); + return Boolean.TRUE.equals(this.accesslog.getEnabled()); } /** @@ -1055,14 +1055,30 @@ public class ServerProperties void customizeUndertow(ServerProperties serverProperties, UndertowEmbeddedServletContainerFactory factory) { - factory.setBufferSize(this.bufferSize); - factory.setBuffersPerRegion(this.buffersPerRegion); - factory.setIoThreads(this.ioThreads); - factory.setWorkerThreads(this.workerThreads); - factory.setDirectBuffers(this.directBuffers); - factory.setAccessLogDirectory(this.accesslog.dir); - factory.setAccessLogPattern(this.accesslog.pattern); - factory.setAccessLogEnabled(this.accesslog.enabled); + if (this.bufferSize != null) { + factory.setBufferSize(this.bufferSize); + } + if (this.buffersPerRegion != null) { + factory.setBuffersPerRegion(this.buffersPerRegion); + } + if (this.ioThreads != null) { + factory.setIoThreads(this.ioThreads); + } + if (this.workerThreads != null) { + factory.setWorkerThreads(this.workerThreads); + } + if (this.directBuffers != null) { + factory.setDirectBuffers(this.directBuffers); + } + if (this.accesslog.dir != null) { + factory.setAccessLogDirectory(this.accesslog.dir); + } + if (this.accesslog.pattern != null) { + factory.setAccessLogPattern(this.accesslog.pattern); + } + if (this.accesslog.enabled != null) { + factory.setAccessLogEnabled(this.accesslog.enabled); + } factory.setUseForwardHeaders(serverProperties.getOrDeduceUseForwardHeaders()); } @@ -1071,7 +1087,7 @@ public class ServerProperties /** * Enable access log. */ - private boolean enabled = false; + private Boolean enabled; /** * Format pattern for access logs. @@ -1083,11 +1099,11 @@ public class ServerProperties */ private File dir = new File("logs"); - public boolean isEnabled() { + public Boolean getEnabled() { return this.enabled; } - public void setEnabled(boolean enabled) { + public void setEnabled(Boolean enabled) { this.enabled = enabled; } 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 a12f8fb3a48..4ea0e62c825 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * 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. @@ -53,6 +53,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.mockito.BDDMockito.given; +import static org.mockito.Matchers.anyBoolean; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -412,6 +413,14 @@ public class ServerPropertiesTests { verify(container).setSessionStoreDir(new File("myfolder")); } + @Test + public void skipNullElementsForUndertow() throws Exception { + UndertowEmbeddedServletContainerFactory container = mock( + UndertowEmbeddedServletContainerFactory.class); + this.properties.customize(container); + verify(container, never()).setAccessLogEnabled(anyBoolean()); + } + private void bindProperties(Map map) { new RelaxedDataBinder(this.properties, "server") .bind(new MutablePropertyValues(map)); From a1284bce61e6391b8c6dcb4e5e4d97612a32ded1 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Sat, 9 Apr 2016 23:25:16 -0700 Subject: [PATCH 5/5] Configure ChildManagementContext container type Ensure any ChildManagementContext created to start a management server on a different port uses the same EmbeddedServletContainerFactory type. Fixes gh-5474 --- .../EndpointWebMvcAutoConfiguration.java | 26 +++++++++- .../EndpointWebMvcAutoConfigurationTests.java | 49 +++++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfiguration.java index 1687b696c47..66b72769f77 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfiguration.java @@ -30,11 +30,14 @@ import org.apache.commons.logging.LogFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.boot.actuate.endpoint.Endpoint; import org.springframework.boot.actuate.endpoint.mvc.ManagementServletContext; import org.springframework.boot.autoconfigure.AutoConfigureAfter; @@ -55,6 +58,7 @@ import org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfigurat import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration; import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext; +import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.EmbeddedWebApplicationContext; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -104,6 +108,8 @@ public class EndpointWebMvcAutoConfiguration private static final Log logger = LogFactory .getLog(EndpointWebMvcAutoConfiguration.class); + private static final ConfigurableListableBeanFactory BeanDefinitionRegistry = null; + private ApplicationContext applicationContext; private BeanFactory beanFactory; @@ -164,7 +170,7 @@ public class EndpointWebMvcAutoConfiguration } private void createChildManagementContext() { - final AnnotationConfigEmbeddedWebApplicationContext childContext = new AnnotationConfigEmbeddedWebApplicationContext(); + AnnotationConfigEmbeddedWebApplicationContext childContext = new AnnotationConfigEmbeddedWebApplicationContext(); childContext.setParent(this.applicationContext); childContext.setNamespace("management"); childContext.setId(this.applicationContext.getId() + ":management"); @@ -172,12 +178,30 @@ public class EndpointWebMvcAutoConfiguration PropertyPlaceholderAutoConfiguration.class, EmbeddedServletContainerAutoConfiguration.class, DispatcherServletAutoConfiguration.class); + registerEmbeddedServletContainerFactory(childContext); CloseEventPropagationListener.addIfPossible(this.applicationContext, childContext); childContext.refresh(); managementContextResolver().setApplicationContext(childContext); } + private void registerEmbeddedServletContainerFactory( + AnnotationConfigEmbeddedWebApplicationContext childContext) { + try { + EmbeddedServletContainerFactory servletContainerFactory = this.applicationContext + .getBean(EmbeddedServletContainerFactory.class); + ConfigurableListableBeanFactory beanFactory = childContext.getBeanFactory(); + if (beanFactory instanceof BeanDefinitionRegistry) { + BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; + registry.registerBeanDefinition("embeddedServletContainerFactory", + new RootBeanDefinition(servletContainerFactory.getClass())); + } + } + catch (NoSuchBeanDefinitionException ex) { + // Ignore and assume auto-configuration + } + } + /** * Add an alias for 'local.management.port' that actually resolves using * 'local.server.port'. diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfigurationTests.java index 3f7bfb2146b..cf2b94b32bf 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfigurationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfigurationTests.java @@ -58,7 +58,9 @@ import org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebAppl import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer; import org.springframework.boot.context.embedded.EmbeddedServletContainer; import org.springframework.boot.context.embedded.EmbeddedServletContainerException; +import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent; +import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory; import org.springframework.boot.context.web.ServerPortInfoApplicationContextInitializer; import org.springframework.boot.test.EnvironmentTestUtils; import org.springframework.context.ApplicationContext; @@ -85,6 +87,7 @@ import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.sameInstance; import static org.hamcrest.Matchers.startsWith; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -177,6 +180,35 @@ public class EndpointWebMvcAutoConfigurationTests { assertAllClosed(); } + @Test + public void onDifferentPortWithSpecificContainer() throws Exception { + this.applicationContext.register(SpecificContainerConfig.class, RootConfig.class, + DifferentPortConfig.class, EndpointConfig.class, BaseConfiguration.class, + EndpointWebMvcAutoConfiguration.class, ErrorMvcAutoConfiguration.class); + this.applicationContext.refresh(); + assertContent("/controller", ports.get().server, "controlleroutput"); + assertContent("/endpoint", ports.get().server, null); + assertContent("/controller", ports.get().management, null); + assertContent("/endpoint", ports.get().management, "endpointoutput"); + assertContent("/error", ports.get().management, startsWith("{")); + ApplicationContext managementContext = this.applicationContext + .getBean(ManagementContextResolver.class).getApplicationContext(); + List interceptors = (List) ReflectionTestUtils.getField( + managementContext.getBean(EndpointHandlerMapping.class), "interceptors"); + assertEquals(1, interceptors.size()); + EmbeddedServletContainerFactory parentContainerFactory = this.applicationContext + .getBean(EmbeddedServletContainerFactory.class); + EmbeddedServletContainerFactory managementContainerFactory = managementContext + .getBean(EmbeddedServletContainerFactory.class); + assertThat(parentContainerFactory, + instanceOf(SpecificEmbeddedServletContainerFactory.class)); + assertThat(managementContainerFactory, + instanceOf(SpecificEmbeddedServletContainerFactory.class)); + assertThat(managementContainerFactory, not(sameInstance(parentContainerFactory))); + this.applicationContext.close(); + assertAllClosed(); + } + @Test public void onDifferentPortAndContext() throws Exception { this.applicationContext.register(RootConfig.class, EndpointConfig.class, @@ -611,6 +643,16 @@ public class EndpointWebMvcAutoConfigurationTests { } + @Configuration + public static class SpecificContainerConfig { + + @Bean + public SpecificEmbeddedServletContainerFactory embeddedServletContainerFactory() { + return new SpecificEmbeddedServletContainerFactory(); + } + + } + @Configuration @Import(ServerPortConfig.class) public static class DifferentPortConfig { @@ -638,6 +680,7 @@ public class EndpointWebMvcAutoConfigurationTests { } protected static class TestInterceptor extends HandlerInterceptorAdapter { + private int count = 0; @Override @@ -650,6 +693,7 @@ public class EndpointWebMvcAutoConfigurationTests { public int getCount() { return this.count; } + } } @@ -730,4 +774,9 @@ public class EndpointWebMvcAutoConfigurationTests { } + private static class SpecificEmbeddedServletContainerFactory + extends TomcatEmbeddedServletContainerFactory { + + } + }