Commit d829d522 authored by Vedran Pavic's avatar Vedran Pavic Committed by Stephane Nicoll

Introduce HealthIndicatorRegistry

This commit introduces HealthIndicatorRegistry which handles
registration of HealthIndicator instances. Registering new
HealthIndicator instances is now possible in runtime.

See gh-4965
parent ffdcdc0d
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
......@@ -16,10 +16,13 @@
package org.springframework.boot.actuate.autoconfigure.health;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint;
import org.springframework.boot.actuate.health.HealthAggregator;
import org.springframework.boot.actuate.health.HealthEndpoint;
import org.springframework.boot.actuate.health.HealthIndicatorRegistry;
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
......@@ -27,15 +30,27 @@ import org.springframework.context.annotation.Configuration;
* Configuration for {@link HealthEndpoint}.
*
* @author Stephane Nicoll
* @author Vedran Pavic
*/
@Configuration
class HealthEndpointConfiguration {
private final HealthAggregator healthAggregator;
private final HealthIndicatorRegistry healthIndicatorRegistry;
HealthEndpointConfiguration(ObjectProvider<HealthAggregator> healthAggregator,
ObjectProvider<HealthIndicatorRegistry> healthIndicatorRegistry) {
this.healthAggregator = healthAggregator
.getIfAvailable(OrderedHealthAggregator::new);
this.healthIndicatorRegistry = healthIndicatorRegistry.getObject();
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
public HealthEndpoint healthEndpoint(ApplicationContext applicationContext) {
return new HealthEndpoint(HealthIndicatorBeansComposite.get(applicationContext));
public HealthEndpoint healthEndpoint() {
return new HealthEndpoint(this.healthAggregator, this.healthIndicatorRegistry);
}
}
......@@ -36,7 +36,6 @@ 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;
......@@ -107,11 +106,9 @@ class HealthEndpointWebExtensionConfiguration {
@ConditionalOnEnabledEndpoint
@ConditionalOnBean(HealthEndpoint.class)
public HealthEndpointWebExtension healthEndpointWebExtension(
ApplicationContext applicationContext,
HealthEndpoint healthEndpoint,
HealthWebEndpointResponseMapper responseMapper) {
return new HealthEndpointWebExtension(
HealthIndicatorBeansComposite.get(applicationContext),
responseMapper);
return new HealthEndpointWebExtension(healthEndpoint, responseMapper);
}
}
......
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
......@@ -16,16 +16,23 @@
package org.springframework.boot.actuate.autoconfigure.health;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
import org.springframework.boot.actuate.health.DefaultHealthIndicatorRegistry;
import org.springframework.boot.actuate.health.HealthAggregator;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.HealthIndicatorRegistry;
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 HealthIndicator}s.
......@@ -33,6 +40,7 @@ import org.springframework.context.annotation.Configuration;
* @author Andy Wilkinson
* @author Stephane Nicoll
* @author Phillip Webb
* @author Vedran Pavic
* @since 2.0.0
*/
@Configuration
......@@ -61,4 +69,34 @@ public class HealthIndicatorAutoConfiguration {
return healthAggregator;
}
@Bean
@ConditionalOnMissingBean(HealthIndicatorRegistry.class)
public HealthIndicatorRegistry healthIndicatorRegistry(
ApplicationContext applicationContext) {
HealthIndicatorRegistry registry = new DefaultHealthIndicatorRegistry();
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);
}
indicators.forEach(registry::register);
return registry;
}
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();
}
}
}
/*
* 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();
}
}
}
......@@ -34,8 +34,9 @@ import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
import org.springframework.boot.actuate.endpoint.web.PathMapper;
import org.springframework.boot.actuate.endpoint.web.WebOperation;
import org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExtension;
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.HealthIndicatorRegistry;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
......@@ -109,7 +110,8 @@ public class CloudFoundryWebEndpointDiscovererTests {
@Bean
public HealthEndpoint healthEndpoint() {
return new HealthEndpoint(mock(HealthIndicator.class));
return new HealthEndpoint(mock(HealthAggregator.class),
mock(HealthIndicatorRegistry.class));
}
@Bean
......
......@@ -32,6 +32,7 @@ import reactor.ipc.netty.http.HttpResources;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
......@@ -87,6 +88,7 @@ public class ReactiveCloudFoundryActuatorAutoConfigurationTests {
WebClientCustomizerConfig.class, WebClientAutoConfiguration.class,
ManagementContextAutoConfiguration.class,
EndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class,
HealthIndicatorAutoConfiguration.class,
HealthEndpointAutoConfiguration.class,
ReactiveCloudFoundryActuatorAutoConfiguration.class));
......
......@@ -24,6 +24,7 @@ import org.junit.Test;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
......@@ -270,7 +271,8 @@ public class CloudFoundryActuatorAutoConfigurationTests {
"vcap.application.application_id:my-app-id",
"vcap.application.cf_api:http://my-cloud-controller.com")
.withConfiguration(
AutoConfigurations.of(HealthEndpointAutoConfiguration.class))
AutoConfigurations.of(HealthIndicatorAutoConfiguration.class,
HealthEndpointAutoConfiguration.class))
.run((context) -> {
Collection<ExposableWebEndpoint> endpoints = context
.getBean("cloudFoundryWebEndpointServletHandlerMapping",
......
......@@ -23,9 +23,10 @@ import javax.sql.DataSource;
import org.junit.Test;
import org.springframework.boot.actuate.health.CompositeHealthIndicator;
import org.springframework.boot.actuate.health.DefaultHealthIndicatorRegistry;
import org.springframework.boot.actuate.health.HealthEndpoint;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.HealthIndicatorRegistry;
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
import org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator;
import org.springframework.boot.actuate.system.DiskSpaceHealthIndicator;
......@@ -72,8 +73,9 @@ public class HealthEndpointDocumentationTests extends MockMvcEndpointDocumentati
@Bean
public HealthEndpoint endpoint(Map<String, HealthIndicator> healthIndicators) {
return new HealthEndpoint(new CompositeHealthIndicator(
new OrderedHealthAggregator(), healthIndicators));
HealthIndicatorRegistry registry = new DefaultHealthIndicatorRegistry();
healthIndicators.forEach(registry::register);
return new HealthEndpoint(new OrderedHealthAggregator(), registry);
}
@Bean
......
......@@ -46,7 +46,8 @@ public class HealthEndpointAutoConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(
AutoConfigurations.of(HealthEndpointAutoConfiguration.class));
AutoConfigurations.of(HealthIndicatorAutoConfiguration.class,
HealthEndpointAutoConfiguration.class));
@Test
public void healthEndpointShowDetailsDefault() {
......
......@@ -28,6 +28,7 @@ import org.junit.Test;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.jmx.JmxEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.trace.http.HttpTraceAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
......@@ -47,7 +48,7 @@ public class JmxEndpointIntegrationTests {
private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(JmxAutoConfiguration.class,
EndpointAutoConfiguration.class, JmxEndpointAutoConfiguration.class,
HttpTraceAutoConfiguration.class))
HttpTraceAutoConfiguration.class, HealthIndicatorAutoConfiguration.class))
.withConfiguration(
AutoConfigurations.of(EndpointAutoConfigurationClasses.ALL));
......
......@@ -28,6 +28,7 @@ import org.junit.Test;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.trace.http.HttpTraceAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration;
......@@ -73,7 +74,8 @@ public class WebMvcEndpointExposureIntegrationTests {
ServletManagementContextAutoConfiguration.class,
ManagementContextAutoConfiguration.class,
ServletManagementContextAutoConfiguration.class,
HttpTraceAutoConfiguration.class))
HttpTraceAutoConfiguration.class,
HealthIndicatorAutoConfiguration.class))
.withConfiguration(
AutoConfigurations.of(EndpointAutoConfigurationClasses.ALL))
.withUserConfiguration(CustomMvcEndpoint.class,
......
/*
* Copyright 2012-2018 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.health;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.springframework.util.Assert;
/**
* Default implementation of {@link HealthIndicatorRegistry}.
*
* @author Vedran Pavic
* @since 2.1.0
*/
public class DefaultHealthIndicatorRegistry implements HealthIndicatorRegistry {
private final Map<String, HealthIndicator> healthIndicators = new HashMap<>();
@Override
public void register(String name, HealthIndicator healthIndicator) {
Assert.notNull(healthIndicator, "HealthIndicator must not be null");
synchronized (this.healthIndicators) {
if (this.healthIndicators.get(name) != null) {
throw new IllegalStateException(
"HealthIndicator with name '" + name + "' already registered");
}
this.healthIndicators.put(name, healthIndicator);
}
}
@Override
public HealthIndicator unregister(String name) {
synchronized (this.healthIndicators) {
return this.healthIndicators.remove(name);
}
}
@Override
public HealthIndicator get(String name) {
synchronized (this.healthIndicators) {
return this.healthIndicators.get(name);
}
}
@Override
public Map<String, HealthIndicator> getAll() {
synchronized (this.healthIndicators) {
return Collections.unmodifiableMap(new HashMap<>(this.healthIndicators));
}
}
}
......@@ -26,25 +26,35 @@ import org.springframework.util.Assert;
* @author Dave Syer
* @author Christian Dupuis
* @author Andy Wilkinson
* @author Vedran Pavic
* @since 2.0.0
*/
@Endpoint(id = "health")
public class HealthEndpoint {
private final HealthIndicator healthIndicator;
private final HealthAggregator healthAggregator;
private final HealthIndicatorRegistry healthIndicatorRegistry;
/**
* Create a new {@link HealthEndpoint} instance.
* @param healthIndicator the health indicator
* @param healthAggregator the health aggregator
* @param healthIndicatorRegistry the health indicator registry
*/
public HealthEndpoint(HealthIndicator healthIndicator) {
Assert.notNull(healthIndicator, "HealthIndicator must not be null");
this.healthIndicator = healthIndicator;
public HealthEndpoint(HealthAggregator healthAggregator,
HealthIndicatorRegistry healthIndicatorRegistry) {
Assert.notNull(healthAggregator, "healthAggregator must not be null");
Assert.notNull(healthIndicatorRegistry, "healthIndicatorRegistry must not be null");
this.healthAggregator = healthAggregator;
this.healthIndicatorRegistry = healthIndicatorRegistry;
}
@ReadOperation
public Health health() {
return this.healthIndicator.health();
CompositeHealthIndicatorFactory factory = new CompositeHealthIndicatorFactory();
CompositeHealthIndicator healthIndicator = factory.createHealthIndicator(
this.healthAggregator, this.healthIndicatorRegistry.getAll());
return healthIndicator.health();
}
}
......@@ -35,11 +35,11 @@ import org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExten
@EndpointWebExtension(endpoint = HealthEndpoint.class)
public class HealthEndpointWebExtension {
private final HealthIndicator delegate;
private final HealthEndpoint delegate;
private final HealthWebEndpointResponseMapper responseMapper;
public HealthEndpointWebExtension(HealthIndicator delegate,
public HealthEndpointWebExtension(HealthEndpoint delegate,
HealthWebEndpointResponseMapper responseMapper) {
this.delegate = delegate;
this.responseMapper = responseMapper;
......
/*
* Copyright 2012-2018 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.health;
import java.util.Map;
/**
* A registry of {@link HealthIndicator}s.
* <p>
* Implementations <strong>must</strong> be thread-safe.
*
* @author Andy Wilkinson
* @author Vedran Pavic
* @since 2.1.0
*/
public interface HealthIndicatorRegistry {
/**
* Registers the given {@code healthIndicator}, associating it with the given
* {@code name}.
* @param name the name of the indicator
* @param healthIndicator the indicator
* @throws IllegalStateException if an indicator with the given {@code name} is
* already registered.
*/
void register(String name, HealthIndicator healthIndicator);
/**
* Unregisters the {@code HealthIndicator} previously registered with the given
* {@code name}.
* @param name the name of the indicator
* @return the unregistered indicator, or {@code null} if no indicator was found in
* the registry for the given {@code name}.
*/
HealthIndicator unregister(String name);
/**
* Returns the health indicator registered with the given {@code name}.
* @param name the name of the indicator
* @return the health indicator, or {@code null} if no indicator was registered with
* the given {@code name}.
*/
HealthIndicator get(String name);
/**
* Returns a snapshot of the registered health indicators and their names. The
* contents of the map do not reflect subsequent changes to the registry.
* @return the snapshot of registered health indicators
*/
Map<String, HealthIndicator> getAll();
}
/*
* Copyright 2012-2018 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.health;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link DefaultHealthIndicatorRegistry}.
*
* @author Vedran Pavic
*/
public class DefaultHealthIndicatorRegistryTest {
@Rule
public ExpectedException thrown = ExpectedException.none();
private HealthIndicator one = mock(HealthIndicator.class);
private HealthIndicator two = mock(HealthIndicator.class);
private DefaultHealthIndicatorRegistry registry;
@Before
public void setUp() {
given(this.one.health()).willReturn(new Health.Builder().up().build());
given(this.two.health()).willReturn(new Health.Builder().unknown().build());
this.registry = new DefaultHealthIndicatorRegistry();
}
@Test
public void register() {
this.registry.register("one", this.one);
this.registry.register("two", this.two);
assertThat(this.registry.getAll()).hasSize(2);
assertThat(this.registry.get("one")).isSameAs(this.one);
assertThat(this.registry.get("two")).isSameAs(this.two);
}
@Test
public void registerAlreadyUsedName() {
this.thrown.expect(IllegalStateException.class);
this.thrown.expectMessage("HealthIndicator with name 'one' already registered");
this.registry.register("one", this.one);
this.registry.register("one", this.two);
}
@Test
public void unregister() {
this.registry.register("one", this.one);
this.registry.register("two", this.two);
assertThat(this.registry.getAll()).hasSize(2);
HealthIndicator two = this.registry.unregister("two");
assertThat(two).isSameAs(this.two);
assertThat(this.registry.getAll()).hasSize(1);
}
@Test
public void unregisterNotKnown() {
this.registry.register("one", this.one);
assertThat(this.registry.getAll()).hasSize(1);
HealthIndicator two = this.registry.unregister("two");
assertThat(two).isNull();
assertThat(this.registry.getAll()).hasSize(1);
}
}
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
......@@ -30,6 +30,7 @@ import static org.assertj.core.api.Assertions.entry;
* @author Phillip Webb
* @author Christian Dupuis
* @author Andy Wilkinson
* @author Vedran Pavic
*/
public class HealthEndpointTests {
......@@ -40,8 +41,8 @@ public class HealthEndpointTests {
.withDetail("first", "1").build());
healthIndicators.put("upAgain", () -> new Health.Builder().status(Status.UP)
.withDetail("second", "2").build());
HealthEndpoint endpoint = new HealthEndpoint(
createHealthIndicator(healthIndicators));
HealthEndpoint endpoint = new HealthEndpoint(new OrderedHealthAggregator(),
createHealthIndicatorRegistry(healthIndicators));
Health health = endpoint.health();
assertThat(health.getStatus()).isEqualTo(Status.UP);
assertThat(health.getDetails()).containsOnlyKeys("up", "upAgain");
......@@ -51,10 +52,11 @@ public class HealthEndpointTests {
assertThat(upAgainHealth.getDetails()).containsOnly(entry("second", "2"));
}
private HealthIndicator createHealthIndicator(
private HealthIndicatorRegistry createHealthIndicatorRegistry(
Map<String, HealthIndicator> healthIndicators) {
return new CompositeHealthIndicatorFactory()
.createHealthIndicator(new OrderedHealthAggregator(), healthIndicators);
HealthIndicatorRegistry registry = new DefaultHealthIndicatorRegistry();
healthIndicators.forEach(registry::register);
return registry;
}
}
......@@ -35,6 +35,7 @@ import org.springframework.test.web.reactive.server.WebTestClient;
* exposed by Jersey, Spring MVC, and WebFlux.
*
* @author Andy Wilkinson
* @author Vedran Pavic
*/
@RunWith(WebEndpointRunners.class)
public class HealthEndpointWebIntegrationTests {
......@@ -66,17 +67,15 @@ public class HealthEndpointWebIntegrationTests {
@Bean
public HealthEndpoint healthEndpoint(
Map<String, HealthIndicator> healthIndicators) {
return new HealthEndpoint(
new CompositeHealthIndicatorFactory().createHealthIndicator(
new OrderedHealthAggregator(), healthIndicators));
HealthIndicatorRegistry registry = new DefaultHealthIndicatorRegistry();
healthIndicators.forEach(registry::register);
return new HealthEndpoint(new OrderedHealthAggregator(), registry);
}
@Bean
public HealthEndpointWebExtension healthWebEndpointExtension(
Map<String, HealthIndicator> healthIndicators) {
return new HealthEndpointWebExtension(
new CompositeHealthIndicatorFactory().createHealthIndicator(
new OrderedHealthAggregator(), healthIndicators),
HealthEndpoint healthEndpoint) {
return new HealthEndpointWebExtension(healthEndpoint,
new HealthWebEndpointResponseMapper(new HealthStatusHttpMapper(),
ShowDetails.ALWAYS,
new HashSet<>(Arrays.asList("ACTUATOR"))));
......
......@@ -726,13 +726,14 @@ configuration must permit access to the health endpoint for both authenticated a
unauthenticated users.
Health information is collected from all
{sc-spring-boot-actuator}/health/HealthIndicator.{sc-ext}[`HealthIndicator`] beans
defined in your `ApplicationContext`. Spring Boot includes a number of auto-configured
`HealthIndicators`, and you can also write your own. By default, the final system state
is derived by the `HealthAggregator`, which sorts the statuses from each
`HealthIndicator` based on an ordered list of statuses. The first status in the sorted
list is used as the overall health status. If no `HealthIndicator` returns a status that
is known to the `HealthAggregator`, an `UNKNOWN` status is used.
{sc-spring-boot-actuator}/health/HealthIndicator.{sc-ext}[`HealthIndicator`] instances
registered with {sc-spring-boot-actuator}/health/HealthIndicatorRegistry.{sc-ext}[`HealthIndicatorRegistry`].
Spring Boot includes a number of auto-configured `HealthIndicators` and you can also write
your own. By default, the final system state is
derived by the `HealthAggregator` which sorts the statuses from each `HealthIndicator`
based on an ordered list of statuses. The first status in the sorted list is used as the
overall health status. If no `HealthIndicator` returns a status that is known to the
`HealthAggregator`, an `UNKNOWN` status is used.
......@@ -818,6 +819,9 @@ NOTE: The identifier for a given `HealthIndicator` is the name of the bean witho
`HealthIndicator` suffix, if it exists. In the preceding example, the health information
is available in an entry named `my`.
Additionally, you can register (and unregister) `HealthIndicator` instances in runtime
using {sc-spring-boot-actuator}/health/HealthIndicatorRegistry.{sc-ext}[`HealthIndicatorRegistry`].
In addition to Spring Boot's predefined
{sc-spring-boot-actuator}/health/Status.{sc-ext}[`Status`] types, it is also possible for
`Health` to return a custom `Status` that represents a new system state. In such cases, a
......
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