Skip to content

Commit 739b92e

Browse files
committed
Consider reactive indicators in HealthEndpointGroupMembershipValidator
Update `HealthEndpointGroupMembershipValidator` to also consider reactive health indicators. Fixes gh-48387
1 parent e93f9c3 commit 739b92e

File tree

2 files changed

+65
-9
lines changed

2 files changed

+65
-9
lines changed

module/spring-boot-health/src/main/java/org/springframework/boot/health/autoconfigure/actuate/endpoint/HealthEndpointConfiguration.java

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.boot.health.autoconfigure.actuate.endpoint;
1818

1919
import java.util.Set;
20+
import java.util.function.BiFunction;
2021

2122
import org.jspecify.annotations.Nullable;
2223

@@ -34,6 +35,7 @@
3435
import org.springframework.boot.health.actuate.endpoint.SimpleStatusAggregator;
3536
import org.springframework.boot.health.actuate.endpoint.StatusAggregator;
3637
import org.springframework.boot.health.contributor.HealthContributors;
38+
import org.springframework.boot.health.contributor.ReactiveHealthContributors;
3739
import org.springframework.boot.health.registry.HealthContributorRegistry;
3840
import org.springframework.boot.health.registry.ReactiveHealthContributorRegistry;
3941
import org.springframework.context.ApplicationContext;
@@ -78,8 +80,10 @@ GroupsHealthContributorNameValidator groupsHealthContributorNameValidator(
7880
@Bean
7981
@ConditionalOnBooleanProperty(name = "management.endpoint.health.validate-group-membership", matchIfMissing = true)
8082
HealthEndpointGroupMembershipValidator healthEndpointGroupMembershipValidator(HealthEndpointProperties properties,
81-
HealthContributorRegistry healthContributorRegistry) {
82-
return new HealthEndpointGroupMembershipValidator(properties, healthContributorRegistry);
83+
HealthContributorRegistry healthContributorRegistry,
84+
ObjectProvider<ReactiveHealthContributorRegistry> reactiveHealthContributorRegistry) {
85+
return new HealthEndpointGroupMembershipValidator(properties, healthContributorRegistry,
86+
reactiveHealthContributorRegistry.getIfAvailable());
8387
}
8488

8589
@Bean
@@ -138,10 +142,13 @@ static class HealthEndpointGroupMembershipValidator implements SmartInitializing
138142

139143
private final HealthContributorRegistry registry;
140144

141-
HealthEndpointGroupMembershipValidator(HealthEndpointProperties properties,
142-
HealthContributorRegistry registry) {
145+
@Nullable ReactiveHealthContributorRegistry fallbackRegistry;
146+
147+
HealthEndpointGroupMembershipValidator(HealthEndpointProperties properties, HealthContributorRegistry registry,
148+
@Nullable ReactiveHealthContributorRegistry fallbackRegistry) {
143149
this.properties = properties;
144150
this.registry = registry;
151+
this.fallbackRegistry = fallbackRegistry;
145152
}
146153

147154
@Override
@@ -172,13 +179,28 @@ private void validate(@Nullable Set<String> names, String type, String group) {
172179
}
173180

174181
private boolean contributorExists(String[] path) {
182+
return contributorExistsInMainRegistry(path) || contributorExistsInFallbackRegistry(path);
183+
}
184+
185+
private boolean contributorExistsInMainRegistry(String[] path) {
186+
return contributorExists(path, this.registry, HealthContributors.class, HealthContributors::getContributor);
187+
}
188+
189+
private boolean contributorExistsInFallbackRegistry(String[] path) {
190+
return contributorExists(path, this.fallbackRegistry, ReactiveHealthContributors.class,
191+
ReactiveHealthContributors::getContributor);
192+
}
193+
194+
@SuppressWarnings("unchecked")
195+
private <C> boolean contributorExists(String[] path, @Nullable Object registry, Class<C> collectionType,
196+
BiFunction<C, String, Object> getFromCollection) {
175197
int pathOffset = 0;
176-
Object contributor = this.registry;
198+
Object contributor = registry;
177199
while (pathOffset < path.length) {
178-
if (!(contributor instanceof HealthContributors)) {
200+
if (contributor == null || !collectionType.isInstance(contributor)) {
179201
return false;
180202
}
181-
contributor = ((HealthContributors) contributor).getContributor(path[pathOffset]);
203+
contributor = getFromCollection.apply((C) contributor, path[pathOffset]);
182204
pathOffset++;
183205
}
184206
return (contributor != null);

module/spring-boot-health/src/test/java/org/springframework/boot/health/autoconfigure/actuate/endpoint/HealthEndpointAutoConfigurationTests.java

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import org.springframework.boot.health.autoconfigure.contributor.HealthContributorAutoConfiguration;
4545
import org.springframework.boot.health.autoconfigure.registry.HealthContributorRegistryAutoConfiguration;
4646
import org.springframework.boot.health.contributor.CompositeHealthContributor;
47+
import org.springframework.boot.health.contributor.CompositeReactiveHealthContributor;
4748
import org.springframework.boot.health.contributor.Health;
4849
import org.springframework.boot.health.contributor.HealthContributors;
4950
import org.springframework.boot.health.contributor.HealthIndicator;
@@ -145,6 +146,20 @@ void runCreatesHealthEndpointGroups() {
145146
});
146147
}
147148

149+
@Test
150+
void runDoesNotFailWhenHealthEndpointGroupIncludesContributorThatExists() {
151+
this.contextRunner.withUserConfiguration(CompositeHealthIndicatorConfiguration.class)
152+
.withPropertyValues("management.endpoint.health.group.ready.include=composite/b/c")
153+
.run((context) -> assertThat(context).hasNotFailed());
154+
}
155+
156+
@Test // gh-48387
157+
void runDoesNotFailWhenHealthEndpointGroupIncludesReactiveContributorThatExists() {
158+
this.contextRunner.withUserConfiguration(CompositeReactiveHealthIndicatorConfiguration.class)
159+
.withPropertyValues("management.endpoint.health.group.ready.include=composite/b/c")
160+
.run((context) -> assertThat(context).hasNotFailed());
161+
}
162+
148163
@Test
149164
void runFailsWhenHealthEndpointGroupIncludesContributorThatDoesNotExist() {
150165
this.contextRunner.withUserConfiguration(CompositeHealthIndicatorConfiguration.class)
@@ -377,8 +392,27 @@ static class CompositeHealthIndicatorConfiguration {
377392

378393
@Bean
379394
CompositeHealthContributor compositeHealthIndicator() {
380-
return CompositeHealthContributor.fromMap(Map.of("a", (HealthIndicator) () -> Health.up().build(), "b",
381-
CompositeHealthContributor.fromMap(Map.of("c", (HealthIndicator) () -> Health.up().build()))));
395+
return CompositeHealthContributor.fromMap(Map.of("a", createHealthIndicator(), "b",
396+
CompositeHealthContributor.fromMap(Map.of("c", createHealthIndicator()))));
397+
}
398+
399+
private HealthIndicator createHealthIndicator() {
400+
return () -> Health.up().build();
401+
}
402+
403+
}
404+
405+
@Configuration(proxyBeanMethods = false)
406+
static class CompositeReactiveHealthIndicatorConfiguration {
407+
408+
@Bean
409+
CompositeReactiveHealthContributor compositeHealthIndicator() {
410+
return CompositeReactiveHealthContributor.fromMap(Map.of("a", createHealthIndicator(), "b",
411+
CompositeReactiveHealthContributor.fromMap(Map.of("c", createHealthIndicator()))));
412+
}
413+
414+
private ReactiveHealthIndicator createHealthIndicator() {
415+
return () -> Mono.just(Health.up().build());
382416
}
383417

384418
}

0 commit comments

Comments
 (0)