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 174b02af599c..15fd336182be 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.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.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 MvcEndpoints mvcEndpoints() { @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 HealthMvcEndpoint healthMvcEndpoint(HealthEndpoint delegate) { @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 static ManagementServerPort get(BeanFactory beanFactory) { } + /** + * {@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 73dab07bab3c..57154f9ef93a 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 void endpointsAllDisabled() throws Exception { 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 @@ private void endpointEnabledOverride(String name, Class t 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))); }