From 2c959b8e2ac9f51fbceb3e288a297bf832651957 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 28 Nov 2017 18:39:45 -0800 Subject: [PATCH] Polish health indicators Align reactive and non-reactive web extensions and update `showDetails` so that it only applies to web exposure. See gh-11113 See gh-11192 --- ...ndpointManagementContextConfiguration.java | 27 ++++--- ...oundryWebAnnotationEndpointDiscoverer.java | 38 ++++----- ...dryReactiveHealthEndpointWebExtension.java | 24 ++---- ...CloudFoundryActuatorAutoConfiguration.java | 3 +- ...CloudFoundryActuatorAutoConfiguration.java | 3 +- ...loudFoundryHealthEndpointWebExtension.java | 20 ++--- .../HealthEndpointAutoConfiguration.java | 49 +----------- .../health/HealthEndpointProperties.java | 3 +- .../health/HealthIndicatorBeansComposite.java | 80 +++++++++++++++++++ ...HealthIndicatorBeansReactiveComposite.java | 57 +++++++++++++ ...ndpointManagementContextConfiguration.java | 12 ++- .../CloudFoundryEndpointFilterTests.java | 15 +++- ...yWebAnnotationEndpointDiscovererTests.java | 5 +- ...FoundryActuatorAutoConfigurationTests.java | 15 ++-- ...FoundryActuatorAutoConfigurationTests.java | 18 +++-- .../HealthEndpointDocumentationTests.java | 2 +- .../HealthEndpointAutoConfigurationTests.java | 2 +- .../AnnotationEndpointDiscoverer.java | 15 +++- .../boot/actuate/health/HealthEndpoint.java | 16 +--- .../health/HealthEndpointWebExtension.java | 16 +++- .../ReactiveHealthEndpointWebExtension.java | 6 +- .../actuate/health/HealthEndpointTests.java | 16 +--- .../HealthEndpointWebIntegrationTests.java | 10 ++- 23 files changed, 280 insertions(+), 172 deletions(-) create mode 100644 spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthIndicatorBeansComposite.java create mode 100644 spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthIndicatorBeansReactiveComposite.java diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryHealthWebEndpointManagementContextConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryHealthWebEndpointManagementContextConfiguration.java index 86f027cbc2..413f508ed4 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryHealthWebEndpointManagementContextConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryHealthWebEndpointManagementContextConfiguration.java @@ -23,13 +23,15 @@ import org.springframework.boot.actuate.autoconfigure.cloudfoundry.servlet.Cloud import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint; import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration; import org.springframework.boot.actuate.health.HealthEndpoint; -import org.springframework.boot.actuate.health.HealthStatusHttpMapper; -import org.springframework.boot.actuate.health.ReactiveHealthIndicator; +import org.springframework.boot.actuate.health.HealthEndpointWebExtension; +import org.springframework.boot.actuate.health.ReactiveHealthEndpointWebExtension; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.boot.cloud.CloudPlatform; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -39,7 +41,9 @@ import org.springframework.context.annotation.Configuration; * @author Madhura Bhave */ @Configuration -@AutoConfigureBefore({ ReactiveCloudFoundryActuatorAutoConfiguration.class, CloudFoundryActuatorAutoConfiguration.class }) +@ConditionalOnCloudPlatform(CloudPlatform.CLOUD_FOUNDRY) +@AutoConfigureBefore({ ReactiveCloudFoundryActuatorAutoConfiguration.class, + CloudFoundryActuatorAutoConfiguration.class }) @AutoConfigureAfter(HealthEndpointAutoConfiguration.class) public class CloudFoundryHealthWebEndpointManagementContextConfiguration { @@ -50,11 +54,10 @@ public class CloudFoundryHealthWebEndpointManagementContextConfiguration { @Bean @ConditionalOnMissingBean @ConditionalOnEnabledEndpoint - @ConditionalOnBean(HealthEndpoint.class) + @ConditionalOnBean({ HealthEndpoint.class, HealthEndpointWebExtension.class }) public CloudFoundryHealthEndpointWebExtension cloudFoundryHealthEndpointWebExtension( - HealthEndpoint healthEndpoint, HealthStatusHttpMapper healthStatusHttpMapper) { - HealthEndpoint delegate = new HealthEndpoint(healthEndpoint.getHealthIndicator(), true); - return new CloudFoundryHealthEndpointWebExtension(delegate, healthStatusHttpMapper); + HealthEndpointWebExtension healthEndpointWebExtension) { + return new CloudFoundryHealthEndpointWebExtension(healthEndpointWebExtension); } } @@ -66,12 +69,12 @@ public class CloudFoundryHealthWebEndpointManagementContextConfiguration { @Bean @ConditionalOnMissingBean @ConditionalOnEnabledEndpoint - @ConditionalOnBean(HealthEndpoint.class) + @ConditionalOnBean({ HealthEndpoint.class, + ReactiveHealthEndpointWebExtension.class }) public CloudFoundryReactiveHealthEndpointWebExtension cloudFoundryReactiveHealthEndpointWebExtension( - ReactiveHealthIndicator reactiveHealthIndicator, - HealthStatusHttpMapper healthStatusHttpMapper) { - return new CloudFoundryReactiveHealthEndpointWebExtension(reactiveHealthIndicator, - healthStatusHttpMapper); + ReactiveHealthEndpointWebExtension reactiveHealthEndpointWebExtension) { + return new CloudFoundryReactiveHealthEndpointWebExtension( + reactiveHealthEndpointWebExtension); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryWebAnnotationEndpointDiscoverer.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryWebAnnotationEndpointDiscoverer.java index 68d167435d..b10b43dbfe 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryWebAnnotationEndpointDiscoverer.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryWebAnnotationEndpointDiscoverer.java @@ -17,9 +17,9 @@ package org.springframework.boot.actuate.autoconfigure.cloudfoundry; import java.util.Collection; -import java.util.Map; import org.springframework.boot.actuate.endpoint.EndpointFilter; +import org.springframework.boot.actuate.endpoint.EndpointInfo; import org.springframework.boot.actuate.endpoint.reflect.OperationMethodInvokerAdvisor; import org.springframework.boot.actuate.endpoint.reflect.ParameterMapper; import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes; @@ -30,34 +30,36 @@ import org.springframework.boot.actuate.health.HealthEndpoint; import org.springframework.context.ApplicationContext; /** - * {@link WebAnnotationEndpointDiscoverer} for Cloud Foundry that uses Cloud Foundry specific - * extensions for the {@link HealthEndpoint}. + * {@link WebAnnotationEndpointDiscoverer} for Cloud Foundry that uses Cloud Foundry + * specific extensions for the {@link HealthEndpoint}. * * @author Madhura Bhave */ -public class CloudFoundryWebAnnotationEndpointDiscoverer extends WebAnnotationEndpointDiscoverer { - - private final ApplicationContext applicationContext; +public class CloudFoundryWebAnnotationEndpointDiscoverer + extends WebAnnotationEndpointDiscoverer { private final Class requiredExtensionType; - public CloudFoundryWebAnnotationEndpointDiscoverer(ApplicationContext applicationContext, ParameterMapper parameterMapper, - EndpointMediaTypes endpointMediaTypes, EndpointPathResolver endpointPathResolver, - Collection invokerAdvisors, Collection> filters, Class requiredExtensionType) { - super(applicationContext, parameterMapper, endpointMediaTypes, endpointPathResolver, invokerAdvisors, filters); - this.applicationContext = applicationContext; + public CloudFoundryWebAnnotationEndpointDiscoverer( + ApplicationContext applicationContext, ParameterMapper parameterMapper, + EndpointMediaTypes endpointMediaTypes, + EndpointPathResolver endpointPathResolver, + Collection invokerAdvisors, + Collection> filters, + Class requiredExtensionType) { + super(applicationContext, parameterMapper, endpointMediaTypes, + endpointPathResolver, invokerAdvisors, filters); this.requiredExtensionType = requiredExtensionType; } @Override - protected void addExtension(Map, DiscoveredEndpoint> endpoints, Map, DiscoveredExtension> extensions, String beanName) { - Class extensionType = this.applicationContext.getType(beanName); - Class endpointType = getEndpointType(extensionType); - if (HealthEndpoint.class.equals(endpointType) && !this.requiredExtensionType.equals(extensionType)) { - return; + protected boolean isExtensionExposed(Class endpointType, Class extensionType, + EndpointInfo endpointInfo) { + if (HealthEndpoint.class.equals(endpointType) + && !this.requiredExtensionType.equals(extensionType)) { + return false; } - super.addExtension(endpoints, extensions, beanName); + return super.isExtensionExposed(endpointType, extensionType, endpointInfo); } } - diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryReactiveHealthEndpointWebExtension.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryReactiveHealthEndpointWebExtension.java index 7f9aa5b2c3..5b6a4a2a0e 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryReactiveHealthEndpointWebExtension.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryReactiveHealthEndpointWebExtension.java @@ -25,36 +25,28 @@ import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse; import org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExtension; import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.HealthEndpoint; -import org.springframework.boot.actuate.health.HealthStatusHttpMapper; -import org.springframework.boot.actuate.health.ReactiveHealthIndicator; +import org.springframework.boot.actuate.health.ReactiveHealthEndpointWebExtension; /** - * Reactive {@link EndpointWebExtension} for the {@link HealthEndpoint} - * that always exposes full health details. + * Reactive {@link EndpointWebExtension} for the {@link HealthEndpoint} that always + * exposes full health details. * * @author Madhura Bhave + * @since 2.0.0 */ @EndpointExtension(filter = CloudFoundryEndpointFilter.class, endpoint = HealthEndpoint.class) public class CloudFoundryReactiveHealthEndpointWebExtension { - private final ReactiveHealthIndicator delegate; + private final ReactiveHealthEndpointWebExtension delegate; - private final HealthStatusHttpMapper statusHttpMapper; - - public CloudFoundryReactiveHealthEndpointWebExtension(ReactiveHealthIndicator delegate, - HealthStatusHttpMapper statusHttpMapper) { + public CloudFoundryReactiveHealthEndpointWebExtension( + ReactiveHealthEndpointWebExtension delegate) { this.delegate = delegate; - this.statusHttpMapper = statusHttpMapper; } @ReadOperation public Mono> health() { - return this.delegate.health().map((health) -> { - Integer status = this.statusHttpMapper.mapStatus(health.getStatus()); - return new WebEndpointResponse<>(health, status); - }); + return this.delegate.health(true); } } - - diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundryActuatorAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundryActuatorAutoConfiguration.java index 16e3abd877..3ddb90c1e1 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundryActuatorAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundryActuatorAutoConfiguration.java @@ -70,7 +70,8 @@ public class ReactiveCloudFoundryActuatorAutoConfiguration { WebClient.Builder webClientBuilder) { CloudFoundryWebAnnotationEndpointDiscoverer endpointDiscoverer = new CloudFoundryWebAnnotationEndpointDiscoverer( this.applicationContext, parameterMapper, endpointMediaTypes, - EndpointPathResolver.useEndpointId(), null, null, CloudFoundryReactiveHealthEndpointWebExtension.class); + EndpointPathResolver.useEndpointId(), null, null, + CloudFoundryReactiveHealthEndpointWebExtension.class); ReactiveCloudFoundrySecurityInterceptor securityInterceptor = getSecurityInterceptor( webClientBuilder, this.applicationContext.getEnvironment()); return new CloudFoundryWebFluxEndpointHandlerMapping( diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryActuatorAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryActuatorAutoConfiguration.java index 4ac21b1a85..f774c4264f 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryActuatorAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryActuatorAutoConfiguration.java @@ -74,7 +74,8 @@ public class CloudFoundryActuatorAutoConfiguration { RestTemplateBuilder restTemplateBuilder) { CloudFoundryWebAnnotationEndpointDiscoverer endpointDiscoverer = new CloudFoundryWebAnnotationEndpointDiscoverer( this.applicationContext, parameterMapper, endpointMediaTypes, - EndpointPathResolver.useEndpointId(), null, null, CloudFoundryHealthEndpointWebExtension.class); + EndpointPathResolver.useEndpointId(), null, null, + CloudFoundryHealthEndpointWebExtension.class); CloudFoundrySecurityInterceptor securityInterceptor = getSecurityInterceptor( restTemplateBuilder, this.applicationContext.getEnvironment()); return new CloudFoundryWebEndpointServletHandlerMapping( diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryHealthEndpointWebExtension.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryHealthEndpointWebExtension.java index b9418c0cb0..90eaaf1863 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryHealthEndpointWebExtension.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryHealthEndpointWebExtension.java @@ -23,33 +23,27 @@ import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse; import org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExtension; import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.HealthEndpoint; -import org.springframework.boot.actuate.health.HealthStatusHttpMapper; +import org.springframework.boot.actuate.health.HealthEndpointWebExtension; /** - * {@link EndpointWebExtension} for the {@link HealthEndpoint} - * that always exposes full health details. + * {@link EndpointWebExtension} for the {@link HealthEndpoint} that always exposes full + * health details. * * @author Madhura Bhave + * @since 2.0.0 */ @EndpointExtension(filter = CloudFoundryEndpointFilter.class, endpoint = HealthEndpoint.class) public class CloudFoundryHealthEndpointWebExtension { - private final HealthEndpoint delegate; + private final HealthEndpointWebExtension delegate; - private final HealthStatusHttpMapper statusHttpMapper; - - public CloudFoundryHealthEndpointWebExtension(HealthEndpoint delegate, - HealthStatusHttpMapper statusHttpMapper) { + public CloudFoundryHealthEndpointWebExtension(HealthEndpointWebExtension delegate) { this.delegate = delegate; - this.statusHttpMapper = statusHttpMapper; } @ReadOperation public WebEndpointResponse getHealth() { - Health health = this.delegate.health(); - Integer status = this.statusHttpMapper.mapStatus(health.getStatus()); - return new WebEndpointResponse<>(health, status); + return this.delegate.getHealth(true); } } - diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointAutoConfiguration.java index ff492261a0..eb06dd9773 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointAutoConfiguration.java @@ -16,24 +16,14 @@ package org.springframework.boot.actuate.autoconfigure.health; -import java.util.LinkedHashMap; -import java.util.Map; - -import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint; -import org.springframework.boot.actuate.health.CompositeHealthIndicatorFactory; -import org.springframework.boot.actuate.health.HealthAggregator; import org.springframework.boot.actuate.health.HealthEndpoint; -import org.springframework.boot.actuate.health.HealthIndicator; -import org.springframework.boot.actuate.health.OrderedHealthAggregator; -import org.springframework.boot.actuate.health.ReactiveHealthIndicator; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.util.ClassUtils; /** * {@link EnableAutoConfiguration Auto-configuration} for {@link HealthEndpoint}. @@ -47,46 +37,11 @@ import org.springframework.util.ClassUtils; @EnableConfigurationProperties(HealthEndpointProperties.class) public class HealthEndpointAutoConfiguration { - private final HealthIndicator healthIndicator; - - public HealthEndpointAutoConfiguration(ApplicationContext applicationContext, - ObjectProvider healthAggregator) { - this.healthIndicator = getHealthIndicator(applicationContext, - healthAggregator.getIfAvailable(OrderedHealthAggregator::new)); - } - - private HealthIndicator getHealthIndicator(ApplicationContext applicationContext, - HealthAggregator healthAggregator) { - Map indicators = new LinkedHashMap<>(); - indicators.putAll(applicationContext.getBeansOfType(HealthIndicator.class)); - if (ClassUtils.isPresent("reactor.core.publisher.Flux", null)) { - new ReactiveHealthIndicators().get(applicationContext) - .forEach(indicators::putIfAbsent); - } - CompositeHealthIndicatorFactory factory = new CompositeHealthIndicatorFactory(); - return factory.createHealthIndicator(healthAggregator, indicators); - } - @Bean @ConditionalOnMissingBean @ConditionalOnEnabledEndpoint - public HealthEndpoint healthEndpoint(HealthEndpointProperties properties) { - return new HealthEndpoint(this.healthIndicator, properties.isShowDetails()); - } - - private static class ReactiveHealthIndicators { - - public Map get(ApplicationContext applicationContext) { - Map indicators = new LinkedHashMap<>(); - applicationContext.getBeansOfType(ReactiveHealthIndicator.class) - .forEach((name, indicator) -> indicators.put(name, adapt(indicator))); - return indicators; - } - - private HealthIndicator adapt(ReactiveHealthIndicator indicator) { - return () -> indicator.health().block(); - } - + public HealthEndpoint healthEndpoint(ApplicationContext applicationContext) { + return new HealthEndpoint(HealthIndicatorBeansComposite.get(applicationContext)); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointProperties.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointProperties.java index 7f0eb2d064..defb7eef88 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointProperties.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointProperties.java @@ -28,7 +28,8 @@ import org.springframework.boot.context.properties.ConfigurationProperties; public class HealthEndpointProperties { /** - * Whether to show full health details instead of just the status. + * Whether to show full health details instead of just the status when exposed over a + * potentially insecure connection. */ private boolean showDetails; diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthIndicatorBeansComposite.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthIndicatorBeansComposite.java new file mode 100644 index 0000000000..502eb9ebaf --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthIndicatorBeansComposite.java @@ -0,0 +1,80 @@ +/* + * Copyright 2012-2017 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.health; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.boot.actuate.health.CompositeHealthIndicator; +import org.springframework.boot.actuate.health.CompositeHealthIndicatorFactory; +import org.springframework.boot.actuate.health.HealthAggregator; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.boot.actuate.health.OrderedHealthAggregator; +import org.springframework.boot.actuate.health.ReactiveHealthIndicator; +import org.springframework.context.ApplicationContext; +import org.springframework.util.ClassUtils; + +/** + * Creates a {@link CompositeHealthIndicator} from beans in the + * {@link ApplicationContext}. + * + * @author Phillip Webb + */ +final class HealthIndicatorBeansComposite { + + private HealthIndicatorBeansComposite() { + } + + public static HealthIndicator get(ApplicationContext applicationContext) { + HealthAggregator healthAggregator = getHealthAggregator(applicationContext); + Map indicators = new LinkedHashMap<>(); + indicators.putAll(applicationContext.getBeansOfType(HealthIndicator.class)); + if (ClassUtils.isPresent("reactor.core.publisher.Flux", null)) { + new ReactiveHealthIndicators().get(applicationContext) + .forEach(indicators::putIfAbsent); + } + CompositeHealthIndicatorFactory factory = new CompositeHealthIndicatorFactory(); + return factory.createHealthIndicator(healthAggregator, indicators); + } + + private static HealthAggregator getHealthAggregator( + ApplicationContext applicationContext) { + try { + return applicationContext.getBean(HealthAggregator.class); + } + catch (NoSuchBeanDefinitionException ex) { + return new OrderedHealthAggregator(); + } + } + + private static class ReactiveHealthIndicators { + + public Map get(ApplicationContext applicationContext) { + Map indicators = new LinkedHashMap<>(); + applicationContext.getBeansOfType(ReactiveHealthIndicator.class) + .forEach((name, indicator) -> indicators.put(name, adapt(indicator))); + return indicators; + } + + private HealthIndicator adapt(ReactiveHealthIndicator indicator) { + return () -> indicator.health().block(); + } + + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthIndicatorBeansReactiveComposite.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthIndicatorBeansReactiveComposite.java new file mode 100644 index 0000000000..2b08d4872a --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthIndicatorBeansReactiveComposite.java @@ -0,0 +1,57 @@ +/* + * Copyright 2012-2017 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.health; + +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.boot.actuate.health.CompositeReactiveHealthIndicator; +import org.springframework.boot.actuate.health.CompositeReactiveHealthIndicatorFactory; +import org.springframework.boot.actuate.health.HealthAggregator; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.boot.actuate.health.OrderedHealthAggregator; +import org.springframework.boot.actuate.health.ReactiveHealthIndicator; +import org.springframework.context.ApplicationContext; + +/** + * Creates a {@link CompositeReactiveHealthIndicator} from beans in the + * {@link ApplicationContext}. + * + * @author Phillip Webb + */ +final class HealthIndicatorBeansReactiveComposite { + + private HealthIndicatorBeansReactiveComposite() { + } + + public static ReactiveHealthIndicator get(ApplicationContext applicationContext) { + HealthAggregator healthAggregator = getHealthAggregator(applicationContext); + return new CompositeReactiveHealthIndicatorFactory() + .createReactiveHealthIndicator(healthAggregator, + applicationContext.getBeansOfType(ReactiveHealthIndicator.class), + applicationContext.getBeansOfType(HealthIndicator.class)); + } + + private static HealthAggregator getHealthAggregator( + ApplicationContext applicationContext) { + try { + return applicationContext.getBean(HealthAggregator.class); + } + catch (NoSuchBeanDefinitionException ex) { + return new OrderedHealthAggregator(); + } + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthWebEndpointManagementContextConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthWebEndpointManagementContextConfiguration.java index 752ffaa864..264317c600 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthWebEndpointManagementContextConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthWebEndpointManagementContextConfiguration.java @@ -36,6 +36,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -65,7 +66,8 @@ public class HealthWebEndpointManagementContextConfiguration { static class ReactiveWebHealthConfiguration { @Bean - public ReactiveHealthIndicator reactiveHealthIndicator(ObjectProvider healthAggregator, + public ReactiveHealthIndicator reactiveHealthIndicator( + ObjectProvider healthAggregator, ObjectProvider> reactiveHealthIndicators, ObjectProvider> healthIndicators) { return new CompositeReactiveHealthIndicatorFactory() @@ -99,8 +101,12 @@ public class HealthWebEndpointManagementContextConfiguration { @ConditionalOnEnabledEndpoint @ConditionalOnBean(HealthEndpoint.class) public HealthEndpointWebExtension healthEndpointWebExtension( - HealthEndpoint delegate, HealthStatusHttpMapper healthStatusHttpMapper) { - return new HealthEndpointWebExtension(delegate, healthStatusHttpMapper); + ApplicationContext applicationContext, + HealthStatusHttpMapper healthStatusHttpMapper, + HealthEndpointProperties properties) { + return new HealthEndpointWebExtension( + HealthIndicatorBeansComposite.get(applicationContext), + healthStatusHttpMapper, properties.isShowDetails()); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryEndpointFilterTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryEndpointFilterTests.java index 2d3e84f294..9a5524cd89 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryEndpointFilterTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryEndpointFilterTests.java @@ -49,20 +49,27 @@ public class CloudFoundryEndpointFilterTests { @Test public void matchIfDiscovererCloudFoundryShouldReturnFalse() throws Exception { - CloudFoundryWebAnnotationEndpointDiscoverer discoverer = Mockito.mock(CloudFoundryWebAnnotationEndpointDiscoverer.class); + CloudFoundryWebAnnotationEndpointDiscoverer discoverer = Mockito + .mock(CloudFoundryWebAnnotationEndpointDiscoverer.class); assertThat(this.filter.match(null, discoverer)).isTrue(); } @Test public void matchIfDiscovererNotCloudFoundryShouldReturnFalse() throws Exception { - WebAnnotationEndpointDiscoverer discoverer = Mockito.mock(WebAnnotationEndpointDiscoverer.class); + WebAnnotationEndpointDiscoverer discoverer = Mockito + .mock(WebAnnotationEndpointDiscoverer.class); assertThat(this.filter.match(null, discoverer)).isFalse(); } static class TestEndpointDiscoverer extends WebAnnotationEndpointDiscoverer { - TestEndpointDiscoverer(ApplicationContext applicationContext, ParameterMapper parameterMapper, EndpointMediaTypes endpointMediaTypes, EndpointPathResolver endpointPathResolver, Collection invokerAdvisors, Collection> filters) { - super(applicationContext, parameterMapper, endpointMediaTypes, endpointPathResolver, invokerAdvisors, filters); + TestEndpointDiscoverer(ApplicationContext applicationContext, + ParameterMapper parameterMapper, EndpointMediaTypes endpointMediaTypes, + EndpointPathResolver endpointPathResolver, + Collection invokerAdvisors, + Collection> filters) { + super(applicationContext, parameterMapper, endpointMediaTypes, + endpointPathResolver, invokerAdvisors, filters); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryWebAnnotationEndpointDiscovererTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryWebAnnotationEndpointDiscovererTests.java index d436a9c4e7..73f5930b58 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryWebAnnotationEndpointDiscovererTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryWebAnnotationEndpointDiscovererTests.java @@ -50,7 +50,8 @@ public class CloudFoundryWebAnnotationEndpointDiscovererTests { @Test public void discovererShouldAddSuppliedExtensionForHealthEndpoint() throws Exception { load(TestConfiguration.class, endpointDiscoverer -> { - Collection> endpoints = endpointDiscoverer.discoverEndpoints(); + Collection> endpoints = endpointDiscoverer + .discoverEndpoints(); assertThat(endpoints.size()).isEqualTo(2); }); } @@ -92,7 +93,7 @@ public class CloudFoundryWebAnnotationEndpointDiscovererTests { @Bean public HealthEndpoint healthEndpoint() { - return new HealthEndpoint(null, true); + return new HealthEndpoint(null); } @Bean diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundryActuatorAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundryActuatorAutoConfigurationTests.java index 55d19506a7..4fa66dde4f 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundryActuatorAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundryActuatorAutoConfigurationTests.java @@ -229,14 +229,19 @@ public class ReactiveCloudFoundryActuatorAutoConfigurationTests { @Test public void healthEndpointInvokerShouldBeCloudFoundryWebExtension() throws Exception { setupContextWithCloudEnabled(); - this.context.register(HealthEndpointAutoConfiguration.class, HealthWebEndpointManagementContextConfiguration.class, + this.context.register(HealthEndpointAutoConfiguration.class, + HealthWebEndpointManagementContextConfiguration.class, CloudFoundryHealthWebEndpointManagementContextConfiguration.class); this.context.refresh(); - Collection> endpoints = getHandlerMapping().getEndpoints(); + Collection> endpoints = getHandlerMapping() + .getEndpoints(); EndpointInfo endpointInfo = (EndpointInfo) (endpoints.toArray()[0]); - WebOperation webOperation = (WebOperation) endpointInfo.getOperations().toArray()[0]; - ReflectiveOperationInvoker invoker = (ReflectiveOperationInvoker) webOperation.getInvoker(); - assertThat(ReflectionTestUtils.getField(invoker, "target")).isInstanceOf(CloudFoundryReactiveHealthEndpointWebExtension.class); + WebOperation webOperation = (WebOperation) endpointInfo.getOperations() + .toArray()[0]; + ReflectiveOperationInvoker invoker = (ReflectiveOperationInvoker) webOperation + .getInvoker(); + assertThat(ReflectionTestUtils.getField(invoker, "target")) + .isInstanceOf(CloudFoundryReactiveHealthEndpointWebExtension.class); } private void setupContextWithCloudEnabled() { diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryActuatorAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryActuatorAutoConfigurationTests.java index 804986bb38..e4c8dc9da0 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryActuatorAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryActuatorAutoConfigurationTests.java @@ -251,15 +251,21 @@ public class CloudFoundryActuatorAutoConfigurationTests { .of("VCAP_APPLICATION:---", "vcap.application.application_id:my-app-id", "vcap.application.cf_api:http://my-cloud-controller.com") .applyTo(this.context); - this.context.register(HealthEndpointAutoConfiguration.class, HealthWebEndpointManagementContextConfiguration.class, + this.context.register(HealthEndpointAutoConfiguration.class, + HealthWebEndpointManagementContextConfiguration.class, CloudFoundryHealthWebEndpointManagementContextConfiguration.class); this.context.refresh(); - Collection> endpoints = this.context.getBean("cloudFoundryWebEndpointServletHandlerMapping", - CloudFoundryWebEndpointServletHandlerMapping.class).getEndpoints(); + Collection> endpoints = this.context + .getBean("cloudFoundryWebEndpointServletHandlerMapping", + CloudFoundryWebEndpointServletHandlerMapping.class) + .getEndpoints(); EndpointInfo endpointInfo = (EndpointInfo) (endpoints.toArray()[0]); - WebOperation webOperation = (WebOperation) endpointInfo.getOperations().toArray()[0]; - ReflectiveOperationInvoker invoker = (ReflectiveOperationInvoker) webOperation.getInvoker(); - assertThat(ReflectionTestUtils.getField(invoker, "target")).isInstanceOf(CloudFoundryHealthEndpointWebExtension.class); + WebOperation webOperation = (WebOperation) endpointInfo.getOperations() + .toArray()[0]; + ReflectiveOperationInvoker invoker = (ReflectiveOperationInvoker) webOperation + .getInvoker(); + assertThat(ReflectionTestUtils.getField(invoker, "target")) + .isInstanceOf(CloudFoundryHealthEndpointWebExtension.class); } private CloudFoundryWebEndpointServletHandlerMapping getHandlerMapping() { diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/HealthEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/HealthEndpointDocumentationTests.java index ad7e9c90da..aad70d26f5 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/HealthEndpointDocumentationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/HealthEndpointDocumentationTests.java @@ -74,7 +74,7 @@ public class HealthEndpointDocumentationTests extends AbstractEndpointDocumentat @Bean public HealthEndpoint endpoint(Map healthIndicators) { return new HealthEndpoint(new CompositeHealthIndicator( - new OrderedHealthAggregator(), healthIndicators), true); + new OrderedHealthAggregator(), healthIndicators)); } @Bean diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointAutoConfigurationTests.java index 9cbed94aa1..d2f3b20c43 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointAutoConfigurationTests.java @@ -57,7 +57,7 @@ public class HealthEndpointAutoConfigurationTests { verify(indicator, times(0)).health(); Health health = context.getBean(HealthEndpoint.class).health(); assertThat(health.getStatus()).isEqualTo(Status.UP); - assertThat(health.getDetails()).isEmpty(); + assertThat(health.getDetails()).isNotEmpty(); verify(indicator, times(1)).health(); }); } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/AnnotationEndpointDiscoverer.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/AnnotationEndpointDiscoverer.java index 666ebd62c9..17765cdf1b 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/AnnotationEndpointDiscoverer.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/AnnotationEndpointDiscoverer.java @@ -173,13 +173,13 @@ public abstract class AnnotationEndpointDiscoverer return extensions; } - protected void addExtension(Map, DiscoveredEndpoint> endpoints, + private void addExtension(Map, DiscoveredEndpoint> endpoints, Map, DiscoveredExtension> extensions, String beanName) { Class extensionType = this.applicationContext.getType(beanName); Class endpointType = getEndpointType(extensionType); DiscoveredEndpoint endpoint = getExtendingEndpoint(endpoints, extensionType, endpointType); - if (isExtensionExposed(extensionType, endpoint.getInfo())) { + if (isExtensionExposed(endpointType, extensionType, endpoint.getInfo())) { Assert.state(endpoint.isExposed() || isEndpointFiltered(endpoint.getInfo()), () -> "Invalid extension " + extensionType.getName() + "': endpoint '" + endpointType.getName() @@ -199,7 +199,7 @@ public abstract class AnnotationEndpointDiscoverer } } - protected Class getEndpointType(Class extensionType) { + private Class getEndpointType(Class extensionType) { AnnotationAttributes attributes = AnnotatedElementUtils .getMergedAnnotationAttributes(extensionType, EndpointExtension.class); Class endpointType = attributes.getClass("endpoint"); @@ -242,7 +242,14 @@ public abstract class AnnotationEndpointDiscoverer return false; } - private boolean isExtensionExposed(Class extensionType, + /** + * Determines if an extension is exposed. + * @param endpointType the endpoint type + * @param extensionType the extension type + * @param endpointInfo the endpoint info + * @return if the extension is exposed + */ + protected boolean isExtensionExposed(Class endpointType, Class extensionType, EndpointInfo endpointInfo) { AnnotationAttributes annotationAttributes = AnnotatedElementUtils .getMergedAnnotationAttributes(extensionType, EndpointExtension.class); diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpoint.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpoint.java index 032f55ba3f..5d7f7b4291 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpoint.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpoint.java @@ -32,29 +32,17 @@ public class HealthEndpoint { private final HealthIndicator healthIndicator; - private final boolean showDetails; - /** * Create a new {@link HealthEndpoint} instance. * @param healthIndicator the health indicator - * @param showDetails if full details should be returned instead of just the status */ - public HealthEndpoint(HealthIndicator healthIndicator, boolean showDetails) { + public HealthEndpoint(HealthIndicator healthIndicator) { this.healthIndicator = healthIndicator; - this.showDetails = showDetails; } @ReadOperation public Health health() { - Health health = this.healthIndicator.health(); - if (this.showDetails) { - return health; - } - return Health.status(health.getStatus()).build(); - } - - public HealthIndicator getHealthIndicator() { - return this.healthIndicator; + return this.healthIndicator.health(); } } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointWebExtension.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointWebExtension.java index 5c4e35bd2a..6b36ab55b4 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointWebExtension.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointWebExtension.java @@ -34,20 +34,30 @@ import org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExten @EndpointWebExtension(endpoint = HealthEndpoint.class) public class HealthEndpointWebExtension { - private final HealthEndpoint delegate; + private final HealthIndicator delegate; private final HealthStatusHttpMapper statusHttpMapper; - public HealthEndpointWebExtension(HealthEndpoint delegate, - HealthStatusHttpMapper statusHttpMapper) { + private final boolean showDetails; + + public HealthEndpointWebExtension(HealthIndicator delegate, + HealthStatusHttpMapper statusHttpMapper, boolean showDetails) { this.delegate = delegate; this.statusHttpMapper = statusHttpMapper; + this.showDetails = showDetails; } @ReadOperation public WebEndpointResponse getHealth() { + return getHealth(this.showDetails); + } + + public WebEndpointResponse getHealth(boolean showDetails) { Health health = this.delegate.health(); Integer status = this.statusHttpMapper.mapStatus(health.getStatus()); + if (!showDetails) { + health = Health.status(health.getStatus()).build(); + } return new WebEndpointResponse<>(health, status); } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/ReactiveHealthEndpointWebExtension.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/ReactiveHealthEndpointWebExtension.java index e025dddd8a..dd792ad721 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/ReactiveHealthEndpointWebExtension.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/ReactiveHealthEndpointWebExtension.java @@ -46,9 +46,13 @@ public class ReactiveHealthEndpointWebExtension { @ReadOperation public Mono> health() { + return health(this.showDetails); + } + + public Mono> health(boolean showDetails) { return this.delegate.health().map((health) -> { Integer status = this.statusHttpMapper.mapStatus(health.getStatus()); - if (!this.showDetails) { + if (!showDetails) { health = Health.status(health.getStatus()).build(); } return new WebEndpointResponse<>(health, status); diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/HealthEndpointTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/HealthEndpointTests.java index cf4efbee01..66da11d9ae 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/HealthEndpointTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/HealthEndpointTests.java @@ -41,7 +41,7 @@ public class HealthEndpointTests { healthIndicators.put("upAgain", () -> new Health.Builder().status(Status.UP) .withDetail("second", "2").build()); HealthEndpoint endpoint = new HealthEndpoint( - createHealthIndicator(healthIndicators), true); + createHealthIndicator(healthIndicators)); Health health = endpoint.health(); assertThat(health.getStatus()).isEqualTo(Status.UP); assertThat(health.getDetails()).containsOnlyKeys("up", "upAgain"); @@ -51,20 +51,6 @@ public class HealthEndpointTests { assertThat(upAgainHealth.getDetails()).containsOnly(entry("second", "2")); } - @Test - public void onlyStatusIsExposed() { - Map healthIndicators = new HashMap<>(); - healthIndicators.put("up", () -> new Health.Builder().status(Status.UP) - .withDetail("first", "1").build()); - healthIndicators.put("upAgain", () -> new Health.Builder().status(Status.UP) - .withDetail("second", "2").build()); - HealthEndpoint endpoint = new HealthEndpoint( - createHealthIndicator(healthIndicators), false); - Health health = endpoint.health(); - assertThat(health.getStatus()).isEqualTo(Status.UP); - assertThat(health.getDetails()).isEmpty(); - } - private HealthIndicator createHealthIndicator( Map healthIndicators) { return new CompositeHealthIndicatorFactory() diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/HealthEndpointWebIntegrationTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/HealthEndpointWebIntegrationTests.java index 7f16e1aaef..dc919d05a4 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/HealthEndpointWebIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/HealthEndpointWebIntegrationTests.java @@ -66,14 +66,16 @@ public class HealthEndpointWebIntegrationTests { Map healthIndicators) { return new HealthEndpoint( new CompositeHealthIndicatorFactory().createHealthIndicator( - new OrderedHealthAggregator(), healthIndicators), - true); + new OrderedHealthAggregator(), healthIndicators)); } @Bean public HealthEndpointWebExtension healthWebEndpointExtension( - HealthEndpoint delegate) { - return new HealthEndpointWebExtension(delegate, new HealthStatusHttpMapper()); + Map healthIndicators) { + return new HealthEndpointWebExtension( + new CompositeHealthIndicatorFactory().createHealthIndicator( + new OrderedHealthAggregator(), healthIndicators), + new HealthStatusHttpMapper(), true); } @Bean