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-17926pull/17939/head
parent
24b5b0d93e
commit
3c535e0de3
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
55
spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/HealthIndicatorAutoConfigurationTests.java → spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/HealthContributorAutoConfigurationTests.java
55
spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/HealthIndicatorAutoConfigurationTests.java → spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/HealthContributorAutoConfigurationTests.java
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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> {
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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
Loading…
Reference in New Issue