From 86cd7275a1550c9772da6f113812541bf6a5fcdb Mon Sep 17 00:00:00 2001 From: Madhura Bhave Date: Wed, 6 Sep 2017 14:34:39 -0700 Subject: [PATCH] MVC endpoints bean methods are @ConditionalOnMissingBean Fixes gh-10105 --- ...ermediaManagementContextConfiguration.java | 4 +- ...tWebMvcManagementContextConfiguration.java | 6 + ...iaManagementContextConfigurationTests.java | 73 ++++- ...vcManagementContextConfigurationTests.java | 259 +++++++++++++++++- 4 files changed, 328 insertions(+), 14 deletions(-) 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 f47155dfd7a..0f6761368df 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 @@ -111,8 +111,9 @@ public class EndpointWebMvcHypermediaManagementContextConfiguration { }; } - @ConditionalOnEnabledEndpoint("actuator") @Bean + @ConditionalOnEnabledEndpoint("actuator") + @ConditionalOnMissingBean public HalJsonMvcEndpoint halJsonMvcEndpoint( ManagementServletContext managementServletContext, ResourceProperties resources, ResourceLoader resourceLoader) { @@ -137,6 +138,7 @@ public class EndpointWebMvcHypermediaManagementContextConfiguration { static class DocsMvcEndpointConfiguration { @Bean + @ConditionalOnMissingBean @ConditionalOnEnabledEndpoint("docs") @ConditionalOnResource(resources = "classpath:/META-INF/resources/spring-boot-actuator/docs/index.html") public DocsMvcEndpoint docsMvcEndpoint( diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcManagementContextConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcManagementContextConfiguration.java index c7d30a745c0..978ea185c2f 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcManagementContextConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcManagementContextConfiguration.java @@ -143,6 +143,7 @@ public class EndpointWebMvcManagementContextConfiguration { } @Bean + @ConditionalOnMissingBean @ConditionalOnBean(EnvironmentEndpoint.class) @ConditionalOnEnabledEndpoint("env") public EnvironmentMvcEndpoint environmentMvcEndpoint(EnvironmentEndpoint delegate) { @@ -173,6 +174,7 @@ public class EndpointWebMvcManagementContextConfiguration { } @Bean + @ConditionalOnMissingBean @ConditionalOnBean(LoggersEndpoint.class) @ConditionalOnEnabledEndpoint("loggers") public LoggersMvcEndpoint loggersMvcEndpoint(LoggersEndpoint delegate) { @@ -180,6 +182,7 @@ public class EndpointWebMvcManagementContextConfiguration { } @Bean + @ConditionalOnMissingBean @ConditionalOnBean(MetricsEndpoint.class) @ConditionalOnEnabledEndpoint("metrics") public MetricsMvcEndpoint metricsMvcEndpoint(MetricsEndpoint delegate) { @@ -187,6 +190,7 @@ public class EndpointWebMvcManagementContextConfiguration { } @Bean + @ConditionalOnMissingBean @ConditionalOnEnabledEndpoint("logfile") @Conditional(LogFileCondition.class) public LogFileMvcEndpoint logfileMvcEndpoint() { @@ -194,6 +198,7 @@ public class EndpointWebMvcManagementContextConfiguration { } @Bean + @ConditionalOnMissingBean @ConditionalOnBean(ShutdownEndpoint.class) @ConditionalOnEnabledEndpoint(value = "shutdown", enabledByDefault = false) public ShutdownMvcEndpoint shutdownMvcEndpoint(ShutdownEndpoint delegate) { @@ -201,6 +206,7 @@ public class EndpointWebMvcManagementContextConfiguration { } @Bean + @ConditionalOnMissingBean @ConditionalOnBean(AuditEventRepository.class) @ConditionalOnEnabledEndpoint("auditevents") public AuditEventsMvcEndpoint auditEventMvcEndpoint( 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 index f588333ab67..99a1cf169db 100644 --- 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 @@ -45,6 +45,7 @@ import static org.assertj.core.api.Assertions.assertThat; * Tests for {@link EndpointWebMvcHypermediaManagementContextConfiguration}. * * @author Andy Wilkinson + * @author Madhura Bhave */ public class EndpointWebMvcHypermediaManagementContextConfigurationTests { @@ -112,7 +113,38 @@ public class EndpointWebMvcHypermediaManagementContextConfigurationTests { .isEqualTo("http://localhost/docs/#spring_boot_actuator__{rel}"); } + @Test + public void halJsonMvcEndpointIsConditionalOnMissingBean() throws Exception { + createContext(); + this.context.register(HalJsonConfiguration.class, TestConfiguration.class, + HttpMessageConvertersAutoConfiguration.class, + EndpointWebMvcHypermediaManagementContextConfiguration.class); + this.context.refresh(); + HalJsonMvcEndpoint bean = this.context.getBean(HalJsonMvcEndpoint.class); + assertThat(bean).isInstanceOf(TestHalJsonMvcEndpoint.class); + } + + @Test + public void docsMvcEndpointIsConditionalOnMissingBean() throws Exception { + createContext(); + this.context.register(DocsConfiguration.class, TestConfiguration.class, + HttpMessageConvertersAutoConfiguration.class, + EndpointWebMvcHypermediaManagementContextConfiguration.class); + this.context.refresh(); + DocsMvcEndpoint bean = this.context.getBean(DocsMvcEndpoint.class); + assertThat(bean).isInstanceOf(TestDocsMvcEndpoint.class); + } + private void load(String... properties) { + createContext(); + EnvironmentTestUtils.addEnvironment(this.context, properties); + this.context.register(TestConfiguration.class, + HttpMessageConvertersAutoConfiguration.class, + EndpointWebMvcHypermediaManagementContextConfiguration.class); + this.context.refresh(); + } + + private void createContext() { this.context = new AnnotationConfigWebApplicationContext(); this.context.setClassLoader(new ClassLoader(getClass().getClassLoader()) { @@ -126,11 +158,6 @@ public class EndpointWebMvcHypermediaManagementContextConfigurationTests { } }); - EnvironmentTestUtils.addEnvironment(this.context, properties); - this.context.register(TestConfiguration.class, - HttpMessageConvertersAutoConfiguration.class, - EndpointWebMvcHypermediaManagementContextConfiguration.class); - this.context.refresh(); } private String getCurieHref() { @@ -152,4 +179,40 @@ public class EndpointWebMvcHypermediaManagementContextConfigurationTests { } + @Configuration + static class DocsConfiguration { + + @Bean + public DocsMvcEndpoint testDocsMvcEndpoint(ManagementServletContext managementServletContext) { + return new TestDocsMvcEndpoint(managementServletContext); + } + + } + + @Configuration + static class HalJsonConfiguration { + + @Bean + public HalJsonMvcEndpoint testHalJsonMvcEndpoint(ManagementServletContext managementServletContext) { + return new TestHalJsonMvcEndpoint(managementServletContext); + } + + } + + static class TestDocsMvcEndpoint extends DocsMvcEndpoint { + + TestDocsMvcEndpoint(ManagementServletContext managementServletContext) { + super(managementServletContext); + } + + } + + static class TestHalJsonMvcEndpoint extends HalJsonMvcEndpoint { + + TestHalJsonMvcEndpoint(ManagementServletContext managementServletContext) { + super(managementServletContext); + } + + } + } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcManagementContextConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcManagementContextConfigurationTests.java index ae96f997c3d..af531ad336f 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcManagementContextConfigurationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcManagementContextConfigurationTests.java @@ -16,21 +16,43 @@ package org.springframework.boot.actuate.autoconfigure; +import java.security.Principal; import java.util.List; +import javax.servlet.http.HttpServletRequest; + import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.springframework.boot.actuate.audit.AuditEventRepository; +import org.springframework.boot.actuate.audit.InMemoryAuditEventRepository; +import org.springframework.boot.actuate.endpoint.EnvironmentEndpoint; +import org.springframework.boot.actuate.endpoint.HealthEndpoint; +import org.springframework.boot.actuate.endpoint.LoggersEndpoint; +import org.springframework.boot.actuate.endpoint.MetricsEndpoint; +import org.springframework.boot.actuate.endpoint.ShutdownEndpoint; +import org.springframework.boot.actuate.endpoint.mvc.AuditEventsMvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping; +import org.springframework.boot.actuate.endpoint.mvc.EnvironmentMvcEndpoint; +import org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint; +import org.springframework.boot.actuate.endpoint.mvc.HeapdumpMvcEndpoint; +import org.springframework.boot.actuate.endpoint.mvc.LogFileMvcEndpoint; +import org.springframework.boot.actuate.endpoint.mvc.LoggersMvcEndpoint; +import org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.MvcEndpointSecurityInterceptor; +import org.springframework.boot.actuate.endpoint.mvc.ShutdownMvcEndpoint; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration; import org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration; import org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration; import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration; +import org.springframework.boot.logging.LoggingSystem; import org.springframework.boot.test.util.EnvironmentTestUtils; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.mock.web.MockServletContext; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; @@ -50,14 +72,6 @@ public class EndpointWebMvcManagementContextConfigurationTests { public void setup() { this.context = new AnnotationConfigWebApplicationContext(); this.context.setServletContext(new MockServletContext()); - this.context.register(SecurityAutoConfiguration.class, - WebMvcAutoConfiguration.class, JacksonAutoConfiguration.class, - HttpMessageConvertersAutoConfiguration.class, - EndpointAutoConfiguration.class, EndpointWebMvcAutoConfiguration.class, - ManagementServerPropertiesAutoConfiguration.class, - PropertyPlaceholderAutoConfiguration.class, - WebClientAutoConfiguration.class, - EndpointWebMvcManagementContextConfiguration.class); } @After @@ -72,6 +86,7 @@ public class EndpointWebMvcManagementContextConfigurationTests { EnvironmentTestUtils.addEnvironment(this.context, "management.security.enabled=false", "management.security.roles=my-role,your-role"); + this.context.register(TestEndpointConfiguration.class); this.context.refresh(); EndpointHandlerMapping mapping = this.context.getBean("endpointHandlerMapping", EndpointHandlerMapping.class); @@ -84,9 +99,237 @@ public class EndpointWebMvcManagementContextConfigurationTests { assertThat(roles).containsExactly("my-role", "your-role"); } + @Test + public void healthMvcEndpointIsConditionalOnMissingBean() throws Exception { + this.context.register(HealthConfiguration.class, TestEndpointConfiguration.class); + this.context.refresh(); + HealthMvcEndpoint mvcEndpoint = this.context.getBean(HealthMvcEndpoint.class); + assertThat(mvcEndpoint).isInstanceOf(TestHealthMvcEndpoint.class); + } + + @Test + public void envMvcEndpointIsConditionalOnMissingBean() throws Exception { + this.context.register(EnvConfiguration.class, TestEndpointConfiguration.class); + this.context.refresh(); + EnvironmentMvcEndpoint mvcEndpoint = this.context.getBean(EnvironmentMvcEndpoint.class); + assertThat(mvcEndpoint).isInstanceOf(TestEnvMvcEndpoint.class); + } + + @Test + public void metricsMvcEndpointIsConditionalOnMissingBean() throws Exception { + this.context.register(MetricsConfiguration.class, TestEndpointConfiguration.class); + this.context.refresh(); + MetricsMvcEndpoint mvcEndpoint = this.context.getBean(MetricsMvcEndpoint.class); + assertThat(mvcEndpoint).isInstanceOf(TestMetricsMvcEndpoint.class); + } + + @Test + public void logFileMvcEndpointIsConditionalOnMissingBean() throws Exception { + this.context.register(LogFileConfiguration.class, TestEndpointConfiguration.class); + this.context.refresh(); + LogFileMvcEndpoint mvcEndpoint = this.context.getBean(LogFileMvcEndpoint.class); + assertThat(mvcEndpoint).isInstanceOf(TestLogFileMvcEndpoint.class); + } + + @Test + public void shutdownEndpointIsConditionalOnMissingBean() throws Exception { + this.context.register(ShutdownConfiguration.class, TestEndpointConfiguration.class); + this.context.refresh(); + ShutdownMvcEndpoint mvcEndpoint = this.context.getBean(ShutdownMvcEndpoint.class); + assertThat(mvcEndpoint).isInstanceOf(TestShutdownMvcEndpoint.class); + } + + @Test + public void auditEventsMvcEndpointIsConditionalOnMissingBean() throws Exception { + this.context.register(AuditEventsConfiguration.class, TestEndpointConfiguration.class); + this.context.refresh(); + AuditEventsMvcEndpoint mvcEndpoint = this.context.getBean(AuditEventsMvcEndpoint.class); + assertThat(mvcEndpoint).isInstanceOf(TestAuditEventsMvcEndpoint.class); + } + + @Test + public void loggersMvcEndpointIsConditionalOnMissingBean() throws Exception { + this.context.register(LoggersConfiguration.class, TestEndpointConfiguration.class); + this.context.refresh(); + LoggersMvcEndpoint mvcEndpoint = this.context.getBean(LoggersMvcEndpoint.class); + assertThat(mvcEndpoint).isInstanceOf(TestLoggersMvcEndpoint.class); + } + + @Test + public void heapdumpMvcEndpointIsConditionalOnMissingBean() throws Exception { + this.context.register(HeapdumpConfiguration.class, TestEndpointConfiguration.class); + this.context.refresh(); + HeapdumpMvcEndpoint mvcEndpoint = this.context.getBean(HeapdumpMvcEndpoint.class); + assertThat(mvcEndpoint).isInstanceOf(TestHeapdumpMvcEndpoint.class); + } + @SuppressWarnings("unchecked") private List getRoles(MvcEndpointSecurityInterceptor securityInterceptor) { return (List) ReflectionTestUtils.getField(securityInterceptor, "roles"); } + @Configuration + @ImportAutoConfiguration({ SecurityAutoConfiguration.class, + WebMvcAutoConfiguration.class, JacksonAutoConfiguration.class, + HttpMessageConvertersAutoConfiguration.class, + EndpointAutoConfiguration.class, EndpointWebMvcAutoConfiguration.class, + ManagementServerPropertiesAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class, + WebClientAutoConfiguration.class, + EndpointWebMvcManagementContextConfiguration.class }) + static class TestEndpointConfiguration { + + } + + @Configuration + static class HealthConfiguration { + + @Bean + public HealthMvcEndpoint testHealthMvcEndpoint(HealthEndpoint endpoint) { + return new TestHealthMvcEndpoint(endpoint); + } + + } + + static class EnvConfiguration { + + @Bean + public EnvironmentMvcEndpoint testEnvironmentMvcEndpoint(EnvironmentEndpoint endpoint) { + return new TestEnvMvcEndpoint(endpoint); + } + + } + + static class MetricsConfiguration { + + @Bean + public MetricsMvcEndpoint testMetricsMvcEndpoint(MetricsEndpoint endpoint) { + return new TestMetricsMvcEndpoint(endpoint); + } + + } + + static class LoggersConfiguration { + + @Bean + public LoggersMvcEndpoint testLoggersMvcEndpoint(LoggersEndpoint endpoint) { + return new TestLoggersMvcEndpoint(endpoint); + } + + @Bean + LoggersEndpoint loggersEndpoint() { + return new LoggersEndpoint(new LoggingSystem() { + @Override + public void beforeInitialize() { + + } + }); + } + + } + + static class LogFileConfiguration { + + @Bean + public LogFileMvcEndpoint testLogFileMvcEndpoint() { + return new TestLogFileMvcEndpoint(); + } + + } + + static class AuditEventsConfiguration { + + @Bean + public AuditEventRepository repository() { + return new TestAuditEventRepository(); + } + + @Bean + public AuditEventsMvcEndpoint testAuditEventsMvcEndpoint(AuditEventRepository repository) { + return new TestAuditEventsMvcEndpoint(repository); + } + + } + + static class TestAuditEventRepository extends InMemoryAuditEventRepository { + + } + + static class HeapdumpConfiguration { + + @Bean + public HeapdumpMvcEndpoint testHeapdumpMvcEndpoint() { + return new TestHeapdumpMvcEndpoint(); + } + + } + + static class ShutdownConfiguration { + + @Bean + public ShutdownMvcEndpoint testShutdownMvcEndpoint(ShutdownEndpoint endpoint) { + return new TestShutdownMvcEndpoint(endpoint); + } + + } + + static class TestHealthMvcEndpoint extends HealthMvcEndpoint { + + TestHealthMvcEndpoint(HealthEndpoint delegate) { + super(delegate); + } + + @Override + protected boolean exposeHealthDetails(HttpServletRequest request, + Principal principal) { + return true; + } + + } + + static class TestEnvMvcEndpoint extends EnvironmentMvcEndpoint { + + TestEnvMvcEndpoint(EnvironmentEndpoint delegate) { + super(delegate); + } + + } + + static class TestLoggersMvcEndpoint extends LoggersMvcEndpoint { + + TestLoggersMvcEndpoint(LoggersEndpoint delegate) { + super(delegate); + } + + } + + static class TestHeapdumpMvcEndpoint extends HeapdumpMvcEndpoint { + + } + + static class TestLogFileMvcEndpoint extends LogFileMvcEndpoint { + + } + + static class TestMetricsMvcEndpoint extends MetricsMvcEndpoint { + + TestMetricsMvcEndpoint(MetricsEndpoint delegate) { + super(delegate); + } + } + + static class TestAuditEventsMvcEndpoint extends AuditEventsMvcEndpoint { + + TestAuditEventsMvcEndpoint(AuditEventRepository auditEventRepository) { + super(auditEventRepository); + } + } + + static class TestShutdownMvcEndpoint extends ShutdownMvcEndpoint { + + TestShutdownMvcEndpoint(ShutdownEndpoint delegate) { + super(delegate); + } + } + }