Skip to content

Commit 6118023

Browse files
philwebbpull[bot]
authored andcommitted
Add HealthContributor and refactor HealthEndpoint
Overhaul `HealthEndpoint` support to make it easier to support health groups. Prior to this commit the `HealthIndicator` interface was used for both regular indicators and composite indicators. In addition the `Health` result was used to both represent individual, system and composite health. This design unfortunately means that all health contributors need to be aware of the `HealthAggregator` and could not easily support heath groups if per-group aggregation is required. This commit reworks many aspects of the health support in order to provide a cleaner separation between a `HealthIndicator`and a composite. The following changes have been made: - A `HealthContributor` interface has been introduced to represent the general concept of something that contributes health information. A contributor can either be a `HealthIndicator` or a `CompositeHealthContributor`. - A `HealthComponent` class has been introduced to mirror the contributor arrangement. The component can be either `CompositeHealth` or `Health`. - The `HealthAggregator` interface has been replaced with a more focused `StatusAggregator` interface which only deals with `Status` results. - `CompositeHealthIndicator` has been replaced with `CompositeHealthContributor` which only provides access to other contributors. A composite can no longer directly return `Health`. - `HealthIndicatorRegistry` has been replaced with `HealthContributorRegistry` and the default implementation now uses a copy-on-write strategy. - `HealthEndpoint`, `HealthEndpointWebExtension` and `ReactiveHealthEndpointWebExtension` now extend a common `HealthEndpointSupport` class. They are now driven by a health contributor registry and `HealthEndpointSettings`. - The `HealthStatusHttpMapper` class has been replaced by a `HttpCodeStatusMapper` interface. - The `HealthWebEndpointResponseMapper` class has been replaced by a `HealthEndpointSettings` strategy. This allows us to move role related logic and `ShowDetails` to the auto-configure module. - `SimpleHttpCodeStatusMapper` and `SimpleStatusAggregator` implementations have been added which are configured via constructor arguments rather than setters. - Endpoint auto-configuration has been reworked and the `CompositeHealthIndicatorConfiguration` class has been replaced by `CompositeHealthContributorConfiguration`. - The endpoint JSON has been changed make `details` distinct from `components`. See spring-projectsgh-17926
1 parent a3b9b6b commit 6118023

File tree

143 files changed

+5264
-1298
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

143 files changed

+5264
-1298
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryReactiveHealthEndpointWebExtension.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@
1919
import reactor.core.publisher.Mono;
2020

2121
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.EndpointCloudFoundryExtension;
22+
import org.springframework.boot.actuate.endpoint.SecurityContext;
2223
import org.springframework.boot.actuate.endpoint.annotation.EndpointExtension;
2324
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
25+
import org.springframework.boot.actuate.endpoint.annotation.Selector;
26+
import org.springframework.boot.actuate.endpoint.annotation.Selector.Match;
2427
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
25-
import org.springframework.boot.actuate.health.Health;
28+
import org.springframework.boot.actuate.health.HealthComponent;
2629
import org.springframework.boot.actuate.health.HealthEndpoint;
2730
import org.springframework.boot.actuate.health.ReactiveHealthEndpointWebExtension;
28-
import org.springframework.boot.actuate.health.ShowDetails;
2931

3032
/**
3133
* Reactive {@link EndpointExtension @EndpointExtension} for the {@link HealthEndpoint}
@@ -44,8 +46,14 @@ public CloudFoundryReactiveHealthEndpointWebExtension(ReactiveHealthEndpointWebE
4446
}
4547

4648
@ReadOperation
47-
public Mono<WebEndpointResponse<Health>> health() {
48-
return this.delegate.health(null, ShowDetails.ALWAYS);
49+
public Mono<WebEndpointResponse<? extends HealthComponent>> health() {
50+
return this.delegate.health(SecurityContext.NONE, true);
51+
}
52+
53+
@ReadOperation
54+
public Mono<WebEndpointResponse<? extends HealthComponent>> health(
55+
@Selector(match = Match.ALL_REMAINING) String... path) {
56+
return this.delegate.health(SecurityContext.NONE, true, path);
4957
}
5058

5159
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryHealthEndpointWebExtension.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@
1717
package org.springframework.boot.actuate.autoconfigure.cloudfoundry.servlet;
1818

1919
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.EndpointCloudFoundryExtension;
20+
import org.springframework.boot.actuate.endpoint.SecurityContext;
2021
import org.springframework.boot.actuate.endpoint.annotation.EndpointExtension;
2122
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
23+
import org.springframework.boot.actuate.endpoint.annotation.Selector;
24+
import org.springframework.boot.actuate.endpoint.annotation.Selector.Match;
2225
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
23-
import org.springframework.boot.actuate.health.Health;
26+
import org.springframework.boot.actuate.health.HealthComponent;
2427
import org.springframework.boot.actuate.health.HealthEndpoint;
2528
import org.springframework.boot.actuate.health.HealthEndpointWebExtension;
26-
import org.springframework.boot.actuate.health.ShowDetails;
2729

2830
/**
2931
* {@link EndpointExtension @EndpointExtension} for the {@link HealthEndpoint} that always
@@ -42,8 +44,13 @@ public CloudFoundryHealthEndpointWebExtension(HealthEndpointWebExtension delegat
4244
}
4345

4446
@ReadOperation
45-
public WebEndpointResponse<Health> getHealth() {
46-
return this.delegate.getHealth(null, ShowDetails.ALWAYS);
47+
public WebEndpointResponse<HealthComponent> health() {
48+
return this.delegate.health(SecurityContext.NONE, true);
49+
}
50+
51+
@ReadOperation
52+
public WebEndpointResponse<HealthComponent> health(@Selector(match = Match.ALL_REMAINING) String... path) {
53+
return this.delegate.health(SecurityContext.NONE, true, path);
4754
}
4855

4956
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
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.autoconfigure.health;
18+
19+
import java.lang.reflect.Constructor;
20+
import java.util.Map;
21+
22+
import org.springframework.beans.BeanUtils;
23+
import org.springframework.core.ResolvableType;
24+
import org.springframework.util.Assert;
25+
26+
/**
27+
* Base class for health contributor configurations that can combine source beans into a
28+
* composite.
29+
*
30+
* @param <C> the contributor type
31+
* @param <I> the health indicator type
32+
* @param <B> the bean type
33+
* @author Stephane Nicoll
34+
* @author Phillip Webb
35+
* @since 2.2.0
36+
*/
37+
public abstract class AbstractCompositeHealthContributorConfiguration<C, I extends C, B> {
38+
39+
private final Class<?> indicatorType;
40+
41+
private final Class<?> beanType;
42+
43+
AbstractCompositeHealthContributorConfiguration() {
44+
ResolvableType type = ResolvableType.forClass(AbstractCompositeHealthContributorConfiguration.class,
45+
getClass());
46+
this.indicatorType = type.resolveGeneric(1);
47+
this.beanType = type.resolveGeneric(2);
48+
49+
}
50+
51+
protected final C createContributor(Map<String, B> beans) {
52+
Assert.notEmpty(beans, "Beans must not be empty");
53+
if (beans.size() == 1) {
54+
return createIndicator(beans.values().iterator().next());
55+
}
56+
return createComposite(beans);
57+
}
58+
59+
protected abstract C createComposite(Map<String, B> beans);
60+
61+
@SuppressWarnings("unchecked")
62+
protected I createIndicator(B bean) {
63+
try {
64+
Constructor<I> constructor = (Constructor<I>) this.indicatorType.getDeclaredConstructor(this.beanType);
65+
return BeanUtils.instantiateClass(constructor, bean);
66+
}
67+
catch (Exception ex) {
68+
throw new IllegalStateException(
69+
"Unable to create health indicator " + this.indicatorType + " for bean type " + this.beanType, ex);
70+
}
71+
}
72+
73+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
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.autoconfigure.health;
18+
19+
import java.util.Collection;
20+
21+
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointProperties.ShowDetails;
22+
import org.springframework.boot.actuate.endpoint.SecurityContext;
23+
import org.springframework.boot.actuate.health.HealthEndpointSettings;
24+
import org.springframework.boot.actuate.health.HttpCodeStatusMapper;
25+
import org.springframework.boot.actuate.health.StatusAggregator;
26+
import org.springframework.util.CollectionUtils;
27+
28+
/**
29+
* Auto-configured {@link HealthEndpointSettings} backed by
30+
* {@link HealthEndpointProperties}.
31+
*
32+
* @author Phillip Webb
33+
* @author Andy Wilkinson
34+
*/
35+
class AutoConfiguredHealthEndpointSettings implements HealthEndpointSettings {
36+
37+
private final StatusAggregator statusAggregator;
38+
39+
private final HttpCodeStatusMapper httpCodeStatusMapper;
40+
41+
private final ShowDetails showDetails;
42+
43+
private final Collection<String> roles;
44+
45+
/**
46+
* Create a new {@link AutoConfiguredHealthEndpointSettings} instance.
47+
* @param statusAggregator the status aggregator to use
48+
* @param httpCodeStatusMapper the HTTP code status mapper to use
49+
* @param showDetails the show details setting
50+
* @param roles the roles to match
51+
*/
52+
AutoConfiguredHealthEndpointSettings(StatusAggregator statusAggregator, HttpCodeStatusMapper httpCodeStatusMapper,
53+
ShowDetails showDetails, Collection<String> roles) {
54+
this.statusAggregator = statusAggregator;
55+
this.httpCodeStatusMapper = httpCodeStatusMapper;
56+
this.showDetails = showDetails;
57+
this.roles = roles;
58+
}
59+
60+
@Override
61+
public boolean includeDetails(SecurityContext securityContext) {
62+
ShowDetails showDetails = this.showDetails;
63+
switch (showDetails) {
64+
case NEVER:
65+
return false;
66+
case ALWAYS:
67+
return true;
68+
case WHEN_AUTHORIZED:
69+
return isAuthorized(securityContext);
70+
}
71+
throw new IllegalStateException("Unsupported ShowDetails value " + showDetails);
72+
}
73+
74+
private boolean isAuthorized(SecurityContext securityContext) {
75+
if (securityContext.getPrincipal() == null) {
76+
return false;
77+
}
78+
return CollectionUtils.isEmpty(this.roles) || this.roles.stream().anyMatch(securityContext::isUserInRole);
79+
}
80+
81+
@Override
82+
public StatusAggregator getStatusAggregator() {
83+
return this.statusAggregator;
84+
}
85+
86+
@Override
87+
public HttpCodeStatusMapper getHttpCodeStatusMapper() {
88+
return this.httpCodeStatusMapper;
89+
}
90+
91+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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.autoconfigure.health;
18+
19+
import java.util.Map;
20+
21+
import org.springframework.boot.actuate.health.CompositeHealthContributor;
22+
import org.springframework.boot.actuate.health.HealthContributor;
23+
import org.springframework.boot.actuate.health.HealthIndicator;
24+
25+
/**
26+
* Base class for health contributor configurations that can combine source beans into a
27+
* composite.
28+
*
29+
* @param <I> the health indicator type
30+
* @param <B> the bean type
31+
* @author Stephane Nicoll
32+
* @author Phillip Webb
33+
* @since 2.2.0
34+
*/
35+
public abstract class CompositeHealthContributorConfiguration<I extends HealthIndicator, B>
36+
extends AbstractCompositeHealthContributorConfiguration<HealthContributor, I, B> {
37+
38+
@Override
39+
protected final HealthContributor createComposite(Map<String, B> beans) {
40+
return CompositeHealthContributor.fromMap(beans, this::createIndicator);
41+
}
42+
43+
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@
3434
* @param <S> the bean source type
3535
* @author Stephane Nicoll
3636
* @since 2.0.0
37+
* @deprecated since 2.0.0 in favor of {@link CompositeHealthContributorConfiguration}
3738
*/
39+
@Deprecated
3840
public abstract class CompositeHealthIndicatorConfiguration<H extends HealthIndicator, S> {
3941

4042
@Autowired
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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.autoconfigure.health;
18+
19+
import java.util.Map;
20+
21+
import org.springframework.boot.actuate.health.CompositeReactiveHealthContributor;
22+
import org.springframework.boot.actuate.health.ReactiveHealthContributor;
23+
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
24+
25+
/**
26+
* Base class for health contributor configurations that can combine source beans into a
27+
* composite.
28+
*
29+
* @param <I> the health indicator type
30+
* @param <B> the bean type
31+
* @author Stephane Nicoll
32+
* @author Phillip Webb
33+
* @since 2.2.0
34+
*/
35+
public abstract class CompositeReactiveHealthContributorConfiguration<I extends ReactiveHealthIndicator, B>
36+
extends AbstractCompositeHealthContributorConfiguration<ReactiveHealthContributor, I, B> {
37+
38+
@Override
39+
protected final ReactiveHealthContributor createComposite(Map<String, B> beans) {
40+
return CompositeReactiveHealthContributor.fromMap(beans, this::createIndicator);
41+
}
42+
43+
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@
3333
* @param <S> the bean source type
3434
* @author Stephane Nicoll
3535
* @since 2.0.0
36+
* @deprecated since 2.2.0 in favor of
37+
* {@link CompositeReactiveHealthContributorConfiguration}
3638
*/
39+
@Deprecated
3740
public abstract class CompositeReactiveHealthIndicatorConfiguration<H extends ReactiveHealthIndicator, S> {
3841

3942
@Autowired

0 commit comments

Comments
 (0)