Skip to content

Commit d1a7e6b

Browse files
committed
Update CompositeHealthIndicator to use HealthIndicatorStrategy
to collect healths from the all indicators.
1 parent cdb275c commit d1a7e6b

File tree

10 files changed

+437
-255
lines changed

10 files changed

+437
-255
lines changed

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

+4-2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.springframework.boot.actuate.health.HealthAggregator;
2222
import org.springframework.boot.actuate.health.HealthEndpoint;
2323
import org.springframework.boot.actuate.health.HealthIndicatorRegistry;
24+
import org.springframework.boot.actuate.health.HealthIndicatorStrategy;
2425
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
2526
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
2627
import org.springframework.context.annotation.Bean;
@@ -38,8 +39,9 @@ class HealthEndpointConfiguration {
3839

3940
@Bean
4041
@ConditionalOnMissingBean
41-
HealthEndpoint healthEndpoint(HealthAggregator healthAggregator, HealthIndicatorRegistry registry) {
42-
return new HealthEndpoint(new CompositeHealthIndicator(healthAggregator, registry));
42+
HealthEndpoint healthEndpoint(HealthAggregator healthAggregator, HealthIndicatorRegistry registry,
43+
HealthIndicatorStrategy healthIndicatorStrategy) {
44+
return new HealthEndpoint(new CompositeHealthIndicator(healthAggregator, registry, healthIndicatorStrategy));
4345
}
4446

4547
}

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

+8
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@
2121
import reactor.core.publisher.Flux;
2222

2323
import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
24+
import org.springframework.boot.actuate.health.DefaultHealthIndicatorStrategy;
2425
import org.springframework.boot.actuate.health.HealthAggregator;
2526
import org.springframework.boot.actuate.health.HealthIndicator;
2627
import org.springframework.boot.actuate.health.HealthIndicatorRegistry;
28+
import org.springframework.boot.actuate.health.HealthIndicatorStrategy;
2729
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
2830
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
2931
import org.springframework.boot.actuate.health.ReactiveHealthIndicatorRegistry;
@@ -71,6 +73,12 @@ public HealthIndicatorRegistry healthIndicatorRegistry(ApplicationContext applic
7173
return HealthIndicatorRegistryBeans.get(applicationContext);
7274
}
7375

76+
@Bean
77+
@ConditionalOnMissingBean
78+
public HealthIndicatorStrategy healthIndicatorStrategy() {
79+
return new DefaultHealthIndicatorStrategy();
80+
}
81+
7482
@Configuration(proxyBeanMethods = false)
7583
@ConditionalOnClass(Flux.class)
7684
static class ReactiveHealthIndicatorConfiguration {

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

+24
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,18 @@
1616

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

19+
import java.util.Collections;
1920
import java.util.LinkedHashMap;
2021
import java.util.Map;
2122

2223
import org.junit.jupiter.api.Test;
2324

2425
import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
26+
import org.springframework.boot.actuate.health.DefaultHealthIndicatorStrategy;
2527
import org.springframework.boot.actuate.health.Health;
2628
import org.springframework.boot.actuate.health.HealthAggregator;
2729
import org.springframework.boot.actuate.health.HealthIndicator;
30+
import org.springframework.boot.actuate.health.HealthIndicatorStrategy;
2831
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
2932
import org.springframework.boot.actuate.health.Status;
3033
import org.springframework.boot.autoconfigure.AutoConfigurations;
@@ -81,6 +84,18 @@ void runShouldCreateOrderedHealthAggregator() {
8184
.isInstanceOf(OrderedHealthAggregator.class));
8285
}
8386

87+
@Test
88+
void shouldCreateDefaultHealthIndicatorStrategy() {
89+
this.contextRunner.run((context) -> assertThat(context).getBean(HealthIndicatorStrategy.class)
90+
.isInstanceOf(DefaultHealthIndicatorStrategy.class));
91+
}
92+
93+
@Test
94+
void shouldNotCreateDefaultHealthIndicatorCustomStrategyPresent() {
95+
this.contextRunner.withBean(CustomHealthIndicatorStrategy.class).run((context) -> assertThat(context)
96+
.getBean(HealthIndicatorStrategy.class).isInstanceOf(CustomHealthIndicatorStrategy.class));
97+
}
98+
8499
@Test
85100
void runWhenHasCustomOrderPropertyShouldCreateOrderedHealthAggregator() {
86101
this.contextRunner.withPropertyValues("management.health.status.order:UP,DOWN").run((context) -> {
@@ -111,6 +126,15 @@ HealthIndicator customHealthIndicator() {
111126

112127
}
113128

129+
static class CustomHealthIndicatorStrategy implements HealthIndicatorStrategy {
130+
131+
@Override
132+
public Map<String, Health> doHealth(Map<String, HealthIndicator> healthIndicators) {
133+
return Collections.emptyMap();
134+
}
135+
136+
}
137+
114138
static class CustomHealthIndicator implements HealthIndicator {
115139

116140
@Override

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

+16-121
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,12 @@
1616

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

19-
import java.util.LinkedHashMap;
2019
import java.util.Map;
21-
import java.util.concurrent.CompletableFuture;
22-
import java.util.concurrent.ExecutionException;
23-
import java.util.concurrent.Executor;
24-
import java.util.concurrent.Future;
25-
import java.util.concurrent.TimeUnit;
26-
import java.util.concurrent.TimeoutException;
27-
28-
import org.springframework.util.Assert;
2920

3021
/**
31-
* {@link HealthIndicator} that returns health indications from all registered delegates.
22+
* {@link HealthIndicator} that returns health indications from all registered delegates
23+
* using the {@link HealthIndicatorStrategy} and aggregates the result via
24+
* {@link HealthAggregator} into a final one.
3225
*
3326
* @author Tyler J. Frederick
3427
* @author Phillip Webb
@@ -41,7 +34,7 @@ public class CompositeHealthIndicator implements HealthIndicator {
4134

4235
private final HealthAggregator aggregator;
4336

44-
private final HealthStrategy strategy;
37+
private final HealthIndicatorStrategy strategy;
4538

4639
/**
4740
* Create a new {@link CompositeHealthIndicator} from the specified indicators.
@@ -60,11 +53,20 @@ public CompositeHealthIndicator(HealthAggregator healthAggregator, Map<String, H
6053
* @param registry the registry of {@link HealthIndicator HealthIndicators}.
6154
*/
6255
public CompositeHealthIndicator(HealthAggregator healthAggregator, HealthIndicatorRegistry registry) {
63-
this(healthAggregator, registry, new SequentialStrategy());
56+
this(healthAggregator, registry, new DefaultHealthIndicatorStrategy());
6457
}
6558

66-
private CompositeHealthIndicator(HealthAggregator healthAggregator, HealthIndicatorRegistry registry,
67-
HealthStrategy strategy) {
59+
/**
60+
* Create a new {@link CompositeHealthIndicator} from the indicators in the given
61+
* {@code registry}.
62+
* @param healthAggregator the health aggregator
63+
* @param registry the registry of {@link HealthIndicator HealthIndicators}.
64+
* @param strategy the strategy how {@link HealthIndicator HealthIndicator} instnaces
65+
* should be called.
66+
* @since 2.2.0
67+
*/
68+
public CompositeHealthIndicator(HealthAggregator healthAggregator, HealthIndicatorRegistry registry,
69+
HealthIndicatorStrategy strategy) {
6870
this.aggregator = healthAggregator;
6971
this.registry = registry;
7072
this.strategy = strategy;
@@ -79,117 +81,10 @@ public HealthIndicatorRegistry getRegistry() {
7981
return this.registry;
8082
}
8183

82-
/**
83-
* Returns a new {@link CompositeHealthIndicator} with a parallel strategy that
84-
* returns health indications from all registered delegates concurrently.
85-
* @param timeout number of milliseconds to wait before using the
86-
* {@code timeoutHealth}
87-
* @param timeoutHealth the {@link Health} to use if an health indicator reached the
88-
* {@code timeout}. Defaults to {@code unknown} status.
89-
* @param executor the executor to submit {@link HealthIndicator HealthIndicators} on.
90-
* @return new instance with a parallel strategy
91-
* @since 2.2.0
92-
*/
93-
public CompositeHealthIndicator parallel(Executor executor, long timeout, Health timeoutHealth) {
94-
Assert.notNull(executor, "Executor must not be null");
95-
ParallelStrategy strategy = new ParallelStrategy(executor, timeout, timeoutHealth);
96-
return new CompositeHealthIndicator(this.aggregator, this.registry, strategy);
97-
}
98-
99-
/**
100-
* Returns a new {@link CompositeHealthIndicator} with a parallel strategy that
101-
* returns health indications from all registered delegates concurrently.
102-
* @param executor the executor to submit {@link HealthIndicator HealthIndicators} on.
103-
* @return new instance with a parallel strategy
104-
* @since 2.2.0
105-
*/
106-
public CompositeHealthIndicator parallel(Executor executor) {
107-
Assert.notNull(executor, "Executor must not be null");
108-
ParallelStrategy strategy = new ParallelStrategy(executor, null, null);
109-
return new CompositeHealthIndicator(this.aggregator, this.registry, strategy);
110-
}
111-
112-
/**
113-
* Returns a new {@link CompositeHealthIndicator} with a sequential strategy that
114-
* returns health indications from all registered delegates sequentially.
115-
* @return new instance with a sequential strategy
116-
* @since 2.2.0
117-
*/
118-
public CompositeHealthIndicator sequential() {
119-
return new CompositeHealthIndicator(this.aggregator, this.registry, new SequentialStrategy());
120-
}
121-
12284
@Override
12385
public Health health() {
12486
Map<String, Health> healths = this.strategy.doHealth(this.registry.getAll());
12587
return this.aggregator.aggregate(healths);
12688
}
12789

128-
@FunctionalInterface
129-
private interface HealthStrategy {
130-
131-
Map<String, Health> doHealth(Map<String, HealthIndicator> healthIndicators);
132-
133-
}
134-
135-
private static final class SequentialStrategy implements HealthStrategy {
136-
137-
@Override
138-
public Map<String, Health> doHealth(Map<String, HealthIndicator> healthIndicators) {
139-
Map<String, Health> healths = new LinkedHashMap<>();
140-
for (Map.Entry<String, HealthIndicator> entry : healthIndicators.entrySet()) {
141-
healths.put(entry.getKey(), entry.getValue().health());
142-
}
143-
return healths;
144-
}
145-
146-
}
147-
148-
private static final class ParallelStrategy implements HealthStrategy {
149-
150-
private final Executor executor;
151-
152-
private final Long timeout;
153-
154-
private final Health timeoutHealth;
155-
156-
private ParallelStrategy(Executor executor, Long timeout, Health timeoutHealth) {
157-
this.executor = executor;
158-
this.timeout = timeout;
159-
this.timeoutHealth = (timeoutHealth != null) ? timeoutHealth : Health.unknown().build();
160-
}
161-
162-
@Override
163-
public Map<String, Health> doHealth(Map<String, HealthIndicator> healthIndicators) {
164-
Map<String, Future<Health>> healthsFutures = new LinkedHashMap<>();
165-
for (Map.Entry<String, HealthIndicator> entry : healthIndicators.entrySet()) {
166-
healthsFutures.put(entry.getKey(),
167-
CompletableFuture.supplyAsync(entry.getValue()::health, this.executor));
168-
}
169-
Map<String, Health> healths = new LinkedHashMap<>();
170-
for (Map.Entry<String, Future<Health>> entry : healthsFutures.entrySet()) {
171-
healths.put(entry.getKey(), getHealth(entry.getValue(), this.timeout, this.timeoutHealth));
172-
}
173-
return healths;
174-
}
175-
176-
private static Health getHealth(Future<Health> healthFuture, Long timeout, Health timeoutHealth) {
177-
try {
178-
return (timeout != null) ? healthFuture.get(timeout, TimeUnit.MILLISECONDS) : healthFuture.get();
179-
}
180-
catch (InterruptedException ex) {
181-
Thread.currentThread().interrupt();
182-
return Health.unknown().withException(ex).build();
183-
}
184-
catch (TimeoutException ex) {
185-
return timeoutHealth;
186-
}
187-
catch (ExecutionException ex) {
188-
Throwable cause = ex.getCause();
189-
return Health.down((cause instanceof Exception) ? ((Exception) cause) : ex).build();
190-
}
191-
}
192-
193-
}
194-
19590
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Copyright 2012-2019 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+
* https://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.health;
18+
19+
import java.util.LinkedHashMap;
20+
import java.util.Map;
21+
import java.util.concurrent.CompletableFuture;
22+
import java.util.concurrent.ExecutionException;
23+
import java.util.concurrent.Executor;
24+
import java.util.concurrent.Future;
25+
import java.util.concurrent.TimeUnit;
26+
import java.util.concurrent.TimeoutException;
27+
28+
/**
29+
*
30+
* {@link HealthIndicatorStrategy} that returns health indications from all
31+
* {@link HealthIndicator} instances concurrently.
32+
*
33+
* @author Dmytro Nosan
34+
* @since 2.2.0
35+
*/
36+
public class ConcurrentlyHealthIndicatorStrategy implements HealthIndicatorStrategy {
37+
38+
private static final Health UNKNOWN = Health.unknown().build();
39+
40+
private final Executor executor;
41+
42+
private final Long timeout;
43+
44+
private final Health timeoutHealth;
45+
46+
/**
47+
* Returns a new {@link ConcurrentlyHealthIndicatorStrategy} with no timeout.
48+
* @param executor the executor to submit {@link HealthIndicator HealthIndicators} on.
49+
*/
50+
public ConcurrentlyHealthIndicatorStrategy(Executor executor) {
51+
this.executor = executor;
52+
this.timeout = null;
53+
this.timeoutHealth = null;
54+
}
55+
56+
/**
57+
* Returns a new {@link ConcurrentlyHealthIndicatorStrategy} with timeout.
58+
* @param executor the executor to submit {@link HealthIndicator HealthIndicators} on.
59+
* @param timeout number of milliseconds to wait before using the
60+
* {@code timeoutHealth}
61+
* @param timeoutHealth the {@link Health} to use if an health indicator reached the
62+
* {@code timeout}. Defaults to {@code unknown} status.
63+
*/
64+
public ConcurrentlyHealthIndicatorStrategy(Executor executor, long timeout, Health timeoutHealth) {
65+
this.executor = executor;
66+
this.timeout = timeout;
67+
this.timeoutHealth = (timeoutHealth != null) ? timeoutHealth : UNKNOWN;
68+
}
69+
70+
@Override
71+
public Map<String, Health> doHealth(Map<String, HealthIndicator> healthIndicators) {
72+
Map<String, Future<Health>> healthsFutures = new LinkedHashMap<>();
73+
for (Map.Entry<String, HealthIndicator> entry : healthIndicators.entrySet()) {
74+
healthsFutures.put(entry.getKey(), CompletableFuture.supplyAsync(entry.getValue()::health, this.executor));
75+
}
76+
Map<String, Health> healths = new LinkedHashMap<>();
77+
for (Map.Entry<String, Future<Health>> entry : healthsFutures.entrySet()) {
78+
healths.put(entry.getKey(), getHealth(entry.getValue(), this.timeout, this.timeoutHealth));
79+
}
80+
return healths;
81+
}
82+
83+
private static Health getHealth(Future<Health> healthFuture, Long timeout, Health timeoutHealth) {
84+
try {
85+
return (timeout != null) ? healthFuture.get(timeout, TimeUnit.MILLISECONDS) : healthFuture.get();
86+
}
87+
catch (InterruptedException ex) {
88+
Thread.currentThread().interrupt();
89+
return Health.unknown().withException(ex).build();
90+
}
91+
catch (TimeoutException ex) {
92+
return timeoutHealth;
93+
}
94+
catch (ExecutionException ex) {
95+
Throwable cause = ex.getCause();
96+
return Health.down((cause instanceof Exception) ? ((Exception) cause) : ex).build();
97+
}
98+
}
99+
100+
}

0 commit comments

Comments
 (0)