Commit 83f842a2 authored by Andy Wilkinson's avatar Andy Wilkinson

Reinstate support for relaxed binding for endpoint enablement

This commit improves upon the changes made in a8bf9d34 by adding
support for relaxed binding of the endpoints.enabled and
endpoints.<name>.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
parent d8f45ab6
...@@ -17,6 +17,10 @@ ...@@ -17,6 +17,10 @@
package org.springframework.boot.actuate.autoconfigure; package org.springframework.boot.actuate.autoconfigure;
import java.io.IOException; 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 java.util.List;
import javax.servlet.Filter; import javax.servlet.Filter;
...@@ -49,15 +53,17 @@ import org.springframework.boot.actuate.endpoint.mvc.ShutdownMvcEndpoint; ...@@ -49,15 +53,17 @@ import org.springframework.boot.actuate.endpoint.mvc.ShutdownMvcEndpoint;
import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; 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.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 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.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; 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.DispatcherServletAutoConfiguration;
import org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration; import org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration;
import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration; 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.AnnotationConfigEmbeddedWebApplicationContext;
import org.springframework.boot.context.embedded.EmbeddedServletContainerException; import org.springframework.boot.context.embedded.EmbeddedServletContainerException;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
...@@ -66,10 +72,14 @@ import org.springframework.context.ApplicationContextAware; ...@@ -66,10 +72,14 @@ import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean; 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.annotation.Configuration;
import org.springframework.context.event.ContextClosedEvent; import org.springframework.context.event.ContextClosedEvent;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertySource; import org.springframework.core.env.PropertySource;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.DispatcherServlet;
...@@ -156,14 +166,14 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware, ...@@ -156,14 +166,14 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware,
@Bean @Bean
@ConditionalOnBean(EnvironmentEndpoint.class) @ConditionalOnBean(EnvironmentEndpoint.class)
@ConditionalOnExpression("${endpoints.env.enabled:${endpoints.enabled:true}}") @ConditionalOnEnabledEndpoint("env")
public EnvironmentMvcEndpoint environmentMvcEndpoint(EnvironmentEndpoint delegate) { public EnvironmentMvcEndpoint environmentMvcEndpoint(EnvironmentEndpoint delegate) {
return new EnvironmentMvcEndpoint(delegate); return new EnvironmentMvcEndpoint(delegate);
} }
@Bean @Bean
@ConditionalOnBean(HealthEndpoint.class) @ConditionalOnBean(HealthEndpoint.class)
@ConditionalOnExpression("${endpoints.health.enabled:${endpoints.enabled:true}}") @ConditionalOnEnabledEndpoint("health")
public HealthMvcEndpoint healthMvcEndpoint(HealthEndpoint delegate) { public HealthMvcEndpoint healthMvcEndpoint(HealthEndpoint delegate) {
Security security = this.managementServerProperties.getSecurity(); Security security = this.managementServerProperties.getSecurity();
boolean secure = (security == null || security.isEnabled()); boolean secure = (security == null || security.isEnabled());
...@@ -177,14 +187,14 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware, ...@@ -177,14 +187,14 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware,
@Bean @Bean
@ConditionalOnBean(MetricsEndpoint.class) @ConditionalOnBean(MetricsEndpoint.class)
@ConditionalOnExpression("${endpoints.metrics.enabled:${endpoints.enabled:true}}") @ConditionalOnEnabledEndpoint("metrics")
public MetricsMvcEndpoint metricsMvcEndpoint(MetricsEndpoint delegate) { public MetricsMvcEndpoint metricsMvcEndpoint(MetricsEndpoint delegate) {
return new MetricsMvcEndpoint(delegate); return new MetricsMvcEndpoint(delegate);
} }
@Bean @Bean
@ConditionalOnBean(ShutdownEndpoint.class) @ConditionalOnBean(ShutdownEndpoint.class)
@ConditionalOnExpression("${endpoints.shutdown.enabled:false}") @ConditionalOnEnabledEndpoint(value = "shutdown", enabledByDefault = false)
public ShutdownMvcEndpoint shutdownMvcEndpoint(ShutdownEndpoint delegate) { public ShutdownMvcEndpoint shutdownMvcEndpoint(ShutdownEndpoint delegate) {
return new ShutdownMvcEndpoint(delegate); return new ShutdownMvcEndpoint(delegate);
} }
...@@ -330,4 +340,77 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware, ...@@ -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.<name>.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");
}
}
} }
...@@ -280,7 +280,7 @@ public class EndpointWebMvcAutoConfigurationTests { ...@@ -280,7 +280,7 @@ public class EndpointWebMvcAutoConfigurationTests {
this.applicationContext.register(RootConfig.class, BaseConfiguration.class, this.applicationContext.register(RootConfig.class, BaseConfiguration.class,
ServerPortConfig.class, EndpointWebMvcAutoConfiguration.class); ServerPortConfig.class, EndpointWebMvcAutoConfiguration.class);
EnvironmentTestUtils.addEnvironment(this.applicationContext, EnvironmentTestUtils.addEnvironment(this.applicationContext,
"endpoints.enabled:false"); "ENDPOINTS_ENABLED:false");
this.applicationContext.refresh(); this.applicationContext.refresh();
assertThat(this.applicationContext.getBeansOfType(MvcEndpoint.class).size(), assertThat(this.applicationContext.getBeansOfType(MvcEndpoint.class).size(),
is(equalTo(0))); is(equalTo(0)));
...@@ -342,7 +342,7 @@ public class EndpointWebMvcAutoConfigurationTests { ...@@ -342,7 +342,7 @@ public class EndpointWebMvcAutoConfigurationTests {
ServerPortConfig.class, EndpointWebMvcAutoConfiguration.class); ServerPortConfig.class, EndpointWebMvcAutoConfiguration.class);
EnvironmentTestUtils.addEnvironment(this.applicationContext, EnvironmentTestUtils.addEnvironment(this.applicationContext,
"endpoints.enabled:false", "endpoints.enabled:false",
String.format("endpoints.%s.enabled:true", name)); String.format("endpoints_%s_enabled:true", name));
this.applicationContext.refresh(); this.applicationContext.refresh();
assertThat(this.applicationContext.getBeansOfType(type).size(), is(equalTo(1))); assertThat(this.applicationContext.getBeansOfType(type).size(), is(equalTo(1)));
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment