diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcHypermediaManagementContextConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcHypermediaManagementContextConfiguration.java index 2994f282b4..12a0457404 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcHypermediaManagementContextConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcHypermediaManagementContextConfiguration.java @@ -54,6 +54,7 @@ import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; +import org.springframework.context.annotation.Configuration; import org.springframework.core.MethodParameter; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.io.ResourceLoader; @@ -117,14 +118,6 @@ public class EndpointWebMvcHypermediaManagementContextConfiguration { return new HalJsonMvcEndpoint(managementServletContext); } - @Bean - @ConditionalOnEnabledEndpoint("docs") - @ConditionalOnResource(resources = "classpath:/META-INF/resources/spring-boot-actuator/docs/index.html") - public DocsMvcEndpoint docsMvcEndpoint( - ManagementServletContext managementServletContext) { - return new DocsMvcEndpoint(managementServletContext); - } - @Bean @ConditionalOnBean(DocsMvcEndpoint.class) @ConditionalOnMissingBean(CurieProvider.class) @@ -133,12 +126,33 @@ public class EndpointWebMvcHypermediaManagementContextConfiguration { ManagementServerProperties management, DocsMvcEndpoint endpoint) { String path = management.getContextPath() + endpoint.getPath() + "/#spring_boot_actuator__{rel}"; - if (server.getPort().equals(management.getPort()) && management.getPort() != 0) { + if (serverAndManagementPortsAreTheSame(server, management)) { path = server.getPath(path); } return new DefaultCurieProvider("boot", new UriTemplate(path)); } + private boolean serverAndManagementPortsAreTheSame(ServerProperties server, + ManagementServerProperties management) { + if (server.getPort() == null) { + return management.getPort() == null; + } + return server.getPort().equals(management.getPort()) && management.getPort() != 0; + } + + @Configuration + static class DocsMvcEndpointConfiguration { + + @Bean + @ConditionalOnEnabledEndpoint("docs") + @ConditionalOnResource(resources = "classpath:/META-INF/resources/spring-boot-actuator/docs/index.html") + public DocsMvcEndpoint docsMvcEndpoint( + ManagementServletContext managementServletContext) { + return new DocsMvcEndpoint(managementServletContext); + } + + } + /** * Controller advice that adds links to the actuator endpoint's path. */ diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcHypermediaManagementContextConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcHypermediaManagementContextConfigurationTests.java new file mode 100644 index 0000000000..055a64f181 --- /dev/null +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcHypermediaManagementContextConfigurationTests.java @@ -0,0 +1,155 @@ +/* + * 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.actuate.autoconfigure; + +import java.net.URL; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import org.springframework.boot.actuate.endpoint.mvc.DocsMvcEndpoint; +import org.springframework.boot.actuate.endpoint.mvc.HalJsonMvcEndpoint; +import org.springframework.boot.actuate.endpoint.mvc.ManagementServletContext; +import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints; +import org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration; +import org.springframework.boot.autoconfigure.web.ServerProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.test.util.EnvironmentTestUtils; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.hateoas.Link; +import org.springframework.hateoas.hal.DefaultCurieProvider; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link EndpointWebMvcHypermediaManagementContextConfigurationTests}. + * + * @author Andy Wilkinson + */ +public class EndpointWebMvcHypermediaManagementContextConfigurationTests { + + private AnnotationConfigWebApplicationContext context; + + @Before + public void setRequestAttributes() { + RequestContextHolder.setRequestAttributes( + new ServletRequestAttributes(new MockHttpServletRequest())); + } + + @After + public void resetRequestAttributes() { + RequestContextHolder.resetRequestAttributes(); + } + + @After + public void closeContext() { + this.context.close(); + } + + @Test + public void basicConfiguration() { + load(); + assertThat(this.context.getBeansOfType(ManagementServletContext.class)) + .hasSize(1); + assertThat(this.context.getBeansOfType(HalJsonMvcEndpoint.class)).hasSize(1); + assertThat(this.context.getBeansOfType(DocsMvcEndpoint.class)).hasSize(1); + assertThat(this.context.getBeansOfType(DefaultCurieProvider.class)).isEmpty(); + } + + @Test + public void curiesEnabledWithDefaultPorts() { + load("endpoints.docs.curies.enabled:true"); + assertThat(getCurieHref()) + .isEqualTo("http://localhost/docs/#spring_boot_actuator__{rel}"); + } + + @Test + public void curiesEnabledWithRandomPorts() { + load("endpoints.docs.curies.enabled:true", "server.port:0", "management.port:0"); + assertThat(getCurieHref()) + .isEqualTo("http://localhost/docs/#spring_boot_actuator__{rel}"); + } + + @Test + public void curiesEnabledWithSpecificServerPort() { + load("endpoints.docs.curies.enabled:true", "server.port:8080"); + assertThat(getCurieHref()) + .isEqualTo("http://localhost/docs/#spring_boot_actuator__{rel}"); + } + + @Test + public void curiesEnabledWithSpecificManagementPort() { + load("endpoints.docs.curies.enabled:true", "management.port:8081"); + assertThat(getCurieHref()) + .isEqualTo("http://localhost/docs/#spring_boot_actuator__{rel}"); + } + + @Test + public void curiesEnabledWithSpecificManagementAndServerPorts() { + load("endpoints.docs.curies.enabled:true", "server.port:8080", + "management.port:8081"); + assertThat(getCurieHref()) + .isEqualTo("http://localhost/docs/#spring_boot_actuator__{rel}"); + } + + private void load(String... properties) { + this.context = new AnnotationConfigWebApplicationContext(); + this.context.setClassLoader(new ClassLoader(getClass().getClassLoader()) { + + @Override + public URL getResource(String name) { + if ("META-INF/resources/spring-boot-actuator/docs/index.html" + .equals(name)) { + return super.getResource("actuator-docs-index.html"); + } + return super.getResource(name); + } + + }); + EnvironmentTestUtils.addEnvironment(this.context, properties); + this.context.register(TestConfiguration.class, + HttpMessageConvertersAutoConfiguration.class, + EndpointWebMvcHypermediaManagementContextConfiguration.class); + this.context.refresh(); + } + + private String getCurieHref() { + DefaultCurieProvider curieProvider = this.context + .getBean(DefaultCurieProvider.class); + Link link = (Link) curieProvider.getCurieInformation(null).iterator().next(); + return link.getHref(); + } + + @Configuration + @EnableConfigurationProperties({ ManagementServerProperties.class, + ServerProperties.class }) + static class TestConfiguration { + + @Bean + public MvcEndpoints mvcEndpoints() { + return new MvcEndpoints(); + } + + } + +} diff --git a/spring-boot-actuator/src/test/resources/actuator-docs-index.html b/spring-boot-actuator/src/test/resources/actuator-docs-index.html new file mode 100644 index 0000000000..e69de29bb2