Skip to content

Commit 2c176a3

Browse files
committed
Add support for ReactiveHealthIndicatorRegistry
This commit updates the initial proposal to add support for reactive use cases as well. A reactive application can use ReactiveHealthIndicatorRegistry as an alternative to HealthIndicatorRegistry. Closes gh-4965
1 parent 95b2515 commit 2c176a3

16 files changed

+589
-146
lines changed

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@
2020

2121
import org.springframework.beans.factory.annotation.Autowired;
2222
import org.springframework.boot.actuate.health.CompositeReactiveHealthIndicator;
23+
import org.springframework.boot.actuate.health.DefaultReactiveHealthIndicatorRegistry;
2324
import org.springframework.boot.actuate.health.HealthAggregator;
2425
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
26+
import org.springframework.boot.actuate.health.ReactiveHealthIndicatorRegistry;
2527
import org.springframework.core.ResolvableType;
2628

2729
/**
@@ -41,11 +43,10 @@ protected ReactiveHealthIndicator createHealthIndicator(Map<String, S> beans) {
4143
if (beans.size() == 1) {
4244
return createHealthIndicator(beans.values().iterator().next());
4345
}
44-
CompositeReactiveHealthIndicator composite = new CompositeReactiveHealthIndicator(
45-
this.healthAggregator);
46-
beans.forEach((name, source) -> composite.addHealthIndicator(name,
46+
ReactiveHealthIndicatorRegistry registry = new DefaultReactiveHealthIndicatorRegistry();
47+
beans.forEach((name, source) -> registry.register(name,
4748
createHealthIndicator(source)));
48-
return composite;
49+
return new CompositeReactiveHealthIndicator(this.healthAggregator, registry);
4950
}
5051

5152
@SuppressWarnings("unchecked")

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

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

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

19-
import java.util.Collections;
20-
import java.util.Map;
21-
2219
import org.springframework.beans.factory.ObjectProvider;
2320
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint;
24-
import org.springframework.boot.actuate.health.CompositeReactiveHealthIndicatorFactory;
21+
import org.springframework.boot.actuate.health.CompositeReactiveHealthIndicator;
2522
import org.springframework.boot.actuate.health.HealthAggregator;
2623
import org.springframework.boot.actuate.health.HealthEndpoint;
2724
import org.springframework.boot.actuate.health.HealthEndpointWebExtension;
28-
import org.springframework.boot.actuate.health.HealthIndicator;
2925
import org.springframework.boot.actuate.health.HealthStatusHttpMapper;
3026
import org.springframework.boot.actuate.health.HealthWebEndpointResponseMapper;
3127
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
3228
import org.springframework.boot.actuate.health.ReactiveHealthEndpointWebExtension;
3329
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
30+
import org.springframework.boot.actuate.health.ReactiveHealthIndicatorRegistry;
3431
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
3532
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
33+
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
3634
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
3735
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
3836
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@@ -70,19 +68,16 @@ public HealthWebEndpointResponseMapper healthWebEndpointResponseMapper(
7068

7169
@Configuration
7270
@ConditionalOnWebApplication(type = Type.REACTIVE)
71+
@ConditionalOnSingleCandidate(ReactiveHealthIndicatorRegistry.class)
7372
static class ReactiveWebHealthConfiguration {
7473

7574
private final ReactiveHealthIndicator reactiveHealthIndicator;
7675

7776
ReactiveWebHealthConfiguration(ObjectProvider<HealthAggregator> healthAggregator,
78-
ObjectProvider<Map<String, ReactiveHealthIndicator>> reactiveHealthIndicators,
79-
ObjectProvider<Map<String, HealthIndicator>> healthIndicators) {
80-
this.reactiveHealthIndicator = new CompositeReactiveHealthIndicatorFactory()
81-
.createReactiveHealthIndicator(
82-
healthAggregator.getIfAvailable(OrderedHealthAggregator::new),
83-
reactiveHealthIndicators
84-
.getIfAvailable(Collections::emptyMap),
85-
healthIndicators.getIfAvailable(Collections::emptyMap));
77+
ReactiveHealthIndicatorRegistry registry) {
78+
this.reactiveHealthIndicator = new CompositeReactiveHealthIndicator(
79+
healthAggregator.getIfAvailable(OrderedHealthAggregator::new),
80+
registry);
8681
}
8782

8883
@Bean

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,22 @@
1616

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

19+
import java.util.Collections;
20+
import java.util.Map;
21+
22+
import reactor.core.publisher.Flux;
23+
24+
import org.springframework.beans.factory.ObjectProvider;
1925
import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
2026
import org.springframework.boot.actuate.health.HealthAggregator;
2127
import org.springframework.boot.actuate.health.HealthIndicator;
2228
import org.springframework.boot.actuate.health.HealthIndicatorRegistry;
2329
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
2430
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
31+
import org.springframework.boot.actuate.health.ReactiveHealthIndicatorRegistry;
32+
import org.springframework.boot.actuate.health.ReactiveHealthIndicatorRegistryFactory;
2533
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
34+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2635
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
2736
import org.springframework.boot.context.properties.EnableConfigurationProperties;
2837
import org.springframework.context.ApplicationContext;
@@ -71,4 +80,20 @@ public HealthIndicatorRegistry healthIndicatorRegistry(
7180
return HealthIndicatorRegistryBeans.get(applicationContext);
7281
}
7382

83+
@Configuration
84+
@ConditionalOnClass(Flux.class)
85+
static class ReactiveHealthIndicatorConfiguration {
86+
87+
@Bean
88+
@ConditionalOnMissingBean
89+
public ReactiveHealthIndicatorRegistry reactiveHealthIndicatorRegistry(
90+
ObjectProvider<Map<String, ReactiveHealthIndicator>> reactiveHealthIndicators,
91+
ObjectProvider<Map<String, HealthIndicator>> healthIndicators) {
92+
return new ReactiveHealthIndicatorRegistryFactory().createReactiveHealthIndicatorRegistry(
93+
reactiveHealthIndicators.getIfAvailable(Collections::emptyMap),
94+
healthIndicators.getIfAvailable(Collections::emptyMap));
95+
}
96+
97+
}
98+
7499
}

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

Lines changed: 0 additions & 57 deletions
This file was deleted.

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/ReactiveHealthEndpointWebExtensionTests.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.springframework.boot.actuate.health.HealthWebEndpointResponseMapper;
2929
import org.springframework.boot.actuate.health.ReactiveHealthEndpointWebExtension;
3030
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
31+
import org.springframework.boot.actuate.health.ReactiveHealthIndicatorRegistry;
3132
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
3233
import org.springframework.context.annotation.Bean;
3334
import org.springframework.context.annotation.Configuration;
@@ -217,6 +218,24 @@ public void roleCanBeCustomized() {
217218
});
218219
}
219220

221+
@Test
222+
public void registryCanBeAltered() {
223+
this.contextRunner
224+
.withUserConfiguration(HealthIndicatorsConfiguration.class)
225+
.withPropertyValues("management.endpoint.health.show-details=always")
226+
.run((context) -> {
227+
ReactiveHealthIndicatorRegistry registry = context.getBean(
228+
ReactiveHealthIndicatorRegistry.class);
229+
ReactiveHealthEndpointWebExtension extension = context
230+
.getBean(ReactiveHealthEndpointWebExtension.class);
231+
assertThat(extension.health(null).block().getBody().getDetails())
232+
.containsOnlyKeys("application", "first", "second");
233+
assertThat(registry.unregister("second")).isNotNull();
234+
assertThat(extension.health(null).block().getBody().getDetails())
235+
.containsKeys("application", "first");
236+
});
237+
}
238+
220239
@Configuration
221240
static class HealthIndicatorsConfiguration {
222241

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

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@
2525
import reactor.core.publisher.Mono;
2626
import reactor.util.function.Tuple2;
2727

28-
import org.springframework.util.Assert;
29-
3028
/**
3129
* {@link ReactiveHealthIndicator} that returns health indications from all registered
3230
* delegates. Provides an alternative {@link Health} for a delegate that reaches a
@@ -37,7 +35,7 @@
3735
*/
3836
public class CompositeReactiveHealthIndicator implements ReactiveHealthIndicator {
3937

40-
private final Map<String, ReactiveHealthIndicator> indicators;
38+
private final ReactiveHealthIndicatorRegistry registry;
4139

4240
private final HealthAggregator healthAggregator;
4341

@@ -47,15 +45,42 @@ public class CompositeReactiveHealthIndicator implements ReactiveHealthIndicator
4745

4846
private final Function<Mono<Health>, Mono<Health>> timeoutCompose;
4947

48+
/**
49+
* Create a new {@link CompositeReactiveHealthIndicator}.
50+
* @param healthAggregator the health aggregator
51+
* @deprecated since 2.1.0 in favour of
52+
* {@link #CompositeReactiveHealthIndicator(HealthAggregator, ReactiveHealthIndicatorRegistry)}
53+
*/
54+
@Deprecated
5055
public CompositeReactiveHealthIndicator(HealthAggregator healthAggregator) {
5156
this(healthAggregator, new LinkedHashMap<>());
5257
}
5358

59+
/**
60+
* Create a new {@link CompositeReactiveHealthIndicator} from the specified
61+
* indicators.
62+
* @param healthAggregator the health aggregator
63+
* @param indicators a map of {@link ReactiveHealthIndicator HealthIndicators} with
64+
* the key being used as an indicator name.
65+
* @deprecated since 2.1.0 in favour of
66+
* {@link #CompositeReactiveHealthIndicator(HealthAggregator, ReactiveHealthIndicatorRegistry)}
67+
*/
68+
@Deprecated
5469
public CompositeReactiveHealthIndicator(HealthAggregator healthAggregator,
5570
Map<String, ReactiveHealthIndicator> indicators) {
56-
Assert.notNull(healthAggregator, "HealthAggregator must not be null");
57-
Assert.notNull(indicators, "Indicators must not be null");
58-
this.indicators = new LinkedHashMap<>(indicators);
71+
this(healthAggregator, new DefaultReactiveHealthIndicatorRegistry(indicators));
72+
73+
}
74+
75+
/**
76+
* Create a new {@link CompositeReactiveHealthIndicator} from the indicators in the
77+
* given {@code registry}.
78+
* @param healthAggregator the health aggregator
79+
* @param registry the registry of {@link ReactiveHealthIndicator HealthIndicators}.
80+
*/
81+
public CompositeReactiveHealthIndicator(HealthAggregator healthAggregator,
82+
ReactiveHealthIndicatorRegistry registry) {
83+
this.registry = registry;
5984
this.healthAggregator = healthAggregator;
6085
this.timeoutCompose = (mono) -> (this.timeout != null ? mono.timeout(
6186
Duration.ofMillis(this.timeout), Mono.just(this.timeoutHealth)) : mono);
@@ -66,10 +91,15 @@ public CompositeReactiveHealthIndicator(HealthAggregator healthAggregator,
6691
* @param name the name of the health indicator
6792
* @param indicator the health indicator to add
6893
* @return this instance
94+
* @throws IllegalStateException if an indicator with the given {@code name}
95+
* is already registered.
96+
* @deprecated since 2.1.0 in favour of
97+
* {@link ReactiveHealthIndicatorRegistry#register(String, ReactiveHealthIndicator)}
6998
*/
99+
@Deprecated
70100
public CompositeReactiveHealthIndicator addHealthIndicator(String name,
71101
ReactiveHealthIndicator indicator) {
72-
this.indicators.put(name, indicator);
102+
this.registry.register(name, indicator);
73103
return this;
74104
}
75105

@@ -92,7 +122,7 @@ public CompositeReactiveHealthIndicator timeoutStrategy(long timeout,
92122

93123
@Override
94124
public Mono<Health> health() {
95-
return Flux.fromIterable(this.indicators.entrySet())
125+
return Flux.fromIterable(this.registry.getAll().entrySet())
96126
.flatMap((entry) -> Mono.zip(Mono.just(entry.getKey()),
97127
entry.getValue().health().compose(this.timeoutCompose)))
98128
.collectMap(Tuple2::getT1, Tuple2::getT2)

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

Lines changed: 9 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2017 the original author or authors.
2+
* Copyright 2012-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,19 +16,20 @@
1616

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

19-
import java.util.LinkedHashMap;
2019
import java.util.Map;
2120
import java.util.function.Function;
2221

2322
import org.springframework.util.Assert;
24-
import org.springframework.util.ObjectUtils;
2523

2624
/**
2725
* Factory to create a {@link CompositeReactiveHealthIndicator}.
2826
*
2927
* @author Stephane Nicoll
3028
* @since 2.0.0
29+
* @deprecated since 2.1.0 in favor of
30+
* {@link CompositeReactiveHealthIndicator#CompositeReactiveHealthIndicator(HealthAggregator, ReactiveHealthIndicatorRegistry)}
3131
*/
32+
@Deprecated
3233
public class CompositeReactiveHealthIndicatorFactory {
3334

3435
private final Function<String, String> healthIndicatorNameFactory;
@@ -62,30 +63,11 @@ public CompositeReactiveHealthIndicator createReactiveHealthIndicator(
6263
Assert.notNull(healthAggregator, "HealthAggregator must not be null");
6364
Assert.notNull(reactiveHealthIndicators,
6465
"ReactiveHealthIndicators must not be null");
65-
CompositeReactiveHealthIndicator healthIndicator = new CompositeReactiveHealthIndicator(
66-
healthAggregator);
67-
merge(reactiveHealthIndicators, healthIndicators)
68-
.forEach((beanName, indicator) -> {
69-
String name = this.healthIndicatorNameFactory.apply(beanName);
70-
healthIndicator.addHealthIndicator(name, indicator);
71-
});
72-
return healthIndicator;
73-
}
74-
75-
private Map<String, ReactiveHealthIndicator> merge(
76-
Map<String, ReactiveHealthIndicator> reactiveHealthIndicators,
77-
Map<String, HealthIndicator> healthIndicators) {
78-
if (ObjectUtils.isEmpty(healthIndicators)) {
79-
return reactiveHealthIndicators;
80-
}
81-
Map<String, ReactiveHealthIndicator> allIndicators = new LinkedHashMap<>(
82-
reactiveHealthIndicators);
83-
healthIndicators.forEach((beanName, indicator) -> {
84-
String name = this.healthIndicatorNameFactory.apply(beanName);
85-
allIndicators.computeIfAbsent(name,
86-
(n) -> new HealthIndicatorReactiveAdapter(indicator));
87-
});
88-
return allIndicators;
66+
ReactiveHealthIndicatorRegistryFactory factory = new ReactiveHealthIndicatorRegistryFactory(
67+
this.healthIndicatorNameFactory);
68+
return new CompositeReactiveHealthIndicator(healthAggregator,
69+
factory.createReactiveHealthIndicatorRegistry(reactiveHealthIndicators,
70+
healthIndicators));
8971
}
9072

9173
}

0 commit comments

Comments
 (0)