Skip to content

Commit 23585d2

Browse files
committed
Polish "Introduce HealthIndicatorRegistry"
This commit is work in progress and polishes the initial submission by making sure that the CompositeHealthIndicator is also a registry. The current infrastructure prevents the composite to be registered as a bean as it would be an extra HealthIndicator in the context and therefore may be taken into account as a regular indicator. The registry on the other hand must be registered in the context so that users can manipulate its content. Ultimately, both those objects share common base features that we should not duplicate. This commit makes sure that the registry has a `health()` method but does not implement HealthIndicator. The composite now extends from the registry and implements HealthIndicator, simply delegating to that parent method. It's unclear at this point if we want to keep this arrangement as the duplication of factories is a bit annoying. See gh-4965
1 parent d2bc034 commit 23585d2

File tree

19 files changed

+287
-180
lines changed

19 files changed

+287
-180
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/CompositeHealthIndicatorConfiguration.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ protected HealthIndicator createHealthIndicator(Map<String, S> beans) {
4444
}
4545
CompositeHealthIndicator composite = new CompositeHealthIndicator(
4646
this.healthAggregator);
47-
beans.forEach((name, source) -> composite.addHealthIndicator(name,
47+
beans.forEach((name, source) -> composite.register(name,
4848
createHealthIndicator(source)));
4949
return composite;
5050
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointConfiguration.java

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,41 +16,28 @@
1616

1717
package org.springframework.boot.actuate.autoconfigure.health;
1818

19-
import org.springframework.beans.factory.ObjectProvider;
2019
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint;
21-
import org.springframework.boot.actuate.health.HealthAggregator;
2220
import org.springframework.boot.actuate.health.HealthEndpoint;
2321
import org.springframework.boot.actuate.health.HealthIndicatorRegistry;
24-
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
2522
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
23+
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
2624
import org.springframework.context.annotation.Bean;
2725
import org.springframework.context.annotation.Configuration;
2826

2927
/**
3028
* Configuration for {@link HealthEndpoint}.
3129
*
3230
* @author Stephane Nicoll
33-
* @author Vedran Pavic
3431
*/
3532
@Configuration
33+
@ConditionalOnSingleCandidate(HealthIndicatorRegistry.class)
3634
class HealthEndpointConfiguration {
3735

38-
private final HealthAggregator healthAggregator;
39-
40-
private final HealthIndicatorRegistry healthIndicatorRegistry;
41-
42-
HealthEndpointConfiguration(ObjectProvider<HealthAggregator> healthAggregator,
43-
ObjectProvider<HealthIndicatorRegistry> healthIndicatorRegistry) {
44-
this.healthAggregator = healthAggregator
45-
.getIfAvailable(OrderedHealthAggregator::new);
46-
this.healthIndicatorRegistry = healthIndicatorRegistry.getObject();
47-
}
48-
4936
@Bean
5037
@ConditionalOnMissingBean
5138
@ConditionalOnEnabledEndpoint
52-
public HealthEndpoint healthEndpoint() {
53-
return new HealthEndpoint(this.healthAggregator, this.healthIndicatorRegistry);
39+
public HealthEndpoint healthEndpoint(HealthIndicatorRegistry registry) {
40+
return new HealthEndpoint(registry);
5441
}
5542

5643
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthIndicatorAutoConfiguration.java

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,7 @@
1616

1717
package org.springframework.boot.actuate.autoconfigure.health;
1818

19-
import java.util.LinkedHashMap;
20-
import java.util.Map;
21-
2219
import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
23-
import org.springframework.boot.actuate.health.DefaultHealthIndicatorRegistry;
2420
import org.springframework.boot.actuate.health.HealthAggregator;
2521
import org.springframework.boot.actuate.health.HealthIndicator;
2622
import org.springframework.boot.actuate.health.HealthIndicatorRegistry;
@@ -32,7 +28,6 @@
3228
import org.springframework.context.ApplicationContext;
3329
import org.springframework.context.annotation.Bean;
3430
import org.springframework.context.annotation.Configuration;
35-
import org.springframework.util.ClassUtils;
3631

3732
/**
3833
* {@link EnableAutoConfiguration Auto-configuration} for {@link HealthIndicator}s.
@@ -73,30 +68,7 @@ public OrderedHealthAggregator healthAggregator() {
7368
@ConditionalOnMissingBean(HealthIndicatorRegistry.class)
7469
public HealthIndicatorRegistry healthIndicatorRegistry(
7570
ApplicationContext applicationContext) {
76-
HealthIndicatorRegistry registry = new DefaultHealthIndicatorRegistry();
77-
Map<String, HealthIndicator> indicators = new LinkedHashMap<>();
78-
indicators.putAll(applicationContext.getBeansOfType(HealthIndicator.class));
79-
if (ClassUtils.isPresent("reactor.core.publisher.Flux", null)) {
80-
new ReactiveHealthIndicators().get(applicationContext)
81-
.forEach(indicators::putIfAbsent);
82-
}
83-
indicators.forEach(registry::register);
84-
return registry;
85-
}
86-
87-
private static class ReactiveHealthIndicators {
88-
89-
public Map<String, HealthIndicator> get(ApplicationContext applicationContext) {
90-
Map<String, HealthIndicator> indicators = new LinkedHashMap<>();
91-
applicationContext.getBeansOfType(ReactiveHealthIndicator.class)
92-
.forEach((name, indicator) -> indicators.put(name, adapt(indicator)));
93-
return indicators;
94-
}
95-
96-
private HealthIndicator adapt(ReactiveHealthIndicator indicator) {
97-
return () -> indicator.health().block();
98-
}
99-
71+
return HealthIndicatorRegistryBeans.get(applicationContext);
10072
}
10173

10274
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Copyright 2012-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.autoconfigure.health;
18+
19+
import java.util.LinkedHashMap;
20+
import java.util.Map;
21+
22+
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
23+
import org.springframework.boot.actuate.health.HealthAggregator;
24+
import org.springframework.boot.actuate.health.HealthIndicator;
25+
import org.springframework.boot.actuate.health.HealthIndicatorRegistry;
26+
import org.springframework.boot.actuate.health.HealthIndicatorRegistryFactory;
27+
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
28+
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
29+
import org.springframework.context.ApplicationContext;
30+
import org.springframework.util.ClassUtils;
31+
32+
/**
33+
* Creates a {@link HealthIndicatorRegistry} from beans in the {@link ApplicationContext}.
34+
*
35+
* @author Phillip Webb
36+
* @author Stephane Nicoll
37+
*/
38+
final class HealthIndicatorRegistryBeans {
39+
40+
private HealthIndicatorRegistryBeans() {
41+
}
42+
43+
public static HealthIndicatorRegistry get(ApplicationContext applicationContext) {
44+
HealthAggregator healthAggregator = getHealthAggregator(applicationContext);
45+
Map<String, HealthIndicator> indicators = new LinkedHashMap<>();
46+
indicators.putAll(applicationContext.getBeansOfType(HealthIndicator.class));
47+
if (ClassUtils.isPresent("reactor.core.publisher.Flux", null)) {
48+
new ReactiveHealthIndicators().get(applicationContext)
49+
.forEach(indicators::putIfAbsent);
50+
}
51+
HealthIndicatorRegistryFactory factory = new HealthIndicatorRegistryFactory();
52+
return factory.createHealthIndicatorRegistry(healthAggregator, indicators);
53+
}
54+
55+
private static HealthAggregator getHealthAggregator(
56+
ApplicationContext applicationContext) {
57+
try {
58+
return applicationContext.getBean(HealthAggregator.class);
59+
}
60+
catch (NoSuchBeanDefinitionException ex) {
61+
return new OrderedHealthAggregator();
62+
}
63+
}
64+
65+
private static class ReactiveHealthIndicators {
66+
67+
public Map<String, HealthIndicator> get(ApplicationContext applicationContext) {
68+
Map<String, HealthIndicator> indicators = new LinkedHashMap<>();
69+
applicationContext.getBeansOfType(ReactiveHealthIndicator.class)
70+
.forEach((name, indicator) -> indicators.put(name, adapt(indicator)));
71+
return indicators;
72+
}
73+
74+
private HealthIndicator adapt(ReactiveHealthIndicator indicator) {
75+
return () -> indicator.health().block();
76+
}
77+
78+
}
79+
80+
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryWebEndpointDiscovererTests.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
import org.springframework.boot.actuate.endpoint.web.PathMapper;
3535
import org.springframework.boot.actuate.endpoint.web.WebOperation;
3636
import org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExtension;
37-
import org.springframework.boot.actuate.health.HealthAggregator;
3837
import org.springframework.boot.actuate.health.HealthEndpoint;
3938
import org.springframework.boot.actuate.health.HealthIndicatorRegistry;
4039
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
@@ -110,8 +109,7 @@ public TestEndpointWebExtension testEndpointWebExtension() {
110109

111110
@Bean
112111
public HealthEndpoint healthEndpoint() {
113-
return new HealthEndpoint(mock(HealthAggregator.class),
114-
mock(HealthIndicatorRegistry.class));
112+
return new HealthEndpoint(mock(HealthIndicatorRegistry.class));
115113
}
116114

117115
@Bean

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/HealthEndpointDocumentationTests.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,9 @@
2323

2424
import org.junit.Test;
2525

26-
import org.springframework.boot.actuate.health.DefaultHealthIndicatorRegistry;
26+
import org.springframework.boot.actuate.health.CompositeHealthIndicator;
2727
import org.springframework.boot.actuate.health.HealthEndpoint;
2828
import org.springframework.boot.actuate.health.HealthIndicator;
29-
import org.springframework.boot.actuate.health.HealthIndicatorRegistry;
3029
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
3130
import org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator;
3231
import org.springframework.boot.actuate.system.DiskSpaceHealthIndicator;
@@ -73,9 +72,8 @@ static class TestConfiguration {
7372

7473
@Bean
7574
public HealthEndpoint endpoint(Map<String, HealthIndicator> healthIndicators) {
76-
HealthIndicatorRegistry registry = new DefaultHealthIndicatorRegistry();
77-
healthIndicators.forEach(registry::register);
78-
return new HealthEndpoint(new OrderedHealthAggregator(), registry);
75+
return new HealthEndpoint(new CompositeHealthIndicator(
76+
new OrderedHealthAggregator(), healthIndicators));
7977
}
8078

8179
@Bean

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/JmxEndpointIntegrationTests.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ public class JmxEndpointIntegrationTests {
4848
private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
4949
.withConfiguration(AutoConfigurations.of(JmxAutoConfiguration.class,
5050
EndpointAutoConfiguration.class, JmxEndpointAutoConfiguration.class,
51-
HttpTraceAutoConfiguration.class, HealthIndicatorAutoConfiguration.class))
51+
HealthIndicatorAutoConfiguration.class,
52+
HttpTraceAutoConfiguration.class))
5253
.withConfiguration(
5354
AutoConfigurations.of(EndpointAutoConfigurationClasses.ALL));
5455

spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/CompositeHealthIndicator.java

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,8 @@
1616

1717
package org.springframework.boot.actuate.health;
1818

19-
import java.util.LinkedHashMap;
2019
import java.util.Map;
2120

22-
import org.springframework.util.Assert;
23-
2421
/**
2522
* {@link HealthIndicator} that returns health indications from all registered delegates.
2623
*
@@ -29,18 +26,15 @@
2926
* @author Christian Dupuis
3027
* @since 1.1.0
3128
*/
32-
public class CompositeHealthIndicator implements HealthIndicator {
33-
34-
private final Map<String, HealthIndicator> indicators;
35-
36-
private final HealthAggregator healthAggregator;
29+
public class CompositeHealthIndicator
30+
extends DefaultHealthIndicatorRegistry implements HealthIndicator {
3731

3832
/**
3933
* Create a new {@link CompositeHealthIndicator}.
4034
* @param healthAggregator the health aggregator
4135
*/
4236
public CompositeHealthIndicator(HealthAggregator healthAggregator) {
43-
this(healthAggregator, new LinkedHashMap<>());
37+
super(healthAggregator);
4438
}
4539

4640
/**
@@ -51,23 +45,21 @@ public CompositeHealthIndicator(HealthAggregator healthAggregator) {
5145
*/
5246
public CompositeHealthIndicator(HealthAggregator healthAggregator,
5347
Map<String, HealthIndicator> indicators) {
54-
Assert.notNull(healthAggregator, "HealthAggregator must not be null");
55-
Assert.notNull(indicators, "Indicators must not be null");
56-
this.indicators = new LinkedHashMap<>(indicators);
57-
this.healthAggregator = healthAggregator;
48+
super(healthAggregator, indicators);
5849
}
5950

51+
/**
52+
* Registers the given {@code healthIndicator}, associating it with the given
53+
* {@code name}.
54+
* @param name the name of the indicator
55+
* @param indicator the indicator
56+
* @throws IllegalStateException if an indicator with the given {@code name} is
57+
* already registered.
58+
* @deprecated as of 2.1.0 in favour of {@link #register(String, HealthIndicator)}
59+
*/
60+
@Deprecated
6061
public void addHealthIndicator(String name, HealthIndicator indicator) {
61-
this.indicators.put(name, indicator);
62-
}
63-
64-
@Override
65-
public Health health() {
66-
Map<String, Health> healths = new LinkedHashMap<>();
67-
for (Map.Entry<String, HealthIndicator> entry : this.indicators.entrySet()) {
68-
healths.put(entry.getKey(), entry.getValue().health());
69-
}
70-
return this.healthAggregator.aggregate(healths);
62+
register(name, indicator);
7163
}
7264

7365
}

spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/CompositeHealthIndicatorFactory.java

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,15 @@
2727
* @author Stephane Nicoll
2828
* @since 2.0.0
2929
*/
30-
public class CompositeHealthIndicatorFactory {
31-
32-
private final Function<String, String> healthIndicatorNameFactory;
30+
public class CompositeHealthIndicatorFactory extends HealthIndicatorRegistryFactory {
3331

3432
public CompositeHealthIndicatorFactory(
3533
Function<String, String> healthIndicatorNameFactory) {
36-
this.healthIndicatorNameFactory = healthIndicatorNameFactory;
34+
super(healthIndicatorNameFactory);
3735
}
3836

3937
public CompositeHealthIndicatorFactory() {
40-
this(new HealthIndicatorNameFactory());
38+
super();
4139
}
4240

4341
/**
@@ -52,13 +50,8 @@ public CompositeHealthIndicator createHealthIndicator(
5250
Map<String, HealthIndicator> healthIndicators) {
5351
Assert.notNull(healthAggregator, "HealthAggregator must not be null");
5452
Assert.notNull(healthIndicators, "HealthIndicators must not be null");
55-
CompositeHealthIndicator healthIndicator = new CompositeHealthIndicator(
56-
healthAggregator);
57-
for (Map.Entry<String, HealthIndicator> entry : healthIndicators.entrySet()) {
58-
String name = this.healthIndicatorNameFactory.apply(entry.getKey());
59-
healthIndicator.addHealthIndicator(name, entry.getValue());
60-
}
61-
return healthIndicator;
53+
return initialize(new CompositeHealthIndicator(healthAggregator),
54+
healthIndicators);
6255
}
6356

6457
}

0 commit comments

Comments
 (0)