From 83f842a22617ff216ec247104567675aa5f9e38b Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 8 Apr 2015 10:44:50 +0100 Subject: [PATCH] Reinstate support for relaxed binding for endpoint enablement This commit improves upon the changes made in a8bf9d3 by adding support for relaxed binding of the endpoints.enabled and endpoints..enabled properties. This is achieved by replacing use of @ConditionalOnExpression (which does not support relaxed binding) with a custom condition implementation that uses RelaxedPropertyResolver. Closes gh-2767 --- .../EndpointWebMvcAutoConfiguration.java | 93 ++++++++++++++++++- .../EndpointWebMvcAutoConfigurationTests.java | 4 +- 2 files changed, 90 insertions(+), 7 deletions(-) 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 174b02af599..15fd336182b 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 @@ -17,6 +17,10 @@ package org.springframework.boot.actuate.autoconfigure; import java.io.IOException; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.List; import javax.servlet.Filter; @@ -49,15 +53,17 @@ import org.springframework.boot.actuate.endpoint.mvc.ShutdownMvcEndpoint; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.boot.autoconfigure.condition.SpringBootCondition; import org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration; import org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration; import org.springframework.boot.autoconfigure.web.ServerProperties; 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.EmbeddedServletContainerException; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -66,10 +72,14 @@ import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationListener; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.context.event.ContextClosedEvent; +import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.PropertySource; +import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.servlet.DispatcherServlet; @@ -156,14 +166,14 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware, @Bean @ConditionalOnBean(EnvironmentEndpoint.class) - @ConditionalOnExpression("${endpoints.env.enabled:${endpoints.enabled:true}}") + @ConditionalOnEnabledEndpoint("env") public EnvironmentMvcEndpoint environmentMvcEndpoint(EnvironmentEndpoint delegate) { return new EnvironmentMvcEndpoint(delegate); } @Bean @ConditionalOnBean(HealthEndpoint.class) - @ConditionalOnExpression("${endpoints.health.enabled:${endpoints.enabled:true}}") + @ConditionalOnEnabledEndpoint("health") public HealthMvcEndpoint healthMvcEndpoint(HealthEndpoint delegate) { Security security = this.managementServerProperties.getSecurity(); boolean secure = (security == null || security.isEnabled()); @@ -177,14 +187,14 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware, @Bean @ConditionalOnBean(MetricsEndpoint.class) - @ConditionalOnExpression("${endpoints.metrics.enabled:${endpoints.enabled:true}}") + @ConditionalOnEnabledEndpoint("metrics") public MetricsMvcEndpoint metricsMvcEndpoint(MetricsEndpoint delegate) { return new MetricsMvcEndpoint(delegate); } @Bean @ConditionalOnBean(ShutdownEndpoint.class) - @ConditionalOnExpression("${endpoints.shutdown.enabled:false}") + @ConditionalOnEnabledEndpoint(value = "shutdown", enabledByDefault = false) public ShutdownMvcEndpoint shutdownMvcEndpoint(ShutdownEndpoint delegate) { return new ShutdownMvcEndpoint(delegate); } @@ -330,4 +340,77 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware, } + /** + * {@link Conditional} that checks whether or not an endpoint is enabled. Matches if + * the value of the {@code endpoints..enabled} property is {@code true}. Does + * not match if the property's value or {@code enabledByDefault} is {@code false}. + * Otherwise, matches if the value of the {@code endpoints.enabled} property is + * {@code true} or if the property is not configured. + * + * @since 1.2.4 + */ + @Conditional(OnEnabledEndpointCondition.class) + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + public static @interface ConditionalOnEnabledEndpoint { + + /** + * The name of the endpoint. + * @return The name of the endpoint + */ + public String value(); + + /** + * Returns whether or not the endpoint is enabled by default. + * @return {@code true} if the endpoint is enabled by default, otherwise + * {@code false} + */ + public boolean enabledByDefault() default true; + + } + + private static class OnEnabledEndpointCondition extends SpringBootCondition { + + @Override + public ConditionOutcome getMatchOutcome(ConditionContext context, + AnnotatedTypeMetadata metadata) { + AnnotationAttributes annotationAttributes = AnnotationAttributes + .fromMap(metadata.getAnnotationAttributes(ConditionalOnEnabledEndpoint.class + .getName())); + String endpointName = annotationAttributes.getString("value"); + boolean enabledByDefault = annotationAttributes + .getBoolean("enabledByDefault"); + ConditionOutcome specificEndpointOutcome = determineSpecificEndpointOutcome( + endpointName, enabledByDefault, context); + if (specificEndpointOutcome != null) { + return specificEndpointOutcome; + } + return determineAllEndpointsOutcome(context); + + } + + private ConditionOutcome determineSpecificEndpointOutcome(String endpointName, + boolean enabledByDefault, ConditionContext context) { + RelaxedPropertyResolver endpointPropertyResolver = new RelaxedPropertyResolver( + context.getEnvironment(), "endpoints." + endpointName + "."); + if (endpointPropertyResolver.containsProperty("enabled") || !enabledByDefault) { + boolean match = endpointPropertyResolver.getProperty("enabled", + Boolean.class, enabledByDefault); + return new ConditionOutcome(match, "The " + endpointName + " is " + + (match ? "enabled" : "disabled")); + } + return null; + } + + private ConditionOutcome determineAllEndpointsOutcome(ConditionContext context) { + RelaxedPropertyResolver allEndpointsPropertyResolver = new RelaxedPropertyResolver( + context.getEnvironment(), "endpoints."); + boolean match = Boolean.valueOf(allEndpointsPropertyResolver.getProperty( + "enabled", "true")); + return new ConditionOutcome(match, "All endpoints are " + + (match ? "enabled" : "disabled") + " by default"); + } + + } + } 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 73dab07bab3..57154f9ef93 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 @@ -280,7 +280,7 @@ public class EndpointWebMvcAutoConfigurationTests { this.applicationContext.register(RootConfig.class, BaseConfiguration.class, ServerPortConfig.class, EndpointWebMvcAutoConfiguration.class); EnvironmentTestUtils.addEnvironment(this.applicationContext, - "endpoints.enabled:false"); + "ENDPOINTS_ENABLED:false"); this.applicationContext.refresh(); assertThat(this.applicationContext.getBeansOfType(MvcEndpoint.class).size(), is(equalTo(0))); @@ -342,7 +342,7 @@ public class EndpointWebMvcAutoConfigurationTests { ServerPortConfig.class, EndpointWebMvcAutoConfiguration.class); EnvironmentTestUtils.addEnvironment(this.applicationContext, "endpoints.enabled:false", - String.format("endpoints.%s.enabled:true", name)); + String.format("endpoints_%s_enabled:true", name)); this.applicationContext.refresh(); assertThat(this.applicationContext.getBeansOfType(type).size(), is(equalTo(1))); }