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
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<? extends OperationMethodInvokerAdvisor> invokerAdvisors, Collection<? extends EndpointFilter<WebOperation>> filters, Class<?> requiredExtensionType) {
|
||||
super(applicationContext, parameterMapper, endpointMediaTypes, endpointPathResolver, invokerAdvisors, filters);
|
||||
this.applicationContext = applicationContext;
|
||||
public CloudFoundryWebAnnotationEndpointDiscoverer(
|
||||
ApplicationContext applicationContext, ParameterMapper parameterMapper,
|
||||
EndpointMediaTypes endpointMediaTypes,
|
||||
EndpointPathResolver endpointPathResolver,
|
||||
Collection<? extends OperationMethodInvokerAdvisor> invokerAdvisors,
|
||||
Collection<? extends EndpointFilter<WebOperation>> filters,
|
||||
Class<?> requiredExtensionType) {
|
||||
super(applicationContext, parameterMapper, endpointMediaTypes,
|
||||
endpointPathResolver, invokerAdvisors, filters);
|
||||
this.requiredExtensionType = requiredExtensionType;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addExtension(Map<Class<?>, DiscoveredEndpoint> endpoints, Map<Class<?>, 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<WebOperation> endpointInfo) {
|
||||
if (HealthEndpoint.class.equals(endpointType)
|
||||
&& !this.requiredExtensionType.equals(extensionType)) {
|
||||
return false;
|
||||
}
|
||||
super.addExtension(endpoints, extensions, beanName);
|
||||
return super.isExtensionExposed(endpointType, extensionType, endpointInfo);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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<WebEndpointResponse<Health>> health() {
|
||||
return this.delegate.health().map((health) -> {
|
||||
Integer status = this.statusHttpMapper.mapStatus(health.getStatus());
|
||||
return new WebEndpointResponse<>(health, status);
|
||||
});
|
||||
return this.delegate.health(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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<Health> getHealth() {
|
||||
Health health = this.delegate.health();
|
||||
Integer status = this.statusHttpMapper.mapStatus(health.getStatus());
|
||||
return new WebEndpointResponse<>(health, status);
|
||||
return this.delegate.getHealth(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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> healthAggregator) {
|
||||
this.healthIndicator = getHealthIndicator(applicationContext,
|
||||
healthAggregator.getIfAvailable(OrderedHealthAggregator::new));
|
||||
}
|
||||
|
||||
private HealthIndicator getHealthIndicator(ApplicationContext applicationContext,
|
||||
HealthAggregator healthAggregator) {
|
||||
Map<String, HealthIndicator> 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<String, HealthIndicator> get(ApplicationContext applicationContext) {
|
||||
Map<String, HealthIndicator> 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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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<String, HealthIndicator> 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<String, HealthIndicator> get(ApplicationContext applicationContext) {
|
||||
Map<String, HealthIndicator> 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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> healthAggregator,
|
||||
public ReactiveHealthIndicator reactiveHealthIndicator(
|
||||
ObjectProvider<HealthAggregator> healthAggregator,
|
||||
ObjectProvider<Map<String, ReactiveHealthIndicator>> reactiveHealthIndicators,
|
||||
ObjectProvider<Map<String, HealthIndicator>> 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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<? extends OperationMethodInvokerAdvisor> invokerAdvisors, Collection<? extends EndpointFilter<WebOperation>> filters) {
|
||||
super(applicationContext, parameterMapper, endpointMediaTypes, endpointPathResolver, invokerAdvisors, filters);
|
||||
TestEndpointDiscoverer(ApplicationContext applicationContext,
|
||||
ParameterMapper parameterMapper, EndpointMediaTypes endpointMediaTypes,
|
||||
EndpointPathResolver endpointPathResolver,
|
||||
Collection<? extends OperationMethodInvokerAdvisor> invokerAdvisors,
|
||||
Collection<? extends EndpointFilter<WebOperation>> filters) {
|
||||
super(applicationContext, parameterMapper, endpointMediaTypes,
|
||||
endpointPathResolver, invokerAdvisors, filters);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -50,7 +50,8 @@ public class CloudFoundryWebAnnotationEndpointDiscovererTests {
|
||||
@Test
|
||||
public void discovererShouldAddSuppliedExtensionForHealthEndpoint() throws Exception {
|
||||
load(TestConfiguration.class, endpointDiscoverer -> {
|
||||
Collection<EndpointInfo<WebOperation>> endpoints = endpointDiscoverer.discoverEndpoints();
|
||||
Collection<EndpointInfo<WebOperation>> 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
|
||||
|
||||
@@ -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<EndpointInfo<WebOperation>> endpoints = getHandlerMapping().getEndpoints();
|
||||
Collection<EndpointInfo<WebOperation>> 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() {
|
||||
|
||||
@@ -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<EndpointInfo<WebOperation>> endpoints = this.context.getBean("cloudFoundryWebEndpointServletHandlerMapping",
|
||||
CloudFoundryWebEndpointServletHandlerMapping.class).getEndpoints();
|
||||
Collection<EndpointInfo<WebOperation>> 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() {
|
||||
|
||||
@@ -74,7 +74,7 @@ public class HealthEndpointDocumentationTests extends AbstractEndpointDocumentat
|
||||
@Bean
|
||||
public HealthEndpoint endpoint(Map<String, HealthIndicator> healthIndicators) {
|
||||
return new HealthEndpoint(new CompositeHealthIndicator(
|
||||
new OrderedHealthAggregator(), healthIndicators), true);
|
||||
new OrderedHealthAggregator(), healthIndicators));
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -173,13 +173,13 @@ public abstract class AnnotationEndpointDiscoverer<K, T extends Operation>
|
||||
return extensions;
|
||||
}
|
||||
|
||||
protected void addExtension(Map<Class<?>, DiscoveredEndpoint> endpoints,
|
||||
private void addExtension(Map<Class<?>, DiscoveredEndpoint> endpoints,
|
||||
Map<Class<?>, 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<K, T extends Operation>
|
||||
}
|
||||
}
|
||||
|
||||
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<K, T extends Operation>
|
||||
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<T> endpointInfo) {
|
||||
AnnotationAttributes annotationAttributes = AnnotatedElementUtils
|
||||
.getMergedAnnotationAttributes(extensionType, EndpointExtension.class);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<Health> getHealth() {
|
||||
return getHealth(this.showDetails);
|
||||
}
|
||||
|
||||
public WebEndpointResponse<Health> 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -46,9 +46,13 @@ public class ReactiveHealthEndpointWebExtension {
|
||||
|
||||
@ReadOperation
|
||||
public Mono<WebEndpointResponse<Health>> health() {
|
||||
return health(this.showDetails);
|
||||
}
|
||||
|
||||
public Mono<WebEndpointResponse<Health>> 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);
|
||||
|
||||
@@ -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<String, HealthIndicator> 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<String, HealthIndicator> healthIndicators) {
|
||||
return new CompositeHealthIndicatorFactory()
|
||||
|
||||
@@ -66,14 +66,16 @@ public class HealthEndpointWebIntegrationTests {
|
||||
Map<String, HealthIndicator> 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<String, HealthIndicator> healthIndicators) {
|
||||
return new HealthEndpointWebExtension(
|
||||
new CompositeHealthIndicatorFactory().createHealthIndicator(
|
||||
new OrderedHealthAggregator(), healthIndicators),
|
||||
new HealthStatusHttpMapper(), true);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
Reference in New Issue
Block a user