diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroups.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroupsRegistry.java similarity index 51% rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroups.java rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroupsRegistry.java index b0001dafaa..170d60720e 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroups.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroupsRegistry.java @@ -22,6 +22,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Consumer; import java.util.function.Predicate; import java.util.function.Supplier; @@ -31,10 +32,12 @@ import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils; import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointProperties.Group; -import org.springframework.boot.actuate.autoconfigure.health.HealthProperties.Show; import org.springframework.boot.actuate.autoconfigure.health.HealthProperties.Status; import org.springframework.boot.actuate.health.HealthEndpointGroup; +import org.springframework.boot.actuate.health.HealthEndpointGroup.Show; +import org.springframework.boot.actuate.health.HealthEndpointGroupConfigurer; import org.springframework.boot.actuate.health.HealthEndpointGroups; +import org.springframework.boot.actuate.health.HealthEndpointGroupsRegistry; import org.springframework.boot.actuate.health.HttpCodeStatusMapper; import org.springframework.boot.actuate.health.SimpleHttpCodeStatusMapper; import org.springframework.boot.actuate.health.SimpleStatusAggregator; @@ -45,71 +48,113 @@ import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; /** - * Auto-configured {@link HealthEndpointGroups}. + * Auto-configured {@link HealthEndpointGroupsRegistry}. * * @author Phillip Webb + * @author Brian Clozel */ -class AutoConfiguredHealthEndpointGroups implements HealthEndpointGroups { +class AutoConfiguredHealthEndpointGroupsRegistry implements HealthEndpointGroupsRegistry { private static Predicate ALL = (name) -> true; - private final HealthEndpointGroup primaryGroup; + private final StatusAggregator defaultStatusAggregator; + + private final HttpCodeStatusMapper defaultHttpCodeStatusMapper; + + private final Show defaultShowComponents; + + private final Show defaultShowDetails; + + private final Set defaultRoles; + + private HealthEndpointGroup primaryGroup; private final Map groups; /** - * Create a new {@link AutoConfiguredHealthEndpointGroups} instance. + * Create a new {@link AutoConfiguredHealthEndpointGroupsRegistry} instance. * @param applicationContext the application context used to check for override beans * @param properties the health endpoint properties */ - AutoConfiguredHealthEndpointGroups(ApplicationContext applicationContext, HealthEndpointProperties properties) { + AutoConfiguredHealthEndpointGroupsRegistry(ApplicationContext applicationContext, + HealthEndpointProperties properties) { ListableBeanFactory beanFactory = (applicationContext instanceof ConfigurableApplicationContext) ? ((ConfigurableApplicationContext) applicationContext).getBeanFactory() : applicationContext; - Show showComponents = properties.getShowComponents(); - Show showDetails = properties.getShowDetails(); - Set roles = properties.getRoles(); + this.defaultShowComponents = convertVisibility(properties.getShowComponents()); + this.defaultShowDetails = convertVisibility(properties.getShowDetails()); + this.defaultRoles = properties.getRoles(); + StatusAggregator statusAggregator = getNonQualifiedBean(beanFactory, StatusAggregator.class); if (statusAggregator == null) { statusAggregator = new SimpleStatusAggregator(properties.getStatus().getOrder()); } + this.defaultStatusAggregator = statusAggregator; + HttpCodeStatusMapper httpCodeStatusMapper = getNonQualifiedBean(beanFactory, HttpCodeStatusMapper.class); if (httpCodeStatusMapper == null) { httpCodeStatusMapper = new SimpleHttpCodeStatusMapper(properties.getStatus().getHttpMapping()); } - this.primaryGroup = new AutoConfiguredHealthEndpointGroup(ALL, statusAggregator, httpCodeStatusMapper, - showComponents, showDetails, roles); - this.groups = createGroups(properties.getGroup(), beanFactory, statusAggregator, httpCodeStatusMapper, - showComponents, showDetails, roles); + this.defaultHttpCodeStatusMapper = httpCodeStatusMapper; + + this.primaryGroup = new DefaultHealthEndpointGroup(ALL, statusAggregator, httpCodeStatusMapper, + this.defaultShowComponents, this.defaultShowDetails, this.defaultRoles); + + this.groups = createGroups(properties.getGroup(), beanFactory); + } + + @Override + public HealthEndpointGroupsRegistry add(String groupName, Consumer consumer) { + DefaultHealthEndpointGroupConfigurer groupConfigurer = new DefaultHealthEndpointGroupConfigurer( + this.defaultStatusAggregator, this.defaultHttpCodeStatusMapper, this.defaultShowComponents, + this.defaultShowDetails, this.defaultRoles); + consumer.accept(groupConfigurer); + this.groups.put(groupName, groupConfigurer.toHealthEndpointGroup()); + return this; + } + + @Override + public HealthEndpointGroupsRegistry remove(String groupName) { + this.groups.remove(groupName); + return this; } - private Map createGroups(Map groupProperties, BeanFactory beanFactory, - StatusAggregator defaultStatusAggregator, HttpCodeStatusMapper defaultHttpCodeStatusMapper, - Show defaultShowComponents, Show defaultShowDetails, Set defaultRoles) { + @Override + public HealthEndpointGroups toGroups() { + return HealthEndpointGroups.of(this.primaryGroup, Collections.unmodifiableMap(this.groups)); + } + + private Map createGroups(Map groupProperties, BeanFactory beanFactory) { Map groups = new LinkedHashMap<>(); - groupProperties.forEach((groupName, group) -> { - Status status = group.getStatus(); - Show showComponents = (group.getShowComponents() != null) ? group.getShowComponents() - : defaultShowComponents; - Show showDetails = (group.getShowDetails() != null) ? group.getShowDetails() : defaultShowDetails; - Set roles = !CollectionUtils.isEmpty(group.getRoles()) ? group.getRoles() : defaultRoles; - StatusAggregator statusAggregator = getQualifiedBean(beanFactory, StatusAggregator.class, groupName, () -> { - if (!CollectionUtils.isEmpty(status.getOrder())) { - return new SimpleStatusAggregator(status.getOrder()); - } - return defaultStatusAggregator; - }); - HttpCodeStatusMapper httpCodeStatusMapper = getQualifiedBean(beanFactory, HttpCodeStatusMapper.class, - groupName, () -> { - if (!CollectionUtils.isEmpty(status.getHttpMapping())) { - return new SimpleHttpCodeStatusMapper(status.getHttpMapping()); - } - return defaultHttpCodeStatusMapper; - }); - Predicate members = new IncludeExcludeGroupMemberPredicate(group.getInclude(), group.getExclude()); - groups.put(groupName, new AutoConfiguredHealthEndpointGroup(members, statusAggregator, httpCodeStatusMapper, - showComponents, showDetails, roles)); + groupProperties + .forEach((groupName, group) -> groups.put(groupName, createGroup(groupName, group, beanFactory))); + return groups; + } + + private HealthEndpointGroup createGroup(String groupName, Group groupProperties, BeanFactory beanFactory) { + Status status = groupProperties.getStatus(); + Show showComponents = (groupProperties.getShowComponents() != null) + ? convertVisibility(groupProperties.getShowComponents()) : this.defaultShowComponents; + Show showDetails = (groupProperties.getShowDetails() != null) + ? convertVisibility(groupProperties.getShowDetails()) : this.defaultShowDetails; + Set roles = !CollectionUtils.isEmpty(groupProperties.getRoles()) ? groupProperties.getRoles() + : this.defaultRoles; + StatusAggregator statusAggregator = getQualifiedBean(beanFactory, StatusAggregator.class, groupName, () -> { + if (!CollectionUtils.isEmpty(status.getOrder())) { + return new SimpleStatusAggregator(status.getOrder()); + } + return this.defaultStatusAggregator; }); - return Collections.unmodifiableMap(groups); + HttpCodeStatusMapper httpCodeStatusMapper = getQualifiedBean(beanFactory, HttpCodeStatusMapper.class, groupName, + () -> { + if (!CollectionUtils.isEmpty(status.getHttpMapping())) { + return new SimpleHttpCodeStatusMapper(status.getHttpMapping()); + } + return this.defaultHttpCodeStatusMapper; + }); + Predicate members = new IncludeExcludeGroupMemberPredicate(groupProperties.getInclude(), + groupProperties.getExclude()); + return new DefaultHealthEndpointGroup(members, statusAggregator, httpCodeStatusMapper, showComponents, + showDetails, roles); } private T getNonQualifiedBean(ListableBeanFactory beanFactory, Class type) { @@ -140,6 +185,21 @@ class AutoConfiguredHealthEndpointGroups implements HealthEndpointGroups { } } + private Show convertVisibility(HealthProperties.Show show) { + if (show == null) { + return null; + } + switch (show) { + case ALWAYS: + return Show.ALWAYS; + case NEVER: + return Show.NEVER; + case WHEN_AUTHORIZED: + return Show.WHEN_AUTHORIZED; + } + throw new IllegalStateException("Unsupported 'show' value " + show); + } + @Override public HealthEndpointGroup getPrimary() { return this.primaryGroup; diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroup.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/DefaultHealthEndpointGroup.java similarity index 91% rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroup.java rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/DefaultHealthEndpointGroup.java index f460617b44..ebfe07b5ee 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroup.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/DefaultHealthEndpointGroup.java @@ -20,7 +20,6 @@ import java.security.Principal; import java.util.Collection; import java.util.function.Predicate; -import org.springframework.boot.actuate.autoconfigure.health.HealthProperties.Show; import org.springframework.boot.actuate.endpoint.SecurityContext; import org.springframework.boot.actuate.health.HealthEndpointGroup; import org.springframework.boot.actuate.health.HttpCodeStatusMapper; @@ -31,12 +30,12 @@ import org.springframework.util.ClassUtils; import org.springframework.util.CollectionUtils; /** - * Auto-configured {@link HealthEndpointGroup} backed by {@link HealthProperties}. + * Auto-configured {@link HealthEndpointGroup}. * * @author Phillip Webb * @author Andy Wilkinson */ -class AutoConfiguredHealthEndpointGroup implements HealthEndpointGroup { +class DefaultHealthEndpointGroup implements HealthEndpointGroup { private final Predicate members; @@ -51,7 +50,7 @@ class AutoConfiguredHealthEndpointGroup implements HealthEndpointGroup { private final Collection roles; /** - * Create a new {@link AutoConfiguredHealthEndpointGroup} instance. + * Create a new {@link DefaultHealthEndpointGroup} instance. * @param members a predicate used to test for group membership * @param statusAggregator the status aggregator to use * @param httpCodeStatusMapper the HTTP code status mapper to use @@ -59,7 +58,7 @@ class AutoConfiguredHealthEndpointGroup implements HealthEndpointGroup { * @param showDetails the show details setting * @param roles the roles to match */ - AutoConfiguredHealthEndpointGroup(Predicate members, StatusAggregator statusAggregator, + DefaultHealthEndpointGroup(Predicate members, StatusAggregator statusAggregator, HttpCodeStatusMapper httpCodeStatusMapper, Show showComponents, Show showDetails, Collection roles) { this.members = members; diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/DefaultHealthEndpointGroupConfigurer.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/DefaultHealthEndpointGroupConfigurer.java new file mode 100644 index 0000000000..122c4f25c2 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/DefaultHealthEndpointGroupConfigurer.java @@ -0,0 +1,110 @@ +/* + * Copyright 2012-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.actuate.autoconfigure.health; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import org.springframework.boot.actuate.health.HealthEndpointGroup; +import org.springframework.boot.actuate.health.HealthEndpointGroup.Show; +import org.springframework.boot.actuate.health.HealthEndpointGroupConfigurer; +import org.springframework.boot.actuate.health.HttpCodeStatusMapper; +import org.springframework.boot.actuate.health.StatusAggregator; + +/** + * Mutable {@link HealthEndpointGroupConfigurer configurer} for + * {@link HealthEndpointGroup}. + * + * @author Brian Clozel + */ +class DefaultHealthEndpointGroupConfigurer implements HealthEndpointGroupConfigurer { + + Set includedIndicators; + + Set excludedIndicators; + + private StatusAggregator statusAggregator; + + private HttpCodeStatusMapper httpCodeStatusMapper; + + private Show showComponents; + + private Show showDetails; + + private Set roles; + + DefaultHealthEndpointGroupConfigurer(StatusAggregator defaultStatusAggregator, + HttpCodeStatusMapper defaultHttpCodeStatusMapper, Show defaultShowComponents, Show defaultShowDetails, + Set defaultRoles) { + this.statusAggregator = defaultStatusAggregator; + this.httpCodeStatusMapper = defaultHttpCodeStatusMapper; + this.showComponents = defaultShowComponents; + this.showDetails = defaultShowDetails; + this.roles = new HashSet<>(defaultRoles); + } + + @Override + public HealthEndpointGroupConfigurer include(String... indicators) { + this.includedIndicators = new HashSet<>(Arrays.asList(indicators)); + return this; + } + + @Override + public HealthEndpointGroupConfigurer exclude(String... exclude) { + this.excludedIndicators = new HashSet<>(Arrays.asList(exclude)); + return this; + } + + @Override + public HealthEndpointGroupConfigurer statusAggregator(StatusAggregator statusAggregator) { + this.statusAggregator = statusAggregator; + return this; + } + + @Override + public HealthEndpointGroupConfigurer httpCodeStatusMapper(HttpCodeStatusMapper httpCodeStatusMapper) { + this.httpCodeStatusMapper = httpCodeStatusMapper; + return this; + } + + @Override + public HealthEndpointGroupConfigurer showComponents(Show showComponents) { + this.showComponents = showComponents; + return this; + } + + @Override + public HealthEndpointGroupConfigurer showDetails(Show showDetails) { + this.showDetails = showDetails; + return this; + } + + @Override + public HealthEndpointGroupConfigurer roles(String... roles) { + this.roles = new HashSet<>(Arrays.asList(roles)); + return this; + } + + HealthEndpointGroup toHealthEndpointGroup() { + IncludeExcludeGroupMemberPredicate predicate = new IncludeExcludeGroupMemberPredicate(this.includedIndicators, + this.excludedIndicators); + return new DefaultHealthEndpointGroup(predicate, this.statusAggregator, this.httpCodeStatusMapper, + this.showComponents, this.showDetails, this.roles); + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointConfiguration.java index 54b074dce3..d224e99944 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.actuate.health.CompositeHealthContributor; import org.springframework.boot.actuate.health.CompositeReactiveHealthContributor; import org.springframework.boot.actuate.health.Health; @@ -28,6 +29,7 @@ import org.springframework.boot.actuate.health.HealthContributor; import org.springframework.boot.actuate.health.HealthContributorRegistry; import org.springframework.boot.actuate.health.HealthEndpoint; import org.springframework.boot.actuate.health.HealthEndpointGroups; +import org.springframework.boot.actuate.health.HealthEndpointGroupsRegistryCustomizer; import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.boot.actuate.health.HttpCodeStatusMapper; import org.springframework.boot.actuate.health.NamedContributor; @@ -66,8 +68,11 @@ class HealthEndpointConfiguration { @Bean @ConditionalOnMissingBean HealthEndpointGroups healthEndpointGroups(ApplicationContext applicationContext, - HealthEndpointProperties properties) { - return new AutoConfiguredHealthEndpointGroups(applicationContext, properties); + HealthEndpointProperties properties, ObjectProvider customizers) { + AutoConfiguredHealthEndpointGroupsRegistry registry = new AutoConfiguredHealthEndpointGroupsRegistry( + applicationContext, properties); + customizers.orderedStream().forEach((customizer) -> customizer.customize(registry)); + return registry.toGroups(); } @Bean diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroupsTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroupsBuilderTests.java similarity index 97% rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroupsTests.java rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroupsBuilderTests.java index b63154031a..9e5e4f03a8 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroupsTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroupsBuilderTests.java @@ -40,11 +40,11 @@ import org.springframework.context.annotation.Primary; import static org.assertj.core.api.Assertions.assertThat; /** - * Tests for {@link AutoConfiguredHealthEndpointGroups}. + * Tests for {@link AutoConfiguredHealthEndpointGroupsRegistry}. * * @author Phillip Webb */ -class AutoConfiguredHealthEndpointGroupsTests { +class AutoConfiguredHealthEndpointGroupsBuilderTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(AutoConfiguredHealthEndpointGroupsTestConfiguration.class)); @@ -313,9 +313,9 @@ class AutoConfiguredHealthEndpointGroupsTests { static class AutoConfiguredHealthEndpointGroupsTestConfiguration { @Bean - AutoConfiguredHealthEndpointGroups healthEndpointGroups(ConfigurableApplicationContext applicationContext, - HealthEndpointProperties properties) { - return new AutoConfiguredHealthEndpointGroups(applicationContext, properties); + AutoConfiguredHealthEndpointGroupsRegistry healthEndpointGroups( + ConfigurableApplicationContext applicationContext, HealthEndpointProperties properties) { + return new AutoConfiguredHealthEndpointGroupsRegistry(applicationContext, properties); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroupTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/DefaultHealthEndpointGroupTests.java similarity index 61% rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroupTests.java rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/DefaultHealthEndpointGroupTests.java index 3d06379534..478f856a77 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroupTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/DefaultHealthEndpointGroupTests.java @@ -25,8 +25,8 @@ import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.springframework.boot.actuate.autoconfigure.health.HealthProperties.Show; import org.springframework.boot.actuate.endpoint.SecurityContext; +import org.springframework.boot.actuate.health.HealthEndpointGroup.Show; import org.springframework.boot.actuate.health.HttpCodeStatusMapper; import org.springframework.boot.actuate.health.StatusAggregator; import org.springframework.security.core.Authentication; @@ -37,11 +37,11 @@ import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; /** - * Tests for {@link AutoConfiguredHealthEndpointGroup}. + * Tests for {@link DefaultHealthEndpointGroup}. * * @author Phillip Webb */ -class AutoConfiguredHealthEndpointGroupTests { +class DefaultHealthEndpointGroupTests { @Mock private StatusAggregator statusAggregator; @@ -62,7 +62,7 @@ class AutoConfiguredHealthEndpointGroupTests { @Test void isMemberWhenMemberPredicateMatchesAcceptsTrue() { - AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> name.startsWith("a"), + DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> name.startsWith("a"), this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet()); assertThat(group.isMember("albert")).isTrue(); assertThat(group.isMember("arnold")).isTrue(); @@ -70,7 +70,7 @@ class AutoConfiguredHealthEndpointGroupTests { @Test void isMemberWhenMemberPredicateRejectsReturnsTrue() { - AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> name.startsWith("a"), + DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> name.startsWith("a"), this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet()); assertThat(group.isMember("bert")).isFalse(); assertThat(group.isMember("ernie")).isFalse(); @@ -78,39 +78,38 @@ class AutoConfiguredHealthEndpointGroupTests { @Test void showDetailsWhenShowDetailsIsNeverReturnsFalse() { - AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, - this.statusAggregator, this.httpCodeStatusMapper, null, Show.NEVER, Collections.emptySet()); + DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, + this.httpCodeStatusMapper, null, Show.NEVER, Collections.emptySet()); assertThat(group.showDetails(SecurityContext.NONE)).isFalse(); } @Test void showDetailsWhenShowDetailsIsAlwaysReturnsTrue() { - AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, - this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet()); + DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, + this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet()); assertThat(group.showDetails(SecurityContext.NONE)).isTrue(); } @Test void showDetailsWhenShowDetailsIsWhenAuthorizedAndPrincipalIsNullReturnsFalse() { - AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, - this.statusAggregator, this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, Collections.emptySet()); + DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, + this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, Collections.emptySet()); given(this.securityContext.getPrincipal()).willReturn(null); assertThat(group.showDetails(this.securityContext)).isFalse(); } @Test void showDetailsWhenShowDetailsIsWhenAuthorizedAndRolesAreEmptyReturnsTrue() { - AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, - this.statusAggregator, this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, Collections.emptySet()); + DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, + this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, Collections.emptySet()); given(this.securityContext.getPrincipal()).willReturn(this.principal); assertThat(group.showDetails(this.securityContext)).isTrue(); } @Test void showDetailsWhenShowDetailsIsWhenAuthorizedAndUseIsInRoleReturnsTrue() { - AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, - this.statusAggregator, this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, - Arrays.asList("admin", "root", "bossmode")); + DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, + this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, Arrays.asList("admin", "root", "bossmode")); given(this.securityContext.getPrincipal()).willReturn(this.principal); given(this.securityContext.isUserInRole("root")).willReturn(true); assertThat(group.showDetails(this.securityContext)).isTrue(); @@ -118,9 +117,8 @@ class AutoConfiguredHealthEndpointGroupTests { @Test void showDetailsWhenShowDetailsIsWhenAuthorizedAndUseIsNotInRoleReturnsFalse() { - AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, - this.statusAggregator, this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, - Arrays.asList("admin", "rot", "bossmode")); + DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, + this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, Arrays.asList("admin", "rot", "bossmode")); given(this.securityContext.getPrincipal()).willReturn(this.principal); given(this.securityContext.isUserInRole("root")).willReturn(true); assertThat(group.showDetails(this.securityContext)).isFalse(); @@ -128,9 +126,8 @@ class AutoConfiguredHealthEndpointGroupTests { @Test void showDetailsWhenShowDetailsIsWhenAuthorizedAndUserHasRightAuthorityReturnsTrue() { - AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, - this.statusAggregator, this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, - Arrays.asList("admin", "root", "bossmode")); + DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, + this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, Arrays.asList("admin", "root", "bossmode")); Authentication principal = mock(Authentication.class); given(principal.getAuthorities()) .willAnswer((invocation) -> Collections.singleton(new SimpleGrantedAuthority("admin"))); @@ -140,9 +137,8 @@ class AutoConfiguredHealthEndpointGroupTests { @Test void showDetailsWhenShowDetailsIsWhenAuthorizedAndUserDoesNotHaveRightAuthoritiesReturnsFalse() { - AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, - this.statusAggregator, this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, - Arrays.asList("admin", "rot", "bossmode")); + DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, + this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, Arrays.asList("admin", "rot", "bossmode")); Authentication principal = mock(Authentication.class); given(principal.getAuthorities()) .willAnswer((invocation) -> Collections.singleton(new SimpleGrantedAuthority("other"))); @@ -152,50 +148,48 @@ class AutoConfiguredHealthEndpointGroupTests { @Test void showComponentsWhenShowComponentsIsNullDelegatesToShowDetails() { - AutoConfiguredHealthEndpointGroup alwaysGroup = new AutoConfiguredHealthEndpointGroup((name) -> true, - this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet()); + DefaultHealthEndpointGroup alwaysGroup = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, + this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet()); assertThat(alwaysGroup.showComponents(SecurityContext.NONE)).isTrue(); - AutoConfiguredHealthEndpointGroup neverGroup = new AutoConfiguredHealthEndpointGroup((name) -> true, - this.statusAggregator, this.httpCodeStatusMapper, null, Show.NEVER, Collections.emptySet()); + DefaultHealthEndpointGroup neverGroup = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, + this.httpCodeStatusMapper, null, Show.NEVER, Collections.emptySet()); assertThat(neverGroup.showComponents(SecurityContext.NONE)).isFalse(); } @Test void showComponentsWhenShowComponentsIsNeverReturnsFalse() { - AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, - this.statusAggregator, this.httpCodeStatusMapper, Show.NEVER, Show.ALWAYS, Collections.emptySet()); + DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, + this.httpCodeStatusMapper, Show.NEVER, Show.ALWAYS, Collections.emptySet()); assertThat(group.showComponents(SecurityContext.NONE)).isFalse(); } @Test void showComponentsWhenShowComponentsIsAlwaysReturnsTrue() { - AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, - this.statusAggregator, this.httpCodeStatusMapper, Show.ALWAYS, Show.NEVER, Collections.emptySet()); + DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, + this.httpCodeStatusMapper, Show.ALWAYS, Show.NEVER, Collections.emptySet()); assertThat(group.showComponents(SecurityContext.NONE)).isTrue(); } @Test void showComponentsWhenShowComponentsIsWhenAuthorizedAndPrincipalIsNullReturnsFalse() { - AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, - this.statusAggregator, this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER, - Collections.emptySet()); + DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, + this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER, Collections.emptySet()); given(this.securityContext.getPrincipal()).willReturn(null); assertThat(group.showComponents(this.securityContext)).isFalse(); } @Test void showComponentsWhenShowComponentsIsWhenAuthorizedAndRolesAreEmptyReturnsTrue() { - AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, - this.statusAggregator, this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER, - Collections.emptySet()); + DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, + this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER, Collections.emptySet()); given(this.securityContext.getPrincipal()).willReturn(this.principal); assertThat(group.showComponents(this.securityContext)).isTrue(); } @Test void showComponentsWhenShowComponentsIsWhenAuthorizedAndUseIsInRoleReturnsTrue() { - AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, - this.statusAggregator, this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER, + DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, + this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER, Arrays.asList("admin", "root", "bossmode")); given(this.securityContext.getPrincipal()).willReturn(this.principal); given(this.securityContext.isUserInRole("root")).willReturn(true); @@ -204,9 +198,8 @@ class AutoConfiguredHealthEndpointGroupTests { @Test void showComponentsWhenShowComponentsIsWhenAuthorizedAndUseIsNotInRoleReturnsFalse() { - AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, - this.statusAggregator, this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER, - Arrays.asList("admin", "rot", "bossmode")); + DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, + this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER, Arrays.asList("admin", "rot", "bossmode")); given(this.securityContext.getPrincipal()).willReturn(this.principal); given(this.securityContext.isUserInRole("root")).willReturn(true); assertThat(group.showComponents(this.securityContext)).isFalse(); @@ -214,8 +207,8 @@ class AutoConfiguredHealthEndpointGroupTests { @Test void showComponentsWhenShowComponentsIsWhenAuthorizedAndUserHasRightAuthoritiesReturnsTrue() { - AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, - this.statusAggregator, this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER, + DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, + this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER, Arrays.asList("admin", "root", "bossmode")); Authentication principal = mock(Authentication.class); given(principal.getAuthorities()) @@ -226,9 +219,8 @@ class AutoConfiguredHealthEndpointGroupTests { @Test void showComponentsWhenShowComponentsIsWhenAuthorizedAndUserDoesNotHaveRightAuthoritiesReturnsFalse() { - AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, - this.statusAggregator, this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER, - Arrays.asList("admin", "rot", "bossmode")); + DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, + this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER, Arrays.asList("admin", "rot", "bossmode")); Authentication principal = mock(Authentication.class); given(principal.getAuthorities()) .willAnswer((invocation) -> Collections.singleton(new SimpleGrantedAuthority("other"))); @@ -238,15 +230,15 @@ class AutoConfiguredHealthEndpointGroupTests { @Test void getStatusAggregatorReturnsStatusAggregator() { - AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, - this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet()); + DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, + this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet()); assertThat(group.getStatusAggregator()).isSameAs(this.statusAggregator); } @Test void getHttpCodeStatusMapperReturnsHttpCodeStatusMapper() { - AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, - this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet()); + DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, + this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet()); assertThat(group.getHttpCodeStatusMapper()).isSameAs(this.httpCodeStatusMapper); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointAutoConfigurationTests.java index 1bbb3febba..a69d18cea2 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointAutoConfigurationTests.java @@ -37,6 +37,7 @@ import org.springframework.boot.actuate.health.HealthComponent; import org.springframework.boot.actuate.health.HealthContributorRegistry; import org.springframework.boot.actuate.health.HealthEndpoint; import org.springframework.boot.actuate.health.HealthEndpointGroups; +import org.springframework.boot.actuate.health.HealthEndpointGroupsRegistryCustomizer; import org.springframework.boot.actuate.health.HealthEndpointWebExtension; import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.boot.actuate.health.HttpCodeStatusMapper; @@ -154,7 +155,6 @@ class HealthEndpointAutoConfigurationTests { void runCreatesHealthEndpointGroups() { this.contextRunner.withPropertyValues("management.endpoint.health.group.ready.include=*").run((context) -> { HealthEndpointGroups groups = context.getBean(HealthEndpointGroups.class); - assertThat(groups).isInstanceOf(AutoConfiguredHealthEndpointGroups.class); assertThat(groups.getNames()).containsOnly("ready"); }); } @@ -301,6 +301,15 @@ class HealthEndpointAutoConfigurationTests { .run((context) -> assertThat(context).doesNotHaveBean(ReactiveHealthIndicatorRegistry.class)); } + @Test + void runWhenHealthEndpointGroupsRegistryCustomizerAddsHealthEndpointGroup() { + this.contextRunner.withUserConfiguration(HealthEndpointGroupsRegistryCustomizerConfig.class).run((context) -> { + assertThat(context).hasSingleBean(HealthEndpointGroupsRegistryCustomizer.class); + HealthEndpointGroups groups = context.getBean(HealthEndpointGroups.class); + assertThat(groups.getNames()).contains("test"); + }); + } + @Configuration(proxyBeanMethods = false) static class HealthIndicatorsConfiguration { @@ -420,4 +429,14 @@ class HealthEndpointAutoConfigurationTests { } + @Configuration(proxyBeanMethods = false) + static class HealthEndpointGroupsRegistryCustomizerConfig { + + @Bean + HealthEndpointGroupsRegistryCustomizer customHealthEndpointGroup() { + return (registry) -> registry.add("test", (configurer) -> configurer.include("ping")); + } + + } + } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointGroup.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointGroup.java index ec8a29035a..c3a54b1be0 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointGroup.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointGroup.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ import org.springframework.boot.actuate.endpoint.SecurityContext; * by the {@link HealthEndpoint}. * * @author Phillip Webb + * @author Brian Clozel * @since 2.2.0 */ public interface HealthEndpointGroup { @@ -62,4 +63,27 @@ public interface HealthEndpointGroup { */ HttpCodeStatusMapper getHttpCodeStatusMapper(); + /** + * Options for showing items in responses from the {@link HealthEndpointGroup} web + * extensions. + */ + enum Show { + + /** + * Never show the item in the response. + */ + NEVER, + + /** + * Show the item in the response when accessed by an authorized user. + */ + WHEN_AUTHORIZED, + + /** + * Always show the item in the response. + */ + ALWAYS + + } + } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointGroupConfigurer.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointGroupConfigurer.java new file mode 100644 index 0000000000..b4e707fe0a --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointGroupConfigurer.java @@ -0,0 +1,84 @@ +/* + * Copyright 2012-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.actuate.health; + +import org.springframework.boot.actuate.health.HealthEndpointGroup.Show; + +/** + * A configurer for customizing an {@link HealthEndpointGroup} being built. + * + * @author Brian Clozel + * @since 2.3.0 + */ +public interface HealthEndpointGroupConfigurer { + + /** + * Configure the indicator endpoint ids to include in this group. + * @param indicators the indicator endpoint ids + * @return the configurer instance + */ + HealthEndpointGroupConfigurer include(String... indicators); + + /** + * Configure the indicator endpoint ids to exclude from this group. + * @param indicators the indicator endpoint ids + * @return the configurer instance + */ + HealthEndpointGroupConfigurer exclude(String... indicators); + + /** + * Configure the {@link StatusAggregator} to use for this group. + *

+ * If none set, this will default to the globalmy configured {@link StatusAggregator}. + * @param statusAggregator the status aggregator + * @return the configurer instance + */ + HealthEndpointGroupConfigurer statusAggregator(StatusAggregator statusAggregator); + + /** + * Configure the {@link HttpCodeStatusMapper} to use for this group. + *

+ * If none set, this will default to the globalmy configured + * {@link HttpCodeStatusMapper}. + * @param httpCodeStatusMapper the status code mapper + * @return the configurer instance + */ + HealthEndpointGroupConfigurer httpCodeStatusMapper(HttpCodeStatusMapper httpCodeStatusMapper); + + /** + * Configure the {@link Show visbility option} for showing components of this group. + * @param showComponents the components visibility + * @return the configurer instance + */ + HealthEndpointGroupConfigurer showComponents(Show showComponents); + + /** + * Configure the {@link Show visbility option} for showing details of this group. + * @param showDetails the details visibility + * @return the configurer instance + */ + HealthEndpointGroupConfigurer showDetails(Show showDetails); + + /** + * Configure roles used to determine whether or not a user is authorized to be shown + * details. + * @param roles the roles + * @return the configurer instance + */ + HealthEndpointGroupConfigurer roles(String... roles); + +} diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointGroupsRegistry.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointGroupsRegistry.java new file mode 100644 index 0000000000..a9f69e95a7 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointGroupsRegistry.java @@ -0,0 +1,50 @@ +/* + * Copyright 2012-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.actuate.health; + +import java.util.function.Consumer; + +/** + * Builder for an {@link HealthEndpointGroups} immutable instance. + * + * @author Brian Clozel + * @since 2.3.0 + */ +public interface HealthEndpointGroupsRegistry extends HealthEndpointGroups { + + /** + * Add a new {@link HealthEndpointGroup}. + * @param groupName the name of the group to add + * @param builder the group to add + * @return the builder instance + */ + HealthEndpointGroupsRegistry add(String groupName, Consumer builder); + + /** + * Remove an existing {@link HealthEndpointGroup}. + * @param groupName the name of the group to remove + * @return the builder instance + */ + HealthEndpointGroupsRegistry remove(String groupName); + + /** + * Build an immutable {@link HealthEndpointGroups}. + * @return the {@link HealthEndpointGroups} + */ + HealthEndpointGroups toGroups(); + +} diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointGroupsRegistryCustomizer.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointGroupsRegistryCustomizer.java new file mode 100644 index 0000000000..4d3a49d38a --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointGroupsRegistryCustomizer.java @@ -0,0 +1,35 @@ +/* + * Copyright 2012-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.actuate.health; + +/** + * Callback interface that can be used to customize a + * {@link HealthEndpointGroupsRegistry}. + * + * @author Brian Clozel + * @since 2.3.0 + */ +@FunctionalInterface +public interface HealthEndpointGroupsRegistryCustomizer { + + /** + * Callback to customize a {@link HealthEndpointGroupsRegistry} instance. + * @param healthEndpointGroupsRegistry the registry to customize + */ + void customize(HealthEndpointGroupsRegistry healthEndpointGroupsRegistry); + +}