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 gh-17926
This commit is contained in:
@@ -19,13 +19,15 @@ package org.springframework.boot.actuate.autoconfigure.cloudfoundry.reactive;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.EndpointCloudFoundryExtension;
|
||||
import org.springframework.boot.actuate.endpoint.SecurityContext;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.EndpointExtension;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Selector;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Selector.Match;
|
||||
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.HealthComponent;
|
||||
import org.springframework.boot.actuate.health.HealthEndpoint;
|
||||
import org.springframework.boot.actuate.health.ReactiveHealthEndpointWebExtension;
|
||||
import org.springframework.boot.actuate.health.ShowDetails;
|
||||
|
||||
/**
|
||||
* Reactive {@link EndpointExtension @EndpointExtension} for the {@link HealthEndpoint}
|
||||
@@ -44,8 +46,14 @@ public class CloudFoundryReactiveHealthEndpointWebExtension {
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public Mono<WebEndpointResponse<Health>> health() {
|
||||
return this.delegate.health(null, ShowDetails.ALWAYS);
|
||||
public Mono<WebEndpointResponse<? extends HealthComponent>> health() {
|
||||
return this.delegate.health(SecurityContext.NONE, true);
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public Mono<WebEndpointResponse<? extends HealthComponent>> health(
|
||||
@Selector(match = Match.ALL_REMAINING) String... path) {
|
||||
return this.delegate.health(SecurityContext.NONE, true, path);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,13 +17,15 @@
|
||||
package org.springframework.boot.actuate.autoconfigure.cloudfoundry.servlet;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.EndpointCloudFoundryExtension;
|
||||
import org.springframework.boot.actuate.endpoint.SecurityContext;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.EndpointExtension;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Selector;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Selector.Match;
|
||||
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.HealthComponent;
|
||||
import org.springframework.boot.actuate.health.HealthEndpoint;
|
||||
import org.springframework.boot.actuate.health.HealthEndpointWebExtension;
|
||||
import org.springframework.boot.actuate.health.ShowDetails;
|
||||
|
||||
/**
|
||||
* {@link EndpointExtension @EndpointExtension} for the {@link HealthEndpoint} that always
|
||||
@@ -42,8 +44,13 @@ public class CloudFoundryHealthEndpointWebExtension {
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public WebEndpointResponse<Health> getHealth() {
|
||||
return this.delegate.getHealth(null, ShowDetails.ALWAYS);
|
||||
public WebEndpointResponse<HealthComponent> health() {
|
||||
return this.delegate.health(SecurityContext.NONE, true);
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public WebEndpointResponse<HealthComponent> health(@Selector(match = Match.ALL_REMAINING) String... path) {
|
||||
return this.delegate.health(SecurityContext.NONE, true, path);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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.lang.reflect.Constructor;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Base class for health contributor configurations that can combine source beans into a
|
||||
* composite.
|
||||
*
|
||||
* @param <C> the contributor type
|
||||
* @param <I> the health indicator type
|
||||
* @param <B> the bean type
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
* @since 2.2.0
|
||||
*/
|
||||
public abstract class AbstractCompositeHealthContributorConfiguration<C, I extends C, B> {
|
||||
|
||||
private final Class<?> indicatorType;
|
||||
|
||||
private final Class<?> beanType;
|
||||
|
||||
AbstractCompositeHealthContributorConfiguration() {
|
||||
ResolvableType type = ResolvableType.forClass(AbstractCompositeHealthContributorConfiguration.class,
|
||||
getClass());
|
||||
this.indicatorType = type.resolveGeneric(1);
|
||||
this.beanType = type.resolveGeneric(2);
|
||||
|
||||
}
|
||||
|
||||
protected final C createContributor(Map<String, B> beans) {
|
||||
Assert.notEmpty(beans, "Beans must not be empty");
|
||||
if (beans.size() == 1) {
|
||||
return createIndicator(beans.values().iterator().next());
|
||||
}
|
||||
return createComposite(beans);
|
||||
}
|
||||
|
||||
protected abstract C createComposite(Map<String, B> beans);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected I createIndicator(B bean) {
|
||||
try {
|
||||
Constructor<I> constructor = (Constructor<I>) this.indicatorType.getDeclaredConstructor(this.beanType);
|
||||
return BeanUtils.instantiateClass(constructor, bean);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(
|
||||
"Unable to create health indicator " + this.indicatorType + " for bean type " + this.beanType, ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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.Collection;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointProperties.ShowDetails;
|
||||
import org.springframework.boot.actuate.endpoint.SecurityContext;
|
||||
import org.springframework.boot.actuate.health.HealthEndpointSettings;
|
||||
import org.springframework.boot.actuate.health.HttpCodeStatusMapper;
|
||||
import org.springframework.boot.actuate.health.StatusAggregator;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Auto-configured {@link HealthEndpointSettings} backed by
|
||||
* {@link HealthEndpointProperties}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class AutoConfiguredHealthEndpointSettings implements HealthEndpointSettings {
|
||||
|
||||
private final StatusAggregator statusAggregator;
|
||||
|
||||
private final HttpCodeStatusMapper httpCodeStatusMapper;
|
||||
|
||||
private final ShowDetails showDetails;
|
||||
|
||||
private final Collection<String> roles;
|
||||
|
||||
/**
|
||||
* Create a new {@link AutoConfiguredHealthEndpointSettings} instance.
|
||||
* @param statusAggregator the status aggregator to use
|
||||
* @param httpCodeStatusMapper the HTTP code status mapper to use
|
||||
* @param showDetails the show details setting
|
||||
* @param roles the roles to match
|
||||
*/
|
||||
AutoConfiguredHealthEndpointSettings(StatusAggregator statusAggregator, HttpCodeStatusMapper httpCodeStatusMapper,
|
||||
ShowDetails showDetails, Collection<String> roles) {
|
||||
this.statusAggregator = statusAggregator;
|
||||
this.httpCodeStatusMapper = httpCodeStatusMapper;
|
||||
this.showDetails = showDetails;
|
||||
this.roles = roles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean includeDetails(SecurityContext securityContext) {
|
||||
ShowDetails showDetails = this.showDetails;
|
||||
switch (showDetails) {
|
||||
case NEVER:
|
||||
return false;
|
||||
case ALWAYS:
|
||||
return true;
|
||||
case WHEN_AUTHORIZED:
|
||||
return isAuthorized(securityContext);
|
||||
}
|
||||
throw new IllegalStateException("Unsupported ShowDetails value " + showDetails);
|
||||
}
|
||||
|
||||
private boolean isAuthorized(SecurityContext securityContext) {
|
||||
if (securityContext.getPrincipal() == null) {
|
||||
return false;
|
||||
}
|
||||
return CollectionUtils.isEmpty(this.roles) || this.roles.stream().anyMatch(securityContext::isUserInRole);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StatusAggregator getStatusAggregator() {
|
||||
return this.statusAggregator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpCodeStatusMapper getHttpCodeStatusMapper() {
|
||||
return this.httpCodeStatusMapper;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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.Map;
|
||||
|
||||
import org.springframework.boot.actuate.health.CompositeHealthContributor;
|
||||
import org.springframework.boot.actuate.health.HealthContributor;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
|
||||
/**
|
||||
* Base class for health contributor configurations that can combine source beans into a
|
||||
* composite.
|
||||
*
|
||||
* @param <I> the health indicator type
|
||||
* @param <B> the bean type
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
* @since 2.2.0
|
||||
*/
|
||||
public abstract class CompositeHealthContributorConfiguration<I extends HealthIndicator, B>
|
||||
extends AbstractCompositeHealthContributorConfiguration<HealthContributor, I, B> {
|
||||
|
||||
@Override
|
||||
protected final HealthContributor createComposite(Map<String, B> beans) {
|
||||
return CompositeHealthContributor.fromMap(beans, this::createIndicator);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -34,7 +34,9 @@ import org.springframework.core.ResolvableType;
|
||||
* @param <S> the bean source type
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.0
|
||||
* @deprecated since 2.0.0 in favor of {@link CompositeHealthContributorConfiguration}
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class CompositeHealthIndicatorConfiguration<H extends HealthIndicator, S> {
|
||||
|
||||
@Autowired
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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.Map;
|
||||
|
||||
import org.springframework.boot.actuate.health.CompositeReactiveHealthContributor;
|
||||
import org.springframework.boot.actuate.health.ReactiveHealthContributor;
|
||||
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
|
||||
|
||||
/**
|
||||
* Base class for health contributor configurations that can combine source beans into a
|
||||
* composite.
|
||||
*
|
||||
* @param <I> the health indicator type
|
||||
* @param <B> the bean type
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
* @since 2.2.0
|
||||
*/
|
||||
public abstract class CompositeReactiveHealthContributorConfiguration<I extends ReactiveHealthIndicator, B>
|
||||
extends AbstractCompositeHealthContributorConfiguration<ReactiveHealthContributor, I, B> {
|
||||
|
||||
@Override
|
||||
protected final ReactiveHealthContributor createComposite(Map<String, B> beans) {
|
||||
return CompositeReactiveHealthContributor.fromMap(beans, this::createIndicator);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -33,7 +33,10 @@ import org.springframework.core.ResolvableType;
|
||||
* @param <S> the bean source type
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.0
|
||||
* @deprecated since 2.2.0 in favor of
|
||||
* {@link CompositeReactiveHealthContributorConfiguration}
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class CompositeReactiveHealthIndicatorConfiguration<H extends ReactiveHealthIndicator, S> {
|
||||
|
||||
@Autowired
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.HealthAggregator;
|
||||
import org.springframework.boot.actuate.health.Status;
|
||||
import org.springframework.boot.actuate.health.StatusAggregator;
|
||||
|
||||
/**
|
||||
* Adapter class to convert a legacy {@link HealthAggregator} to a
|
||||
* {@link StatusAggregator}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
class HealthAggregatorStatusAggregatorAdapter implements StatusAggregator {
|
||||
|
||||
private HealthAggregator healthAggregator;
|
||||
|
||||
HealthAggregatorStatusAggregatorAdapter(HealthAggregator healthAggregator) {
|
||||
this.healthAggregator = healthAggregator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Status getAggregateStatus(Set<Status> statuses) {
|
||||
int index = 0;
|
||||
Map<String, Health> healths = new LinkedHashMap<String, Health>();
|
||||
for (Status status : statuses) {
|
||||
index++;
|
||||
healths.put("health" + index, asHealth(status));
|
||||
}
|
||||
Health aggregate = this.healthAggregator.aggregate(healths);
|
||||
return aggregate.getStatus();
|
||||
}
|
||||
|
||||
private Health asHealth(Status status) {
|
||||
return Health.status(status).build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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 org.springframework.boot.actuate.health.ApplicationHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.HealthContributor;
|
||||
import org.springframework.boot.actuate.health.ReactiveHealthContributor;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for {@link HealthContributor health
|
||||
* contributors}. Technology specific auto-configurations should be ordered before this
|
||||
* auto-configuration.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 2.2.0
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class HealthContributorAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean({ HealthContributor.class, ReactiveHealthContributor.class })
|
||||
public ApplicationHealthIndicator applicationHealthContributor() {
|
||||
return new ApplicationHealthIndicator();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.health;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint;
|
||||
import org.springframework.boot.actuate.health.HealthEndpoint;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
@@ -32,9 +33,12 @@ import org.springframework.context.annotation.Import;
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties({ HealthEndpointProperties.class, HealthIndicatorProperties.class })
|
||||
@AutoConfigureAfter(HealthIndicatorAutoConfiguration.class)
|
||||
@Import({ HealthEndpointConfiguration.class, HealthEndpointWebExtensionConfiguration.class })
|
||||
@EnableConfigurationProperties(HealthEndpointProperties.class)
|
||||
@AutoConfigureAfter(HealthContributorAutoConfiguration.class)
|
||||
@ConditionalOnAvailableEndpoint(endpoint = HealthEndpoint.class)
|
||||
@Import({ LegacyHealthEndpointAdaptersConfiguration.class, LegacyHealthEndpointCompatibiltyConfiguration.class,
|
||||
HealthEndpointConfiguration.class, ReactiveHealthEndpointConfiguration.class,
|
||||
HealthEndpointWebExtensionConfiguration.class, HealthEndpointReactiveWebExtensionConfiguration.class })
|
||||
public class HealthEndpointAutoConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@@ -16,30 +16,66 @@
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.health;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint;
|
||||
import org.springframework.boot.actuate.health.CompositeHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.HealthAggregator;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.actuate.health.DefaultHealthContributorRegistry;
|
||||
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.HealthIndicatorRegistry;
|
||||
import org.springframework.boot.actuate.health.HealthEndpointSettings;
|
||||
import org.springframework.boot.actuate.health.HttpCodeStatusMapper;
|
||||
import org.springframework.boot.actuate.health.SimpleHttpCodeStatusMapper;
|
||||
import org.springframework.boot.actuate.health.SimpleStatusAggregator;
|
||||
import org.springframework.boot.actuate.health.StatusAggregator;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Configuration for {@link HealthEndpoint}.
|
||||
* Configuration for {@link HealthEndpoint} infrastructure beans.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
* @see HealthEndpointAutoConfiguration
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnSingleCandidate(HealthIndicatorRegistry.class)
|
||||
@ConditionalOnAvailableEndpoint(endpoint = HealthEndpoint.class)
|
||||
class HealthEndpointConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
HealthEndpoint healthEndpoint(HealthAggregator healthAggregator, HealthIndicatorRegistry registry) {
|
||||
return new HealthEndpoint(new CompositeHealthIndicator(healthAggregator, registry));
|
||||
StatusAggregator healthStatusAggregator(HealthEndpointProperties properties) {
|
||||
return new SimpleStatusAggregator(properties.getStatus().getOrder());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
HttpCodeStatusMapper healthHttpCodeStatusMapper(HealthEndpointProperties properties) {
|
||||
return new SimpleHttpCodeStatusMapper(properties.getStatus().getHttpMapping());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
HealthEndpointSettings healthEndpointSettings(HealthEndpointProperties properties,
|
||||
ObjectProvider<StatusAggregator> statusAggregatorProvider,
|
||||
ObjectProvider<HttpCodeStatusMapper> httpCodeStatusMapperProvider) {
|
||||
StatusAggregator statusAggregator = statusAggregatorProvider
|
||||
.getIfAvailable(() -> new SimpleStatusAggregator(properties.getStatus().getOrder()));
|
||||
HttpCodeStatusMapper httpCodeStatusMapper = httpCodeStatusMapperProvider
|
||||
.getIfAvailable(() -> new SimpleHttpCodeStatusMapper(properties.getStatus().getHttpMapping()));
|
||||
return new AutoConfiguredHealthEndpointSettings(statusAggregator, httpCodeStatusMapper,
|
||||
properties.getShowDetails(), properties.getRoles());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
HealthContributorRegistry healthContributorRegistry(Map<String, HealthContributor> healthContributors) {
|
||||
return new DefaultHealthContributorRegistry(healthContributors);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
HealthEndpoint healthEndpoint(HealthContributorRegistry registry, HealthEndpointSettings settings) {
|
||||
return new HealthEndpoint(registry, settings);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,11 +16,13 @@
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.health;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.boot.actuate.health.HealthEndpoint;
|
||||
import org.springframework.boot.actuate.health.ShowDetails;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
@@ -32,6 +34,8 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
@ConfigurationProperties("management.endpoint.health")
|
||||
public class HealthEndpointProperties {
|
||||
|
||||
private final Status status = new Status();
|
||||
|
||||
/**
|
||||
* When to show full health details.
|
||||
*/
|
||||
@@ -43,6 +47,10 @@ public class HealthEndpointProperties {
|
||||
*/
|
||||
private Set<String> roles = new HashSet<>();
|
||||
|
||||
public Status getStatus() {
|
||||
return this.status;
|
||||
}
|
||||
|
||||
public ShowDetails getShowDetails() {
|
||||
return this.showDetails;
|
||||
}
|
||||
@@ -59,4 +67,59 @@ public class HealthEndpointProperties {
|
||||
this.roles = roles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Status properties for the group.
|
||||
*/
|
||||
public static class Status {
|
||||
|
||||
/**
|
||||
* Comma-separated list of health statuses in order of severity.
|
||||
*/
|
||||
private List<String> order = null;
|
||||
|
||||
/**
|
||||
* Mapping of health statuses to HTTP status codes. By default, registered health
|
||||
* statuses map to sensible defaults (for example, UP maps to 200).
|
||||
*/
|
||||
private final Map<String, Integer> httpMapping = new HashMap<>();
|
||||
|
||||
public List<String> getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
public void setOrder(List<String> statusOrder) {
|
||||
if (statusOrder != null && !statusOrder.isEmpty()) {
|
||||
this.order = statusOrder;
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, Integer> getHttpMapping() {
|
||||
return this.httpMapping;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for showing details in responses from the {@link HealthEndpoint} web
|
||||
* extensions.
|
||||
*/
|
||||
public enum ShowDetails {
|
||||
|
||||
/**
|
||||
* Never show details in the response.
|
||||
*/
|
||||
NEVER,
|
||||
|
||||
/**
|
||||
* Show details in the response when accessed by an authorized user.
|
||||
*/
|
||||
WHEN_AUTHORIZED,
|
||||
|
||||
/**
|
||||
* Always show details in the response.
|
||||
*/
|
||||
ALWAYS
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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 org.springframework.boot.actuate.health.HealthEndpoint;
|
||||
import org.springframework.boot.actuate.health.HealthEndpointSettings;
|
||||
import org.springframework.boot.actuate.health.ReactiveHealthContributorRegistry;
|
||||
import org.springframework.boot.actuate.health.ReactiveHealthEndpointWebExtension;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Configuration for {@link HealthEndpoint} reactive web extensions.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @see HealthEndpointAutoConfiguration
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnWebApplication(type = Type.REACTIVE)
|
||||
@ConditionalOnBean(HealthEndpoint.class)
|
||||
class HealthEndpointReactiveWebExtensionConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnBean(HealthEndpoint.class)
|
||||
ReactiveHealthEndpointWebExtension reactiveHealthEndpointWebExtension(
|
||||
ReactiveHealthContributorRegistry reactiveHealthContributorRegistry, HealthEndpointSettings settings) {
|
||||
return new ReactiveHealthEndpointWebExtension(reactiveHealthContributorRegistry, settings);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,83 +16,34 @@
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.health;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint;
|
||||
import org.springframework.boot.actuate.health.CompositeReactiveHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.HealthAggregator;
|
||||
import org.springframework.boot.actuate.health.HealthContributorRegistry;
|
||||
import org.springframework.boot.actuate.health.HealthEndpoint;
|
||||
import org.springframework.boot.actuate.health.HealthEndpointSettings;
|
||||
import org.springframework.boot.actuate.health.HealthEndpointWebExtension;
|
||||
import org.springframework.boot.actuate.health.HealthStatusHttpMapper;
|
||||
import org.springframework.boot.actuate.health.HealthWebEndpointResponseMapper;
|
||||
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
|
||||
import org.springframework.boot.actuate.health.ReactiveHealthEndpointWebExtension;
|
||||
import org.springframework.boot.actuate.health.ReactiveHealthIndicatorRegistry;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Configuration for health endpoint web extensions.
|
||||
* Configuration for {@link HealthEndpoint} web extensions.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
* @see HealthEndpointAutoConfiguration
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties(HealthIndicatorProperties.class)
|
||||
@ConditionalOnAvailableEndpoint(endpoint = HealthEndpoint.class)
|
||||
@ConditionalOnWebApplication(type = Type.SERVLET)
|
||||
@ConditionalOnBean(HealthEndpoint.class)
|
||||
class HealthEndpointWebExtensionConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(HealthEndpoint.class)
|
||||
@ConditionalOnMissingBean
|
||||
HealthStatusHttpMapper createHealthStatusHttpMapper(HealthIndicatorProperties healthIndicatorProperties) {
|
||||
HealthStatusHttpMapper statusHttpMapper = new HealthStatusHttpMapper();
|
||||
if (healthIndicatorProperties.getHttpMapping() != null) {
|
||||
statusHttpMapper.addStatusMapping(healthIndicatorProperties.getHttpMapping());
|
||||
}
|
||||
return statusHttpMapper;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
HealthWebEndpointResponseMapper healthWebEndpointResponseMapper(HealthStatusHttpMapper statusHttpMapper,
|
||||
HealthEndpointProperties properties) {
|
||||
return new HealthWebEndpointResponseMapper(statusHttpMapper, properties.getShowDetails(),
|
||||
properties.getRoles());
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnWebApplication(type = Type.REACTIVE)
|
||||
@ConditionalOnSingleCandidate(ReactiveHealthIndicatorRegistry.class)
|
||||
static class ReactiveWebHealthConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnBean(HealthEndpoint.class)
|
||||
ReactiveHealthEndpointWebExtension reactiveHealthEndpointWebExtension(
|
||||
ObjectProvider<HealthAggregator> healthAggregator, ReactiveHealthIndicatorRegistry registry,
|
||||
HealthWebEndpointResponseMapper responseMapper) {
|
||||
return new ReactiveHealthEndpointWebExtension(new CompositeReactiveHealthIndicator(
|
||||
healthAggregator.getIfAvailable(OrderedHealthAggregator::new), registry), responseMapper);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnWebApplication(type = Type.SERVLET)
|
||||
static class ServletWebHealthConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnBean(HealthEndpoint.class)
|
||||
HealthEndpointWebExtension healthEndpointWebExtension(HealthEndpoint healthEndpoint,
|
||||
HealthWebEndpointResponseMapper responseMapper) {
|
||||
return new HealthEndpointWebExtension(healthEndpoint, responseMapper);
|
||||
}
|
||||
|
||||
HealthEndpointWebExtension healthEndpointWebExtension(HealthContributorRegistry healthContributorRegistry,
|
||||
HealthEndpointSettings settings) {
|
||||
return new HealthEndpointWebExtension(healthContributorRegistry, settings);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,74 +16,25 @@
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.health;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.HealthAggregator;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.actuate.health.HealthIndicatorRegistry;
|
||||
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
|
||||
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.ReactiveHealthIndicatorRegistry;
|
||||
import org.springframework.boot.actuate.health.ReactiveHealthIndicatorRegistryFactory;
|
||||
import org.springframework.boot.actuate.health.HealthContributor;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for {@link HealthIndicator}s.
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for {@link HealthContributor health
|
||||
* contributors}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
* @author Vedran Pavic
|
||||
* @since 2.0.0
|
||||
* @deprecated since 2.2.0 in favor of {@link HealthContributorAutoConfiguration}
|
||||
*/
|
||||
@Deprecated
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties({ HealthIndicatorProperties.class })
|
||||
@AutoConfigureBefore(HealthContributorAutoConfiguration.class)
|
||||
public class HealthIndicatorAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean({ HealthIndicator.class, ReactiveHealthIndicator.class })
|
||||
public ApplicationHealthIndicator applicationHealthIndicator() {
|
||||
return new ApplicationHealthIndicator();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(HealthAggregator.class)
|
||||
public OrderedHealthAggregator healthAggregator(HealthIndicatorProperties properties) {
|
||||
OrderedHealthAggregator healthAggregator = new OrderedHealthAggregator();
|
||||
if (properties.getOrder() != null) {
|
||||
healthAggregator.setStatusOrder(properties.getOrder());
|
||||
}
|
||||
return healthAggregator;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(HealthIndicatorRegistry.class)
|
||||
public HealthIndicatorRegistry healthIndicatorRegistry(ApplicationContext applicationContext) {
|
||||
return HealthIndicatorRegistryBeans.get(applicationContext);
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(Flux.class)
|
||||
static class ReactiveHealthIndicatorConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
ReactiveHealthIndicatorRegistry reactiveHealthIndicatorRegistry(
|
||||
Map<String, ReactiveHealthIndicator> reactiveHealthIndicators,
|
||||
Map<String, HealthIndicator> healthIndicators) {
|
||||
return new ReactiveHealthIndicatorRegistryFactory()
|
||||
.createReactiveHealthIndicatorRegistry(reactiveHealthIndicators, healthIndicators);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,44 +16,41 @@
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.health;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.DeprecatedConfigurationProperty;
|
||||
|
||||
/**
|
||||
* Configuration properties for some health properties.
|
||||
*
|
||||
* @author Christian Dupuis
|
||||
* @since 2.0.0
|
||||
* @deprecated since 2.2.0 in favor of {@link HealthEndpointProperties}
|
||||
*/
|
||||
@Deprecated
|
||||
@ConfigurationProperties(prefix = "management.health.status")
|
||||
public class HealthIndicatorProperties {
|
||||
|
||||
/**
|
||||
* Comma-separated list of health statuses in order of severity.
|
||||
*/
|
||||
private List<String> order = null;
|
||||
private final HealthEndpointProperties healthEndpointProperties;
|
||||
|
||||
/**
|
||||
* Mapping of health statuses to HTTP status codes. By default, registered health
|
||||
* statuses map to sensible defaults (for example, UP maps to 200).
|
||||
*/
|
||||
private final Map<String, Integer> httpMapping = new HashMap<>();
|
||||
HealthIndicatorProperties(HealthEndpointProperties healthEndpointProperties) {
|
||||
this.healthEndpointProperties = healthEndpointProperties;
|
||||
}
|
||||
|
||||
@DeprecatedConfigurationProperty(replacement = "management.endpoint.health.status.order")
|
||||
public List<String> getOrder() {
|
||||
return this.order;
|
||||
return this.healthEndpointProperties.getStatus().getOrder();
|
||||
}
|
||||
|
||||
public void setOrder(List<String> statusOrder) {
|
||||
if (statusOrder != null && !statusOrder.isEmpty()) {
|
||||
this.order = statusOrder;
|
||||
}
|
||||
this.healthEndpointProperties.getStatus().setOrder(statusOrder);
|
||||
}
|
||||
|
||||
@DeprecatedConfigurationProperty(replacement = "management.endpoint.health.status.http-mapping")
|
||||
public Map<String, Integer> getHttpMapping() {
|
||||
return this.httpMapping;
|
||||
return this.healthEndpointProperties.getStatus().getHttpMapping();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.actuate.health.HealthIndicatorRegistry;
|
||||
import org.springframework.boot.actuate.health.HealthIndicatorRegistryFactory;
|
||||
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Creates a {@link HealthIndicatorRegistry} from beans in the {@link ApplicationContext}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
final class HealthIndicatorRegistryBeans {
|
||||
|
||||
private HealthIndicatorRegistryBeans() {
|
||||
}
|
||||
|
||||
static HealthIndicatorRegistry get(ApplicationContext applicationContext) {
|
||||
Map<String, HealthIndicator> indicators = new LinkedHashMap<>(
|
||||
applicationContext.getBeansOfType(HealthIndicator.class));
|
||||
if (ClassUtils.isPresent("reactor.core.publisher.Flux", null)) {
|
||||
new ReactiveHealthIndicators().get(applicationContext).forEach(indicators::putIfAbsent);
|
||||
}
|
||||
HealthIndicatorRegistryFactory factory = new HealthIndicatorRegistryFactory();
|
||||
return factory.createHealthIndicatorRegistry(indicators);
|
||||
}
|
||||
|
||||
private static class ReactiveHealthIndicators {
|
||||
|
||||
Map<String, HealthIndicator> get(ApplicationContext applicationContext) {
|
||||
Map<String, HealthIndicator> indicators = new LinkedHashMap<>();
|
||||
applicationContext.getBeansOfType(ReactiveHealthIndicator.class)
|
||||
.forEach((name, indicator) -> indicators.put(name, adapt(indicator)));
|
||||
return indicators;
|
||||
}
|
||||
|
||||
private HealthIndicator adapt(ReactiveHealthIndicator indicator) {
|
||||
return () -> indicator.health().block();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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 org.springframework.boot.actuate.health.HealthStatusHttpMapper;
|
||||
import org.springframework.boot.actuate.health.HttpCodeStatusMapper;
|
||||
import org.springframework.boot.actuate.health.Status;
|
||||
|
||||
/**
|
||||
* Adapter class to convert a legacy {@link HealthStatusHttpMapper} to a
|
||||
* {@link HttpCodeStatusMapper}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
class HealthStatusHttpMapperHttpCodeStatusMapperAdapter implements HttpCodeStatusMapper {
|
||||
|
||||
private final HealthStatusHttpMapper healthStatusHttpMapper;
|
||||
|
||||
HealthStatusHttpMapperHttpCodeStatusMapperAdapter(HealthStatusHttpMapper healthStatusHttpMapper) {
|
||||
this.healthStatusHttpMapper = healthStatusHttpMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStatusCode(Status status) {
|
||||
return this.healthStatusHttpMapper.mapStatus(status);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Member predicate that matches based on {@code include} and {@code exclude} sets.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class IncludeExcludeGroupMemberPredicate implements Predicate<String> {
|
||||
|
||||
private final Set<String> include;
|
||||
|
||||
private final Set<String> exclude;
|
||||
|
||||
IncludeExcludeGroupMemberPredicate(Set<String> include, Set<String> exclude) {
|
||||
this.include = clean(include);
|
||||
this.exclude = clean(exclude);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(String name) {
|
||||
return isIncluded(name) && !isExcluded(name);
|
||||
}
|
||||
|
||||
private boolean isIncluded(String name) {
|
||||
return this.include.contains("*") || this.include.contains(clean(name));
|
||||
}
|
||||
|
||||
private boolean isExcluded(String name) {
|
||||
return this.exclude.contains("*") || this.exclude.contains(clean(name));
|
||||
}
|
||||
|
||||
private Set<String> clean(Set<String> names) {
|
||||
if (names == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
Set<String> cleaned = new LinkedHashSet<>(names.size());
|
||||
for (String name : names) {
|
||||
cleaned.add(name);
|
||||
}
|
||||
return Collections.unmodifiableSet(cleaned);
|
||||
}
|
||||
|
||||
private String clean(String name) {
|
||||
return name.trim().toLowerCase();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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 org.springframework.boot.actuate.health.HttpCodeStatusMapper;
|
||||
import org.springframework.boot.actuate.health.StatusAggregator;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Configuration to adapt legacy deprecated health endpoint classes and interfaces.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @see HealthEndpointAutoConfiguration
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@SuppressWarnings("deprecation")
|
||||
class LegacyHealthEndpointAdaptersConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(org.springframework.boot.actuate.health.HealthAggregator.class)
|
||||
StatusAggregator healthAggregatorStatusAggregatorAdapter(
|
||||
org.springframework.boot.actuate.health.HealthAggregator healthAggregator) {
|
||||
return new HealthAggregatorStatusAggregatorAdapter(healthAggregator);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(org.springframework.boot.actuate.health.HealthStatusHttpMapper.class)
|
||||
HttpCodeStatusMapper healthStatusHttpMapperHttpCodeStatusMapperAdapter(
|
||||
org.springframework.boot.actuate.health.HealthStatusHttpMapper healthStatusHttpMapper) {
|
||||
return new HealthStatusHttpMapperHttpCodeStatusMapperAdapter(healthStatusHttpMapper);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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 org.springframework.boot.actuate.health.HealthAggregator;
|
||||
import org.springframework.boot.actuate.health.HealthStatusHttpMapper;
|
||||
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Configuration to adapt legacy deprecated health endpoint classes and interfaces.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @see HealthEndpointAutoConfiguration
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@SuppressWarnings("deprecation")
|
||||
class LegacyHealthEndpointCompatibiltyConfiguration {
|
||||
|
||||
@Bean
|
||||
HealthIndicatorProperties healthIndicatorProperties(HealthEndpointProperties healthEndpointProperties) {
|
||||
return new HealthIndicatorProperties(healthEndpointProperties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
HealthAggregator healthAggregator(HealthIndicatorProperties healthIndicatorProperties) {
|
||||
OrderedHealthAggregator aggregator = new OrderedHealthAggregator();
|
||||
if (healthIndicatorProperties.getOrder() != null) {
|
||||
aggregator.setStatusOrder(healthIndicatorProperties.getOrder());
|
||||
}
|
||||
return aggregator;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
HealthStatusHttpMapper healthStatusHttpMapper(HealthIndicatorProperties healthIndicatorProperties) {
|
||||
HealthStatusHttpMapper mapper = new HealthStatusHttpMapper();
|
||||
if (healthIndicatorProperties.getHttpMapping() != null) {
|
||||
mapper.setStatusMapping(healthIndicatorProperties.getHttpMapping());
|
||||
}
|
||||
return mapper;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.boot.actuate.health.DefaultReactiveHealthContributorRegistry;
|
||||
import org.springframework.boot.actuate.health.HealthContributor;
|
||||
import org.springframework.boot.actuate.health.HealthEndpoint;
|
||||
import org.springframework.boot.actuate.health.ReactiveHealthContributor;
|
||||
import org.springframework.boot.actuate.health.ReactiveHealthContributorRegistry;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Configuration for reactive {@link HealthEndpoint} infrastructure beans.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @see HealthEndpointAutoConfiguration
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(Flux.class)
|
||||
@ConditionalOnBean(HealthEndpoint.class)
|
||||
class ReactiveHealthEndpointConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
ReactiveHealthContributorRegistry reactiveHealthContributorRegistry(
|
||||
Map<String, HealthContributor> healthContributors,
|
||||
Map<String, ReactiveHealthContributor> reactiveHealthContributors) {
|
||||
Map<String, ReactiveHealthContributor> allContributors = new LinkedHashMap<>(reactiveHealthContributors);
|
||||
healthContributors.forEach((name, contributor) -> allContributors.computeIfAbsent(name,
|
||||
(key) -> ReactiveHealthContributor.adapt(contributor)));
|
||||
return new DefaultReactiveHealthContributorRegistry(allContributors);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,6 +21,7 @@ org.springframework.boot.actuate.autoconfigure.endpoint.jmx.JmxEndpointAutoConfi
|
||||
org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration,\
|
||||
org.springframework.boot.actuate.autoconfigure.env.EnvironmentEndpointAutoConfiguration,\
|
||||
org.springframework.boot.actuate.autoconfigure.flyway.FlywayEndpointAutoConfiguration,\
|
||||
org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration,\
|
||||
org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration,\
|
||||
org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration,\
|
||||
org.springframework.boot.actuate.autoconfigure.influx.InfluxDbHealthIndicatorAutoConfiguration,\
|
||||
|
||||
@@ -19,6 +19,8 @@ package org.springframework.boot.actuate.autoconfigure.amqp;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.actuate.amqp.RabbitHealthIndicator;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
@@ -36,7 +38,8 @@ class RabbitHealthIndicatorAutoConfigurationTests {
|
||||
|
||||
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(RabbitAutoConfiguration.class,
|
||||
RabbitHealthIndicatorAutoConfiguration.class, HealthIndicatorAutoConfiguration.class));
|
||||
RabbitHealthIndicatorAutoConfiguration.class, HealthIndicatorAutoConfiguration.class,
|
||||
HealthContributorAutoConfiguration.class, HealthEndpointAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void runShouldCreateIndicator() {
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.springframework.boot.actuate.autoconfigure.cassandra;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.cassandra.CassandraHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
|
||||
@@ -40,7 +42,8 @@ class CassandraHealthIndicatorAutoConfigurationTests {
|
||||
|
||||
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(CassandraConfiguration.class,
|
||||
CassandraHealthIndicatorAutoConfiguration.class, HealthIndicatorAutoConfiguration.class));
|
||||
CassandraHealthIndicatorAutoConfiguration.class, HealthIndicatorAutoConfiguration.class,
|
||||
HealthContributorAutoConfiguration.class, HealthEndpointAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void runShouldCreateIndicator() {
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.springframework.boot.actuate.autoconfigure.cassandra;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.cassandra.CassandraHealthIndicator;
|
||||
import org.springframework.boot.actuate.cassandra.CassandraReactiveHealthIndicator;
|
||||
@@ -40,7 +42,8 @@ class CassandraReactiveHealthIndicatorAutoConfigurationTests {
|
||||
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withBean(ReactiveCassandraOperations.class, () -> mock(ReactiveCassandraOperations.class))
|
||||
.withConfiguration(AutoConfigurations.of(CassandraReactiveHealthIndicatorAutoConfiguration.class,
|
||||
HealthIndicatorAutoConfiguration.class));
|
||||
HealthIndicatorAutoConfiguration.class, HealthContributorAutoConfiguration.class,
|
||||
HealthEndpointAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void runShouldCreateIndicator() {
|
||||
|
||||
@@ -35,8 +35,9 @@ import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.web.PathMapper;
|
||||
import org.springframework.boot.actuate.endpoint.web.WebOperation;
|
||||
import org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExtension;
|
||||
import org.springframework.boot.actuate.health.HealthContributorRegistry;
|
||||
import org.springframework.boot.actuate.health.HealthEndpoint;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.actuate.health.HealthEndpointSettings;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -110,7 +111,9 @@ class CloudFoundryWebEndpointDiscovererTests {
|
||||
|
||||
@Bean
|
||||
HealthEndpoint healthEndpoint() {
|
||||
return new HealthEndpoint(mock(HealthIndicator.class));
|
||||
HealthContributorRegistry registry = mock(HealthContributorRegistry.class);
|
||||
HealthEndpointSettings settings = mock(HealthEndpointSettings.class);
|
||||
return new HealthEndpoint(registry, settings);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -22,9 +22,13 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
|
||||
import org.springframework.boot.actuate.health.CompositeHealth;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.HealthComponent;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
|
||||
@@ -53,16 +57,28 @@ class CloudFoundryReactiveHealthEndpointWebExtensionTests {
|
||||
ReactiveCloudFoundryActuatorAutoConfigurationTests.WebClientCustomizerConfig.class,
|
||||
WebClientAutoConfiguration.class, ManagementContextAutoConfiguration.class,
|
||||
EndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class,
|
||||
HealthIndicatorAutoConfiguration.class, HealthEndpointAutoConfiguration.class,
|
||||
ReactiveCloudFoundryActuatorAutoConfiguration.class));
|
||||
HealthContributorAutoConfiguration.class, HealthEndpointAutoConfiguration.class,
|
||||
ReactiveCloudFoundryActuatorAutoConfiguration.class))
|
||||
.withUserConfiguration(TestHealthIndicator.class);
|
||||
|
||||
@Test
|
||||
void healthDetailsAlwaysPresent() {
|
||||
this.contextRunner.run((context) -> {
|
||||
CloudFoundryReactiveHealthEndpointWebExtension extension = context
|
||||
.getBean(CloudFoundryReactiveHealthEndpointWebExtension.class);
|
||||
assertThat(extension.health().block(Duration.ofSeconds(30)).getBody().getDetails()).isNotEmpty();
|
||||
HealthComponent body = extension.health().block(Duration.ofSeconds(30)).getBody();
|
||||
HealthComponent health = ((CompositeHealth) body).getDetails().entrySet().iterator().next().getValue();
|
||||
assertThat(((Health) health).getDetails()).containsEntry("spring", "boot");
|
||||
});
|
||||
}
|
||||
|
||||
private static class TestHealthIndicator implements HealthIndicator {
|
||||
|
||||
@Override
|
||||
public Health health() {
|
||||
return Health.up().withDetail("spring", "boot").build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,8 +32,8 @@ import reactor.netty.http.HttpResources;
|
||||
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.servlet.CloudFoundryInfoEndpointWebExtension;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.info.InfoContributorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.info.InfoEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
|
||||
@@ -87,7 +87,7 @@ class ReactiveCloudFoundryActuatorAutoConfigurationTests {
|
||||
PropertyPlaceholderAutoConfiguration.class, WebClientCustomizerConfig.class,
|
||||
WebClientAutoConfiguration.class, ManagementContextAutoConfiguration.class,
|
||||
EndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class,
|
||||
HealthIndicatorAutoConfiguration.class, HealthEndpointAutoConfiguration.class,
|
||||
HealthContributorAutoConfiguration.class, HealthEndpointAutoConfiguration.class,
|
||||
InfoContributorAutoConfiguration.class, InfoEndpointAutoConfiguration.class,
|
||||
ProjectInfoAutoConfiguration.class, ReactiveCloudFoundryActuatorAutoConfiguration.class));
|
||||
|
||||
@@ -237,7 +237,7 @@ class ReactiveCloudFoundryActuatorAutoConfigurationTests {
|
||||
.run((context) -> {
|
||||
Collection<ExposableWebEndpoint> endpoints = getHandlerMapping(context).getEndpoints();
|
||||
ExposableWebEndpoint endpoint = endpoints.iterator().next();
|
||||
assertThat(endpoint.getOperations()).hasSize(3);
|
||||
assertThat(endpoint.getOperations()).hasSize(2);
|
||||
WebOperation webOperation = findOperationWithRequestPath(endpoint, "health");
|
||||
Object invoker = ReflectionTestUtils.getField(webOperation, "invoker");
|
||||
assertThat(ReflectionTestUtils.getField(invoker, "target"))
|
||||
|
||||
@@ -23,8 +23,8 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration;
|
||||
import org.springframework.boot.actuate.endpoint.EndpointId;
|
||||
@@ -220,7 +220,7 @@ class CloudFoundryActuatorAutoConfigurationTests {
|
||||
this.contextRunner
|
||||
.withPropertyValues("VCAP_APPLICATION:---", "vcap.application.application_id:my-app-id",
|
||||
"vcap.application.cf_api:https://my-cloud-controller.com")
|
||||
.withConfiguration(AutoConfigurations.of(HealthIndicatorAutoConfiguration.class,
|
||||
.withConfiguration(AutoConfigurations.of(HealthContributorAutoConfiguration.class,
|
||||
HealthEndpointAutoConfiguration.class))
|
||||
.run((context) -> {
|
||||
Collection<ExposableWebEndpoint> endpoints = context
|
||||
@@ -228,7 +228,7 @@ class CloudFoundryActuatorAutoConfigurationTests {
|
||||
CloudFoundryWebEndpointServletHandlerMapping.class)
|
||||
.getEndpoints();
|
||||
ExposableWebEndpoint endpoint = endpoints.iterator().next();
|
||||
assertThat(endpoint.getOperations()).hasSize(3);
|
||||
assertThat(endpoint.getOperations()).hasSize(2);
|
||||
WebOperation webOperation = findOperationWithRequestPath(endpoint, "health");
|
||||
Object invoker = ReflectionTestUtils.getField(webOperation, "invoker");
|
||||
assertThat(ReflectionTestUtils.getField(invoker, "target"))
|
||||
|
||||
@@ -20,10 +20,14 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration;
|
||||
import org.springframework.boot.actuate.health.CompositeHealth;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.HealthComponent;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
|
||||
@@ -50,16 +54,28 @@ class CloudFoundryHealthEndpointWebExtensionTests {
|
||||
HttpMessageConvertersAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class,
|
||||
RestTemplateAutoConfiguration.class, ManagementContextAutoConfiguration.class,
|
||||
ServletManagementContextAutoConfiguration.class, EndpointAutoConfiguration.class,
|
||||
WebEndpointAutoConfiguration.class, HealthIndicatorAutoConfiguration.class,
|
||||
HealthEndpointAutoConfiguration.class, CloudFoundryActuatorAutoConfiguration.class));
|
||||
WebEndpointAutoConfiguration.class, HealthContributorAutoConfiguration.class,
|
||||
HealthEndpointAutoConfiguration.class, CloudFoundryActuatorAutoConfiguration.class))
|
||||
.withUserConfiguration(TestHealthIndicator.class);
|
||||
|
||||
@Test
|
||||
void healthDetailsAlwaysPresent() {
|
||||
this.contextRunner.run((context) -> {
|
||||
CloudFoundryHealthEndpointWebExtension extension = context
|
||||
.getBean(CloudFoundryHealthEndpointWebExtension.class);
|
||||
assertThat(extension.getHealth().getBody().getDetails()).isNotEmpty();
|
||||
HealthComponent body = extension.health().getBody();
|
||||
HealthComponent health = ((CompositeHealth) body).getDetails().entrySet().iterator().next().getValue();
|
||||
assertThat(((Health) health).getDetails()).containsEntry("spring", "boot");
|
||||
});
|
||||
}
|
||||
|
||||
private static class TestHealthIndicator implements HealthIndicator {
|
||||
|
||||
@Override
|
||||
public Health health() {
|
||||
return Health.up().withDetail("spring", "boot").build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.springframework.boot.actuate.autoconfigure.couchbase;
|
||||
import com.couchbase.client.java.Cluster;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.couchbase.CouchbaseHealthIndicator;
|
||||
import org.springframework.boot.actuate.couchbase.CouchbaseReactiveHealthIndicator;
|
||||
@@ -37,8 +39,10 @@ import static org.mockito.Mockito.mock;
|
||||
class CouchbaseHealthIndicatorAutoConfigurationTests {
|
||||
|
||||
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withBean(Cluster.class, () -> mock(Cluster.class)).withConfiguration(AutoConfigurations
|
||||
.of(CouchbaseHealthIndicatorAutoConfiguration.class, HealthIndicatorAutoConfiguration.class));
|
||||
.withBean(Cluster.class, () -> mock(Cluster.class))
|
||||
.withConfiguration(AutoConfigurations.of(CouchbaseHealthIndicatorAutoConfiguration.class,
|
||||
HealthIndicatorAutoConfiguration.class, HealthContributorAutoConfiguration.class,
|
||||
HealthEndpointAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void runShouldCreateIndicator() {
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.springframework.boot.actuate.autoconfigure.couchbase;
|
||||
import com.couchbase.client.java.Cluster;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.couchbase.CouchbaseHealthIndicator;
|
||||
import org.springframework.boot.actuate.couchbase.CouchbaseReactiveHealthIndicator;
|
||||
@@ -36,8 +38,10 @@ import static org.mockito.Mockito.mock;
|
||||
class CouchbaseReactiveHealthIndicatorAutoConfigurationTests {
|
||||
|
||||
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withBean(Cluster.class, () -> mock(Cluster.class)).withConfiguration(AutoConfigurations.of(
|
||||
CouchbaseReactiveHealthIndicatorAutoConfiguration.class, HealthIndicatorAutoConfiguration.class));
|
||||
.withBean(Cluster.class, () -> mock(Cluster.class))
|
||||
.withConfiguration(AutoConfigurations.of(CouchbaseReactiveHealthIndicatorAutoConfiguration.class,
|
||||
HealthIndicatorAutoConfiguration.class, HealthContributorAutoConfiguration.class,
|
||||
HealthEndpointAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void runShouldCreateIndicator() {
|
||||
|
||||
@@ -19,6 +19,8 @@ package org.springframework.boot.actuate.autoconfigure.elasticsearch;
|
||||
import io.searchbox.client.JestClient;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.elasticsearch.ElasticsearchHealthIndicator;
|
||||
import org.springframework.boot.actuate.elasticsearch.ElasticsearchJestHealthIndicator;
|
||||
@@ -41,7 +43,8 @@ class ElasticsearchHealthIndicatorAutoConfigurationTests {
|
||||
|
||||
private ApplicationContextRunner contextRunner = new ApplicationContextRunner().withConfiguration(AutoConfigurations
|
||||
.of(ElasticsearchAutoConfiguration.class, ElasticSearchClientHealthIndicatorAutoConfiguration.class,
|
||||
ElasticSearchJestHealthIndicatorAutoConfiguration.class, HealthIndicatorAutoConfiguration.class));
|
||||
ElasticSearchJestHealthIndicatorAutoConfiguration.class, HealthIndicatorAutoConfiguration.class,
|
||||
HealthContributorAutoConfiguration.class, HealthEndpointAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void runShouldCreateIndicator() {
|
||||
|
||||
@@ -26,12 +26,19 @@ import javax.sql.DataSource;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.actuate.health.CompositeHealthIndicator;
|
||||
import org.springframework.boot.actuate.endpoint.SecurityContext;
|
||||
import org.springframework.boot.actuate.health.CompositeHealthContributor;
|
||||
import org.springframework.boot.actuate.health.DefaultHealthContributorRegistry;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
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.HealthEndpointSettings;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.actuate.health.HealthIndicatorRegistryFactory;
|
||||
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
|
||||
import org.springframework.boot.actuate.health.HttpCodeStatusMapper;
|
||||
import org.springframework.boot.actuate.health.SimpleHttpCodeStatusMapper;
|
||||
import org.springframework.boot.actuate.health.SimpleStatusAggregator;
|
||||
import org.springframework.boot.actuate.health.StatusAggregator;
|
||||
import org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator;
|
||||
import org.springframework.boot.actuate.system.DiskSpaceHealthIndicator;
|
||||
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
|
||||
@@ -63,14 +70,18 @@ class HealthEndpointDocumentationTests extends MockMvcEndpointDocumentationTests
|
||||
|
||||
@Test
|
||||
void health() throws Exception {
|
||||
FieldDescriptor status = fieldWithPath("status").description("Overall status of the application.");
|
||||
FieldDescriptor components = fieldWithPath("details").description("The components that make up the health.");
|
||||
FieldDescriptor componentStatus = fieldWithPath("details.*.status")
|
||||
.description("Status of a specific part of the application.");
|
||||
FieldDescriptor componentDetails = subsectionWithPath("details.*.details")
|
||||
.description("Details of the health of a specific part of the application. "
|
||||
+ "Presence is controlled by `management.endpoint.health.show-details`.")
|
||||
.optional();
|
||||
FieldDescriptor nestedComponents = subsectionWithPath("details.*.details")
|
||||
.description("Nested components that make up the health.").optional();
|
||||
this.mockMvc.perform(get("/actuator/health")).andExpect(status().isOk()).andDo(document("health",
|
||||
responseFields(fieldWithPath("status").description("Overall status of the application."),
|
||||
fieldWithPath("details")
|
||||
.description("Details of the health of the application. Presence is controlled by "
|
||||
+ "`management.endpoint.health.show-details`)."),
|
||||
fieldWithPath("details.*.status").description("Status of a specific part of the application."),
|
||||
subsectionWithPath("details.*.details")
|
||||
.description("Details of the health of a specific part of the application."))));
|
||||
responseFields(status, components, componentStatus, componentDetails, nestedComponents)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -91,9 +102,10 @@ class HealthEndpointDocumentationTests extends MockMvcEndpointDocumentationTests
|
||||
static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
HealthEndpoint endpoint(Map<String, HealthIndicator> healthIndicators) {
|
||||
return new HealthEndpoint(new CompositeHealthIndicator(new OrderedHealthAggregator(),
|
||||
new HealthIndicatorRegistryFactory().createHealthIndicatorRegistry(healthIndicators)));
|
||||
HealthEndpoint healthEndpoint(Map<String, HealthContributor> healthContributors) {
|
||||
HealthContributorRegistry registry = new DefaultHealthContributorRegistry(healthContributors);
|
||||
HealthEndpointSettings settings = new TestHealthEndpointSettings();
|
||||
return new HealthEndpoint(registry, settings);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -107,11 +119,34 @@ class HealthEndpointDocumentationTests extends MockMvcEndpointDocumentationTests
|
||||
}
|
||||
|
||||
@Bean
|
||||
CompositeHealthIndicator brokerHealthIndicator() {
|
||||
CompositeHealthContributor brokerHealthContributor() {
|
||||
Map<String, HealthIndicator> indicators = new LinkedHashMap<>();
|
||||
indicators.put("us1", () -> Health.up().withDetail("version", "1.0.2").build());
|
||||
indicators.put("us2", () -> Health.up().withDetail("version", "1.0.4").build());
|
||||
return new CompositeHealthIndicator(new OrderedHealthAggregator(), indicators);
|
||||
return CompositeHealthContributor.fromMap(indicators);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class TestHealthEndpointSettings implements HealthEndpointSettings {
|
||||
|
||||
private final StatusAggregator statusAggregator = new SimpleStatusAggregator();
|
||||
|
||||
private final HttpCodeStatusMapper httpCodeStatusMapper = new SimpleHttpCodeStatusMapper();
|
||||
|
||||
@Override
|
||||
public boolean includeDetails(SecurityContext securityContext) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StatusAggregator getStatusAggregator() {
|
||||
return this.statusAggregator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpCodeStatusMapper getHttpCodeStatusMapper() {
|
||||
return this.httpCodeStatusMapper;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ package org.springframework.boot.actuate.autoconfigure.hazelcast;
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.hazelcast.HazelcastHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
@@ -38,7 +40,8 @@ class HazelcastHealthIndicatorAutoConfigurationIntegrationTests {
|
||||
|
||||
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(HazelcastHealthIndicatorAutoConfiguration.class,
|
||||
HazelcastAutoConfiguration.class, HealthIndicatorAutoConfiguration.class));
|
||||
HazelcastAutoConfiguration.class, HealthIndicatorAutoConfiguration.class,
|
||||
HealthContributorAutoConfiguration.class, HealthEndpointAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void hazelcastUp() {
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.springframework.boot.actuate.autoconfigure.hazelcast;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.hazelcast.HazelcastHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
|
||||
@@ -36,7 +38,8 @@ class HazelcastHealthIndicatorAutoConfigurationTests {
|
||||
|
||||
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(HazelcastAutoConfiguration.class,
|
||||
HazelcastHealthIndicatorAutoConfiguration.class, HealthIndicatorAutoConfiguration.class));
|
||||
HazelcastHealthIndicatorAutoConfiguration.class, HealthIndicatorAutoConfiguration.class,
|
||||
HealthContributorAutoConfiguration.class, HealthEndpointAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void runShouldCreateIndicator() {
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import io.lettuce.core.dynamic.support.ResolvableType;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Tests for {@link AbstractCompositeHealthContributorConfiguration}.
|
||||
*
|
||||
* @param <C> the contributor type
|
||||
* @param <I> the health indicator type
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
abstract class AbstractCompositeHealthContributorConfigurationTests<C, I extends C> {
|
||||
|
||||
private final Class<?> indicatorType;
|
||||
|
||||
AbstractCompositeHealthContributorConfigurationTests() {
|
||||
ResolvableType type = ResolvableType.forClass(AbstractCompositeHealthContributorConfigurationTests.class,
|
||||
getClass());
|
||||
this.indicatorType = type.resolveGeneric(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createContributorWhenBeansIsEmptyThrowsException() {
|
||||
Map<String, TestBean> beans = Collections.emptyMap();
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> newComposite().createContributor(beans))
|
||||
.withMessage("Beans must not be empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
void createContributorWhenBeansHasSingleElementCreatesIndicator() {
|
||||
Map<String, TestBean> beans = Collections.singletonMap("test", new TestBean());
|
||||
C contributor = newComposite().createContributor(beans);
|
||||
assertThat(contributor).isInstanceOf(this.indicatorType);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createContributorWhenBeansHasMultipleElementsCreatesComposite() {
|
||||
Map<String, TestBean> beans = new LinkedHashMap<>();
|
||||
beans.put("test1", new TestBean());
|
||||
beans.put("test2", new TestBean());
|
||||
C contributor = newComposite().createContributor(beans);
|
||||
assertThat(contributor).isNotInstanceOf(this.indicatorType);
|
||||
assertThat(ClassUtils.getShortName(contributor.getClass())).startsWith("Composite");
|
||||
}
|
||||
|
||||
protected abstract AbstractCompositeHealthContributorConfiguration<C, I, TestBean> newComposite();
|
||||
|
||||
static class TestBean {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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.security.Principal;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointProperties.ShowDetails;
|
||||
import org.springframework.boot.actuate.endpoint.SecurityContext;
|
||||
import org.springframework.boot.actuate.health.HttpCodeStatusMapper;
|
||||
import org.springframework.boot.actuate.health.StatusAggregator;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
|
||||
/**
|
||||
* Tests for {@link AutoConfiguredHealthEndpointSettings}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class AutoConfiguredHealthEndpointSettingsTests {
|
||||
|
||||
@Mock
|
||||
private StatusAggregator statusAggregator;
|
||||
|
||||
@Mock
|
||||
private HttpCodeStatusMapper httpCodeStatusMapper;
|
||||
|
||||
@Mock
|
||||
private SecurityContext securityContext;
|
||||
|
||||
@Mock
|
||||
private Principal principal;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
void includeDetailsWhenShowDetailsIsNeverReturnsFalse() {
|
||||
AutoConfiguredHealthEndpointSettings settings = new AutoConfiguredHealthEndpointSettings(this.statusAggregator,
|
||||
this.httpCodeStatusMapper, ShowDetails.NEVER, Collections.emptySet());
|
||||
assertThat(settings.includeDetails(SecurityContext.NONE)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void includeDetailsWhenShowDetailsIsAlwaysReturnsTrue() {
|
||||
AutoConfiguredHealthEndpointSettings settings = new AutoConfiguredHealthEndpointSettings(this.statusAggregator,
|
||||
this.httpCodeStatusMapper, ShowDetails.ALWAYS, Collections.emptySet());
|
||||
assertThat(settings.includeDetails(SecurityContext.NONE)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void includeDetailsWhenShowDetailsIsWhenAuthorizedAndPrincipalIsNullReturnsFalse() {
|
||||
AutoConfiguredHealthEndpointSettings settings = new AutoConfiguredHealthEndpointSettings(this.statusAggregator,
|
||||
this.httpCodeStatusMapper, ShowDetails.WHEN_AUTHORIZED, Collections.emptySet());
|
||||
given(this.securityContext.getPrincipal()).willReturn(null);
|
||||
assertThat(settings.includeDetails(this.securityContext)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void includeDetailsWhenShowDetailsIsWhenAuthorizedAndRolesAreEmptyReturnsTrue() {
|
||||
AutoConfiguredHealthEndpointSettings settings = new AutoConfiguredHealthEndpointSettings(this.statusAggregator,
|
||||
this.httpCodeStatusMapper, ShowDetails.WHEN_AUTHORIZED, Collections.emptySet());
|
||||
given(this.securityContext.getPrincipal()).willReturn(this.principal);
|
||||
assertThat(settings.includeDetails(this.securityContext)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void includeDetailsWhenShowDetailsIsWhenAuthorizedAndUseIsInRoleReturnsTrue() {
|
||||
AutoConfiguredHealthEndpointSettings settings = new AutoConfiguredHealthEndpointSettings(this.statusAggregator,
|
||||
this.httpCodeStatusMapper, ShowDetails.WHEN_AUTHORIZED, Arrays.asList("admin", "root", "bossmode"));
|
||||
given(this.securityContext.getPrincipal()).willReturn(this.principal);
|
||||
given(this.securityContext.isUserInRole("root")).willReturn(true);
|
||||
assertThat(settings.includeDetails(this.securityContext)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void includeDetailsWhenShowDetailsIsWhenAuthorizedAndUseIsNotInRoleReturnsFalse() {
|
||||
AutoConfiguredHealthEndpointSettings settings = new AutoConfiguredHealthEndpointSettings(this.statusAggregator,
|
||||
this.httpCodeStatusMapper, ShowDetails.WHEN_AUTHORIZED, Arrays.asList("admin", "rot", "bossmode"));
|
||||
given(this.securityContext.getPrincipal()).willReturn(this.principal);
|
||||
given(this.securityContext.isUserInRole("root")).willReturn(true);
|
||||
assertThat(settings.includeDetails(this.securityContext)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getStatusAggregatorReturnsStatusAggregator() {
|
||||
AutoConfiguredHealthEndpointSettings settings = new AutoConfiguredHealthEndpointSettings(this.statusAggregator,
|
||||
this.httpCodeStatusMapper, ShowDetails.ALWAYS, Collections.emptySet());
|
||||
assertThat(settings.getStatusAggregator()).isSameAs(this.statusAggregator);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getHttpCodeStatusMapperReturnsHttpCodeStatusMapper() {
|
||||
AutoConfiguredHealthEndpointSettings settings = new AutoConfiguredHealthEndpointSettings(this.statusAggregator,
|
||||
this.httpCodeStatusMapper, ShowDetails.ALWAYS, Collections.emptySet());
|
||||
assertThat(settings.getHttpCodeStatusMapper()).isSameAs(this.httpCodeStatusMapper);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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 org.springframework.boot.actuate.autoconfigure.health.CompositeHealthContributorConfigurationTests.TestHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.Health.Builder;
|
||||
import org.springframework.boot.actuate.health.HealthContributor;
|
||||
|
||||
/**
|
||||
* Tests for {@link CompositeHealthContributorConfiguration}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class CompositeHealthContributorConfigurationTests
|
||||
extends AbstractCompositeHealthContributorConfigurationTests<HealthContributor, TestHealthIndicator> {
|
||||
|
||||
@Override
|
||||
protected AbstractCompositeHealthContributorConfiguration<HealthContributor, TestHealthIndicator, TestBean> newComposite() {
|
||||
return new TestCompositeHealthContributorConfiguration();
|
||||
}
|
||||
|
||||
static class TestCompositeHealthContributorConfiguration
|
||||
extends CompositeHealthContributorConfiguration<TestHealthIndicator, TestBean> {
|
||||
|
||||
}
|
||||
|
||||
static class TestHealthIndicator extends AbstractHealthIndicator {
|
||||
|
||||
TestHealthIndicator(TestBean testBean) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doHealthCheck(Builder builder) throws Exception {
|
||||
builder.up();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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 reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.health.CompositeReactiveHealthContributorConfigurationTests.TestReactiveHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.AbstractReactiveHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.Health.Builder;
|
||||
import org.springframework.boot.actuate.health.ReactiveHealthContributor;
|
||||
|
||||
/**
|
||||
* Tests for {@link CompositeReactiveHealthContributorConfiguration}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class CompositeReactiveHealthContributorConfigurationTests extends
|
||||
AbstractCompositeHealthContributorConfigurationTests<ReactiveHealthContributor, TestReactiveHealthIndicator> {
|
||||
|
||||
@Override
|
||||
protected AbstractCompositeHealthContributorConfiguration<ReactiveHealthContributor, TestReactiveHealthIndicator, TestBean> newComposite() {
|
||||
return new TestCompositeReactiveHealthContributorConfiguration();
|
||||
}
|
||||
|
||||
static class TestCompositeReactiveHealthContributorConfiguration
|
||||
extends CompositeReactiveHealthContributorConfiguration<TestReactiveHealthIndicator, TestBean> {
|
||||
|
||||
}
|
||||
|
||||
static class TestReactiveHealthIndicator extends AbstractReactiveHealthIndicator {
|
||||
|
||||
TestReactiveHealthIndicator(TestBean testBean) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Mono<Health> doHealthCheck(Builder builder) {
|
||||
return Mono.just(builder.up().build());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.actuate.health.AbstractHealthAggregator;
|
||||
import org.springframework.boot.actuate.health.Status;
|
||||
import org.springframework.boot.actuate.health.StatusAggregator;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link HealthAggregatorStatusAggregatorAdapter}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
class HealthAggregatorStatusAggregatorAdapterTests {
|
||||
|
||||
@Test
|
||||
void getAggregateStatusDelegateToHealthAggregator() {
|
||||
StatusAggregator adapter = new HealthAggregatorStatusAggregatorAdapter(new TestHealthAggregator());
|
||||
Status status = adapter.getAggregateStatus(Status.UP, Status.DOWN);
|
||||
assertThat(status.getCode()).isEqualTo("called2");
|
||||
}
|
||||
|
||||
private static class TestHealthAggregator extends AbstractHealthAggregator {
|
||||
|
||||
@Override
|
||||
protected Status aggregateStatus(List<Status> candidates) {
|
||||
return new Status("called" + candidates.size());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,17 +16,11 @@
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.health;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.HealthAggregator;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
|
||||
import org.springframework.boot.actuate.health.Status;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -35,31 +29,31 @@ import org.springframework.context.annotation.Configuration;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link HealthIndicatorAutoConfiguration}.
|
||||
* Tests for {@link HealthContributorAutoConfiguration}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class HealthIndicatorAutoConfigurationTests {
|
||||
class HealthContributorAutoConfigurationTests {
|
||||
|
||||
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(HealthIndicatorAutoConfiguration.class));
|
||||
.withConfiguration(AutoConfigurations.of(HealthContributorAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void runWhenNoOtherIndicatorsShouldCreateDefaultApplicationHealthIndicator() {
|
||||
void runWhenNoOtherIndicatorsCreatesDefaultApplicationHealthIndicator() {
|
||||
this.contextRunner.run((context) -> assertThat(context).getBean(HealthIndicator.class)
|
||||
.isInstanceOf(ApplicationHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void runWhenHasDefinedIndicatorShouldNotCreateDefaultApplicationHealthIndicator() {
|
||||
void runWhenHasDefinedIndicatorDoesNotCreateDefaultApplicationHealthIndicator() {
|
||||
this.contextRunner.withUserConfiguration(CustomHealthIndicatorConfiguration.class)
|
||||
.run((context) -> assertThat(context).getBean(HealthIndicator.class)
|
||||
.isInstanceOf(CustomHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void runWhenHasDefaultsDisabledAndNoSingleIndicatorEnabledShouldCreateDefaultApplicationHealthIndicator() {
|
||||
void runWhenHasDefaultsDisabledAndNoSingleIndicatorEnabledCreatesDefaultApplicationHealthIndicator() {
|
||||
this.contextRunner.withUserConfiguration(CustomHealthIndicatorConfiguration.class)
|
||||
.withPropertyValues("management.health.defaults.enabled:false").run((context) -> assertThat(context)
|
||||
.getBean(HealthIndicator.class).isInstanceOf(ApplicationHealthIndicator.class));
|
||||
@@ -67,7 +61,7 @@ class HealthIndicatorAutoConfigurationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void runWhenHasDefaultsDisabledAndSingleIndicatorEnabledShouldCreateEnabledIndicator() {
|
||||
void runWhenHasDefaultsDisabledAndSingleIndicatorEnabledDoesNotCreateEnabledIndicator() {
|
||||
this.contextRunner.withUserConfiguration(CustomHealthIndicatorConfiguration.class)
|
||||
.withPropertyValues("management.health.defaults.enabled:false", "management.health.custom.enabled:true")
|
||||
.run((context) -> assertThat(context).getBean(HealthIndicator.class)
|
||||
@@ -75,31 +69,6 @@ class HealthIndicatorAutoConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void runShouldCreateOrderedHealthAggregator() {
|
||||
this.contextRunner.run((context) -> assertThat(context).getBean(HealthAggregator.class)
|
||||
.isInstanceOf(OrderedHealthAggregator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void runWhenHasCustomOrderPropertyShouldCreateOrderedHealthAggregator() {
|
||||
this.contextRunner.withPropertyValues("management.health.status.order:UP,DOWN").run((context) -> {
|
||||
OrderedHealthAggregator aggregator = context.getBean(OrderedHealthAggregator.class);
|
||||
Map<String, Health> healths = new LinkedHashMap<>();
|
||||
healths.put("foo", Health.up().build());
|
||||
healths.put("bar", Health.down().build());
|
||||
Health aggregate = aggregator.aggregate(healths);
|
||||
assertThat(aggregate.getStatus()).isEqualTo(Status.UP);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void runWhenHasCustomHealthAggregatorShouldNotCreateOrderedHealthAggregator() {
|
||||
this.contextRunner.withUserConfiguration(CustomHealthAggregatorConfiguration.class)
|
||||
.run((context) -> assertThat(context).getBean(HealthAggregator.class)
|
||||
.isNotInstanceOf(OrderedHealthAggregator.class));
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class CustomHealthIndicatorConfiguration {
|
||||
|
||||
@@ -120,14 +89,4 @@ class HealthIndicatorAutoConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class CustomHealthAggregatorConfiguration {
|
||||
|
||||
@Bean
|
||||
HealthAggregator healthAggregator() {
|
||||
return (healths) -> Health.down().build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,91 +16,387 @@
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.health;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.SecurityContext;
|
||||
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
|
||||
import org.springframework.boot.actuate.health.AbstractHealthAggregator;
|
||||
import org.springframework.boot.actuate.health.DefaultHealthContributorRegistry;
|
||||
import org.springframework.boot.actuate.health.DefaultReactiveHealthContributorRegistry;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.HealthAggregator;
|
||||
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.HealthEndpointSettings;
|
||||
import org.springframework.boot.actuate.health.HealthEndpointWebExtension;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.actuate.health.HealthStatusHttpMapper;
|
||||
import org.springframework.boot.actuate.health.HttpCodeStatusMapper;
|
||||
import org.springframework.boot.actuate.health.NamedContributor;
|
||||
import org.springframework.boot.actuate.health.ReactiveHealthContributorRegistry;
|
||||
import org.springframework.boot.actuate.health.ReactiveHealthEndpointWebExtension;
|
||||
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.Status;
|
||||
import org.springframework.boot.actuate.health.StatusAggregator;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
|
||||
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* Tests for {@link HealthEndpointAutoConfiguration}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
class HealthEndpointAutoConfigurationTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withConfiguration(
|
||||
AutoConfigurations.of(HealthIndicatorAutoConfiguration.class, HealthEndpointAutoConfiguration.class));
|
||||
private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
|
||||
.withUserConfiguration(HealthIndicatorsConfiguration.class).withConfiguration(AutoConfigurations
|
||||
.of(HealthContributorAutoConfiguration.class, HealthEndpointAutoConfiguration.class));
|
||||
|
||||
private ReactiveWebApplicationContextRunner reactiveContextRunner = new ReactiveWebApplicationContextRunner()
|
||||
.withUserConfiguration(HealthIndicatorsConfiguration.class).withConfiguration(AutoConfigurations
|
||||
.of(HealthContributorAutoConfiguration.class, HealthEndpointAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void healthEndpointShowDetailsDefault() {
|
||||
this.contextRunner.withBean(ReactiveHealthIndicator.class, this::reactiveHealthIndicator).run((context) -> {
|
||||
ReactiveHealthIndicator indicator = context.getBean("reactiveHealthIndicator",
|
||||
ReactiveHealthIndicator.class);
|
||||
verify(indicator, never()).health();
|
||||
Health health = context.getBean(HealthEndpoint.class).health();
|
||||
assertThat(health.getStatus()).isEqualTo(Status.UP);
|
||||
assertThat(health.getDetails()).isNotEmpty();
|
||||
verify(indicator, times(1)).health();
|
||||
void runWhenHealthEndpointIsDisabledDoesNotCreateBeans() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.health.enabled=false").run((context) -> {
|
||||
assertThat(context).doesNotHaveBean(StatusAggregator.class);
|
||||
assertThat(context).doesNotHaveBean(HttpCodeStatusMapper.class);
|
||||
assertThat(context).doesNotHaveBean(HealthEndpointSettings.class);
|
||||
assertThat(context).doesNotHaveBean(HealthContributorRegistry.class);
|
||||
assertThat(context).doesNotHaveBean(HealthEndpoint.class);
|
||||
assertThat(context).doesNotHaveBean(ReactiveHealthContributorRegistry.class);
|
||||
assertThat(context).doesNotHaveBean(HealthEndpointWebExtension.class);
|
||||
assertThat(context).doesNotHaveBean(ReactiveHealthEndpointWebExtension.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void healthEndpointAdaptReactiveHealthIndicator() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.health.show-details=always")
|
||||
.withBean(ReactiveHealthIndicator.class, this::reactiveHealthIndicator).run((context) -> {
|
||||
ReactiveHealthIndicator indicator = context.getBean("reactiveHealthIndicator",
|
||||
ReactiveHealthIndicator.class);
|
||||
verify(indicator, never()).health();
|
||||
Health health = context.getBean(HealthEndpoint.class).health();
|
||||
assertThat(health.getStatus()).isEqualTo(Status.UP);
|
||||
assertThat(health.getDetails()).containsOnlyKeys("reactive");
|
||||
verify(indicator, times(1)).health();
|
||||
void runWhenHasHealthAggregatorAdaptsToStatusAggregator() {
|
||||
this.contextRunner.withUserConfiguration(HealthAggregatorConfiguration.class).run((context) -> {
|
||||
StatusAggregator aggregator = context.getBean(StatusAggregator.class);
|
||||
assertThat(aggregator.getAggregateStatus(Status.UP, Status.DOWN)).isEqualTo(Status.UNKNOWN);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void runWhenHasHealthStatusHttpMapperAdaptsToHttpCodeStatusMapper() {
|
||||
this.contextRunner.withUserConfiguration(HealthStatusHttpMapperConfiguration.class).run((context) -> {
|
||||
HttpCodeStatusMapper mapper = context.getBean(HttpCodeStatusMapper.class);
|
||||
assertThat(mapper.getStatusCode(Status.UP)).isEqualTo(123);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void runCreatesStatusAggregatorFromProperties() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.health.status.order=up,down").run((context) -> {
|
||||
StatusAggregator aggregator = context.getBean(StatusAggregator.class);
|
||||
assertThat(aggregator.getAggregateStatus(Status.UP, Status.DOWN)).isEqualTo(Status.UP);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void runWhenUsingDeprecatedPropertyCreatesStatusAggregatorFromProperties() {
|
||||
this.contextRunner.withPropertyValues("management.health.status.order=up,down").run((context) -> {
|
||||
StatusAggregator aggregator = context.getBean(StatusAggregator.class);
|
||||
assertThat(aggregator.getAggregateStatus(Status.UP, Status.DOWN)).isEqualTo(Status.UP);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void runWhenHasStatusAggregatorBeanIgnoresProperties() {
|
||||
this.contextRunner.withUserConfiguration(StatusAggregatorConfiguration.class)
|
||||
.withPropertyValues("management.health.status.order=up,down").run((context) -> {
|
||||
StatusAggregator aggregator = context.getBean(StatusAggregator.class);
|
||||
assertThat(aggregator.getAggregateStatus(Status.UP, Status.DOWN)).isEqualTo(Status.UNKNOWN);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void healthEndpointMergeRegularAndReactive() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.health.show-details=always")
|
||||
.withBean("simpleHealthIndicator", HealthIndicator.class, this::simpleHealthIndicator)
|
||||
.withBean("reactiveHealthIndicator", ReactiveHealthIndicator.class, this::reactiveHealthIndicator)
|
||||
void runCreatesHttpCodeStatusMapperFromProperties() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.health.status.http-mapping.up=123")
|
||||
.run((context) -> {
|
||||
HealthIndicator indicator = context.getBean("simpleHealthIndicator", HealthIndicator.class);
|
||||
ReactiveHealthIndicator reactiveHealthIndicator = context.getBean("reactiveHealthIndicator",
|
||||
ReactiveHealthIndicator.class);
|
||||
verify(indicator, never()).health();
|
||||
verify(reactiveHealthIndicator, never()).health();
|
||||
Health health = context.getBean(HealthEndpoint.class).health();
|
||||
assertThat(health.getStatus()).isEqualTo(Status.UP);
|
||||
assertThat(health.getDetails()).containsOnlyKeys("simple", "reactive");
|
||||
verify(indicator, times(1)).health();
|
||||
verify(reactiveHealthIndicator, times(1)).health();
|
||||
HttpCodeStatusMapper mapper = context.getBean(HttpCodeStatusMapper.class);
|
||||
assertThat(mapper.getStatusCode(Status.UP)).isEqualTo(123);
|
||||
});
|
||||
}
|
||||
|
||||
private HealthIndicator simpleHealthIndicator() {
|
||||
HealthIndicator mock = mock(HealthIndicator.class);
|
||||
given(mock.health()).willReturn(Health.status(Status.UP).build());
|
||||
return mock;
|
||||
@Test
|
||||
void runUsingDeprecatedPropertyCreatesHttpCodeStatusMapperFromProperties() {
|
||||
this.contextRunner.withPropertyValues("management.health.status.http-mapping.up=123").run((context) -> {
|
||||
HttpCodeStatusMapper mapper = context.getBean(HttpCodeStatusMapper.class);
|
||||
assertThat(mapper.getStatusCode(Status.UP)).isEqualTo(123);
|
||||
});
|
||||
}
|
||||
|
||||
private ReactiveHealthIndicator reactiveHealthIndicator() {
|
||||
ReactiveHealthIndicator mock = mock(ReactiveHealthIndicator.class);
|
||||
given(mock.health()).willReturn(Mono.just(Health.status(Status.UP).build()));
|
||||
return mock;
|
||||
@Test
|
||||
void runWhenHasHttpCodeStatusMapperBeanIgnoresProperties() {
|
||||
this.contextRunner.withUserConfiguration(HttpCodeStatusMapperConfiguration.class)
|
||||
.withPropertyValues("management.health.status.http-mapping.up=123").run((context) -> {
|
||||
HttpCodeStatusMapper mapper = context.getBean(HttpCodeStatusMapper.class);
|
||||
assertThat(mapper.getStatusCode(Status.UP)).isEqualTo(456);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void runCreatesHealthEndpointSettings() {
|
||||
this.contextRunner.run((context) -> {
|
||||
HealthEndpointSettings settings = context.getBean(HealthEndpointSettings.class);
|
||||
assertThat(settings).isInstanceOf(AutoConfiguredHealthEndpointSettings.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void runWhenHasHealthEndpointSettingsBeanDoesNotCreateAdditionalHealthEndpointSettings() {
|
||||
this.contextRunner.withUserConfiguration(HealthEndpointSettingsConfiguration.class).run((context) -> {
|
||||
HealthEndpointSettings settings = context.getBean(HealthEndpointSettings.class);
|
||||
assertThat(Mockito.mockingDetails(settings).isMock()).isTrue();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void runCreatesHealthContributorRegistryContainingHealthBeans() {
|
||||
this.contextRunner.run((context) -> {
|
||||
HealthContributorRegistry registry = context.getBean(HealthContributorRegistry.class);
|
||||
Object[] names = registry.stream().map(NamedContributor::getName).toArray();
|
||||
assertThat(names).containsExactlyInAnyOrder("simple", "additional");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void runWhenHasHealthContributorRegistryBeanDoesNotCreateAdditionalRegistry() {
|
||||
this.contextRunner.withUserConfiguration(HealthContributorRegistryConfiguration.class).run((context) -> {
|
||||
HealthContributorRegistry registry = context.getBean(HealthContributorRegistry.class);
|
||||
Object[] names = registry.stream().map(NamedContributor::getName).toArray();
|
||||
assertThat(names).isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void runCreatesHealthEndpoint() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.health.show-details=always").run((context) -> {
|
||||
HealthEndpoint endpoint = context.getBean(HealthEndpoint.class);
|
||||
Health health = (Health) endpoint.healthForPath("simple");
|
||||
assertThat(health.getDetails()).containsEntry("counter", 42);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void runWhenHasHealthEndpointBeanDoesNotCreateAdditionalHealthEndpoint() {
|
||||
this.contextRunner.withUserConfiguration(HealthEndpointConfiguration.class).run((context) -> {
|
||||
HealthEndpoint endpoint = context.getBean(HealthEndpoint.class);
|
||||
assertThat(endpoint.health()).isNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void runCreatesReactiveHealthContributorRegistryContainingAdaptedBeans() {
|
||||
this.reactiveContextRunner.run((context) -> {
|
||||
ReactiveHealthContributorRegistry registry = context.getBean(ReactiveHealthContributorRegistry.class);
|
||||
Object[] names = registry.stream().map(NamedContributor::getName).toArray();
|
||||
assertThat(names).containsExactlyInAnyOrder("simple", "additional", "reactive");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void runWhenHasReactiveHealthContributorRegistryBeanDoesNotCreateAdditionalReactiveHealthContributorRegistry() {
|
||||
this.reactiveContextRunner.withUserConfiguration(ReactiveHealthContributorRegistryConfiguration.class)
|
||||
.run((context) -> {
|
||||
ReactiveHealthContributorRegistry registry = context
|
||||
.getBean(ReactiveHealthContributorRegistry.class);
|
||||
Object[] names = registry.stream().map(NamedContributor::getName).toArray();
|
||||
assertThat(names).isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void runCreatesHealthEndpointWebExtension() {
|
||||
this.contextRunner.run((context) -> {
|
||||
HealthEndpointWebExtension webExtension = context.getBean(HealthEndpointWebExtension.class);
|
||||
WebEndpointResponse<HealthComponent> response = webExtension.health(SecurityContext.NONE, true, "simple");
|
||||
Health health = (Health) response.getBody();
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
assertThat(health.getDetails()).containsEntry("counter", 42);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void runWhenHasHealthEndpointWebExtensionBeanDoesNotCreateExtraHealthEndpointWebExtension() {
|
||||
this.contextRunner.withUserConfiguration(HealthEndpointWebExtensionConfiguration.class).run((context) -> {
|
||||
HealthEndpointWebExtension webExtension = context.getBean(HealthEndpointWebExtension.class);
|
||||
WebEndpointResponse<HealthComponent> response = webExtension.health(SecurityContext.NONE, true, "simple");
|
||||
assertThat(response).isNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void runCreatesReactiveHealthEndpointWebExtension() {
|
||||
this.reactiveContextRunner.run((context) -> {
|
||||
ReactiveHealthEndpointWebExtension webExtension = context.getBean(ReactiveHealthEndpointWebExtension.class);
|
||||
Mono<WebEndpointResponse<? extends HealthComponent>> response = webExtension.health(SecurityContext.NONE,
|
||||
true, "simple");
|
||||
Health health = (Health) (response.block().getBody());
|
||||
assertThat(health.getDetails()).containsEntry("counter", 42);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void runWhenHasReactiveHealthEndpointWebExtensionBeanDoesNotCreateExtraReactiveHealthEndpointWebExtension() {
|
||||
this.reactiveContextRunner.withUserConfiguration(ReactiveHealthEndpointWebExtensionConfiguration.class)
|
||||
.run((context) -> {
|
||||
ReactiveHealthEndpointWebExtension webExtension = context
|
||||
.getBean(ReactiveHealthEndpointWebExtension.class);
|
||||
Mono<WebEndpointResponse<? extends HealthComponent>> response = webExtension
|
||||
.health(SecurityContext.NONE, true, "simple");
|
||||
assertThat(response).isNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class HealthIndicatorsConfiguration {
|
||||
|
||||
@Bean
|
||||
HealthIndicator simpleHealthIndicator() {
|
||||
return () -> Health.up().withDetail("counter", 42).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
HealthIndicator additionalHealthIndicator() {
|
||||
return () -> Health.up().build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
ReactiveHealthIndicator reactiveHealthIndicator() {
|
||||
return () -> Mono.just(Health.up().build());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class HealthAggregatorConfiguration {
|
||||
|
||||
@Bean
|
||||
HealthAggregator healthAggregator() {
|
||||
return new AbstractHealthAggregator() {
|
||||
|
||||
@Override
|
||||
protected Status aggregateStatus(List<Status> candidates) {
|
||||
return Status.UNKNOWN;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class HealthStatusHttpMapperConfiguration {
|
||||
|
||||
@Bean
|
||||
HealthStatusHttpMapper healthStatusHttpMapper() {
|
||||
return new HealthStatusHttpMapper() {
|
||||
|
||||
@Override
|
||||
public int mapStatus(Status status) {
|
||||
return 123;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class StatusAggregatorConfiguration {
|
||||
|
||||
@Bean
|
||||
StatusAggregator statusAggregator() {
|
||||
return (statuses) -> Status.UNKNOWN;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class HttpCodeStatusMapperConfiguration {
|
||||
|
||||
@Bean
|
||||
HttpCodeStatusMapper httpCodeStatusMapper() {
|
||||
return (status) -> 456;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class HealthEndpointSettingsConfiguration {
|
||||
|
||||
@Bean
|
||||
HealthEndpointSettings healthEndpointSettings() {
|
||||
return mock(HealthEndpointSettings.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class HealthContributorRegistryConfiguration {
|
||||
|
||||
@Bean
|
||||
HealthContributorRegistry healthContributorRegistry() {
|
||||
return new DefaultHealthContributorRegistry();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class HealthEndpointConfiguration {
|
||||
|
||||
@Bean
|
||||
HealthEndpoint healthEndpoint() {
|
||||
return mock(HealthEndpoint.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class ReactiveHealthContributorRegistryConfiguration {
|
||||
|
||||
@Bean
|
||||
ReactiveHealthContributorRegistry reactiveHealthContributorRegistry() {
|
||||
return new DefaultReactiveHealthContributorRegistry();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class HealthEndpointWebExtensionConfiguration {
|
||||
|
||||
@Bean
|
||||
HealthEndpointWebExtension healthEndpointWebExtension() {
|
||||
return mock(HealthEndpointWebExtension.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class ReactiveHealthEndpointWebExtensionConfiguration {
|
||||
|
||||
@Bean
|
||||
ReactiveHealthEndpointWebExtension reactiveHealthEndpointWebExtension() {
|
||||
return mock(ReactiveHealthEndpointWebExtension.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,349 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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.security.Principal;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.SecurityContext;
|
||||
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
|
||||
import org.springframework.boot.actuate.health.CompositeHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.HealthEndpointWebExtension;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.actuate.health.HealthWebEndpointResponseMapper;
|
||||
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.entry;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link HealthEndpointAutoConfiguration} in a servlet environment.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class HealthEndpointWebExtensionTests {
|
||||
|
||||
private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
|
||||
.withUserConfiguration(HealthIndicatorsConfiguration.class).withConfiguration(AutoConfigurations
|
||||
.of(HealthIndicatorAutoConfiguration.class, HealthEndpointAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void runShouldCreateExtensionBeans() {
|
||||
this.contextRunner.run((context) -> assertThat(context).hasSingleBean(HealthEndpointWebExtension.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void runWhenHealthEndpointIsDisabledShouldNotCreateExtensionBeans() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.health.enabled:false")
|
||||
.run((context) -> assertThat(context).doesNotHaveBean(HealthEndpointWebExtension.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void runWithCustomHealthMappingShouldMapStatusCode() {
|
||||
this.contextRunner.withPropertyValues("management.health.status.http-mapping.CUSTOM=500").run((context) -> {
|
||||
Object extension = context.getBean(HealthEndpointWebExtension.class);
|
||||
HealthWebEndpointResponseMapper responseMapper = (HealthWebEndpointResponseMapper) ReflectionTestUtils
|
||||
.getField(extension, "responseMapper");
|
||||
Class<SecurityContext> securityContext = SecurityContext.class;
|
||||
assertThat(responseMapper.map(Health.down().build(), mock(securityContext)).getStatus()).isEqualTo(503);
|
||||
assertThat(responseMapper.map(Health.status("OUT_OF_SERVICE").build(), mock(securityContext)).getStatus())
|
||||
.isEqualTo(503);
|
||||
assertThat(responseMapper.map(Health.status("CUSTOM").build(), mock(securityContext)).getStatus())
|
||||
.isEqualTo(500);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void unauthenticatedUsersAreNotShownDetailsByDefault() {
|
||||
this.contextRunner.run((context) -> {
|
||||
HealthEndpointWebExtension extension = context.getBean(HealthEndpointWebExtension.class);
|
||||
assertThat(extension.health(mock(SecurityContext.class)).getBody().getDetails()).isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void authenticatedUsersAreNotShownDetailsByDefault() {
|
||||
this.contextRunner.run((context) -> {
|
||||
HealthEndpointWebExtension extension = context.getBean(HealthEndpointWebExtension.class);
|
||||
SecurityContext securityContext = mock(SecurityContext.class);
|
||||
given(securityContext.getPrincipal()).willReturn(mock(Principal.class));
|
||||
assertThat(extension.health(securityContext).getBody().getDetails()).isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void authenticatedUsersWhenAuthorizedCanBeShownDetails() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.health.show-details=when-authorized")
|
||||
.run((context) -> {
|
||||
HealthEndpointWebExtension extension = context.getBean(HealthEndpointWebExtension.class);
|
||||
SecurityContext securityContext = mock(SecurityContext.class);
|
||||
given(securityContext.getPrincipal()).willReturn(mock(Principal.class));
|
||||
assertThat(extension.health(securityContext).getBody().getDetails()).isNotEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void unauthenticatedUsersCanBeShownDetails() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.health.show-details=always").run((context) -> {
|
||||
HealthEndpointWebExtension extension = context.getBean(HealthEndpointWebExtension.class);
|
||||
assertThat(extension.health(null).getBody().getDetails()).isNotEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void detailsCanBeHiddenFromAuthenticatedUsers() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.health.show-details=never").run((context) -> {
|
||||
HealthEndpointWebExtension extension = context.getBean(HealthEndpointWebExtension.class);
|
||||
assertThat(extension.health(mock(SecurityContext.class)).getBody().getDetails()).isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void detailsCanBeHiddenFromUnauthorizedUsers() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.health.show-details=when-authorized",
|
||||
"management.endpoint.health.roles=ACTUATOR").run((context) -> {
|
||||
HealthEndpointWebExtension extension = context.getBean(HealthEndpointWebExtension.class);
|
||||
SecurityContext securityContext = mock(SecurityContext.class);
|
||||
given(securityContext.getPrincipal()).willReturn(mock(Principal.class));
|
||||
given(securityContext.isUserInRole("ACTUATOR")).willReturn(false);
|
||||
assertThat(extension.health(securityContext).getBody().getDetails()).isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void detailsCanBeShownToAuthorizedUsers() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.health.show-details=when-authorized",
|
||||
"management.endpoint.health.roles=ACTUATOR").run((context) -> {
|
||||
HealthEndpointWebExtension extension = context.getBean(HealthEndpointWebExtension.class);
|
||||
SecurityContext securityContext = mock(SecurityContext.class);
|
||||
given(securityContext.getPrincipal()).willReturn(mock(Principal.class));
|
||||
given(securityContext.isUserInRole("ACTUATOR")).willReturn(true);
|
||||
assertThat(extension.health(securityContext).getBody().getDetails()).isNotEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void unauthenticatedUsersAreNotShownComponentByDefault() {
|
||||
this.contextRunner.run((context) -> {
|
||||
HealthEndpointWebExtension extension = context.getBean(HealthEndpointWebExtension.class);
|
||||
assertDetailsNotFound(extension.healthForComponent(mock(SecurityContext.class), "simple"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void authenticatedUsersAreNotShownComponentByDefault() {
|
||||
this.contextRunner.run((context) -> {
|
||||
HealthEndpointWebExtension extension = context.getBean(HealthEndpointWebExtension.class);
|
||||
SecurityContext securityContext = mock(SecurityContext.class);
|
||||
given(securityContext.getPrincipal()).willReturn(mock(Principal.class));
|
||||
assertDetailsNotFound(extension.healthForComponent(securityContext, "simple"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void authenticatedUsersWhenAuthorizedCanBeShownComponent() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.health.show-details=when-authorized")
|
||||
.run((context) -> {
|
||||
HealthEndpointWebExtension extension = context.getBean(HealthEndpointWebExtension.class);
|
||||
SecurityContext securityContext = mock(SecurityContext.class);
|
||||
given(securityContext.getPrincipal()).willReturn(mock(Principal.class));
|
||||
assertSimpleComponent(extension.healthForComponent(securityContext, "simple"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void unauthenticatedUsersCanBeShownComponent() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.health.show-details=always").run((context) -> {
|
||||
HealthEndpointWebExtension extension = context.getBean(HealthEndpointWebExtension.class);
|
||||
assertSimpleComponent(extension.healthForComponent(null, "simple"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void componentCanBeHiddenFromAuthenticatedUsers() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.health.show-details=never").run((context) -> {
|
||||
HealthEndpointWebExtension extension = context.getBean(HealthEndpointWebExtension.class);
|
||||
assertDetailsNotFound(extension.healthForComponent(mock(SecurityContext.class), "simple"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void componentCanBeHiddenFromUnauthorizedUsers() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.health.show-details=when-authorized",
|
||||
"management.endpoint.health.roles=ACTUATOR").run((context) -> {
|
||||
HealthEndpointWebExtension extension = context.getBean(HealthEndpointWebExtension.class);
|
||||
SecurityContext securityContext = mock(SecurityContext.class);
|
||||
given(securityContext.getPrincipal()).willReturn(mock(Principal.class));
|
||||
given(securityContext.isUserInRole("ACTUATOR")).willReturn(false);
|
||||
assertDetailsNotFound(extension.healthForComponent(securityContext, "simple"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void componentCanBeShownToAuthorizedUsers() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.health.show-details=when-authorized",
|
||||
"management.endpoint.health.roles=ACTUATOR").run((context) -> {
|
||||
HealthEndpointWebExtension extension = context.getBean(HealthEndpointWebExtension.class);
|
||||
SecurityContext securityContext = mock(SecurityContext.class);
|
||||
given(securityContext.getPrincipal()).willReturn(mock(Principal.class));
|
||||
given(securityContext.isUserInRole("ACTUATOR")).willReturn(true);
|
||||
assertSimpleComponent(extension.healthForComponent(securityContext, "simple"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void componentThatDoesNotExistMapTo404() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.health.show-details=always").run((context) -> {
|
||||
HealthEndpointWebExtension extension = context.getBean(HealthEndpointWebExtension.class);
|
||||
assertDetailsNotFound(extension.healthForComponent(null, "does-not-exist"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void unauthenticatedUsersAreNotShownComponentInstanceByDefault() {
|
||||
this.contextRunner.run((context) -> {
|
||||
HealthEndpointWebExtension extension = context.getBean(HealthEndpointWebExtension.class);
|
||||
assertDetailsNotFound(
|
||||
extension.healthForComponentInstance(mock(SecurityContext.class), "composite", "one"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void authenticatedUsersAreNotShownComponentInstanceByDefault() {
|
||||
this.contextRunner.run((context) -> {
|
||||
HealthEndpointWebExtension extension = context.getBean(HealthEndpointWebExtension.class);
|
||||
SecurityContext securityContext = mock(SecurityContext.class);
|
||||
given(securityContext.getPrincipal()).willReturn(mock(Principal.class));
|
||||
assertDetailsNotFound(extension.healthForComponentInstance(securityContext, "composite", "one"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void authenticatedUsersWhenAuthorizedCanBeShownComponentInstance() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.health.show-details=when-authorized")
|
||||
.run((context) -> {
|
||||
HealthEndpointWebExtension extension = context.getBean(HealthEndpointWebExtension.class);
|
||||
SecurityContext securityContext = mock(SecurityContext.class);
|
||||
given(securityContext.getPrincipal()).willReturn(mock(Principal.class));
|
||||
assertSimpleComponent(extension.healthForComponentInstance(securityContext, "composite", "one"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void unauthenticatedUsersCanBeShownComponentInstance() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.health.show-details=always").run((context) -> {
|
||||
HealthEndpointWebExtension extension = context.getBean(HealthEndpointWebExtension.class);
|
||||
assertSimpleComponent(extension.healthForComponentInstance(null, "composite", "one"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void componentInstanceCanBeHiddenFromAuthenticatedUsers() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.health.show-details=never").run((context) -> {
|
||||
HealthEndpointWebExtension extension = context.getBean(HealthEndpointWebExtension.class);
|
||||
assertDetailsNotFound(
|
||||
extension.healthForComponentInstance(mock(SecurityContext.class), "composite", "one"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void componentInstanceCanBeHiddenFromUnauthorizedUsers() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.health.show-details=when-authorized",
|
||||
"management.endpoint.health.roles=ACTUATOR").run((context) -> {
|
||||
HealthEndpointWebExtension extension = context.getBean(HealthEndpointWebExtension.class);
|
||||
SecurityContext securityContext = mock(SecurityContext.class);
|
||||
given(securityContext.getPrincipal()).willReturn(mock(Principal.class));
|
||||
given(securityContext.isUserInRole("ACTUATOR")).willReturn(false);
|
||||
assertDetailsNotFound(extension.healthForComponentInstance(securityContext, "composite", "one"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void componentInstanceCanBeShownToAuthorizedUsers() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.health.show-details=when-authorized",
|
||||
"management.endpoint.health.roles=ACTUATOR").run((context) -> {
|
||||
HealthEndpointWebExtension extension = context.getBean(HealthEndpointWebExtension.class);
|
||||
SecurityContext securityContext = mock(SecurityContext.class);
|
||||
given(securityContext.getPrincipal()).willReturn(mock(Principal.class));
|
||||
given(securityContext.isUserInRole("ACTUATOR")).willReturn(true);
|
||||
assertSimpleComponent(extension.healthForComponentInstance(securityContext, "composite", "one"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void componentInstanceThatDoesNotExistMapTo404() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.health.show-details=always").run((context) -> {
|
||||
HealthEndpointWebExtension extension = context.getBean(HealthEndpointWebExtension.class);
|
||||
assertDetailsNotFound(extension.healthForComponentInstance(null, "composite", "does-not-exist"));
|
||||
});
|
||||
}
|
||||
|
||||
private void assertDetailsNotFound(WebEndpointResponse<?> response) {
|
||||
assertThat(response.getStatus()).isEqualTo(HttpStatus.NOT_FOUND.value());
|
||||
assertThat(response.getBody()).isNull();
|
||||
}
|
||||
|
||||
private void assertSimpleComponent(WebEndpointResponse<Health> response) {
|
||||
assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());
|
||||
assertThat(response.getBody().getDetails()).containsOnly(entry("counter", 42));
|
||||
}
|
||||
|
||||
@Test
|
||||
void roleCanBeCustomized() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.health.show-details=when-authorized",
|
||||
"management.endpoint.health.roles=ADMIN").run((context) -> {
|
||||
HealthEndpointWebExtension extension = context.getBean(HealthEndpointWebExtension.class);
|
||||
SecurityContext securityContext = mock(SecurityContext.class);
|
||||
given(securityContext.getPrincipal()).willReturn(mock(Principal.class));
|
||||
given(securityContext.isUserInRole("ADMIN")).willReturn(true);
|
||||
assertThat(extension.health(securityContext).getBody().getDetails()).isNotEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class HealthIndicatorsConfiguration {
|
||||
|
||||
@Bean
|
||||
HealthIndicator simpleHealthIndicator() {
|
||||
return () -> Health.up().withDetail("counter", 42).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
HealthIndicator compositeHealthIndicator(HealthIndicator healthIndicator) {
|
||||
Map<String, HealthIndicator> nestedIndicators = new HashMap<>();
|
||||
nestedIndicators.put("one", healthIndicator);
|
||||
nestedIndicators.put("two", () -> Health.up().build());
|
||||
return new CompositeHealthIndicator(new OrderedHealthAggregator(), nestedIndicators);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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 org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.actuate.health.HealthStatusHttpMapper;
|
||||
import org.springframework.boot.actuate.health.HttpCodeStatusMapper;
|
||||
import org.springframework.boot.actuate.health.Status;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link HealthStatusHttpMapperHttpCodeStatusMapperAdapter}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
class HealthStatusHttpMapperHttpCodeStatusMapperAdapterTests {
|
||||
|
||||
@Test
|
||||
void getStatusCodeDelegatesToHealthStatusHttpMapper() {
|
||||
HttpCodeStatusMapper adapter = new HealthStatusHttpMapperHttpCodeStatusMapperAdapter(
|
||||
new TestHealthStatusHttpMapper());
|
||||
assertThat(adapter.getStatusCode(Status.UP)).isEqualTo(123);
|
||||
}
|
||||
|
||||
static class TestHealthStatusHttpMapper extends HealthStatusHttpMapper {
|
||||
|
||||
@Override
|
||||
public int mapStatus(Status status) {
|
||||
return 123;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link IncludeExcludeGroupMemberPredicate}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class IncludeExcludeGroupMemberPredicateTests {
|
||||
|
||||
@Test
|
||||
void testWhenEmptyIncludeAndExcludeRejectsAll() {
|
||||
Predicate<String> predicate = new IncludeExcludeGroupMemberPredicate(null, null);
|
||||
assertThat(predicate).rejects("a", "b", "c");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testWhenStarIncludeAndEmptyExcludeAcceptsAll() {
|
||||
Predicate<String> predicate = include("*").exclude();
|
||||
assertThat(predicate).accepts("a", "b", "c");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testWhenStarIncludeAndSpecificExcludeDoesNotAcceptExclude() {
|
||||
Predicate<String> predicate = include("*").exclude("c");
|
||||
assertThat(predicate).accepts("a", "b").rejects("c");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testWhenSpecificIncludeAcceptsOnlyIncluded() {
|
||||
Predicate<String> predicate = include("a", "b").exclude();
|
||||
assertThat(predicate).accepts("a", "b").rejects("c");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testWhenSpecifiedIncludeAndSpecifiedExcludeAcceptsAsExpected() {
|
||||
Predicate<String> predicate = include("a", "b", "c").exclude("c");
|
||||
assertThat(predicate).accepts("a", "b").rejects("c", "d");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testWhenSpecifiedIncludeAndStarExcludeRejectsAll() {
|
||||
Predicate<String> predicate = include("a", "b", "c").exclude("*");
|
||||
assertThat(predicate).rejects("a", "b", "c", "d");
|
||||
}
|
||||
|
||||
private Builder include(String... include) {
|
||||
return new Builder(include);
|
||||
}
|
||||
|
||||
private static class Builder {
|
||||
|
||||
private final String[] include;
|
||||
|
||||
Builder(String[] include) {
|
||||
this.include = include;
|
||||
}
|
||||
|
||||
Predicate<String> exclude(String... exclude) {
|
||||
return new IncludeExcludeGroupMemberPredicate(asSet(this.include), asSet(exclude));
|
||||
}
|
||||
|
||||
private Set<String> asSet(String[] names) {
|
||||
return (names != null) ? new LinkedHashSet<>(Arrays.asList(names)) : null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,221 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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.security.Principal;
|
||||
import java.time.Duration;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.SecurityContext;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.HealthEndpoint;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.actuate.health.HealthWebEndpointResponseMapper;
|
||||
import org.springframework.boot.actuate.health.ReactiveHealthEndpointWebExtension;
|
||||
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.ReactiveHealthIndicatorRegistry;
|
||||
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link HealthEndpointAutoConfiguration} in a reactive environment.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class ReactiveHealthEndpointWebExtensionTests {
|
||||
|
||||
private ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner()
|
||||
.withUserConfiguration(HealthIndicatorAutoConfiguration.class, HealthEndpointAutoConfiguration.class);
|
||||
|
||||
@Test
|
||||
void runShouldCreateExtensionBeans() {
|
||||
this.contextRunner
|
||||
.run((context) -> assertThat(context).hasSingleBean(ReactiveHealthEndpointWebExtension.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void runWhenHealthEndpointIsDisabledShouldNotCreateExtensionBeans() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.health.enabled:false")
|
||||
.run((context) -> assertThat(context).doesNotHaveBean(ReactiveHealthEndpointWebExtension.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void runWithCustomHealthMappingShouldMapStatusCode() {
|
||||
this.contextRunner.withPropertyValues("management.health.status.http-mapping.CUSTOM=500").run((context) -> {
|
||||
Object extension = context.getBean(ReactiveHealthEndpointWebExtension.class);
|
||||
HealthWebEndpointResponseMapper responseMapper = (HealthWebEndpointResponseMapper) ReflectionTestUtils
|
||||
.getField(extension, "responseMapper");
|
||||
Class<SecurityContext> securityContext = SecurityContext.class;
|
||||
assertThat(responseMapper.map(Health.down().build(), mock(securityContext)).getStatus()).isEqualTo(503);
|
||||
assertThat(responseMapper.map(Health.status("OUT_OF_SERVICE").build(), mock(securityContext)).getStatus())
|
||||
.isEqualTo(503);
|
||||
assertThat(responseMapper.map(Health.status("CUSTOM").build(), mock(securityContext)).getStatus())
|
||||
.isEqualTo(500);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void regularAndReactiveHealthIndicatorsMatch() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.health.show-details=always")
|
||||
.withUserConfiguration(HealthIndicatorsConfiguration.class).run((context) -> {
|
||||
HealthEndpoint endpoint = context.getBean(HealthEndpoint.class);
|
||||
ReactiveHealthEndpointWebExtension extension = context
|
||||
.getBean(ReactiveHealthEndpointWebExtension.class);
|
||||
Health endpointHealth = endpoint.health();
|
||||
SecurityContext securityContext = mock(SecurityContext.class);
|
||||
given(securityContext.getPrincipal()).willReturn(mock(Principal.class));
|
||||
Health extensionHealth = extension.health(securityContext).block(Duration.ofSeconds(30)).getBody();
|
||||
assertThat(endpointHealth.getDetails()).containsOnlyKeys("application", "first", "second");
|
||||
assertThat(extensionHealth.getDetails()).containsOnlyKeys("application", "first", "second");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void unauthenticatedUsersAreNotShownDetailsByDefault() {
|
||||
this.contextRunner.run((context) -> {
|
||||
ReactiveHealthEndpointWebExtension extension = context.getBean(ReactiveHealthEndpointWebExtension.class);
|
||||
assertThat(
|
||||
extension.health(mock(SecurityContext.class)).block(Duration.ofSeconds(30)).getBody().getDetails())
|
||||
.isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void authenticatedUsersAreNotShownDetailsByDefault() {
|
||||
this.contextRunner.run((context) -> {
|
||||
ReactiveHealthEndpointWebExtension extension = context.getBean(ReactiveHealthEndpointWebExtension.class);
|
||||
SecurityContext securityContext = mock(SecurityContext.class);
|
||||
given(securityContext.getPrincipal()).willReturn(mock(Principal.class));
|
||||
assertThat(extension.health(securityContext).block(Duration.ofSeconds(30)).getBody().getDetails())
|
||||
.isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void authenticatedUsersWhenAuthorizedCanBeShownDetails() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.health.show-details=when-authorized")
|
||||
.run((context) -> {
|
||||
ReactiveHealthEndpointWebExtension extension = context
|
||||
.getBean(ReactiveHealthEndpointWebExtension.class);
|
||||
SecurityContext securityContext = mock(SecurityContext.class);
|
||||
given(securityContext.getPrincipal()).willReturn(mock(Principal.class));
|
||||
assertThat(extension.health(securityContext).block(Duration.ofSeconds(30)).getBody().getDetails())
|
||||
.isNotEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void unauthenticatedUsersCanBeShownDetails() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.health.show-details=always").run((context) -> {
|
||||
ReactiveHealthEndpointWebExtension extension = context.getBean(ReactiveHealthEndpointWebExtension.class);
|
||||
assertThat(extension.health(null).block(Duration.ofSeconds(30)).getBody().getDetails()).isNotEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void detailsCanBeHiddenFromAuthenticatedUsers() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.health.show-details=never").run((context) -> {
|
||||
ReactiveHealthEndpointWebExtension extension = context.getBean(ReactiveHealthEndpointWebExtension.class);
|
||||
SecurityContext securityContext = mock(SecurityContext.class);
|
||||
assertThat(extension.health(securityContext).block(Duration.ofSeconds(30)).getBody().getDetails())
|
||||
.isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void detailsCanBeHiddenFromUnauthorizedUsers() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.health.show-details=when-authorized",
|
||||
"management.endpoint.health.roles=ACTUATOR").run((context) -> {
|
||||
ReactiveHealthEndpointWebExtension extension = context
|
||||
.getBean(ReactiveHealthEndpointWebExtension.class);
|
||||
SecurityContext securityContext = mock(SecurityContext.class);
|
||||
given(securityContext.getPrincipal()).willReturn(mock(Principal.class));
|
||||
given(securityContext.isUserInRole("ACTUATOR")).willReturn(false);
|
||||
assertThat(extension.health(securityContext).block(Duration.ofSeconds(30)).getBody().getDetails())
|
||||
.isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void detailsCanBeShownToAuthorizedUsers() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.health.show-details=when-authorized",
|
||||
"management.endpoint.health.roles=ACTUATOR").run((context) -> {
|
||||
ReactiveHealthEndpointWebExtension extension = context
|
||||
.getBean(ReactiveHealthEndpointWebExtension.class);
|
||||
SecurityContext securityContext = mock(SecurityContext.class);
|
||||
given(securityContext.getPrincipal()).willReturn(mock(Principal.class));
|
||||
given(securityContext.isUserInRole("ACTUATOR")).willReturn(true);
|
||||
assertThat(extension.health(securityContext).block(Duration.ofSeconds(30)).getBody().getDetails())
|
||||
.isNotEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void roleCanBeCustomized() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.health.show-details=when-authorized",
|
||||
"management.endpoint.health.roles=ADMIN").run((context) -> {
|
||||
ReactiveHealthEndpointWebExtension extension = context
|
||||
.getBean(ReactiveHealthEndpointWebExtension.class);
|
||||
SecurityContext securityContext = mock(SecurityContext.class);
|
||||
given(securityContext.getPrincipal()).willReturn(mock(Principal.class));
|
||||
given(securityContext.isUserInRole("ADMIN")).willReturn(true);
|
||||
assertThat(extension.health(securityContext).block(Duration.ofSeconds(30)).getBody().getDetails())
|
||||
.isNotEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void registryCanBeAltered() {
|
||||
this.contextRunner.withUserConfiguration(HealthIndicatorsConfiguration.class)
|
||||
.withPropertyValues("management.endpoint.health.show-details=always").run((context) -> {
|
||||
ReactiveHealthIndicatorRegistry registry = context.getBean(ReactiveHealthIndicatorRegistry.class);
|
||||
ReactiveHealthEndpointWebExtension extension = context
|
||||
.getBean(ReactiveHealthEndpointWebExtension.class);
|
||||
assertThat(extension.health(null).block(Duration.ofSeconds(30)).getBody().getDetails())
|
||||
.containsOnlyKeys("application", "first", "second");
|
||||
assertThat(registry.unregister("second")).isNotNull();
|
||||
assertThat(extension.health(null).block(Duration.ofSeconds(30)).getBody().getDetails())
|
||||
.containsKeys("application", "first");
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class HealthIndicatorsConfiguration {
|
||||
|
||||
@Bean
|
||||
HealthIndicator firstHealthIndicator() {
|
||||
return () -> Health.up().build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
ReactiveHealthIndicator secondHealthIndicator() {
|
||||
return () -> Mono.just(Health.up().build());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,6 +19,8 @@ package org.springframework.boot.actuate.autoconfigure.influx;
|
||||
import org.influxdb.InfluxDB;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
|
||||
import org.springframework.boot.actuate.influx.InfluxDbHealthIndicator;
|
||||
@@ -36,8 +38,10 @@ import static org.mockito.Mockito.mock;
|
||||
class InfluxDbHealthIndicatorAutoConfigurationTests {
|
||||
|
||||
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withBean(InfluxDB.class, () -> mock(InfluxDB.class)).withConfiguration(AutoConfigurations
|
||||
.of(InfluxDbHealthIndicatorAutoConfiguration.class, HealthIndicatorAutoConfiguration.class));
|
||||
.withBean(InfluxDB.class, () -> mock(InfluxDB.class))
|
||||
.withConfiguration(AutoConfigurations.of(InfluxDbHealthIndicatorAutoConfiguration.class,
|
||||
HealthIndicatorAutoConfiguration.class, HealthContributorAutoConfiguration.class,
|
||||
HealthEndpointAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void runShouldCreateIndicator() {
|
||||
|
||||
@@ -29,6 +29,8 @@ import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.actuate.audit.InMemoryAuditEventRepository;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.jmx.JmxEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.trace.http.HttpTraceAutoConfiguration;
|
||||
import org.springframework.boot.actuate.trace.http.InMemoryHttpTraceRepository;
|
||||
@@ -52,6 +54,7 @@ class JmxEndpointIntegrationTests {
|
||||
private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(JmxAutoConfiguration.class, EndpointAutoConfiguration.class,
|
||||
JmxEndpointAutoConfiguration.class, HealthIndicatorAutoConfiguration.class,
|
||||
HealthContributorAutoConfiguration.class, HealthEndpointAutoConfiguration.class,
|
||||
HttpTraceAutoConfiguration.class))
|
||||
.withUserConfiguration(HttpTraceRepositoryConfiguration.class, AuditEventRepositoryConfiguration.class)
|
||||
.withPropertyValues("spring.jmx.enabled=true")
|
||||
|
||||
@@ -29,6 +29,8 @@ import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.actuate.audit.InMemoryAuditEventRepository;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.trace.http.HttpTraceAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
|
||||
@@ -73,7 +75,8 @@ class WebMvcEndpointExposureIntegrationTests {
|
||||
EndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class,
|
||||
ManagementContextAutoConfiguration.class, ServletManagementContextAutoConfiguration.class,
|
||||
ManagementContextAutoConfiguration.class, ServletManagementContextAutoConfiguration.class,
|
||||
HttpTraceAutoConfiguration.class, HealthIndicatorAutoConfiguration.class))
|
||||
HttpTraceAutoConfiguration.class, HealthIndicatorAutoConfiguration.class,
|
||||
HealthContributorAutoConfiguration.class, HealthEndpointAutoConfiguration.class))
|
||||
.withConfiguration(AutoConfigurations.of(EndpointAutoConfigurationClasses.ALL))
|
||||
.withUserConfiguration(CustomMvcEndpoint.class, CustomServletEndpoint.class,
|
||||
HttpTraceRepositoryConfiguration.class, AuditEventRepositoryConfiguration.class)
|
||||
|
||||
@@ -20,6 +20,8 @@ import javax.sql.DataSource;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.CompositeHealthIndicator;
|
||||
@@ -49,7 +51,8 @@ class DataSourceHealthIndicatorAutoConfigurationTests {
|
||||
|
||||
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
|
||||
HealthIndicatorAutoConfiguration.class, DataSourceHealthIndicatorAutoConfiguration.class))
|
||||
HealthIndicatorAutoConfiguration.class, HealthContributorAutoConfiguration.class,
|
||||
HealthEndpointAutoConfiguration.class, DataSourceHealthIndicatorAutoConfiguration.class))
|
||||
.withPropertyValues("spring.datasource.initialization-mode=never");
|
||||
|
||||
@Test
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.springframework.boot.actuate.autoconfigure.jms;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
|
||||
import org.springframework.boot.actuate.jms.JmsHealthIndicator;
|
||||
@@ -37,7 +39,8 @@ class JmsHealthIndicatorAutoConfigurationTests {
|
||||
|
||||
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(ActiveMQAutoConfiguration.class,
|
||||
JmsHealthIndicatorAutoConfiguration.class, HealthIndicatorAutoConfiguration.class));
|
||||
JmsHealthIndicatorAutoConfiguration.class, HealthIndicatorAutoConfiguration.class,
|
||||
HealthContributorAutoConfiguration.class, HealthEndpointAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void runShouldCreateIndicator() {
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.springframework.boot.actuate.autoconfigure.ldap;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
|
||||
import org.springframework.boot.actuate.ldap.LdapHealthIndicator;
|
||||
@@ -37,8 +39,10 @@ import static org.mockito.Mockito.mock;
|
||||
class LdapHealthIndicatorAutoConfigurationTests {
|
||||
|
||||
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withBean(LdapOperations.class, () -> mock(LdapOperations.class)).withConfiguration(AutoConfigurations
|
||||
.of(LdapHealthIndicatorAutoConfiguration.class, HealthIndicatorAutoConfiguration.class));
|
||||
.withBean(LdapOperations.class, () -> mock(LdapOperations.class))
|
||||
.withConfiguration(AutoConfigurations.of(LdapHealthIndicatorAutoConfiguration.class,
|
||||
HealthIndicatorAutoConfiguration.class, HealthContributorAutoConfiguration.class,
|
||||
HealthEndpointAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void runShouldCreateIndicator() {
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.springframework.boot.actuate.autoconfigure.mail;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
|
||||
import org.springframework.boot.actuate.mail.MailHealthIndicator;
|
||||
@@ -36,7 +38,8 @@ class MailHealthIndicatorAutoConfigurationTests {
|
||||
|
||||
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(MailSenderAutoConfiguration.class,
|
||||
MailHealthIndicatorAutoConfiguration.class, HealthIndicatorAutoConfiguration.class))
|
||||
MailHealthIndicatorAutoConfiguration.class, HealthIndicatorAutoConfiguration.class,
|
||||
HealthContributorAutoConfiguration.class, HealthEndpointAutoConfiguration.class))
|
||||
.withPropertyValues("spring.mail.host:smtp.example.com");
|
||||
|
||||
@Test
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.springframework.boot.actuate.autoconfigure.mongo;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
|
||||
import org.springframework.boot.actuate.mongo.MongoHealthIndicator;
|
||||
@@ -37,7 +39,8 @@ class MongoHealthIndicatorAutoConfigurationTests {
|
||||
|
||||
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(MongoAutoConfiguration.class, MongoDataAutoConfiguration.class,
|
||||
MongoHealthIndicatorAutoConfiguration.class, HealthIndicatorAutoConfiguration.class));
|
||||
MongoHealthIndicatorAutoConfiguration.class, HealthIndicatorAutoConfiguration.class,
|
||||
HealthContributorAutoConfiguration.class, HealthEndpointAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void runShouldCreateIndicator() {
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.springframework.boot.actuate.autoconfigure.mongo;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
|
||||
import org.springframework.boot.actuate.mongo.MongoHealthIndicator;
|
||||
@@ -41,7 +43,8 @@ class MongoReactiveHealthIndicatorAutoConfigurationTests {
|
||||
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(MongoAutoConfiguration.class, MongoDataAutoConfiguration.class,
|
||||
MongoReactiveAutoConfiguration.class, MongoReactiveDataAutoConfiguration.class,
|
||||
MongoReactiveHealthIndicatorAutoConfiguration.class, HealthIndicatorAutoConfiguration.class));
|
||||
MongoReactiveHealthIndicatorAutoConfiguration.class, HealthIndicatorAutoConfiguration.class,
|
||||
HealthContributorAutoConfiguration.class, HealthEndpointAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void runShouldCreateIndicator() {
|
||||
|
||||
@@ -20,6 +20,8 @@ import org.junit.jupiter.api.Test;
|
||||
import org.neo4j.ogm.session.Session;
|
||||
import org.neo4j.ogm.session.SessionFactory;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
@@ -42,8 +44,10 @@ import static org.mockito.Mockito.mock;
|
||||
class Neo4jHealthIndicatorAutoConfigurationTests {
|
||||
|
||||
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withUserConfiguration(Neo4jConfiguration.class).withConfiguration(AutoConfigurations
|
||||
.of(Neo4jHealthIndicatorAutoConfiguration.class, HealthIndicatorAutoConfiguration.class));
|
||||
.withUserConfiguration(Neo4jConfiguration.class)
|
||||
.withConfiguration(AutoConfigurations.of(Neo4jHealthIndicatorAutoConfiguration.class,
|
||||
HealthIndicatorAutoConfiguration.class, HealthContributorAutoConfiguration.class,
|
||||
HealthEndpointAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void runShouldCreateIndicator() {
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.springframework.boot.actuate.autoconfigure.redis;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
|
||||
import org.springframework.boot.actuate.redis.RedisHealthIndicator;
|
||||
@@ -39,7 +41,8 @@ class RedisHealthIndicatorAutoConfigurationTests {
|
||||
|
||||
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class,
|
||||
RedisHealthIndicatorAutoConfiguration.class, HealthIndicatorAutoConfiguration.class));
|
||||
RedisHealthIndicatorAutoConfiguration.class, HealthIndicatorAutoConfiguration.class,
|
||||
HealthContributorAutoConfiguration.class, HealthEndpointAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void runShouldCreateIndicator() {
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.springframework.boot.actuate.autoconfigure.redis;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
|
||||
import org.springframework.boot.actuate.redis.RedisHealthIndicator;
|
||||
@@ -37,7 +39,8 @@ class RedisReactiveHealthIndicatorAutoConfigurationTests {
|
||||
|
||||
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class,
|
||||
RedisReactiveHealthIndicatorAutoConfiguration.class, HealthIndicatorAutoConfiguration.class));
|
||||
RedisReactiveHealthIndicatorAutoConfiguration.class, HealthIndicatorAutoConfiguration.class,
|
||||
HealthContributorAutoConfiguration.class, HealthEndpointAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void runShouldCreateIndicator() {
|
||||
|
||||
@@ -28,8 +28,8 @@ import org.springframework.beans.BeansException;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.env.EnvironmentEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.info.InfoEndpointAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration;
|
||||
@@ -66,12 +66,12 @@ import static org.mockito.Mockito.mock;
|
||||
class ReactiveManagementWebSecurityAutoConfigurationTests {
|
||||
|
||||
private ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner()
|
||||
.withConfiguration(
|
||||
AutoConfigurations.of(HealthIndicatorAutoConfiguration.class, HealthEndpointAutoConfiguration.class,
|
||||
InfoEndpointAutoConfiguration.class, EnvironmentEndpointAutoConfiguration.class,
|
||||
EndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class,
|
||||
ReactiveSecurityAutoConfiguration.class, ReactiveUserDetailsServiceAutoConfiguration.class,
|
||||
ReactiveManagementWebSecurityAutoConfiguration.class));
|
||||
.withConfiguration(AutoConfigurations.of(HealthContributorAutoConfiguration.class,
|
||||
HealthEndpointAutoConfiguration.class, InfoEndpointAutoConfiguration.class,
|
||||
EnvironmentEndpointAutoConfiguration.class, EndpointAutoConfiguration.class,
|
||||
WebEndpointAutoConfiguration.class, ReactiveSecurityAutoConfiguration.class,
|
||||
ReactiveUserDetailsServiceAutoConfiguration.class,
|
||||
ReactiveManagementWebSecurityAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void permitAllForHealth() {
|
||||
|
||||
@@ -23,8 +23,8 @@ import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.env.EnvironmentEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.info.InfoEndpointAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration;
|
||||
@@ -53,7 +53,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
class ManagementWebSecurityAutoConfigurationTests {
|
||||
|
||||
private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner().withConfiguration(
|
||||
AutoConfigurations.of(HealthIndicatorAutoConfiguration.class, HealthEndpointAutoConfiguration.class,
|
||||
AutoConfigurations.of(HealthContributorAutoConfiguration.class, HealthEndpointAutoConfiguration.class,
|
||||
InfoEndpointAutoConfiguration.class, EnvironmentEndpointAutoConfiguration.class,
|
||||
EndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class,
|
||||
SecurityAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class));
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.springframework.boot.actuate.autoconfigure.solr;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
|
||||
import org.springframework.boot.actuate.solr.SolrHealthIndicator;
|
||||
@@ -36,7 +38,8 @@ class SolrHealthIndicatorAutoConfigurationTests {
|
||||
|
||||
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(SolrAutoConfiguration.class,
|
||||
SolrHealthIndicatorAutoConfiguration.class, HealthIndicatorAutoConfiguration.class));
|
||||
SolrHealthIndicatorAutoConfiguration.class, HealthIndicatorAutoConfiguration.class,
|
||||
HealthContributorAutoConfiguration.class, HealthEndpointAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void runShouldCreateIndicator() {
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.springframework.boot.actuate.autoconfigure.system;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
|
||||
import org.springframework.boot.actuate.system.DiskSpaceHealthIndicator;
|
||||
@@ -36,7 +38,8 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
class DiskSpaceHealthIndicatorAutoConfigurationTests {
|
||||
|
||||
private ApplicationContextRunner contextRunner = new ApplicationContextRunner().withConfiguration(AutoConfigurations
|
||||
.of(DiskSpaceHealthIndicatorAutoConfiguration.class, HealthIndicatorAutoConfiguration.class));
|
||||
.of(DiskSpaceHealthIndicatorAutoConfiguration.class, HealthIndicatorAutoConfiguration.class,
|
||||
HealthContributorAutoConfiguration.class, HealthEndpointAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void runShouldCreateIndicator() {
|
||||
|
||||
@@ -28,7 +28,9 @@ import java.util.stream.Collectors;
|
||||
* @author Christian Dupuis
|
||||
* @author Vedran Pavic
|
||||
* @since 1.1.0
|
||||
* @deprecated since 2.2.0 as {@link HealthAggregator} has been deprecated
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class AbstractHealthAggregator implements HealthAggregator {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link HealthComponent} that is composed of other {@link HealthComponent} instances.
|
||||
* Used to provide a unified view of related components. For example, a database health
|
||||
* indicator may be a composite containing the {@link Health} of each datasource
|
||||
* connection.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 2.2.0
|
||||
*/
|
||||
public class CompositeHealth extends HealthComponent {
|
||||
|
||||
private Status status;
|
||||
|
||||
private Map<String, HealthComponent> details;
|
||||
|
||||
CompositeHealth(Status status, Map<String, HealthComponent> details) {
|
||||
Assert.notNull(status, "Status must not be null");
|
||||
this.status = status;
|
||||
this.details = (details != null) ? new TreeMap<>(details) : details;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Status getStatus() {
|
||||
return this.status;
|
||||
}
|
||||
|
||||
@JsonInclude(Include.NON_EMPTY)
|
||||
public Map<String, HealthComponent> getDetails() {
|
||||
return this.details;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* A {@link HealthContributor} that is composed of other {@link HealthContributor}
|
||||
* instances.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 2.2.0
|
||||
* @see CompositeHealth
|
||||
* @see CompositeReactiveHealthContributor
|
||||
*/
|
||||
public interface CompositeHealthContributor extends HealthContributor, NamedContributors<HealthContributor> {
|
||||
|
||||
/**
|
||||
* Factory method that will create a {@link CompositeHealthContributor} from the
|
||||
* specified map.
|
||||
* @param map the source map
|
||||
* @return a composite health contributor instance
|
||||
*/
|
||||
static CompositeHealthContributor fromMap(Map<String, ? extends HealthContributor> map) {
|
||||
return fromMap(map, Function.identity());
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method that will create a {@link CompositeHealthContributor} from the
|
||||
* specified map.
|
||||
* @param <V> the value type
|
||||
* @param map the source map
|
||||
* @param valueAdapter function used to adapt the map value
|
||||
* @return a composite health contributor instance
|
||||
*/
|
||||
static <V> CompositeHealthContributor fromMap(Map<String, V> map,
|
||||
Function<V, ? extends HealthContributor> valueAdapter) {
|
||||
return new CompositeHealthContributorMapAdapter<>(map, valueAdapter);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* {@link CompositeHealthContributor} backed by a map with values adapted as necessary.
|
||||
*
|
||||
* @param <V> the value type
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class CompositeHealthContributorMapAdapter<V> extends NamedContributorsMapAdapter<V, HealthContributor>
|
||||
implements CompositeHealthContributor {
|
||||
|
||||
CompositeHealthContributorMapAdapter(Map<String, V> map, Function<V, ? extends HealthContributor> valueAdapter) {
|
||||
super(map, valueAdapter);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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.Iterator;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Adapts a {@link CompositeHealthContributor} to a
|
||||
* {@link CompositeReactiveHealthContributor} so that it can be safely invoked in a
|
||||
* reactive environment.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @see ReactiveHealthContributor#adapt(HealthContributor)
|
||||
*/
|
||||
class CompositeHealthContributorReactiveAdapter implements CompositeReactiveHealthContributor {
|
||||
|
||||
private final CompositeHealthContributor delegate;
|
||||
|
||||
CompositeHealthContributorReactiveAdapter(CompositeHealthContributor delegate) {
|
||||
Assert.notNull(delegate, "Delegate must not be null");
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<NamedContributor<ReactiveHealthContributor>> iterator() {
|
||||
Iterator<NamedContributor<HealthContributor>> iterator = this.delegate.iterator();
|
||||
return new Iterator<NamedContributor<ReactiveHealthContributor>>() {
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return iterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NamedContributor<ReactiveHealthContributor> next() {
|
||||
NamedContributor<HealthContributor> namedContributor = iterator.next();
|
||||
return NamedContributor.of(namedContributor.getName(),
|
||||
ReactiveHealthContributor.adapt(namedContributor.getContributor()));
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactiveHealthContributor getContributor(String name) {
|
||||
HealthContributor contributor = this.delegate.getContributor(name);
|
||||
return (contributor != null) ? ReactiveHealthContributor.adapt(contributor) : null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,7 +26,9 @@ import java.util.Map;
|
||||
* @author Phillip Webb
|
||||
* @author Christian Dupuis
|
||||
* @since 1.1.0
|
||||
* @deprecated since 2.2.0 in favor of a {@link CompositeHealthContributor}
|
||||
*/
|
||||
@Deprecated
|
||||
public class CompositeHealthIndicator implements HealthIndicator {
|
||||
|
||||
private final HealthIndicatorRegistry registry;
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* A {@link ReactiveHealthContributor} that is composed of other
|
||||
* {@link ReactiveHealthContributor} instances.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 2.2.0
|
||||
* @see CompositeHealth
|
||||
* @see CompositeHealthContributor
|
||||
*/
|
||||
public interface CompositeReactiveHealthContributor
|
||||
extends ReactiveHealthContributor, NamedContributors<ReactiveHealthContributor> {
|
||||
|
||||
/**
|
||||
* Factory method that will create a {@link CompositeReactiveHealthContributor} from
|
||||
* the specified map.
|
||||
* @param map the source map
|
||||
* @return a composite health contributor instance
|
||||
*/
|
||||
static CompositeReactiveHealthContributor fromMap(Map<String, ? extends ReactiveHealthContributor> map) {
|
||||
return fromMap(map, Function.identity());
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method that will create a {@link CompositeReactiveHealthContributor} from
|
||||
* the specified map.
|
||||
* @param <V> the value type
|
||||
* @param map the source map
|
||||
* @param valueAdapter function used to adapt the map value
|
||||
* @return a composite health contributor instance
|
||||
*/
|
||||
static <V> CompositeReactiveHealthContributor fromMap(Map<String, V> map,
|
||||
Function<V, ? extends ReactiveHealthContributor> valueAdapter) {
|
||||
return new CompositeReactiveHealthContributorMapAdapter<>(map, valueAdapter);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* {@link CompositeReactiveHealthContributor} backed by a map with values adapted as
|
||||
* necessary.
|
||||
*
|
||||
* @param <V> the value type
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class CompositeReactiveHealthContributorMapAdapter<V> extends NamedContributorsMapAdapter<V, ReactiveHealthContributor>
|
||||
implements CompositeReactiveHealthContributor {
|
||||
|
||||
CompositeReactiveHealthContributorMapAdapter(Map<String, V> map,
|
||||
Function<V, ? extends ReactiveHealthContributor> valueAdapter) {
|
||||
super(map, valueAdapter);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -30,7 +30,9 @@ import reactor.util.function.Tuple2;
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.0
|
||||
* @deprecated since 2.2.0 in favor of a {@link CompositeReactiveHealthContributor}
|
||||
*/
|
||||
@Deprecated
|
||||
public class CompositeReactiveHealthIndicator implements ReactiveHealthIndicator {
|
||||
|
||||
private final ReactiveHealthIndicatorRegistry registry;
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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;
|
||||
|
||||
/**
|
||||
* A mutable registry of health endpoint contributors (either {@link HealthContributor} or
|
||||
* {@link ReactiveHealthContributor}).
|
||||
*
|
||||
* @param <C> the contributor type
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @author Vedran Pavic
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.2.0
|
||||
* @see NamedContributors
|
||||
*/
|
||||
public interface ContributorRegistry<C> extends NamedContributors<C> {
|
||||
|
||||
/**
|
||||
* Register a contributor with the given {@code name}.
|
||||
* @param name the name of the contributor
|
||||
* @param contributor the contributor to register
|
||||
* @throws IllegalStateException if the contributor cannot be registered with the
|
||||
* given {@code name}.
|
||||
*/
|
||||
void registerContributor(String name, C contributor);
|
||||
|
||||
/**
|
||||
* Unregister a previously registered contributor.
|
||||
* @param name the name of the contributor to unregister
|
||||
* @return the unregistered indicator, or {@code null} if no indicator was found in
|
||||
* the registry for the given {@code name}.
|
||||
*/
|
||||
C unregisterContributor(String name);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Default {@link ContributorRegistry} implementation.
|
||||
*
|
||||
* @param <C> the health contributor type
|
||||
* @author Phillip Webb
|
||||
* @see DefaultHealthContributorRegistry
|
||||
* @see DefaultReactiveHealthContributorRegistry
|
||||
*/
|
||||
class DefaultContributorRegistry<C> implements ContributorRegistry<C> {
|
||||
|
||||
private final Function<String, String> nameFactory;
|
||||
|
||||
private final Object monitor = new Object();
|
||||
|
||||
private volatile Map<String, C> contributors;
|
||||
|
||||
DefaultContributorRegistry() {
|
||||
this(Collections.emptyMap());
|
||||
}
|
||||
|
||||
DefaultContributorRegistry(Map<String, C> contributors) {
|
||||
this(contributors, HealthContributorNameFactory.INSTANCE);
|
||||
}
|
||||
|
||||
DefaultContributorRegistry(Map<String, C> contributors, Function<String, String> nameFactory) {
|
||||
Assert.notNull(contributors, "Contributors must not be null");
|
||||
Assert.notNull(nameFactory, "NameFactory must not be null");
|
||||
this.nameFactory = nameFactory;
|
||||
Map<String, C> namedContributors = new LinkedHashMap<>();
|
||||
contributors.forEach((name, contributor) -> namedContributors.put(nameFactory.apply(name), contributor));
|
||||
this.contributors = Collections.unmodifiableMap(namedContributors);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerContributor(String name, C contributor) {
|
||||
Assert.notNull(name, "Name must not be null");
|
||||
Assert.notNull(contributor, "Contributor must not be null");
|
||||
String adaptedName = this.nameFactory.apply(name);
|
||||
synchronized (this.monitor) {
|
||||
Assert.state(!this.contributors.containsKey(adaptedName),
|
||||
() -> "A contributor named \"" + adaptedName + "\" has already been registered");
|
||||
Map<String, C> contributors = new LinkedHashMap<>(this.contributors);
|
||||
contributors.put(adaptedName, contributor);
|
||||
this.contributors = Collections.unmodifiableMap(contributors);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public C unregisterContributor(String name) {
|
||||
Assert.notNull(name, "Name must not be null");
|
||||
String adaptedName = this.nameFactory.apply(name);
|
||||
synchronized (this.monitor) {
|
||||
C unregistered = this.contributors.get(adaptedName);
|
||||
if (unregistered != null) {
|
||||
Map<String, C> contributors = new LinkedHashMap<>(this.contributors);
|
||||
contributors.remove(adaptedName);
|
||||
this.contributors = Collections.unmodifiableMap(contributors);
|
||||
}
|
||||
return unregistered;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public C getContributor(String name) {
|
||||
return this.contributors.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<NamedContributor<C>> iterator() {
|
||||
Iterator<Map.Entry<String, C>> iterator = this.contributors.entrySet().iterator();
|
||||
return new Iterator<NamedContributor<C>>() {
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return iterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NamedContributor<C> next() {
|
||||
Entry<String, C> entry = iterator.next();
|
||||
return NamedContributor.of(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Default {@link HealthContributorRegistry} implementation.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 2.2.0
|
||||
*/
|
||||
public class DefaultHealthContributorRegistry extends DefaultContributorRegistry<HealthContributor>
|
||||
implements HealthContributorRegistry {
|
||||
|
||||
public DefaultHealthContributorRegistry() {
|
||||
}
|
||||
|
||||
public DefaultHealthContributorRegistry(Map<String, HealthContributor> contributors) {
|
||||
super(contributors);
|
||||
}
|
||||
|
||||
public DefaultHealthContributorRegistry(Map<String, HealthContributor> contributors,
|
||||
Function<String, String> nameFactory) {
|
||||
super(contributors, nameFactory);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,7 +28,9 @@ import org.springframework.util.Assert;
|
||||
* @author Vedran Pavic
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.1.0
|
||||
* @deprecated since 2.2.0 in favor of {@link DefaultContributorRegistry}
|
||||
*/
|
||||
@Deprecated
|
||||
public class DefaultHealthIndicatorRegistry implements HealthIndicatorRegistry {
|
||||
|
||||
private final Object monitor = new Object();
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Default {@link ReactiveHealthContributorRegistry} implementation.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class DefaultReactiveHealthContributorRegistry extends DefaultContributorRegistry<ReactiveHealthContributor>
|
||||
implements ReactiveHealthContributorRegistry {
|
||||
|
||||
public DefaultReactiveHealthContributorRegistry() {
|
||||
}
|
||||
|
||||
public DefaultReactiveHealthContributorRegistry(Map<String, ReactiveHealthContributor> contributors) {
|
||||
super(contributors);
|
||||
}
|
||||
|
||||
public DefaultReactiveHealthContributorRegistry(Map<String, ReactiveHealthContributor> contributors,
|
||||
Function<String, String> nameFactory) {
|
||||
super(contributors, nameFactory);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,7 +28,9 @@ import org.springframework.util.Assert;
|
||||
* @author Vedran Pavic
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.1.0
|
||||
* @deprecated since 2.2.0 in favor of {@link DefaultContributorRegistry}
|
||||
*/
|
||||
@Deprecated
|
||||
public class DefaultReactiveHealthIndicatorRegistry implements ReactiveHealthIndicatorRegistry {
|
||||
|
||||
private final Object monitor = new Object();
|
||||
|
||||
@@ -22,15 +22,13 @@ import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
import com.fasterxml.jackson.annotation.JsonUnwrapped;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Carries information about the health of a component or subsystem.
|
||||
* <p>
|
||||
* {@link Health} contains a {@link Status} to express the state of a component or
|
||||
* subsystem and some additional details to carry some contextual information.
|
||||
* Carries information about the health of a component or subsystem. Extends
|
||||
* {@link HealthComponent} so that additional contextual details about the system can be
|
||||
* returned along with the {@link Status}.
|
||||
* <p>
|
||||
* {@link Health} instances can be created by using {@link Builder}'s fluent API. Typical
|
||||
* usage in a {@link HealthIndicator} would be:
|
||||
@@ -38,10 +36,10 @@ import org.springframework.util.Assert;
|
||||
* <pre class="code">
|
||||
* try {
|
||||
* // do some test to determine state of component
|
||||
* return new Health.Builder().up().withDetail("version", "1.1.2").build();
|
||||
* return Health.up().withDetail("version", "1.1.2").build();
|
||||
* }
|
||||
* catch (Exception ex) {
|
||||
* return new Health.Builder().down(ex).build();
|
||||
* return Health.down(ex).build();
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
@@ -51,7 +49,7 @@ import org.springframework.util.Assert;
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@JsonInclude(Include.NON_EMPTY)
|
||||
public final class Health {
|
||||
public final class Health extends HealthComponent {
|
||||
|
||||
private final Status status;
|
||||
|
||||
@@ -71,7 +69,7 @@ public final class Health {
|
||||
* Return the status of the health.
|
||||
* @return the status (never {@code null})
|
||||
*/
|
||||
@JsonUnwrapped
|
||||
@Override
|
||||
public Status getStatus() {
|
||||
return this.status;
|
||||
}
|
||||
@@ -80,10 +78,24 @@ public final class Health {
|
||||
* Return the details of the health.
|
||||
* @return the details (or an empty map)
|
||||
*/
|
||||
@JsonInclude(Include.NON_EMPTY)
|
||||
public Map<String, Object> getDetails() {
|
||||
return this.details;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new instance of this {@link Health} with all {@link #getDetails() details}
|
||||
* removed.
|
||||
* @return a new instance without details
|
||||
* @since 2.2.0
|
||||
*/
|
||||
Health withoutDetails() {
|
||||
if (this.details.isEmpty()) {
|
||||
return this;
|
||||
}
|
||||
return status(getStatus()).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
|
||||
@@ -32,8 +32,10 @@ import java.util.Map;
|
||||
*
|
||||
* @author Christian Dupuis
|
||||
* @since 1.1.0
|
||||
* @deprecated since 2.2.0 in favor of {@link StatusAggregator}
|
||||
*/
|
||||
@FunctionalInterface
|
||||
@Deprecated
|
||||
public interface HealthAggregator {
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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 com.fasterxml.jackson.annotation.JsonUnwrapped;
|
||||
|
||||
/**
|
||||
* An component that contributes data to results returned from the {@link HealthEndpoint}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 2.2.0
|
||||
* @see Health
|
||||
* @see CompositeHealth
|
||||
*/
|
||||
public abstract class HealthComponent {
|
||||
|
||||
HealthComponent() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the status of the component.
|
||||
* @return the component status
|
||||
*/
|
||||
@JsonUnwrapped
|
||||
public abstract Status getStatus();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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;
|
||||
|
||||
/**
|
||||
* Tagging interface for classes that contribute to {@link HealthComponent health
|
||||
* components} to the results returned from the {@link HealthEndpoint}. A contributor must
|
||||
* be either a {@link HealthIndicator} or a {@link CompositeHealthContributor}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 2.2.0
|
||||
* @see HealthIndicator
|
||||
* @see CompositeHealthContributor
|
||||
*/
|
||||
public interface HealthContributor {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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.Locale;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Generate a sensible health indicator name based on its bean name.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class HealthContributorNameFactory implements Function<String, String> {
|
||||
|
||||
private static final String[] SUFFIXES = { "healthindicator", "healthcontributor" };
|
||||
|
||||
/**
|
||||
* A shared singleton {@link HealthContributorNameFactory} instance.
|
||||
*/
|
||||
public static final HealthContributorNameFactory INSTANCE = new HealthContributorNameFactory();
|
||||
|
||||
@Override
|
||||
public String apply(String name) {
|
||||
for (String suffix : SUFFIXES) {
|
||||
if (name != null && name.toLowerCase(Locale.ENGLISH).endsWith(suffix)) {
|
||||
return name.substring(0, name.length() - suffix.length());
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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;
|
||||
|
||||
/**
|
||||
* {@link ContributorRegistry} for {@link HealthContributor HealthContributors}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 2.2.0
|
||||
*/
|
||||
public interface HealthContributorRegistry extends ContributorRegistry<HealthContributor> {
|
||||
|
||||
}
|
||||
@@ -16,10 +16,13 @@
|
||||
|
||||
package org.springframework.boot.actuate.health;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.SecurityContext;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Selector;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Selector.Match;
|
||||
|
||||
/**
|
||||
* {@link Endpoint @Endpoint} to expose application health information.
|
||||
@@ -31,57 +34,49 @@ import org.springframework.util.Assert;
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Endpoint(id = "health")
|
||||
public class HealthEndpoint {
|
||||
public class HealthEndpoint extends HealthEndpointSupport<HealthContributor, HealthComponent> {
|
||||
|
||||
private final HealthIndicator healthIndicator;
|
||||
private static final String[] EMPTY_PATH = {};
|
||||
|
||||
/**
|
||||
* Create a new {@link HealthEndpoint} instance that will use the given
|
||||
* {@code healthIndicator} to generate its response.
|
||||
* Create a new {@link HealthEndpoint} instance that will use the given {@code
|
||||
* healthIndicator} to generate its response.
|
||||
* @param healthIndicator the health indicator
|
||||
* @deprecated since 2.2.0 in favor of
|
||||
* {@link #HealthEndpoint(HealthContributorRegistry, HealthEndpointSettings)}
|
||||
*/
|
||||
@Deprecated
|
||||
public HealthEndpoint(HealthIndicator healthIndicator) {
|
||||
Assert.notNull(healthIndicator, "HealthIndicator must not be null");
|
||||
this.healthIndicator = healthIndicator;
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public Health health() {
|
||||
return this.healthIndicator.health();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link Health} of a particular component or {@code null} if such
|
||||
* component does not exist.
|
||||
* @param component the name of a particular {@link HealthIndicator}
|
||||
* @return the {@link Health} for the component or {@code null}
|
||||
* Create a new {@link HealthEndpoint} instance.
|
||||
* @param registry the health contributor registry
|
||||
* @param settings the health endpoint settings
|
||||
*/
|
||||
@ReadOperation
|
||||
public Health healthForComponent(@Selector String component) {
|
||||
HealthIndicator indicator = getNestedHealthIndicator(this.healthIndicator, component);
|
||||
return (indicator != null) ? indicator.health() : null;
|
||||
public HealthEndpoint(HealthContributorRegistry registry, HealthEndpointSettings settings) {
|
||||
super(registry, settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link Health} of a particular {@code instance} managed by the specified
|
||||
* {@code component} or {@code null} if that particular component is not a
|
||||
* {@link CompositeHealthIndicator} or if such instance does not exist.
|
||||
* @param component the name of a particular {@link CompositeHealthIndicator}
|
||||
* @param instance the name of an instance managed by that component
|
||||
* @return the {@link Health} for the component instance of {@code null}
|
||||
*/
|
||||
@ReadOperation
|
||||
public Health healthForComponentInstance(@Selector String component, @Selector String instance) {
|
||||
HealthIndicator indicator = getNestedHealthIndicator(this.healthIndicator, component);
|
||||
HealthIndicator nestedIndicator = getNestedHealthIndicator(indicator, instance);
|
||||
return (nestedIndicator != null) ? nestedIndicator.health() : null;
|
||||
public HealthComponent health() {
|
||||
return healthForPath(EMPTY_PATH);
|
||||
}
|
||||
|
||||
private HealthIndicator getNestedHealthIndicator(HealthIndicator healthIndicator, String name) {
|
||||
if (healthIndicator instanceof CompositeHealthIndicator) {
|
||||
return ((CompositeHealthIndicator) healthIndicator).getRegistry().get(name);
|
||||
}
|
||||
return null;
|
||||
@ReadOperation
|
||||
public HealthComponent healthForPath(@Selector(match = Match.ALL_REMAINING) String... path) {
|
||||
return getHealth(SecurityContext.NONE, true, path);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HealthComponent getHealth(HealthContributor contributor, boolean includeDetails) {
|
||||
return ((HealthIndicator) contributor).getHealth(includeDetails);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HealthComponent aggregateContributions(Map<String, HealthComponent> contributions,
|
||||
StatusAggregator statusAggregator, boolean includeDetails) {
|
||||
return getCompositeHealth(contributions, statusAggregator, includeDetails);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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.endpoint.SecurityContext;
|
||||
|
||||
/**
|
||||
* Setting for a {@link HealthEndpoint}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 2.2.0
|
||||
*/
|
||||
public interface HealthEndpointSettings {
|
||||
|
||||
/**
|
||||
* Returns if {@link Health#getDetails() health details} should be included in the
|
||||
* response.
|
||||
* @param securityContext the endpoint security context
|
||||
* @return {@code true} to included details or {@code false} to hide them
|
||||
*/
|
||||
boolean includeDetails(SecurityContext securityContext);
|
||||
|
||||
/**
|
||||
* Returns the status agreggator that should be used for the endpoint.
|
||||
* @return the status aggregator
|
||||
*/
|
||||
StatusAggregator getStatusAggregator();
|
||||
|
||||
/**
|
||||
* Returns the {@link HttpCodeStatusMapper} that should be used for the endpoint.
|
||||
* @return the HTTP code status mapper
|
||||
*/
|
||||
HttpCodeStatusMapper getHttpCodeStatusMapper();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.SecurityContext;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Base class for health endpoints and health endpoint extensions.
|
||||
*
|
||||
* @param <C> the contributor type
|
||||
* @param <T> the contributed health component type
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
abstract class HealthEndpointSupport<C, T> {
|
||||
|
||||
private final ContributorRegistry<C> registry;
|
||||
|
||||
private final HealthEndpointSettings settings;
|
||||
|
||||
/**
|
||||
* Throw a new {@link IllegalStateException} to indicate a constructor has been
|
||||
* deprecated.
|
||||
* @deprecated since 2.2.0 in order to support deprecated subclass constructors
|
||||
*/
|
||||
@Deprecated
|
||||
HealthEndpointSupport() {
|
||||
throw new IllegalStateException("Unable to create " + getClass() + " using deprecated constructor");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link HealthEndpointSupport} instance.
|
||||
* @param registry the health contributor registry
|
||||
* @param settings the health settings
|
||||
*/
|
||||
HealthEndpointSupport(ContributorRegistry<C> registry, HealthEndpointSettings settings) {
|
||||
Assert.notNull(registry, "Registry must not be null");
|
||||
Assert.notNull(settings, "Settings must not be null");
|
||||
this.registry = registry;
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the health endpoint settings.
|
||||
* @return the settings
|
||||
*/
|
||||
protected final HealthEndpointSettings getSettings() {
|
||||
return this.settings;
|
||||
}
|
||||
|
||||
T getHealth(SecurityContext securityContext, boolean alwaysIncludeDetails, String... path) {
|
||||
boolean includeDetails = alwaysIncludeDetails || this.settings.includeDetails(securityContext);
|
||||
boolean isRoot = path.length == 0;
|
||||
if (!includeDetails && !isRoot) {
|
||||
return null;
|
||||
}
|
||||
Object contributor = getContributor(path);
|
||||
return getContribution(contributor, includeDetails);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Object getContributor(String[] path) {
|
||||
Object contributor = this.registry;
|
||||
int pathOffset = 0;
|
||||
while (pathOffset < path.length) {
|
||||
if (!(contributor instanceof NamedContributors)) {
|
||||
return null;
|
||||
}
|
||||
contributor = ((NamedContributors<C>) contributor).getContributor(path[pathOffset]);
|
||||
pathOffset++;
|
||||
}
|
||||
return contributor;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private T getContribution(Object contributor, boolean includeDetails) {
|
||||
if (contributor instanceof NamedContributors) {
|
||||
return getAggregateHealth((NamedContributors<C>) contributor, includeDetails);
|
||||
}
|
||||
return (contributor != null) ? getHealth((C) contributor, includeDetails) : null;
|
||||
}
|
||||
|
||||
private T getAggregateHealth(NamedContributors<C> namedContributors, boolean includeDetails) {
|
||||
Map<String, T> contributions = new LinkedHashMap<>();
|
||||
for (NamedContributor<C> namedContributor : namedContributors) {
|
||||
String name = namedContributor.getName();
|
||||
T contribution = getContribution(namedContributor.getContributor(), includeDetails);
|
||||
contributions.put(name, contribution);
|
||||
}
|
||||
if (contributions.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return aggregateContributions(contributions, this.settings.getStatusAggregator(), includeDetails);
|
||||
}
|
||||
|
||||
protected abstract T getHealth(C contributor, boolean includeDetails);
|
||||
|
||||
protected abstract T aggregateContributions(Map<String, T> contributions, StatusAggregator statusAggregator,
|
||||
boolean includeDetails);
|
||||
|
||||
protected final CompositeHealth getCompositeHealth(Map<String, HealthComponent> components,
|
||||
StatusAggregator statusAggregator, boolean includeDetails) {
|
||||
Status status = statusAggregator.getAggregateStatus(
|
||||
components.values().stream().map(HealthComponent::getStatus).collect(Collectors.toSet()));
|
||||
Map<String, HealthComponent> includedComponents = includeDetails ? components : null;
|
||||
return new CompositeHealth(status, includedComponents);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,11 +16,12 @@
|
||||
|
||||
package org.springframework.boot.actuate.health;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.SecurityContext;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Selector;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Selector.Match;
|
||||
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
|
||||
import org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExtension;
|
||||
|
||||
@@ -37,37 +38,60 @@ import org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExten
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@EndpointWebExtension(endpoint = HealthEndpoint.class)
|
||||
public class HealthEndpointWebExtension {
|
||||
public class HealthEndpointWebExtension extends HealthEndpointSupport<HealthContributor, HealthComponent> {
|
||||
|
||||
private final HealthEndpoint delegate;
|
||||
|
||||
private final HealthWebEndpointResponseMapper responseMapper;
|
||||
private static final String[] NO_PATH = {};
|
||||
|
||||
/**
|
||||
* Create a new {@link HealthEndpointWebExtension} instance using a delegate endpoint.
|
||||
* @param delegate the delegate endpoint
|
||||
* @param responseMapper the response mapper
|
||||
* @deprecated since 2.2.0 in favor of
|
||||
* {@link #HealthEndpointWebExtension(HealthContributorRegistry, HealthEndpointSettings)}
|
||||
*/
|
||||
@Deprecated
|
||||
public HealthEndpointWebExtension(HealthEndpoint delegate, HealthWebEndpointResponseMapper responseMapper) {
|
||||
this.delegate = delegate;
|
||||
this.responseMapper = responseMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link HealthEndpointWebExtension} instance.
|
||||
* @param registry the health contributor registry
|
||||
* @param settings the health endpoint settings
|
||||
*/
|
||||
public HealthEndpointWebExtension(HealthContributorRegistry registry, HealthEndpointSettings settings) {
|
||||
super(registry, settings);
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public WebEndpointResponse<Health> health(SecurityContext securityContext) {
|
||||
return this.responseMapper.map(this.delegate.health(), securityContext);
|
||||
public WebEndpointResponse<HealthComponent> health(SecurityContext securityContext) {
|
||||
return health(securityContext, NO_PATH);
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public WebEndpointResponse<Health> healthForComponent(SecurityContext securityContext, @Selector String component) {
|
||||
Supplier<Health> health = () -> this.delegate.healthForComponent(component);
|
||||
return this.responseMapper.mapDetails(health, securityContext);
|
||||
public WebEndpointResponse<HealthComponent> health(SecurityContext securityContext,
|
||||
@Selector(match = Match.ALL_REMAINING) String... path) {
|
||||
return health(securityContext, false, path);
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public WebEndpointResponse<Health> healthForComponentInstance(SecurityContext securityContext,
|
||||
@Selector String component, @Selector String instance) {
|
||||
Supplier<Health> health = () -> this.delegate.healthForComponentInstance(component, instance);
|
||||
return this.responseMapper.mapDetails(health, securityContext);
|
||||
public WebEndpointResponse<HealthComponent> health(SecurityContext securityContext, boolean alwaysIncludeDetails,
|
||||
String... path) {
|
||||
HealthComponent health = getHealth(securityContext, alwaysIncludeDetails, path);
|
||||
if (health == null) {
|
||||
return new WebEndpointResponse<>(WebEndpointResponse.STATUS_NOT_FOUND);
|
||||
}
|
||||
int statusCode = getSettings().getHttpCodeStatusMapper().getStatusCode(health.getStatus());
|
||||
return new WebEndpointResponse<>(health, statusCode);
|
||||
}
|
||||
|
||||
public WebEndpointResponse<Health> getHealth(SecurityContext securityContext, ShowDetails showDetails) {
|
||||
return this.responseMapper.map(this.delegate.health(), securityContext, showDetails);
|
||||
@Override
|
||||
protected HealthComponent getHealth(HealthContributor contributor, boolean includeDetails) {
|
||||
return ((HealthIndicator) contributor).getHealth(includeDetails);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HealthComponent aggregateContributions(Map<String, HealthComponent> contributions,
|
||||
StatusAggregator statusAggregator, boolean includeDetails) {
|
||||
return getCompositeHealth(contributions, statusAggregator, includeDetails);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,18 +17,31 @@
|
||||
package org.springframework.boot.actuate.health;
|
||||
|
||||
/**
|
||||
* Strategy interface used to provide an indication of application health.
|
||||
* Strategy interface used to contribute {@link Health} to the results returned from the
|
||||
* {@link HealthEndpoint}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Phillip Webb
|
||||
* @since 1.0.0
|
||||
* @see ApplicationHealthIndicator
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface HealthIndicator {
|
||||
public interface HealthIndicator extends HealthContributor {
|
||||
|
||||
/**
|
||||
* Return an indication of health.
|
||||
* @return the health for
|
||||
* @param includeDetails if details should be included or removed
|
||||
* @return the health
|
||||
* @since 2.2.0
|
||||
*/
|
||||
default Health getHealth(boolean includeDetails) {
|
||||
Health health = health();
|
||||
return includeDetails ? health : health.withoutDetails();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an indication of health.
|
||||
* @return the health
|
||||
*/
|
||||
Health health();
|
||||
|
||||
|
||||
@@ -16,24 +16,15 @@
|
||||
|
||||
package org.springframework.boot.actuate.health;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Generate a sensible health indicator name based on its bean name.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
* @since 2.0.0
|
||||
* @deprecated since 2.2.0 in favor of {@link HealthContributorNameFactory}
|
||||
*/
|
||||
public class HealthIndicatorNameFactory implements Function<String, String> {
|
||||
|
||||
@Override
|
||||
public String apply(String name) {
|
||||
int index = name.toLowerCase(Locale.ENGLISH).indexOf("healthindicator");
|
||||
if (index > 0) {
|
||||
return name.substring(0, index);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
@Deprecated
|
||||
public class HealthIndicatorNameFactory extends HealthContributorNameFactory {
|
||||
|
||||
}
|
||||
|
||||
@@ -27,7 +27,11 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.0
|
||||
* @deprecated since 2.2.0 in favor of
|
||||
* {@link ReactiveHealthContributor#adapt(HealthContributor)}
|
||||
* @see ReactiveHealthContributor#adapt(HealthContributor)
|
||||
*/
|
||||
@Deprecated
|
||||
public class HealthIndicatorReactiveAdapter implements ReactiveHealthIndicator {
|
||||
|
||||
private final HealthIndicator delegate;
|
||||
|
||||
@@ -19,7 +19,7 @@ package org.springframework.boot.actuate.health;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A registry of {@link HealthIndicator HealthIndicators}.
|
||||
* A mutable registry of {@link HealthIndicator HealthIndicators}.
|
||||
* <p>
|
||||
* Implementations <strong>must</strong> be thread-safe.
|
||||
*
|
||||
@@ -27,7 +27,10 @@ import java.util.Map;
|
||||
* @author Vedran Pavic
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.1.0
|
||||
* @see HealthEndpoint
|
||||
* @deprecated since 2.2.0 in favor of a {@link HealthContributorRegistry}
|
||||
*/
|
||||
@Deprecated
|
||||
public interface HealthIndicatorRegistry {
|
||||
|
||||
/**
|
||||
@@ -35,8 +38,8 @@ public interface HealthIndicatorRegistry {
|
||||
* {@code name}.
|
||||
* @param name the name of the indicator
|
||||
* @param healthIndicator the indicator
|
||||
* @throws IllegalStateException if an indicator with the given {@code name} is
|
||||
* already registered.
|
||||
* @throws IllegalStateException if the indicator cannot be registered with the given
|
||||
* {@code name}.
|
||||
*/
|
||||
void register(String name, HealthIndicator healthIndicator);
|
||||
|
||||
|
||||
@@ -26,7 +26,9 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.1.0
|
||||
* @deprecated since 2.2.0 in favor of {@link DefaultHealthIndicatorRegistry}
|
||||
*/
|
||||
@Deprecated
|
||||
public class HealthIndicatorRegistryFactory {
|
||||
|
||||
private final Function<String, String> healthIndicatorNameFactory;
|
||||
|
||||
@@ -28,7 +28,10 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.0
|
||||
* @deprecated since 2.2.0 in favor of {@link HttpCodeStatusMapper} or
|
||||
* {@link SimpleHttpCodeStatusMapper}
|
||||
*/
|
||||
@Deprecated
|
||||
public class HealthStatusHttpMapper {
|
||||
|
||||
private Map<String, Integer> statusMapping = new HashMap<>();
|
||||
|
||||
@@ -28,7 +28,10 @@ import org.springframework.util.CollectionUtils;
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @since 2.0.0
|
||||
* @deprecated since 2.2.0 in favor of {@link HealthEndpointWebExtension} or
|
||||
* {@link ReactiveHealthEndpointWebExtension}
|
||||
*/
|
||||
@Deprecated
|
||||
public class HealthWebEndpointResponseMapper {
|
||||
|
||||
private final HealthStatusHttpMapper statusHttpMapper;
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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;
|
||||
|
||||
/**
|
||||
* Strategy used to map a {@link Status health status} to an HTTP status code.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
* @since 2.2.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface HttpCodeStatusMapper {
|
||||
|
||||
/**
|
||||
* Return the HTTP status code that corresponds to the given {@link Status health
|
||||
* status}.
|
||||
* @param status the health status to map
|
||||
* @return the corresponding HTTP status code
|
||||
*/
|
||||
int getStatusCode(Status status);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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.util.Assert;
|
||||
|
||||
/**
|
||||
* A single named health endpoint contributors (either {@link HealthContributor} or
|
||||
* {@link ReactiveHealthContributor}).
|
||||
*
|
||||
* @param <C> the contributor type
|
||||
* @author Phillip Webb
|
||||
* @since 2.0.0
|
||||
* @see NamedContributors
|
||||
*/
|
||||
public interface NamedContributor<C> {
|
||||
|
||||
/**
|
||||
* Returns the name of the contributor.
|
||||
* @return the contributor name
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Returns the contributor instance.
|
||||
* @return the contributor instance
|
||||
*/
|
||||
C getContributor();
|
||||
|
||||
static <C> NamedContributor<C> of(String name, C contributor) {
|
||||
Assert.notNull(name, "Name must not be null");
|
||||
Assert.notNull(contributor, "Contributor must not be null");
|
||||
return new NamedContributor<C>() {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public C getContributor() {
|
||||
return contributor;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
/**
|
||||
* A collection of named health endpoint contributors (either {@link HealthContributor} or
|
||||
* {@link ReactiveHealthContributor}).
|
||||
*
|
||||
* @param <C> the contributor type
|
||||
* @author Phillip Webb
|
||||
* @since 2.0.0
|
||||
* @see NamedContributor
|
||||
*/
|
||||
public interface NamedContributors<C> extends Iterable<NamedContributor<C>> {
|
||||
|
||||
/**
|
||||
* Return the contributor with the given name.
|
||||
* @param name the name of the contributor
|
||||
* @return a contributor instance of {@code null}
|
||||
*/
|
||||
C getContributor(String name);
|
||||
|
||||
/**
|
||||
* Return a stream of the {@link NamedContributor named contributors}.
|
||||
* @return the stream of named contributors
|
||||
*/
|
||||
default Stream<NamedContributor<C>> stream() {
|
||||
return StreamSupport.stream(spliterator(), false);
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user