Polish "Introduce HealthIndicatorRegistry"

See gh-4965

Co-authored-by: Andy Wilkinson <awilkinson@pivotal.io>
pull/13197/head
Stephane Nicoll 7 years ago
parent d829d522be
commit 95b251590e

@ -20,8 +20,10 @@ import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.health.CompositeHealthIndicator;
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.core.ResolvableType;
/**
@ -42,11 +44,10 @@ public abstract class CompositeHealthIndicatorConfiguration<H extends HealthIndi
if (beans.size() == 1) {
return createHealthIndicator(beans.values().iterator().next());
}
CompositeHealthIndicator composite = new CompositeHealthIndicator(
this.healthAggregator);
beans.forEach((name, source) -> composite.addHealthIndicator(name,
createHealthIndicator(source)));
return composite;
HealthIndicatorRegistry registry = new DefaultHealthIndicatorRegistry();
beans.forEach(
(name, source) -> registry.register(name, createHealthIndicator(source)));
return new CompositeHealthIndicator(this.healthAggregator, registry);
}
@SuppressWarnings("unchecked")

@ -16,13 +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.CompositeHealthIndicator;
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.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -30,27 +30,18 @@ import org.springframework.context.annotation.Configuration;
* Configuration for {@link HealthEndpoint}.
*
* @author Stephane Nicoll
* @author Vedran Pavic
*/
@Configuration
@ConditionalOnSingleCandidate(HealthIndicatorRegistry.class)
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() {
return new HealthEndpoint(this.healthAggregator, this.healthIndicatorRegistry);
public HealthEndpoint healthEndpoint(HealthAggregator healthAggregator,
HealthIndicatorRegistry registry) {
return new HealthEndpoint(
new CompositeHealthIndicator(healthAggregator, registry));
}
}

@ -16,11 +16,7 @@
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;
@ -32,7 +28,6 @@ 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.
@ -73,30 +68,7 @@ public class HealthIndicatorAutoConfiguration {
@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();
}
return HealthIndicatorRegistryBeans.get(applicationContext);
}
}

@ -0,0 +1,66 @@
/*
* 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.autoconfigure.health;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.HealthIndicatorRegistry;
import org.springframework.boot.actuate.health.HealthIndicatorRegistryFactory;
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
import org.springframework.context.ApplicationContext;
import org.springframework.util.ClassUtils;
/**
* Creates a {@link HealthIndicatorRegistry} from beans in the {@link ApplicationContext}.
*
* @author Phillip Webb
* @author Stephane Nicoll
*/
final class HealthIndicatorRegistryBeans {
private HealthIndicatorRegistryBeans() {
}
public static HealthIndicatorRegistry get(ApplicationContext 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);
}
HealthIndicatorRegistryFactory factory = new HealthIndicatorRegistryFactory();
return factory.createHealthIndicatorRegistry(indicators);
}
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,9 +34,8 @@ 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.HealthIndicatorRegistry;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -110,8 +109,7 @@ public class CloudFoundryWebEndpointDiscovererTests {
@Bean
public HealthEndpoint healthEndpoint() {
return new HealthEndpoint(mock(HealthAggregator.class),
mock(HealthIndicatorRegistry.class));
return new HealthEndpoint(mock(HealthIndicator.class));
}
@Bean

@ -23,10 +23,10 @@ import javax.sql.DataSource;
import org.junit.Test;
import org.springframework.boot.actuate.health.DefaultHealthIndicatorRegistry;
import org.springframework.boot.actuate.health.CompositeHealthIndicator;
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.HealthIndicatorRegistryFactory;
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
import org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator;
import org.springframework.boot.actuate.system.DiskSpaceHealthIndicator;
@ -73,9 +73,9 @@ public class HealthEndpointDocumentationTests extends MockMvcEndpointDocumentati
@Bean
public HealthEndpoint endpoint(Map<String, HealthIndicator> healthIndicators) {
HealthIndicatorRegistry registry = new DefaultHealthIndicatorRegistry();
healthIndicators.forEach(registry::register);
return new HealthEndpoint(new OrderedHealthAggregator(), registry);
return new HealthEndpoint(new CompositeHealthIndicator(
new OrderedHealthAggregator(), new HealthIndicatorRegistryFactory()
.createHealthIndicatorRegistry(healthIndicators)));
}
@Bean

@ -48,7 +48,8 @@ public class JmxEndpointIntegrationTests {
private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(JmxAutoConfiguration.class,
EndpointAutoConfiguration.class, JmxEndpointAutoConfiguration.class,
HttpTraceAutoConfiguration.class, HealthIndicatorAutoConfiguration.class))
HealthIndicatorAutoConfiguration.class,
HttpTraceAutoConfiguration.class))
.withConfiguration(
AutoConfigurations.of(EndpointAutoConfigurationClasses.ALL));

@ -1,5 +1,5 @@
/*
* 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.
@ -19,8 +19,6 @@ package org.springframework.boot.actuate.health;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.util.Assert;
/**
* {@link HealthIndicator} that returns health indications from all registered delegates.
*
@ -31,43 +29,71 @@ import org.springframework.util.Assert;
*/
public class CompositeHealthIndicator implements HealthIndicator {
private final Map<String, HealthIndicator> indicators;
private final HealthIndicatorRegistry registry;
private final HealthAggregator healthAggregator;
private final HealthAggregator aggregator;
/**
* Create a new {@link CompositeHealthIndicator}.
* @param healthAggregator the health aggregator
* @deprecated since 2.1.0 in favour of
* {@link #CompositeHealthIndicator(HealthAggregator, HealthIndicatorRegistry)}
*/
@Deprecated
public CompositeHealthIndicator(HealthAggregator healthAggregator) {
this(healthAggregator, new LinkedHashMap<>());
this(healthAggregator, new DefaultHealthIndicatorRegistry());
}
/**
* Create a new {@link CompositeHealthIndicator} from the specified indicators.
* Create a new {@link CompositeHealthIndicator} from the specified
* indicators.
* @param healthAggregator the health aggregator
* @param indicators a map of {@link HealthIndicator}s with the key being used as an
* indicator name.
* @param indicators a map of {@link HealthIndicator HealthIndicators} with
* the key being used as an indicator name.
* @deprecated since 2.1.0 in favour of
* {@link #CompositeHealthIndicator(HealthAggregator, HealthIndicatorRegistry)}
*/
@Deprecated
public CompositeHealthIndicator(HealthAggregator healthAggregator,
Map<String, HealthIndicator> indicators) {
Assert.notNull(healthAggregator, "HealthAggregator must not be null");
Assert.notNull(indicators, "Indicators must not be null");
this.indicators = new LinkedHashMap<>(indicators);
this.healthAggregator = healthAggregator;
this(healthAggregator, new DefaultHealthIndicatorRegistry(indicators));
}
/**
* Create a new {@link CompositeHealthIndicator} from the indicators in the
* given {@code registry}.
* @param healthAggregator the health aggregator
* @param registry the registry of {@link HealthIndicator HealthIndicators}.
*/
public CompositeHealthIndicator(HealthAggregator healthAggregator,
HealthIndicatorRegistry registry) {
this.aggregator = healthAggregator;
this.registry = registry;
}
/**
* Adds the given {@code healthIndicator}, associating it with the given
* {@code name}.
* @param name the name of the indicator
* @param indicator the indicator
* @throws IllegalStateException if an indicator with the given {@code name}
* is already registered.
* @deprecated since 2.1.0 in favour of
* {@link HealthIndicatorRegistry#register(String, HealthIndicator)}
*/
@Deprecated
public void addHealthIndicator(String name, HealthIndicator indicator) {
this.indicators.put(name, indicator);
this.registry.register(name, indicator);
}
@Override
public Health health() {
Map<String, Health> healths = new LinkedHashMap<>();
for (Map.Entry<String, HealthIndicator> entry : this.indicators.entrySet()) {
for (Map.Entry<String, HealthIndicator> entry : this.registry.getAll()
.entrySet()) {
healths.put(entry.getKey(), entry.getValue().health());
}
return this.healthAggregator.aggregate(healths);
return this.aggregator.aggregate(healths);
}
}

@ -1,5 +1,5 @@
/*
* 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.
@ -26,20 +26,23 @@ import org.springframework.util.Assert;
*
* @author Stephane Nicoll
* @since 2.0.0
* @deprecated since 2.1.0 in favor of
* {@link CompositeHealthIndicator#CompositeHealthIndicator(HealthAggregator, HealthIndicatorRegistry)}
*/
@Deprecated
public class CompositeHealthIndicatorFactory {
private final Function<String, String> healthIndicatorNameFactory;
public CompositeHealthIndicatorFactory() {
this(new HealthIndicatorNameFactory());
}
public CompositeHealthIndicatorFactory(
Function<String, String> healthIndicatorNameFactory) {
this.healthIndicatorNameFactory = healthIndicatorNameFactory;
}
public CompositeHealthIndicatorFactory() {
this(new HealthIndicatorNameFactory());
}
/**
* Create a {@link CompositeHealthIndicator} based on the specified health indicators.
* @param healthAggregator the {@link HealthAggregator}
@ -52,13 +55,10 @@ public class CompositeHealthIndicatorFactory {
Map<String, HealthIndicator> healthIndicators) {
Assert.notNull(healthAggregator, "HealthAggregator must not be null");
Assert.notNull(healthIndicators, "HealthIndicators must not be null");
CompositeHealthIndicator healthIndicator = new CompositeHealthIndicator(
healthAggregator);
for (Map.Entry<String, HealthIndicator> entry : healthIndicators.entrySet()) {
String name = this.healthIndicatorNameFactory.apply(entry.getKey());
healthIndicator.addHealthIndicator(name, entry.getValue());
}
return healthIndicator;
HealthIndicatorRegistryFactory factory = new HealthIndicatorRegistryFactory(
this.healthIndicatorNameFactory);
return new CompositeHealthIndicator(
healthAggregator, factory.createHealthIndicatorRegistry(healthIndicators));
}
}

@ -17,7 +17,7 @@
package org.springframework.boot.actuate.health;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.util.Assert;
@ -26,42 +26,68 @@ import org.springframework.util.Assert;
* Default implementation of {@link HealthIndicatorRegistry}.
*
* @author Vedran Pavic
* @author Stephane Nicoll
* @since 2.1.0
*/
public class DefaultHealthIndicatorRegistry implements HealthIndicatorRegistry {
private final Map<String, HealthIndicator> healthIndicators = new HashMap<>();
private final Object monitor = new Object();
private final Map<String, HealthIndicator> healthIndicators;
/**
* Create a new {@link DefaultHealthIndicatorRegistry}.
*/
public DefaultHealthIndicatorRegistry() {
this(new LinkedHashMap<>());
}
/**
* Create a new {@link DefaultHealthIndicatorRegistry} from the specified
* indicators.
* @param healthIndicators a map of {@link HealthIndicator}s with the key
* being used as an indicator name.
*/
public DefaultHealthIndicatorRegistry(Map<String, HealthIndicator> healthIndicators) {
Assert.notNull(healthIndicators, "HealthIndicators must not be null");
this.healthIndicators = new LinkedHashMap<>(healthIndicators);
}
@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) {
Assert.notNull(name, "Name must not be null");
synchronized (this.monitor) {
HealthIndicator existing = this.healthIndicators.putIfAbsent(name,
healthIndicator);
if (existing != null) {
throw new IllegalStateException(
"HealthIndicator with name '" + name + "' already registered");
}
this.healthIndicators.put(name, healthIndicator);
}
}
@Override
public HealthIndicator unregister(String name) {
synchronized (this.healthIndicators) {
Assert.notNull(name, "Name must not be null");
synchronized (this.monitor) {
return this.healthIndicators.remove(name);
}
}
@Override
public HealthIndicator get(String name) {
synchronized (this.healthIndicators) {
Assert.notNull(name, "Name must not be null");
synchronized (this.monitor) {
return this.healthIndicators.get(name);
}
}
@Override
public Map<String, HealthIndicator> getAll() {
synchronized (this.healthIndicators) {
return Collections.unmodifiableMap(new HashMap<>(this.healthIndicators));
synchronized (this.monitor) {
return Collections
.unmodifiableMap(new LinkedHashMap<>(this.healthIndicators));
}
}

@ -1,5 +1,5 @@
/*
* 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.
@ -19,8 +19,7 @@ package org.springframework.boot.actuate.health;
import java.util.Map;
/**
* Strategy interface used by {@link CompositeHealthIndicator} to aggregate {@link Health}
* instances into a final one.
* Strategy interface used to aggregate {@link Health} instances into a final one.
* <p>
* This is especially useful to combine subsystem states expressed through
* {@link Health#getStatus()} into one state for the entire system. The default

@ -26,35 +26,26 @@ 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 HealthAggregator healthAggregator;
private final HealthIndicatorRegistry healthIndicatorRegistry;
private final HealthIndicator healthIndicator;
/**
* Create a new {@link HealthEndpoint} instance.
* @param healthAggregator the health aggregator
* @param healthIndicatorRegistry the health indicator registry
* Create a new {@link HealthEndpoint} instance that will use the given
* {@code healthIndicator} to generate its response.
* @param healthIndicator the health indicator
*/
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;
public HealthEndpoint(HealthIndicator healthIndicator) {
Assert.notNull(healthIndicator, "HealthIndicator must not be null");
this.healthIndicator = healthIndicator;
}
@ReadOperation
public Health health() {
CompositeHealthIndicatorFactory factory = new CompositeHealthIndicatorFactory();
CompositeHealthIndicator healthIndicator = factory.createHealthIndicator(
this.healthAggregator, this.healthIndicatorRegistry.getAll());
return healthIndicator.health();
return this.healthIndicator.health();
}
}

@ -19,46 +19,48 @@ package org.springframework.boot.actuate.health;
import java.util.Map;
/**
* A registry of {@link HealthIndicator}s.
* A registry of {@link HealthIndicator HealthIndicators}.
* <p>
* Implementations <strong>must</strong> be thread-safe.
*
* @author Andy Wilkinson
* @author Vedran Pavic
* @author Stephane Nicoll
* @since 2.1.0
*/
public interface HealthIndicatorRegistry {
/**
* Registers the given {@code healthIndicator}, associating it with the given
* {@code name}.
* 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.
* @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}.
* 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}.
* @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}.
* @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.
* 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();

@ -0,0 +1,66 @@
/*
* 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;
import java.util.function.Function;
import org.springframework.util.Assert;
/**
* Factory to create a {@link HealthIndicatorRegistry}.
*
* @author Stephane Nicoll
* @since 2.1.0
*/
public class HealthIndicatorRegistryFactory {
private final Function<String, String> healthIndicatorNameFactory;
public HealthIndicatorRegistryFactory(
Function<String, String> healthIndicatorNameFactory) {
this.healthIndicatorNameFactory = healthIndicatorNameFactory;
}
public HealthIndicatorRegistryFactory() {
this(new HealthIndicatorNameFactory());
}
/**
* Create a {@link HealthIndicatorRegistry} based on the specified health
* indicators.
* @param healthIndicators the {@link HealthIndicator} instances mapped by
* name
* @return a {@link HealthIndicator} that delegates to the specified
* {@code healthIndicators}.
*/
public HealthIndicatorRegistry createHealthIndicatorRegistry(
Map<String, HealthIndicator> healthIndicators) {
Assert.notNull(healthIndicators, "HealthIndicators must not be null");
return initialize(new DefaultHealthIndicatorRegistry(), healthIndicators);
}
protected <T extends HealthIndicatorRegistry> T initialize(T registry,
Map<String, HealthIndicator> healthIndicators) {
for (Map.Entry<String, HealthIndicator> entry : healthIndicators.entrySet()) {
String name = this.healthIndicatorNameFactory.apply(entry.getKey());
registry.register(name, entry.getValue());
}
return registry;
}
}

@ -1,5 +1,5 @@
/*
* 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.assertThat;
* @author Christian Dupuis
* @author Andy Wilkinson
*/
@Deprecated
public class CompositeHealthIndicatorFactoryTests {
@Test

@ -1,5 +1,5 @@
/*
* 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,6 +16,7 @@
package org.springframework.boot.actuate.health;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@ -45,9 +46,6 @@ public class CompositeHealthIndicatorTests {
@Mock
private HealthIndicator two;
@Mock
private HealthIndicator three;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
@ -55,8 +53,6 @@ public class CompositeHealthIndicatorTests {
.willReturn(new Health.Builder().unknown().withDetail("1", "1").build());
given(this.two.health())
.willReturn(new Health.Builder().unknown().withDetail("2", "2").build());
given(this.three.health())
.willReturn(new Health.Builder().unknown().withDetail("3", "3").build());
this.healthAggregator = new OrderedHealthAggregator();
}
@ -67,7 +63,7 @@ public class CompositeHealthIndicatorTests {
indicators.put("one", this.one);
indicators.put("two", this.two);
CompositeHealthIndicator composite = new CompositeHealthIndicator(
this.healthAggregator, indicators);
this.healthAggregator, new DefaultHealthIndicatorRegistry(indicators));
Health result = composite.health();
assertThat(result.getDetails()).hasSize(2);
assertThat(result.getDetails()).containsEntry("one",
@ -76,48 +72,16 @@ public class CompositeHealthIndicatorTests {
new Health.Builder().unknown().withDetail("2", "2").build());
}
@Test
public void createWithIndicatorsAndAdd() {
Map<String, HealthIndicator> indicators = new HashMap<>();
indicators.put("one", this.one);
indicators.put("two", this.two);
CompositeHealthIndicator composite = new CompositeHealthIndicator(
this.healthAggregator, indicators);
composite.addHealthIndicator("three", this.three);
Health result = composite.health();
assertThat(result.getDetails()).hasSize(3);
assertThat(result.getDetails()).containsEntry("one",
new Health.Builder().unknown().withDetail("1", "1").build());
assertThat(result.getDetails()).containsEntry("two",
new Health.Builder().unknown().withDetail("2", "2").build());
assertThat(result.getDetails()).containsEntry("three",
new Health.Builder().unknown().withDetail("3", "3").build());
}
@Test
public void createWithoutAndAdd() {
CompositeHealthIndicator composite = new CompositeHealthIndicator(
this.healthAggregator);
composite.addHealthIndicator("one", this.one);
composite.addHealthIndicator("two", this.two);
Health result = composite.health();
assertThat(result.getDetails().size()).isEqualTo(2);
assertThat(result.getDetails()).containsEntry("one",
new Health.Builder().unknown().withDetail("1", "1").build());
assertThat(result.getDetails()).containsEntry("two",
new Health.Builder().unknown().withDetail("2", "2").build());
}
@Test
public void testSerialization() throws Exception {
Map<String, HealthIndicator> indicators = new HashMap<>();
indicators.put("db1", this.one);
indicators.put("db2", this.two);
CompositeHealthIndicator innerComposite = new CompositeHealthIndicator(
this.healthAggregator, indicators);
this.healthAggregator, new DefaultHealthIndicatorRegistry(indicators));
CompositeHealthIndicator composite = new CompositeHealthIndicator(
this.healthAggregator);
composite.addHealthIndicator("db", innerComposite);
this.healthAggregator, new DefaultHealthIndicatorRegistry(
Collections.singletonMap("db", innerComposite)));
Health result = composite.health();
ObjectMapper mapper = new ObjectMapper();
assertThat(mapper.writeValueAsString(result)).isEqualTo(

@ -16,6 +16,8 @@
package org.springframework.boot.actuate.health;
import java.util.Map;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@ -29,8 +31,9 @@ import static org.mockito.Mockito.mock;
* Tests for {@link DefaultHealthIndicatorRegistry}.
*
* @author Vedran Pavic
* @author Stephane Nicoll
*/
public class DefaultHealthIndicatorRegistryTest {
public class DefaultHealthIndicatorRegistryTests {
@Rule
public ExpectedException thrown = ExpectedException.none();
@ -43,9 +46,10 @@ public class DefaultHealthIndicatorRegistryTest {
@Before
public void setUp() {
given(this.one.health()).willReturn(new Health.Builder().up().build());
given(this.two.health()).willReturn(new Health.Builder().unknown().build());
given(this.one.health())
.willReturn(new Health.Builder().unknown().withDetail("1", "1").build());
given(this.two.health())
.willReturn(new Health.Builder().unknown().withDetail("2", "2").build());
this.registry = new DefaultHealthIndicatorRegistry();
}
@ -60,9 +64,9 @@ public class DefaultHealthIndicatorRegistryTest {
@Test
public void registerAlreadyUsedName() {
this.registry.register("one", this.one);
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);
}
@ -77,7 +81,7 @@ public class DefaultHealthIndicatorRegistryTest {
}
@Test
public void unregisterNotKnown() {
public void unregisterUnknown() {
this.registry.register("one", this.one);
assertThat(this.registry.getAll()).hasSize(1);
HealthIndicator two = this.registry.unregister("two");
@ -85,4 +89,22 @@ public class DefaultHealthIndicatorRegistryTest {
assertThat(this.registry.getAll()).hasSize(1);
}
@Test
public void getAllIsASnapshot() {
this.registry.register("one", this.one);
Map<String, HealthIndicator> snapshot = this.registry.getAll();
assertThat(snapshot).containsOnlyKeys("one");
this.registry.register("two", this.two);
assertThat(snapshot).containsOnlyKeys("one");
}
@Test
public void getAllIsImmutable() {
this.registry.register("one", this.one);
Map<String, HealthIndicator> snapshot = this.registry.getAll();
this.thrown.expect(UnsupportedOperationException.class);
snapshot.clear();
}
}

@ -30,7 +30,6 @@ import static org.assertj.core.api.Assertions.entry;
* @author Phillip Webb
* @author Christian Dupuis
* @author Andy Wilkinson
* @author Vedran Pavic
*/
public class HealthEndpointTests {
@ -41,8 +40,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(new OrderedHealthAggregator(),
createHealthIndicatorRegistry(healthIndicators));
HealthEndpoint endpoint = new HealthEndpoint(
createHealthIndicator(healthIndicators));
Health health = endpoint.health();
assertThat(health.getStatus()).isEqualTo(Status.UP);
assertThat(health.getDetails()).containsOnlyKeys("up", "upAgain");
@ -52,11 +51,10 @@ public class HealthEndpointTests {
assertThat(upAgainHealth.getDetails()).containsOnly(entry("second", "2"));
}
private HealthIndicatorRegistry createHealthIndicatorRegistry(
private HealthIndicator createHealthIndicator(
Map<String, HealthIndicator> healthIndicators) {
HealthIndicatorRegistry registry = new DefaultHealthIndicatorRegistry();
healthIndicators.forEach(registry::register);
return registry;
return new CompositeHealthIndicator(new OrderedHealthAggregator(),
new DefaultHealthIndicatorRegistry(healthIndicators));
}
}

@ -35,7 +35,6 @@ 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 {
@ -53,23 +52,48 @@ public class HealthEndpointWebIntegrationTests {
@Test
public void whenHealthIsDown503ResponseIsReturned() {
context.getBean("alphaHealthIndicator", TestHealthIndicator.class)
.setHealth(Health.down().build());
client.get().uri("/actuator/health").exchange().expectStatus()
.isEqualTo(HttpStatus.SERVICE_UNAVAILABLE).expectBody().jsonPath("status")
.isEqualTo("DOWN").jsonPath("details.alpha.status").isEqualTo("DOWN")
.jsonPath("details.bravo.status").isEqualTo("UP");
HealthIndicatorRegistry registry = context.getBean(HealthIndicatorRegistry.class);
registry.register("charlie", () -> Health.down().build());
try {
client.get().uri("/actuator/health").exchange().expectStatus()
.isEqualTo(HttpStatus.SERVICE_UNAVAILABLE).expectBody().jsonPath("status")
.isEqualTo("DOWN").jsonPath("details.alpha.status").isEqualTo("UP")
.jsonPath("details.bravo.status").isEqualTo("UP")
.jsonPath("details.charlie.status").isEqualTo("DOWN");
}
finally {
registry.unregister("charlie");
}
}
@Test
public void whenHealthIndicatorIsRemovedResponseIsAltered() {
HealthIndicatorRegistry registry = context.getBean(HealthIndicatorRegistry.class);
HealthIndicator bravo = registry.unregister("bravo");
try {
client.get().uri("/actuator/health").exchange().expectStatus().isOk().expectBody()
.jsonPath("status").isEqualTo("UP").jsonPath("details.alpha.status")
.isEqualTo("UP").jsonPath("details.bravo.status").doesNotExist();
}
finally {
registry.register("bravo", bravo);
}
}
@Configuration
public static class TestConfiguration {
@Bean
public HealthEndpoint healthEndpoint(
public HealthIndicatorRegistry healthIndicatorFactory(
Map<String, HealthIndicator> healthIndicators) {
HealthIndicatorRegistry registry = new DefaultHealthIndicatorRegistry();
healthIndicators.forEach(registry::register);
return new HealthEndpoint(new OrderedHealthAggregator(), registry);
return new HealthIndicatorRegistryFactory()
.createHealthIndicatorRegistry(healthIndicators);
}
@Bean
public HealthEndpoint healthEndpoint(HealthIndicatorRegistry registry) {
return new HealthEndpoint(new CompositeHealthIndicator(
new OrderedHealthAggregator(), registry));
}
@Bean
@ -82,30 +106,13 @@ public class HealthEndpointWebIntegrationTests {
}
@Bean
public TestHealthIndicator alphaHealthIndicator() {
return new TestHealthIndicator();
public HealthIndicator alphaHealthIndicator() {
return () -> Health.up().build();
}
@Bean
public TestHealthIndicator bravoHealthIndicator() {
return new TestHealthIndicator();
}
}
private static class TestHealthIndicator implements HealthIndicator {
private Health health = Health.up().build();
@Override
public Health health() {
Health result = this.health;
this.health = Health.up().build();
return result;
}
void setHealth(Health health) {
this.health = health;
public HealthIndicator bravoHealthIndicator() {
return () -> Health.up().build();
}
}

@ -727,9 +727,9 @@ unauthenticated users.
Health information is collected from all
{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
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
@ -820,7 +820,8 @@ NOTE: The identifier for a given `HealthIndicator` is the name of the bean witho
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`].
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

Loading…
Cancel
Save