Add support for reactive health indicator
This commit introduces a "ReactiveHealthIndicator" contract that can be implemented for health checks against a reactive API. When running in a WebFlux-based web app, the health and status endpoints transparently use this in a WebFlux-based application and regular HealthIndicator are executed on the elastic scheduler. When running in a Servlet-based web app, the endpoints includes and adapts available ReactiveHealthIndicators automatically Closes gh-7972
This commit is contained in:
parent
8df852bf71
commit
a274c78fa0
|
|
@ -303,6 +303,11 @@
|
|||
<artifactId>reactor-netty</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.undertow</groupId>
|
||||
<artifactId>undertow-core</artifactId>
|
||||
|
|
@ -369,5 +374,10 @@
|
|||
<artifactId>snakeyaml</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>redis.clients</groupId>
|
||||
<artifactId>jedis</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
|
|||
|
|
@ -17,8 +17,10 @@
|
|||
package org.springframework.boot.actuate.autoconfigure.endpoint;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import liquibase.integration.spring.SpringLiquibase;
|
||||
import org.flywaydb.core.Flyway;
|
||||
|
|
@ -42,10 +44,11 @@ import org.springframework.boot.actuate.endpoint.ShutdownEndpoint;
|
|||
import org.springframework.boot.actuate.endpoint.StatusEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.ThreadDumpEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.TraceEndpoint;
|
||||
import org.springframework.boot.actuate.health.CompositeHealthIndicatorFactory;
|
||||
import org.springframework.boot.actuate.health.HealthAggregator;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.actuate.health.HealthIndicatorFactory;
|
||||
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
|
||||
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
|
||||
import org.springframework.boot.actuate.info.InfoContributor;
|
||||
import org.springframework.boot.actuate.trace.InMemoryTraceRepository;
|
||||
import org.springframework.boot.actuate.trace.TraceRepository;
|
||||
|
|
@ -55,6 +58,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionEvaluationRepor
|
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
||||
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
|
||||
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration;
|
||||
|
|
@ -62,8 +66,10 @@ import org.springframework.boot.endpoint.Endpoint;
|
|||
import org.springframework.boot.logging.LoggingSystem;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.web.servlet.handler.AbstractHandlerMethodMapping;
|
||||
|
||||
/**
|
||||
|
|
@ -174,15 +180,16 @@ public class EndpointAutoConfiguration {
|
|||
}
|
||||
|
||||
@Configuration
|
||||
@Import(HealthIndicatorsSupplierConfiguration.class)
|
||||
static class HealthEndpointConfiguration {
|
||||
|
||||
private final HealthIndicator healthIndicator;
|
||||
|
||||
HealthEndpointConfiguration(ObjectProvider<HealthAggregator> healthAggregator,
|
||||
ObjectProvider<Map<String, HealthIndicator>> healthIndicators) {
|
||||
this.healthIndicator = new HealthIndicatorFactory().createHealthIndicator(
|
||||
Supplier<Map<String, HealthIndicator>> healthIndicatorsSupplier) {
|
||||
this.healthIndicator = new CompositeHealthIndicatorFactory().createHealthIndicator(
|
||||
healthAggregator.getIfAvailable(OrderedHealthAggregator::new),
|
||||
healthIndicators.getIfAvailable(Collections::emptyMap));
|
||||
healthIndicatorsSupplier.get());
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
|
@ -201,6 +208,55 @@ public class EndpointAutoConfiguration {
|
|||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class HealthIndicatorsSupplierConfiguration {
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnMissingClass("reactor.core.publisher.Flux")
|
||||
static class SimpleHealthIndicatorsSupplierConfiguration {
|
||||
|
||||
@Bean
|
||||
public Supplier<Map<String, HealthIndicator>> allHealthIndicators(
|
||||
ObjectProvider<Map<String, HealthIndicator>> healthIndicators) {
|
||||
return () -> healthIndicators.getIfAvailable(Collections::emptyMap);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(name = "reactor.core.publisher.Flux")
|
||||
static class ReactiveHealthIndicatorsSupplierConfiguration {
|
||||
|
||||
@Bean
|
||||
public Supplier<Map<String, HealthIndicator>> allHealthIndicators(
|
||||
ObjectProvider<Map<String, HealthIndicator>> healthIndicators,
|
||||
ObjectProvider<Map<String, ReactiveHealthIndicator>> reactiveHealthIndicators) {
|
||||
return () -> merge(healthIndicators.getIfAvailable(Collections::emptyMap),
|
||||
reactiveHealthIndicators.getIfAvailable(Collections::emptyMap));
|
||||
}
|
||||
|
||||
private Map<String, HealthIndicator> merge(
|
||||
Map<String, HealthIndicator> healthIndicators,
|
||||
Map<String, ReactiveHealthIndicator> reactiveHealthIndicators) {
|
||||
if (ObjectUtils.isEmpty(reactiveHealthIndicators)) {
|
||||
return healthIndicators;
|
||||
}
|
||||
Map<String, HealthIndicator> allIndicators = new LinkedHashMap<>(
|
||||
healthIndicators);
|
||||
reactiveHealthIndicators.forEach((beanName, indicator) -> {
|
||||
allIndicators.computeIfAbsent(beanName, n -> adapt(indicator));
|
||||
});
|
||||
return allIndicators;
|
||||
}
|
||||
|
||||
private HealthIndicator adapt(ReactiveHealthIndicator healthIndicator) {
|
||||
return () -> healthIndicator.health().block();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnBean(Flyway.class)
|
||||
@ConditionalOnClass(Flyway.class)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.endpoint.web;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.ConditionalOnEnabledEndpoint;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorProperties;
|
||||
import org.springframework.boot.actuate.endpoint.HealthEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.StatusEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.web.HealthReactiveWebEndpointExtension;
|
||||
import org.springframework.boot.actuate.endpoint.web.HealthWebEndpointExtension;
|
||||
import org.springframework.boot.actuate.endpoint.web.StatusReactiveWebEndpointExtension;
|
||||
import org.springframework.boot.actuate.endpoint.web.StatusWebEndpointExtension;
|
||||
import org.springframework.boot.actuate.health.CompositeReactiveHealthIndicatorFactory;
|
||||
import org.springframework.boot.actuate.health.HealthAggregator;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.actuate.health.HealthStatusHttpMapper;
|
||||
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
|
||||
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
|
||||
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.boot.autoconfigure.condition.SearchStrategy;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Configuration for web-specific health endpoints
|
||||
* .
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(HealthIndicatorProperties.class)
|
||||
public class HealthWebEndpointConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public HealthStatusHttpMapper createHealthStatusHttpMapper(
|
||||
HealthIndicatorProperties healthIndicatorProperties) {
|
||||
HealthStatusHttpMapper statusHttpMapper = new HealthStatusHttpMapper();
|
||||
if (healthIndicatorProperties.getHttpMapping() != null) {
|
||||
statusHttpMapper.addStatusMapping(healthIndicatorProperties.getHttpMapping());
|
||||
}
|
||||
return statusHttpMapper;
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnWebApplication(type = Type.REACTIVE)
|
||||
static class ReactiveWebHealthConfiguration {
|
||||
|
||||
private final ReactiveHealthIndicator reactiveHealthIndicator;
|
||||
|
||||
ReactiveWebHealthConfiguration(ObjectProvider<HealthAggregator> healthAggregator,
|
||||
ObjectProvider<Map<String, ReactiveHealthIndicator>> reactiveHealthIndicators,
|
||||
ObjectProvider<Map<String, HealthIndicator>> healthIndicators) {
|
||||
this.reactiveHealthIndicator = new CompositeReactiveHealthIndicatorFactory().createReactiveHealthIndicator(
|
||||
healthAggregator.getIfAvailable(OrderedHealthAggregator::new),
|
||||
reactiveHealthIndicators.getIfAvailable(Collections::emptyMap),
|
||||
healthIndicators.getIfAvailable(Collections::emptyMap));
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnEnabledEndpoint
|
||||
@ConditionalOnBean(value = HealthEndpoint.class, search = SearchStrategy.CURRENT)
|
||||
public HealthReactiveWebEndpointExtension healthWebEndpointExtension(
|
||||
HealthStatusHttpMapper healthStatusHttpMapper) {
|
||||
return new HealthReactiveWebEndpointExtension(this.reactiveHealthIndicator,
|
||||
healthStatusHttpMapper);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnEnabledEndpoint
|
||||
@ConditionalOnBean(value = StatusEndpoint.class, search = SearchStrategy.CURRENT)
|
||||
public StatusReactiveWebEndpointExtension statusWebEndpointExtension(
|
||||
HealthStatusHttpMapper healthStatusHttpMapper) {
|
||||
return new StatusReactiveWebEndpointExtension(this.reactiveHealthIndicator,
|
||||
healthStatusHttpMapper);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnWebApplication(type = Type.SERVLET)
|
||||
static class ServletWebHealthConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnEnabledEndpoint
|
||||
@ConditionalOnBean(value = HealthEndpoint.class, search = SearchStrategy.CURRENT)
|
||||
public HealthWebEndpointExtension healthWebEndpointExtension(
|
||||
HealthEndpoint delegate, HealthStatusHttpMapper healthStatusHttpMapper) {
|
||||
return new HealthWebEndpointExtension(delegate, healthStatusHttpMapper);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnEnabledEndpoint
|
||||
@ConditionalOnBean(value = StatusEndpoint.class, search = SearchStrategy.CURRENT)
|
||||
public StatusWebEndpointExtension statusWebEndpointExtension(
|
||||
StatusEndpoint delegate, HealthStatusHttpMapper healthStatusHttpMapper) {
|
||||
return new StatusWebEndpointExtension(delegate, healthStatusHttpMapper);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -18,26 +18,20 @@ package org.springframework.boot.actuate.autoconfigure.endpoint.web;
|
|||
|
||||
import org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.ConditionalOnEnabledEndpoint;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorProperties;
|
||||
import org.springframework.boot.actuate.endpoint.AuditEventsEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.HealthEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.StatusEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.web.AuditEventsWebEndpointExtension;
|
||||
import org.springframework.boot.actuate.endpoint.web.HealthWebEndpointExtension;
|
||||
import org.springframework.boot.actuate.endpoint.web.HeapDumpWebEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.web.LogFileWebEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.web.StatusWebEndpointExtension;
|
||||
import org.springframework.boot.actuate.health.HealthStatusHttpMapper;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
|
||||
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
|
@ -49,7 +43,7 @@ import org.springframework.util.StringUtils;
|
|||
* @since 2.0.0
|
||||
*/
|
||||
@ManagementContextConfiguration
|
||||
@EnableConfigurationProperties(HealthIndicatorProperties.class)
|
||||
@Import(HealthWebEndpointConfiguration.class)
|
||||
public class WebEndpointManagementContextConfiguration {
|
||||
|
||||
@Bean
|
||||
|
|
@ -59,35 +53,6 @@ public class WebEndpointManagementContextConfiguration {
|
|||
return new HeapDumpWebEndpoint();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnEnabledEndpoint
|
||||
@ConditionalOnBean(value = HealthEndpoint.class, search = SearchStrategy.CURRENT)
|
||||
public HealthWebEndpointExtension healthWebEndpointExtension(HealthEndpoint delegate,
|
||||
HealthStatusHttpMapper healthStatusHttpMapper) {
|
||||
return new HealthWebEndpointExtension(delegate, healthStatusHttpMapper);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnEnabledEndpoint
|
||||
@ConditionalOnBean(value = StatusEndpoint.class, search = SearchStrategy.CURRENT)
|
||||
public StatusWebEndpointExtension statusWebEndpointExtension(StatusEndpoint delegate,
|
||||
HealthStatusHttpMapper healthStatusHttpMapper) {
|
||||
return new StatusWebEndpointExtension(delegate, healthStatusHttpMapper);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public HealthStatusHttpMapper createHealthStatusHttpMapper(
|
||||
HealthIndicatorProperties healthIndicatorProperties) {
|
||||
HealthStatusHttpMapper statusHttpMapper = new HealthStatusHttpMapper();
|
||||
if (healthIndicatorProperties.getHttpMapping() != null) {
|
||||
statusHttpMapper.addStatusMapping(healthIndicatorProperties.getHttpMapping());
|
||||
}
|
||||
return statusHttpMapper;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnEnabledEndpoint
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.actuate.health.CompositeReactiveHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.HealthAggregator;
|
||||
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
|
||||
import org.springframework.core.ResolvableType;
|
||||
|
||||
/**
|
||||
* Reactive variant of {@link CompositeHealthIndicatorConfiguration}.
|
||||
*
|
||||
* @param <H> the health indicator type
|
||||
* @param <S> the bean source type
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class CompositeReactiveHealthIndicatorConfiguration<H extends ReactiveHealthIndicator, S> {
|
||||
|
||||
@Autowired
|
||||
private HealthAggregator healthAggregator;
|
||||
|
||||
protected ReactiveHealthIndicator createHealthIndicator(Map<String, S> beans) {
|
||||
if (beans.size() == 1) {
|
||||
return createHealthIndicator(beans.values().iterator().next());
|
||||
}
|
||||
CompositeReactiveHealthIndicator composite = new CompositeReactiveHealthIndicator(
|
||||
this.healthAggregator);
|
||||
for (Map.Entry<String, S> entry : beans.entrySet()) {
|
||||
composite.addHealthIndicator(entry.getKey(),
|
||||
createHealthIndicator(entry.getValue()));
|
||||
}
|
||||
return composite;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected H createHealthIndicator(S source) {
|
||||
Class<?>[] generics = ResolvableType
|
||||
.forClass(CompositeReactiveHealthIndicatorConfiguration.class, getClass())
|
||||
.resolveGenerics();
|
||||
Class<H> indicatorClass = (Class<H>) generics[0];
|
||||
Class<S> sourceClass = (Class<S>) generics[1];
|
||||
try {
|
||||
return indicatorClass.getConstructor(sourceClass).newInstance(source);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("Unable to create indicator " + indicatorClass
|
||||
+ " for source " + sourceClass, ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -16,46 +16,15 @@
|
|||
|
||||
package org.springframework.boot.actuate.autoconfigure.health;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.jms.ConnectionFactory;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import com.couchbase.client.java.Bucket;
|
||||
import com.datastax.driver.core.Cluster;
|
||||
import org.apache.solr.client.solrj.SolrClient;
|
||||
import org.neo4j.ogm.session.SessionFactory;
|
||||
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.CassandraHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.CouchbaseHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.DataSourceHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.DiskSpaceHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.DiskSpaceHealthIndicatorProperties;
|
||||
import org.springframework.boot.actuate.health.HealthAggregator;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.actuate.health.JmsHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.LdapHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.MailHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.MongoHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.Neo4jHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
|
||||
import org.springframework.boot.actuate.health.RabbitHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.RedisHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.SolrHealthIndicator;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration;
|
||||
|
|
@ -66,9 +35,6 @@ import org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfigurat
|
|||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadata;
|
||||
import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvider;
|
||||
import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProviders;
|
||||
import org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration;
|
||||
|
|
@ -78,14 +44,6 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
|
|||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.cassandra.core.CassandraOperations;
|
||||
import org.springframework.data.couchbase.core.CouchbaseOperations;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
|
||||
import org.springframework.ldap.core.LdapOperations;
|
||||
import org.springframework.mail.javamail.JavaMailSenderImpl;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for {@link HealthIndicator}s.
|
||||
|
|
@ -110,9 +68,8 @@ import org.springframework.mail.javamail.JavaMailSenderImpl;
|
|||
Neo4jDataAutoConfiguration.class, RabbitAutoConfiguration.class,
|
||||
RedisAutoConfiguration.class, SolrAutoConfiguration.class })
|
||||
@EnableConfigurationProperties({ HealthIndicatorProperties.class })
|
||||
@Import({
|
||||
ElasticsearchHealthIndicatorConfiguration.ElasticsearchClientHealthIndicatorConfiguration.class,
|
||||
ElasticsearchHealthIndicatorConfiguration.ElasticsearchJestHealthIndicatorConfiguration.class })
|
||||
@Import({ ReactiveHealthIndicatorsConfiguration.class,
|
||||
HealthIndicatorsConfiguration.class })
|
||||
public class HealthIndicatorAutoConfiguration {
|
||||
|
||||
private final HealthIndicatorProperties properties;
|
||||
|
|
@ -131,307 +88,4 @@ public class HealthIndicatorAutoConfiguration {
|
|||
return healthAggregator;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(HealthIndicator.class)
|
||||
public ApplicationHealthIndicator applicationHealthIndicator() {
|
||||
return new ApplicationHealthIndicator();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass({ CassandraOperations.class, Cluster.class })
|
||||
@ConditionalOnBean(CassandraOperations.class)
|
||||
@ConditionalOnEnabledHealthIndicator("cassandra")
|
||||
public static class CassandraHealthIndicatorConfiguration extends
|
||||
CompositeHealthIndicatorConfiguration<CassandraHealthIndicator, CassandraOperations> {
|
||||
|
||||
private final Map<String, CassandraOperations> cassandraOperations;
|
||||
|
||||
public CassandraHealthIndicatorConfiguration(
|
||||
Map<String, CassandraOperations> cassandraOperations) {
|
||||
this.cassandraOperations = cassandraOperations;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "cassandraHealthIndicator")
|
||||
public HealthIndicator cassandraHealthIndicator() {
|
||||
return createHealthIndicator(this.cassandraOperations);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass({ CouchbaseOperations.class, Bucket.class })
|
||||
@ConditionalOnBean(CouchbaseOperations.class)
|
||||
@ConditionalOnEnabledHealthIndicator("couchbase")
|
||||
public static class CouchbaseHealthIndicatorConfiguration extends
|
||||
CompositeHealthIndicatorConfiguration<CouchbaseHealthIndicator, CouchbaseOperations> {
|
||||
|
||||
private final Map<String, CouchbaseOperations> couchbaseOperations;
|
||||
|
||||
public CouchbaseHealthIndicatorConfiguration(
|
||||
Map<String, CouchbaseOperations> couchbaseOperations) {
|
||||
this.couchbaseOperations = couchbaseOperations;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "couchbaseHealthIndicator")
|
||||
public HealthIndicator couchbaseHealthIndicator() {
|
||||
return createHealthIndicator(this.couchbaseOperations);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass({ JdbcTemplate.class, AbstractRoutingDataSource.class })
|
||||
@ConditionalOnBean(DataSource.class)
|
||||
@ConditionalOnEnabledHealthIndicator("db")
|
||||
public static class DataSourcesHealthIndicatorConfiguration extends
|
||||
CompositeHealthIndicatorConfiguration<DataSourceHealthIndicator, DataSource>
|
||||
implements InitializingBean {
|
||||
|
||||
private final Map<String, DataSource> dataSources;
|
||||
|
||||
private final Collection<DataSourcePoolMetadataProvider> metadataProviders;
|
||||
|
||||
private DataSourcePoolMetadataProvider poolMetadataProvider;
|
||||
|
||||
public DataSourcesHealthIndicatorConfiguration(
|
||||
ObjectProvider<Map<String, DataSource>> dataSources,
|
||||
ObjectProvider<Collection<DataSourcePoolMetadataProvider>> metadataProviders) {
|
||||
this.dataSources = filterDataSources(dataSources.getIfAvailable());
|
||||
this.metadataProviders = metadataProviders.getIfAvailable();
|
||||
}
|
||||
|
||||
private Map<String, DataSource> filterDataSources(
|
||||
Map<String, DataSource> candidates) {
|
||||
if (candidates == null) {
|
||||
return null;
|
||||
}
|
||||
Map<String, DataSource> dataSources = new LinkedHashMap<>();
|
||||
for (Map.Entry<String, DataSource> entry : candidates.entrySet()) {
|
||||
if (!(entry.getValue() instanceof AbstractRoutingDataSource)) {
|
||||
dataSources.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
return dataSources;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
this.poolMetadataProvider = new DataSourcePoolMetadataProviders(
|
||||
this.metadataProviders);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "dbHealthIndicator")
|
||||
public HealthIndicator dbHealthIndicator() {
|
||||
return createHealthIndicator(this.dataSources);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataSourceHealthIndicator createHealthIndicator(DataSource source) {
|
||||
return new DataSourceHealthIndicator(source, getValidationQuery(source));
|
||||
}
|
||||
|
||||
private String getValidationQuery(DataSource source) {
|
||||
DataSourcePoolMetadata poolMetadata = this.poolMetadataProvider
|
||||
.getDataSourcePoolMetadata(source);
|
||||
return (poolMetadata == null ? null : poolMetadata.getValidationQuery());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(LdapOperations.class)
|
||||
@ConditionalOnBean(LdapOperations.class)
|
||||
@ConditionalOnEnabledHealthIndicator("ldap")
|
||||
public static class LdapHealthIndicatorConfiguration extends
|
||||
CompositeHealthIndicatorConfiguration<LdapHealthIndicator, LdapOperations> {
|
||||
|
||||
private final Map<String, LdapOperations> ldapOperations;
|
||||
|
||||
public LdapHealthIndicatorConfiguration(
|
||||
Map<String, LdapOperations> ldapOperations) {
|
||||
this.ldapOperations = ldapOperations;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "ldapHealthIndicator")
|
||||
public HealthIndicator ldapHealthIndicator() {
|
||||
return createHealthIndicator(this.ldapOperations);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(MongoTemplate.class)
|
||||
@ConditionalOnBean(MongoTemplate.class)
|
||||
@ConditionalOnEnabledHealthIndicator("mongo")
|
||||
public static class MongoHealthIndicatorConfiguration extends
|
||||
CompositeHealthIndicatorConfiguration<MongoHealthIndicator, MongoTemplate> {
|
||||
|
||||
private final Map<String, MongoTemplate> mongoTemplates;
|
||||
|
||||
public MongoHealthIndicatorConfiguration(
|
||||
Map<String, MongoTemplate> mongoTemplates) {
|
||||
this.mongoTemplates = mongoTemplates;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "mongoHealthIndicator")
|
||||
public HealthIndicator mongoHealthIndicator() {
|
||||
return createHealthIndicator(this.mongoTemplates);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(SessionFactory.class)
|
||||
@ConditionalOnBean(SessionFactory.class)
|
||||
@ConditionalOnEnabledHealthIndicator("neo4j")
|
||||
public static class Neo4jHealthIndicatorConfiguration extends
|
||||
CompositeHealthIndicatorConfiguration<Neo4jHealthIndicator, SessionFactory> {
|
||||
|
||||
private final Map<String, SessionFactory> sessionFactories;
|
||||
|
||||
public Neo4jHealthIndicatorConfiguration(
|
||||
Map<String, SessionFactory> sessionFactories) {
|
||||
this.sessionFactories = sessionFactories;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "neo4jHealthIndicator")
|
||||
public HealthIndicator neo4jHealthIndicator() {
|
||||
return createHealthIndicator(this.sessionFactories);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(RedisConnectionFactory.class)
|
||||
@ConditionalOnBean(RedisConnectionFactory.class)
|
||||
@ConditionalOnEnabledHealthIndicator("redis")
|
||||
public static class RedisHealthIndicatorConfiguration extends
|
||||
CompositeHealthIndicatorConfiguration<RedisHealthIndicator, RedisConnectionFactory> {
|
||||
|
||||
private final Map<String, RedisConnectionFactory> redisConnectionFactories;
|
||||
|
||||
public RedisHealthIndicatorConfiguration(
|
||||
Map<String, RedisConnectionFactory> redisConnectionFactories) {
|
||||
this.redisConnectionFactories = redisConnectionFactories;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "redisHealthIndicator")
|
||||
public HealthIndicator redisHealthIndicator() {
|
||||
return createHealthIndicator(this.redisConnectionFactories);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(RabbitTemplate.class)
|
||||
@ConditionalOnBean(RabbitTemplate.class)
|
||||
@ConditionalOnEnabledHealthIndicator("rabbit")
|
||||
public static class RabbitHealthIndicatorConfiguration extends
|
||||
CompositeHealthIndicatorConfiguration<RabbitHealthIndicator, RabbitTemplate> {
|
||||
|
||||
private final Map<String, RabbitTemplate> rabbitTemplates;
|
||||
|
||||
public RabbitHealthIndicatorConfiguration(
|
||||
Map<String, RabbitTemplate> rabbitTemplates) {
|
||||
this.rabbitTemplates = rabbitTemplates;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "rabbitHealthIndicator")
|
||||
public HealthIndicator rabbitHealthIndicator() {
|
||||
return createHealthIndicator(this.rabbitTemplates);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(SolrClient.class)
|
||||
@ConditionalOnBean(SolrClient.class)
|
||||
@ConditionalOnEnabledHealthIndicator("solr")
|
||||
public static class SolrHealthIndicatorConfiguration extends
|
||||
CompositeHealthIndicatorConfiguration<SolrHealthIndicator, SolrClient> {
|
||||
|
||||
private final Map<String, SolrClient> solrClients;
|
||||
|
||||
public SolrHealthIndicatorConfiguration(Map<String, SolrClient> solrClients) {
|
||||
this.solrClients = solrClients;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "solrHealthIndicator")
|
||||
public HealthIndicator solrHealthIndicator() {
|
||||
return createHealthIndicator(this.solrClients);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnEnabledHealthIndicator("diskspace")
|
||||
public static class DiskSpaceHealthIndicatorConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "diskSpaceHealthIndicator")
|
||||
public DiskSpaceHealthIndicator diskSpaceHealthIndicator(
|
||||
DiskSpaceHealthIndicatorProperties properties) {
|
||||
return new DiskSpaceHealthIndicator(properties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DiskSpaceHealthIndicatorProperties diskSpaceHealthIndicatorProperties() {
|
||||
return new DiskSpaceHealthIndicatorProperties();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(JavaMailSenderImpl.class)
|
||||
@ConditionalOnBean(JavaMailSenderImpl.class)
|
||||
@ConditionalOnEnabledHealthIndicator("mail")
|
||||
public static class MailHealthIndicatorConfiguration extends
|
||||
CompositeHealthIndicatorConfiguration<MailHealthIndicator, JavaMailSenderImpl> {
|
||||
|
||||
private final Map<String, JavaMailSenderImpl> mailSenders;
|
||||
|
||||
public MailHealthIndicatorConfiguration(
|
||||
ObjectProvider<Map<String, JavaMailSenderImpl>> mailSenders) {
|
||||
this.mailSenders = mailSenders.getIfAvailable();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "mailHealthIndicator")
|
||||
public HealthIndicator mailHealthIndicator() {
|
||||
return createHealthIndicator(this.mailSenders);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(ConnectionFactory.class)
|
||||
@ConditionalOnBean(ConnectionFactory.class)
|
||||
@ConditionalOnEnabledHealthIndicator("jms")
|
||||
public static class JmsHealthIndicatorConfiguration extends
|
||||
CompositeHealthIndicatorConfiguration<JmsHealthIndicator, ConnectionFactory> {
|
||||
|
||||
private final Map<String, ConnectionFactory> connectionFactories;
|
||||
|
||||
public JmsHealthIndicatorConfiguration(
|
||||
ObjectProvider<Map<String, ConnectionFactory>> connectionFactories) {
|
||||
this.connectionFactories = connectionFactories.getIfAvailable();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "jmsHealthIndicator")
|
||||
public HealthIndicator jmsHealthIndicator() {
|
||||
return createHealthIndicator(this.connectionFactories);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,381 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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 java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.jms.ConnectionFactory;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import com.couchbase.client.java.Bucket;
|
||||
import com.datastax.driver.core.Cluster;
|
||||
import org.apache.solr.client.solrj.SolrClient;
|
||||
import org.neo4j.ogm.session.SessionFactory;
|
||||
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.CassandraHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.CouchbaseHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.DataSourceHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.DiskSpaceHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.DiskSpaceHealthIndicatorProperties;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.actuate.health.JmsHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.LdapHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.MailHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.MongoHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.Neo4jHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.RabbitHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.RedisHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.SolrHealthIndicator;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadata;
|
||||
import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvider;
|
||||
import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProviders;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.cassandra.core.CassandraOperations;
|
||||
import org.springframework.data.couchbase.core.CouchbaseOperations;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
|
||||
import org.springframework.ldap.core.LdapOperations;
|
||||
import org.springframework.mail.javamail.JavaMailSenderImpl;
|
||||
|
||||
/**
|
||||
* Configuration for available {@link HealthIndicator health indicators}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Configuration
|
||||
@Import({
|
||||
ElasticsearchHealthIndicatorConfiguration.ElasticsearchClientHealthIndicatorConfiguration.class,
|
||||
ElasticsearchHealthIndicatorConfiguration.ElasticsearchJestHealthIndicatorConfiguration.class })
|
||||
public class HealthIndicatorsConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(HealthIndicator.class)
|
||||
public ApplicationHealthIndicator applicationHealthIndicator() {
|
||||
return new ApplicationHealthIndicator();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass({ CassandraOperations.class, Cluster.class })
|
||||
@ConditionalOnBean(CassandraOperations.class)
|
||||
@ConditionalOnEnabledHealthIndicator("cassandra")
|
||||
public static class CassandraHealthIndicatorConfiguration extends
|
||||
CompositeHealthIndicatorConfiguration<CassandraHealthIndicator, CassandraOperations> {
|
||||
|
||||
private final Map<String, CassandraOperations> cassandraOperations;
|
||||
|
||||
public CassandraHealthIndicatorConfiguration(
|
||||
Map<String, CassandraOperations> cassandraOperations) {
|
||||
this.cassandraOperations = cassandraOperations;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "cassandraHealthIndicator")
|
||||
public HealthIndicator cassandraHealthIndicator() {
|
||||
return createHealthIndicator(this.cassandraOperations);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass({ CouchbaseOperations.class, Bucket.class })
|
||||
@ConditionalOnBean(CouchbaseOperations.class)
|
||||
@ConditionalOnEnabledHealthIndicator("couchbase")
|
||||
public static class CouchbaseHealthIndicatorConfiguration extends
|
||||
CompositeHealthIndicatorConfiguration<CouchbaseHealthIndicator, CouchbaseOperations> {
|
||||
|
||||
private final Map<String, CouchbaseOperations> couchbaseOperations;
|
||||
|
||||
public CouchbaseHealthIndicatorConfiguration(
|
||||
Map<String, CouchbaseOperations> couchbaseOperations) {
|
||||
this.couchbaseOperations = couchbaseOperations;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "couchbaseHealthIndicator")
|
||||
public HealthIndicator couchbaseHealthIndicator() {
|
||||
return createHealthIndicator(this.couchbaseOperations);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass({ JdbcTemplate.class, AbstractRoutingDataSource.class })
|
||||
@ConditionalOnBean(DataSource.class)
|
||||
@ConditionalOnEnabledHealthIndicator("db")
|
||||
public static class DataSourcesHealthIndicatorConfiguration extends
|
||||
CompositeHealthIndicatorConfiguration<DataSourceHealthIndicator, DataSource>
|
||||
implements InitializingBean {
|
||||
|
||||
private final Map<String, DataSource> dataSources;
|
||||
|
||||
private final Collection<DataSourcePoolMetadataProvider> metadataProviders;
|
||||
|
||||
private DataSourcePoolMetadataProvider poolMetadataProvider;
|
||||
|
||||
public DataSourcesHealthIndicatorConfiguration(
|
||||
ObjectProvider<Map<String, DataSource>> dataSources,
|
||||
ObjectProvider<Collection<DataSourcePoolMetadataProvider>> metadataProviders) {
|
||||
this.dataSources = filterDataSources(dataSources.getIfAvailable());
|
||||
this.metadataProviders = metadataProviders.getIfAvailable();
|
||||
}
|
||||
|
||||
private Map<String, DataSource> filterDataSources(
|
||||
Map<String, DataSource> candidates) {
|
||||
if (candidates == null) {
|
||||
return null;
|
||||
}
|
||||
Map<String, DataSource> dataSources = new LinkedHashMap<>();
|
||||
for (Map.Entry<String, DataSource> entry : candidates.entrySet()) {
|
||||
if (!(entry.getValue() instanceof AbstractRoutingDataSource)) {
|
||||
dataSources.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
return dataSources;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
this.poolMetadataProvider = new DataSourcePoolMetadataProviders(
|
||||
this.metadataProviders);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "dbHealthIndicator")
|
||||
public HealthIndicator dbHealthIndicator() {
|
||||
return createHealthIndicator(this.dataSources);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataSourceHealthIndicator createHealthIndicator(DataSource source) {
|
||||
return new DataSourceHealthIndicator(source, getValidationQuery(source));
|
||||
}
|
||||
|
||||
private String getValidationQuery(DataSource source) {
|
||||
DataSourcePoolMetadata poolMetadata = this.poolMetadataProvider
|
||||
.getDataSourcePoolMetadata(source);
|
||||
return (poolMetadata == null ? null : poolMetadata.getValidationQuery());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(LdapOperations.class)
|
||||
@ConditionalOnBean(LdapOperations.class)
|
||||
@ConditionalOnEnabledHealthIndicator("ldap")
|
||||
public static class LdapHealthIndicatorConfiguration extends
|
||||
CompositeHealthIndicatorConfiguration<LdapHealthIndicator, LdapOperations> {
|
||||
|
||||
private final Map<String, LdapOperations> ldapOperations;
|
||||
|
||||
public LdapHealthIndicatorConfiguration(
|
||||
Map<String, LdapOperations> ldapOperations) {
|
||||
this.ldapOperations = ldapOperations;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "ldapHealthIndicator")
|
||||
public HealthIndicator ldapHealthIndicator() {
|
||||
return createHealthIndicator(this.ldapOperations);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(MongoTemplate.class)
|
||||
@ConditionalOnBean(MongoTemplate.class)
|
||||
@ConditionalOnEnabledHealthIndicator("mongo")
|
||||
public static class MongoHealthIndicatorConfiguration extends
|
||||
CompositeHealthIndicatorConfiguration<MongoHealthIndicator, MongoTemplate> {
|
||||
|
||||
private final Map<String, MongoTemplate> mongoTemplates;
|
||||
|
||||
public MongoHealthIndicatorConfiguration(
|
||||
Map<String, MongoTemplate> mongoTemplates) {
|
||||
this.mongoTemplates = mongoTemplates;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "mongoHealthIndicator")
|
||||
public HealthIndicator mongoHealthIndicator() {
|
||||
return createHealthIndicator(this.mongoTemplates);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(SessionFactory.class)
|
||||
@ConditionalOnBean(SessionFactory.class)
|
||||
@ConditionalOnEnabledHealthIndicator("neo4j")
|
||||
public static class Neo4jHealthIndicatorConfiguration extends
|
||||
CompositeHealthIndicatorConfiguration<Neo4jHealthIndicator, SessionFactory> {
|
||||
|
||||
private final Map<String, SessionFactory> sessionFactories;
|
||||
|
||||
public Neo4jHealthIndicatorConfiguration(
|
||||
Map<String, SessionFactory> sessionFactories) {
|
||||
this.sessionFactories = sessionFactories;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "neo4jHealthIndicator")
|
||||
public HealthIndicator neo4jHealthIndicator() {
|
||||
return createHealthIndicator(this.sessionFactories);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(RedisConnectionFactory.class)
|
||||
@ConditionalOnBean(RedisConnectionFactory.class)
|
||||
@ConditionalOnEnabledHealthIndicator("redis")
|
||||
public static class RedisHealthIndicatorConfiguration extends
|
||||
CompositeHealthIndicatorConfiguration<RedisHealthIndicator, RedisConnectionFactory> {
|
||||
|
||||
private final Map<String, RedisConnectionFactory> redisConnectionFactories;
|
||||
|
||||
public RedisHealthIndicatorConfiguration(
|
||||
Map<String, RedisConnectionFactory> redisConnectionFactories) {
|
||||
this.redisConnectionFactories = redisConnectionFactories;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "redisHealthIndicator")
|
||||
public HealthIndicator redisHealthIndicator() {
|
||||
return createHealthIndicator(this.redisConnectionFactories);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(RabbitTemplate.class)
|
||||
@ConditionalOnBean(RabbitTemplate.class)
|
||||
@ConditionalOnEnabledHealthIndicator("rabbit")
|
||||
public static class RabbitHealthIndicatorConfiguration extends
|
||||
CompositeHealthIndicatorConfiguration<RabbitHealthIndicator, RabbitTemplate> {
|
||||
|
||||
private final Map<String, RabbitTemplate> rabbitTemplates;
|
||||
|
||||
public RabbitHealthIndicatorConfiguration(
|
||||
Map<String, RabbitTemplate> rabbitTemplates) {
|
||||
this.rabbitTemplates = rabbitTemplates;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "rabbitHealthIndicator")
|
||||
public HealthIndicator rabbitHealthIndicator() {
|
||||
return createHealthIndicator(this.rabbitTemplates);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(SolrClient.class)
|
||||
@ConditionalOnBean(SolrClient.class)
|
||||
@ConditionalOnEnabledHealthIndicator("solr")
|
||||
public static class SolrHealthIndicatorConfiguration extends
|
||||
CompositeHealthIndicatorConfiguration<SolrHealthIndicator, SolrClient> {
|
||||
|
||||
private final Map<String, SolrClient> solrClients;
|
||||
|
||||
public SolrHealthIndicatorConfiguration(Map<String, SolrClient> solrClients) {
|
||||
this.solrClients = solrClients;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "solrHealthIndicator")
|
||||
public HealthIndicator solrHealthIndicator() {
|
||||
return createHealthIndicator(this.solrClients);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnEnabledHealthIndicator("diskspace")
|
||||
public static class DiskSpaceHealthIndicatorConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "diskSpaceHealthIndicator")
|
||||
public DiskSpaceHealthIndicator diskSpaceHealthIndicator(
|
||||
DiskSpaceHealthIndicatorProperties properties) {
|
||||
return new DiskSpaceHealthIndicator(properties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DiskSpaceHealthIndicatorProperties diskSpaceHealthIndicatorProperties() {
|
||||
return new DiskSpaceHealthIndicatorProperties();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(JavaMailSenderImpl.class)
|
||||
@ConditionalOnBean(JavaMailSenderImpl.class)
|
||||
@ConditionalOnEnabledHealthIndicator("mail")
|
||||
public static class MailHealthIndicatorConfiguration extends
|
||||
CompositeHealthIndicatorConfiguration<MailHealthIndicator, JavaMailSenderImpl> {
|
||||
|
||||
private final Map<String, JavaMailSenderImpl> mailSenders;
|
||||
|
||||
public MailHealthIndicatorConfiguration(
|
||||
ObjectProvider<Map<String, JavaMailSenderImpl>> mailSenders) {
|
||||
this.mailSenders = mailSenders.getIfAvailable();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "mailHealthIndicator")
|
||||
public HealthIndicator mailHealthIndicator() {
|
||||
return createHealthIndicator(this.mailSenders);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(ConnectionFactory.class)
|
||||
@ConditionalOnBean(ConnectionFactory.class)
|
||||
@ConditionalOnEnabledHealthIndicator("jms")
|
||||
public static class JmsHealthIndicatorConfiguration extends
|
||||
CompositeHealthIndicatorConfiguration<JmsHealthIndicator, ConnectionFactory> {
|
||||
|
||||
private final Map<String, ConnectionFactory> connectionFactories;
|
||||
|
||||
public JmsHealthIndicatorConfiguration(
|
||||
ObjectProvider<Map<String, ConnectionFactory>> connectionFactories) {
|
||||
this.connectionFactories = connectionFactories.getIfAvailable();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "jmsHealthIndicator")
|
||||
public HealthIndicator jmsHealthIndicator() {
|
||||
return createHealthIndicator(this.connectionFactories);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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 reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.RedisReactiveHealthIndicator;
|
||||
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;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
|
||||
|
||||
/**
|
||||
* Configuration for available {@link ReactiveHealthIndicator reactive health indicators}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(Flux.class)
|
||||
public class ReactiveHealthIndicatorsConfiguration {
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnBean(ReactiveRedisConnectionFactory.class)
|
||||
@ConditionalOnEnabledHealthIndicator("redis")
|
||||
static class RedisHealthIndicatorConfiguration extends
|
||||
CompositeReactiveHealthIndicatorConfiguration<RedisReactiveHealthIndicator,
|
||||
ReactiveRedisConnectionFactory> {
|
||||
|
||||
private final Map<String, ReactiveRedisConnectionFactory> redisConnectionFactories;
|
||||
|
||||
RedisHealthIndicatorConfiguration(
|
||||
Map<String, ReactiveRedisConnectionFactory> redisConnectionFactories) {
|
||||
this.redisConnectionFactories = redisConnectionFactories;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "redisHealthIndicator")
|
||||
public ReactiveHealthIndicator redisHealthIndicator() {
|
||||
return createHealthIndicator(this.redisConnectionFactories);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.endpoint.web;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.HealthEndpoint;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.HealthStatusHttpMapper;
|
||||
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
|
||||
import org.springframework.boot.endpoint.ReadOperation;
|
||||
import org.springframework.boot.endpoint.web.WebEndpointExtension;
|
||||
import org.springframework.boot.endpoint.web.WebEndpointResponse;
|
||||
|
||||
/**
|
||||
* Reactive {@link WebEndpointExtension} for the {@link HealthEndpoint}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@WebEndpointExtension(endpoint = HealthEndpoint.class)
|
||||
public class HealthReactiveWebEndpointExtension {
|
||||
|
||||
private final ReactiveHealthIndicator delegate;
|
||||
|
||||
private final HealthStatusHttpMapper statusHttpMapper;
|
||||
|
||||
public HealthReactiveWebEndpointExtension(ReactiveHealthIndicator delegate,
|
||||
HealthStatusHttpMapper statusHttpMapper) {
|
||||
this.delegate = delegate;
|
||||
this.statusHttpMapper = statusHttpMapper;
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public Mono<WebEndpointResponse<Health>> health() {
|
||||
return this.delegate.health().map(health -> {
|
||||
Integer status = this.statusHttpMapper.mapStatus(health.getStatus());
|
||||
return new WebEndpointResponse<>(health, status);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.endpoint.web;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.StatusEndpoint;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.HealthStatusHttpMapper;
|
||||
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
|
||||
import org.springframework.boot.endpoint.ReadOperation;
|
||||
import org.springframework.boot.endpoint.web.WebEndpointExtension;
|
||||
import org.springframework.boot.endpoint.web.WebEndpointResponse;
|
||||
|
||||
/**
|
||||
* Reactive {@link WebEndpointExtension} for the {@link StatusEndpoint}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@WebEndpointExtension(endpoint = StatusEndpoint.class)
|
||||
public class StatusReactiveWebEndpointExtension {
|
||||
|
||||
private final ReactiveHealthIndicator delegate;
|
||||
|
||||
private final HealthStatusHttpMapper statusHttpMapper;
|
||||
|
||||
public StatusReactiveWebEndpointExtension(ReactiveHealthIndicator delegate,
|
||||
HealthStatusHttpMapper statusHttpMapper) {
|
||||
this.delegate = delegate;
|
||||
this.statusHttpMapper = statusHttpMapper;
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public Mono<WebEndpointResponse<Health>> health() {
|
||||
return this.delegate.health().map(health -> {
|
||||
Integer status = this.statusHttpMapper.mapStatus(health.getStatus());
|
||||
return new WebEndpointResponse<>(
|
||||
Health.status(health.getStatus()).build(), status);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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 reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* Base {@link ReactiveHealthIndicator} implementations that encapsulates creation of
|
||||
* {@link Health} instance and error handling.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public abstract class AbstractReactiveHealthIndicator
|
||||
implements ReactiveHealthIndicator {
|
||||
|
||||
@Override
|
||||
public final Mono<Health> health() {
|
||||
return doHealthCheck(new Health.Builder())
|
||||
.onErrorResume(ex -> Mono.just(new Health.Builder().down(ex).build()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Actual health check logic. If an error occurs in the pipeline it will be
|
||||
* handled automatically.
|
||||
* @param builder the {@link Health.Builder} to report health status and details
|
||||
* @return a {@link Mono} that provides the {@link Health}
|
||||
*/
|
||||
protected abstract Mono<Health> doHealthCheck(Health.Builder builder);
|
||||
|
||||
}
|
||||
|
|
@ -17,16 +17,28 @@
|
|||
package org.springframework.boot.actuate.health;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Factory to create a {@link HealthIndicator}.
|
||||
* Factory to create a {@link CompositeHealthIndicator}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class HealthIndicatorFactory {
|
||||
public class CompositeHealthIndicatorFactory {
|
||||
|
||||
private final Function<String, String> healthIndicatorNameFactory;
|
||||
|
||||
public CompositeHealthIndicatorFactory(
|
||||
Function<String, String> healthIndicatorNameFactory) {
|
||||
this.healthIndicatorNameFactory = healthIndicatorNameFactory;
|
||||
}
|
||||
|
||||
public CompositeHealthIndicatorFactory() {
|
||||
this(new HealthIndicatorNameFactory());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link CompositeHealthIndicator} based on the specified health indicators.
|
||||
|
|
@ -35,30 +47,17 @@ public class HealthIndicatorFactory {
|
|||
* @return a {@link HealthIndicator} that delegates to the specified
|
||||
* {@code healthIndicators}.
|
||||
*/
|
||||
public HealthIndicator createHealthIndicator(HealthAggregator healthAggregator,
|
||||
public CompositeHealthIndicator createHealthIndicator(HealthAggregator healthAggregator,
|
||||
Map<String, HealthIndicator> healthIndicators) {
|
||||
Assert.notNull(healthAggregator, "HealthAggregator must not be null");
|
||||
Assert.notNull(healthIndicators, "HealthIndicators must not be null");
|
||||
CompositeHealthIndicator healthIndicator = new CompositeHealthIndicator(
|
||||
healthAggregator);
|
||||
for (Map.Entry<String, HealthIndicator> entry : healthIndicators.entrySet()) {
|
||||
healthIndicator.addHealthIndicator(getKey(entry.getKey()), entry.getValue());
|
||||
String name = this.healthIndicatorNameFactory.apply(entry.getKey());
|
||||
healthIndicator.addHealthIndicator(name, entry.getValue());
|
||||
}
|
||||
return healthIndicator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns the health indicator name into a key that can be used in the map of health
|
||||
* information.
|
||||
* @param name the health indicator name
|
||||
* @return the key
|
||||
*/
|
||||
private String getKey(String name) {
|
||||
int index = name.toLowerCase().indexOf("healthindicator");
|
||||
if (index > 0) {
|
||||
return name.substring(0, index);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.time.Duration;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.util.function.Tuple2;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link ReactiveHealthIndicator} that returns health indications from all registered
|
||||
* delegates. Provides an alternative {@link Health} for a delegate that reaches a
|
||||
* configurable timeout.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class CompositeReactiveHealthIndicator implements ReactiveHealthIndicator {
|
||||
|
||||
private final Map<String, ReactiveHealthIndicator> indicators;
|
||||
|
||||
private final HealthAggregator healthAggregator;
|
||||
|
||||
private Long timeout;
|
||||
|
||||
private Health timeoutHealth;
|
||||
|
||||
private final Function<Mono<Health>, Mono<Health>> timeoutCompose;
|
||||
|
||||
public CompositeReactiveHealthIndicator(HealthAggregator healthAggregator) {
|
||||
this(healthAggregator, new LinkedHashMap<>());
|
||||
}
|
||||
|
||||
public CompositeReactiveHealthIndicator(HealthAggregator healthAggregator,
|
||||
Map<String, ReactiveHealthIndicator> indicators) {
|
||||
Assert.notNull(healthAggregator, "HealthAggregator must not be null");
|
||||
Assert.notNull(indicators, "Indicators must not be null");
|
||||
this.indicators = new LinkedHashMap<>(indicators);
|
||||
this.healthAggregator = healthAggregator;
|
||||
this.timeoutCompose = mono -> this.timeout != null ?
|
||||
mono.timeout(Duration.ofMillis(this.timeout), Mono.just(this.timeoutHealth)) :
|
||||
mono;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a {@link ReactiveHealthIndicator} with the specified name.
|
||||
* @param name the name of the health indicator
|
||||
* @param indicator the health indicator to add
|
||||
* @return this instance
|
||||
*/
|
||||
public CompositeReactiveHealthIndicator addHealthIndicator(String name,
|
||||
ReactiveHealthIndicator indicator) {
|
||||
this.indicators.put(name, indicator);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify an alternative timeout {@link Health} if an {@link HealthIndicator} failed
|
||||
* to reply after specified {@code timeout}.
|
||||
* @param timeout number of milliseconds to wait before using the {@code timeoutHealth}
|
||||
* @param timeoutHealth the {@link Health} to use if an health indicator reached the
|
||||
* {@code} timeout
|
||||
* @return this instance
|
||||
*/
|
||||
public CompositeReactiveHealthIndicator timeoutStrategy(long timeout,
|
||||
Health timeoutHealth) {
|
||||
this.timeout = timeout;
|
||||
this.timeoutHealth = (timeoutHealth != null ? timeoutHealth
|
||||
: Health.unknown().build());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Health> health() {
|
||||
return Flux.fromIterable(this.indicators.entrySet())
|
||||
.flatMap(entry -> Mono.just(entry.getKey())
|
||||
.and(entry.getValue().health().compose(this.timeoutCompose)))
|
||||
.collectMap(Tuple2::getT1, Tuple2::getT2)
|
||||
.map(this.healthAggregator::aggregate);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.function.Function;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Factory to create a {@link CompositeReactiveHealthIndicator}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class CompositeReactiveHealthIndicatorFactory {
|
||||
|
||||
private final Function<String, String> healthIndicatorNameFactory;
|
||||
|
||||
public CompositeReactiveHealthIndicatorFactory(
|
||||
Function<String, String> healthIndicatorNameFactory) {
|
||||
this.healthIndicatorNameFactory = healthIndicatorNameFactory;
|
||||
}
|
||||
|
||||
public CompositeReactiveHealthIndicatorFactory() {
|
||||
this(new HealthIndicatorNameFactory());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link CompositeReactiveHealthIndicator} based on the specified health
|
||||
* indicators. Each {@link HealthIndicator} are wrapped to a
|
||||
* {@link HealthIndicatorReactiveAdapter}. If two instances share the same name, the
|
||||
* reactive variant takes precedence.
|
||||
* @param healthAggregator the {@link HealthAggregator}
|
||||
* @param reactiveHealthIndicators the {@link ReactiveHealthIndicator} instances
|
||||
* mapped by name
|
||||
* @param healthIndicators the {@link HealthIndicator} instances mapped by name if
|
||||
* any.
|
||||
* @return a {@link ReactiveHealthIndicator} that delegates to the specified
|
||||
* {@code reactiveHealthIndicators}.
|
||||
*/
|
||||
public CompositeReactiveHealthIndicator createReactiveHealthIndicator(
|
||||
HealthAggregator healthAggregator,
|
||||
Map<String, ReactiveHealthIndicator> reactiveHealthIndicators,
|
||||
Map<String, HealthIndicator> healthIndicators) {
|
||||
Assert.notNull(healthAggregator, "HealthAggregator must not be null");
|
||||
Assert.notNull(reactiveHealthIndicators, "ReactiveHealthIndicators must not be null");
|
||||
CompositeReactiveHealthIndicator healthIndicator = new CompositeReactiveHealthIndicator(
|
||||
healthAggregator);
|
||||
merge(reactiveHealthIndicators, healthIndicators).forEach((beanName, indicator) -> {
|
||||
String name = this.healthIndicatorNameFactory.apply(beanName);
|
||||
healthIndicator.addHealthIndicator(name, indicator);
|
||||
});
|
||||
return healthIndicator;
|
||||
}
|
||||
|
||||
private Map<String, ReactiveHealthIndicator> merge(
|
||||
Map<String, ReactiveHealthIndicator> reactiveHealthIndicators,
|
||||
Map<String, HealthIndicator> healthIndicators) {
|
||||
if (ObjectUtils.isEmpty(healthIndicators)) {
|
||||
return reactiveHealthIndicators;
|
||||
}
|
||||
Map<String, ReactiveHealthIndicator> allIndicators = new LinkedHashMap<>(
|
||||
reactiveHealthIndicators);
|
||||
healthIndicators.forEach((beanName, indicator) -> {
|
||||
String name = this.healthIndicatorNameFactory.apply(beanName);
|
||||
allIndicators.computeIfAbsent(name, n ->
|
||||
new HealthIndicatorReactiveAdapter(indicator));
|
||||
});
|
||||
return allIndicators;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -213,7 +213,7 @@ public final class Health {
|
|||
* @param ex the exception
|
||||
* @return this {@link Builder} instance
|
||||
*/
|
||||
public Builder withException(Exception ex) {
|
||||
public Builder withException(Throwable ex) {
|
||||
Assert.notNull(ex, "Exception must not be null");
|
||||
return withDetail("error", ex.getClass().getName() + ": " + ex.getMessage());
|
||||
}
|
||||
|
|
@ -248,11 +248,11 @@ public final class Health {
|
|||
}
|
||||
|
||||
/**
|
||||
* Set status to {@link Status#DOWN} and add details for given {@link Exception}.
|
||||
* Set status to {@link Status#DOWN} and add details for given {@link Throwable}.
|
||||
* @param ex the exception
|
||||
* @return this {@link Builder} instance
|
||||
*/
|
||||
public Builder down(Exception ex) {
|
||||
public Builder down(Throwable ex) {
|
||||
return down().withException(ex);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.health;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Generate a sensible health indicator name based on its bean name.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class HealthIndicatorNameFactory implements Function<String, String> {
|
||||
|
||||
@Override
|
||||
public String apply(String name) {
|
||||
int index = name.toLowerCase().indexOf("healthindicator");
|
||||
if (index > 0) {
|
||||
return name.substring(0, index);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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 reactor.core.publisher.Mono;
|
||||
import reactor.core.publisher.MonoSink;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Adapts a {@link HealthIndicator} to a {@link ReactiveHealthIndicator} so that it can
|
||||
* be safely invoked in a reactive environment.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class HealthIndicatorReactiveAdapter implements ReactiveHealthIndicator {
|
||||
|
||||
private final HealthIndicator delegate;
|
||||
|
||||
public HealthIndicatorReactiveAdapter(HealthIndicator delegate) {
|
||||
Assert.notNull(delegate, "Delegate must not be null");
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Health> health() {
|
||||
return Mono.create((sink) ->
|
||||
Schedulers.elastic().schedule(() -> invoke(sink)));
|
||||
}
|
||||
|
||||
private void invoke(MonoSink<Health> sink) {
|
||||
try {
|
||||
Health health = this.delegate.health();
|
||||
sink.success(health);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
sink.error(ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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 reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* Defines the {@link Health} of an arbitrary system or component.
|
||||
* <p>
|
||||
* This is non blocking contract that is meant to be used in a reactive application. See
|
||||
* {@link HealthIndicator} for the traditional contract.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.0
|
||||
* @see HealthIndicator
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ReactiveHealthIndicator {
|
||||
|
||||
/**
|
||||
* Provide the indicator of health.
|
||||
* @return a {@link Mono} that provides the {@link Health}
|
||||
*/
|
||||
Mono<Health> health();
|
||||
|
||||
}
|
||||
|
|
@ -35,9 +35,9 @@ import org.springframework.util.Assert;
|
|||
*/
|
||||
public class RedisHealthIndicator extends AbstractHealthIndicator {
|
||||
|
||||
private static final String VERSION = "version";
|
||||
static final String VERSION = "version";
|
||||
|
||||
private static final String REDIS_VERSION = "redis_version";
|
||||
static final String REDIS_VERSION = "redis_version";
|
||||
|
||||
private final RedisConnectionFactory redisConnectionFactory;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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 reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
|
||||
|
||||
/**
|
||||
* A {@link ReactiveHealthIndicator} for Redis.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class RedisReactiveHealthIndicator extends AbstractReactiveHealthIndicator {
|
||||
|
||||
private final ReactiveRedisConnectionFactory connectionFactory;
|
||||
|
||||
public RedisReactiveHealthIndicator(
|
||||
ReactiveRedisConnectionFactory connectionFactory) {
|
||||
this.connectionFactory = connectionFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Mono<Health> doHealthCheck(Health.Builder builder) {
|
||||
return this.connectionFactory.getReactiveConnection().serverCommands().info()
|
||||
.map(info -> builder.up().withDetail(
|
||||
RedisHealthIndicator.VERSION, info.getProperty(
|
||||
RedisHealthIndicator.REDIS_VERSION)).build());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.endpoint;
|
||||
|
||||
import org.junit.Test;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.HealthEndpoint;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.Status;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* Tests for {@link EndpointAutoConfiguration}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class EndpointAutoConfigurationTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(EndpointAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
public void healthEndpointAdaptReactiveHealthIndicator() {
|
||||
this.contextRunner.withUserConfiguration(
|
||||
ReactiveHealthIndicatorConfiguration.class).run((context) -> {
|
||||
ReactiveHealthIndicator reactiveHealthIndicator = context.getBean(
|
||||
"reactiveHealthIndicator", ReactiveHealthIndicator.class);
|
||||
verify(reactiveHealthIndicator, times(0)).health();
|
||||
Health health = context.getBean(HealthEndpoint.class).health();
|
||||
assertThat(health.getStatus()).isEqualTo(Status.UP);
|
||||
assertThat(health.getDetails()).containsOnlyKeys("reactive");
|
||||
verify(reactiveHealthIndicator, times(1)).health();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void healthEndpointMergeRegularAndReactive() {
|
||||
this.contextRunner.withUserConfiguration(HealthIndicatorConfiguration.class,
|
||||
ReactiveHealthIndicatorConfiguration.class).run((context) -> {
|
||||
HealthIndicator simpleHealthIndicator = context.getBean(
|
||||
"simpleHealthIndicator", HealthIndicator.class);
|
||||
ReactiveHealthIndicator reactiveHealthIndicator = context.getBean(
|
||||
"reactiveHealthIndicator", ReactiveHealthIndicator.class);
|
||||
verify(simpleHealthIndicator, times(0)).health();
|
||||
verify(reactiveHealthIndicator, times(0)).health();
|
||||
Health health = context.getBean(HealthEndpoint.class).health();
|
||||
assertThat(health.getStatus()).isEqualTo(Status.UP);
|
||||
assertThat(health.getDetails()).containsOnlyKeys("simple", "reactive");
|
||||
verify(simpleHealthIndicator, times(1)).health();
|
||||
verify(reactiveHealthIndicator, times(1)).health();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
static class HealthIndicatorConfiguration {
|
||||
|
||||
@Bean
|
||||
public HealthIndicator simpleHealthIndicator() {
|
||||
HealthIndicator mock = mock(HealthIndicator.class);
|
||||
given(mock.health()).willReturn(Health.status(Status.UP).build());
|
||||
return mock;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ReactiveHealthIndicatorConfiguration {
|
||||
|
||||
@Bean
|
||||
public ReactiveHealthIndicator reactiveHealthIndicator() {
|
||||
ReactiveHealthIndicator mock = mock(ReactiveHealthIndicator.class);
|
||||
given(mock.health()).willReturn(Mono.just(Health.status(Status.UP).build()));
|
||||
return mock;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -25,14 +25,17 @@ import org.springframework.boot.actuate.endpoint.AuditEventsEndpoint;
|
|||
import org.springframework.boot.actuate.endpoint.HealthEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.StatusEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.web.AuditEventsWebEndpointExtension;
|
||||
import org.springframework.boot.actuate.endpoint.web.HealthReactiveWebEndpointExtension;
|
||||
import org.springframework.boot.actuate.endpoint.web.HealthWebEndpointExtension;
|
||||
import org.springframework.boot.actuate.endpoint.web.HeapDumpWebEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.web.LogFileWebEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.web.StatusReactiveWebEndpointExtension;
|
||||
import org.springframework.boot.actuate.endpoint.web.StatusWebEndpointExtension;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.HealthStatusHttpMapper;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
|
||||
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
|
@ -67,7 +70,7 @@ public class WebEndpointManagementContextConfigurationTests {
|
|||
|
||||
@Test
|
||||
public void healthStatusMappingCanBeCustomized() {
|
||||
ApplicationContextRunner contextRunner = contextRunner()
|
||||
WebApplicationContextRunner contextRunner = webContextRunner()
|
||||
.withPropertyValues("management.health.status.http-mapping.CUSTOM=500")
|
||||
.withUserConfiguration(HealthEndpointConfiguration.class);
|
||||
contextRunner.run((context) -> {
|
||||
|
|
@ -94,7 +97,7 @@ public class WebEndpointManagementContextConfigurationTests {
|
|||
|
||||
@Test
|
||||
public void statusMappingCanBeCustomized() {
|
||||
ApplicationContextRunner contextRunner = contextRunner()
|
||||
WebApplicationContextRunner contextRunner = webContextRunner()
|
||||
.withPropertyValues("management.health.status.http-mapping.CUSTOM=500")
|
||||
.withUserConfiguration(StatusEndpointConfiguration.class);
|
||||
contextRunner.run((context) -> {
|
||||
|
|
@ -113,6 +116,70 @@ public class WebEndpointManagementContextConfigurationTests {
|
|||
"status", StatusEndpointConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void reactiveHealthWebEndpointExtensionIsAutoConfigured() {
|
||||
reactiveWebContextRunner(HealthEndpointConfiguration.class).run((context) -> {
|
||||
assertThat(context).hasSingleBean(HealthReactiveWebEndpointExtension.class);
|
||||
assertThat(context).doesNotHaveBean(HealthWebEndpointExtension.class);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void reactiveHealthStatusMappingCanBeCustomized() {
|
||||
reactiveWebContextRunner(HealthEndpointConfiguration.class)
|
||||
.withPropertyValues("management.health.status.http-mapping.CUSTOM=500")
|
||||
.run((context) -> {
|
||||
HealthReactiveWebEndpointExtension extension = context
|
||||
.getBean(HealthReactiveWebEndpointExtension.class);
|
||||
Map<String, Integer> statusMappings = getStatusMapping(extension);
|
||||
assertThat(statusMappings).containsEntry("DOWN", 503);
|
||||
assertThat(statusMappings).containsEntry("OUT_OF_SERVICE", 503);
|
||||
assertThat(statusMappings).containsEntry("CUSTOM", 500);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void reactiveHealthWebEndpointExtensionCanBeDisabled() {
|
||||
reactiveWebContextRunner(HealthEndpointConfiguration.class)
|
||||
.withPropertyValues("endpoints.health.enabled=false").run((context) -> {
|
||||
assertThat(context).doesNotHaveBean(HealthReactiveWebEndpointExtension.class);
|
||||
assertThat(context).doesNotHaveBean(HealthWebEndpointExtension.class);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void reactiveStatusWebEndpointExtensionIsAutoConfigured() {
|
||||
reactiveWebContextRunner(StatusEndpointConfiguration.class).run((context) -> {
|
||||
assertThat(context).hasSingleBean(StatusReactiveWebEndpointExtension.class);
|
||||
assertThat(context).doesNotHaveBean(StatusWebEndpointExtension.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void reactiveStatusMappingCanBeCustomized() {
|
||||
reactiveWebContextRunner(StatusEndpointConfiguration.class)
|
||||
.withPropertyValues("management.health.status.http-mapping.CUSTOM=500")
|
||||
.run((context) -> {
|
||||
StatusReactiveWebEndpointExtension extension = context
|
||||
.getBean(StatusReactiveWebEndpointExtension.class);
|
||||
Map<String, Integer> statusMappings = getStatusMapping(extension);
|
||||
assertThat(statusMappings).containsEntry("DOWN", 503);
|
||||
assertThat(statusMappings).containsEntry("OUT_OF_SERVICE", 503);
|
||||
assertThat(statusMappings).containsEntry("CUSTOM", 500);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void reactiveStatusWebEndpointExtensionCanBeDisabled() {
|
||||
reactiveWebContextRunner(StatusEndpointConfiguration.class)
|
||||
.withPropertyValues("endpoints.status.enabled=false").run((context) -> {
|
||||
assertThat(context).doesNotHaveBean(StatusReactiveWebEndpointExtension.class);
|
||||
assertThat(context).doesNotHaveBean(StatusWebEndpointExtension.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void auditEventsWebEndpointExtensionIsAutoConfigured() {
|
||||
beanIsAutoConfigured(AuditEventsWebEndpointExtension.class,
|
||||
|
|
@ -128,28 +195,28 @@ public class WebEndpointManagementContextConfigurationTests {
|
|||
|
||||
@Test
|
||||
public void logFileWebEndpointIsAutoConfiguredWhenLoggingFileIsSet() {
|
||||
contextRunner().withPropertyValues("logging.file:test.log").run(
|
||||
webContextRunner().withPropertyValues("logging.file:test.log").run(
|
||||
(context) -> assertThat(context.getBeansOfType(LogFileWebEndpoint.class))
|
||||
.hasSize(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void logFileWebEndpointIsAutoConfiguredWhenLoggingPathIsSet() {
|
||||
contextRunner().withPropertyValues("logging.path:test/logs").run(
|
||||
webContextRunner().withPropertyValues("logging.path:test/logs").run(
|
||||
(context) -> assertThat(context.getBeansOfType(LogFileWebEndpoint.class))
|
||||
.hasSize(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void logFileWebEndpointIsAutoConfiguredWhenExternalFileIsSet() {
|
||||
contextRunner().withPropertyValues("endpoints.logfile.external-file:external.log")
|
||||
webContextRunner().withPropertyValues("endpoints.logfile.external-file:external.log")
|
||||
.run((context) -> assertThat(
|
||||
context.getBeansOfType(LogFileWebEndpoint.class)).hasSize(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void logFileWebEndpointCanBeDisabled() {
|
||||
contextRunner()
|
||||
webContextRunner()
|
||||
.withPropertyValues("logging.file:test.log",
|
||||
"endpoints.logfile.enabled:false")
|
||||
.run((context) -> assertThat(context)
|
||||
|
|
@ -157,19 +224,31 @@ public class WebEndpointManagementContextConfigurationTests {
|
|||
}
|
||||
|
||||
private void beanIsAutoConfigured(Class<?> beanType, Class<?>... config) {
|
||||
contextRunner().withPropertyValues("endpoints.default.web.enabled:true")
|
||||
webContextRunner().withPropertyValues("endpoints.default.web.enabled:true")
|
||||
.withUserConfiguration(config)
|
||||
.run((context) -> assertThat(context).hasSingleBean(beanType));
|
||||
}
|
||||
|
||||
private ReactiveWebApplicationContextRunner reactiveWebContextRunner(
|
||||
Class<?>... config) {
|
||||
return reactiveWebContextRunner()
|
||||
.withPropertyValues("endpoints.default.web.enabled:true")
|
||||
.withUserConfiguration(config);
|
||||
}
|
||||
|
||||
private void beanIsNotAutoConfiguredWhenEndpointIsDisabled(Class<?> webExtension,
|
||||
String id, Class<?>... config) {
|
||||
contextRunner().withPropertyValues("endpoints." + id + ".enabled=false")
|
||||
webContextRunner().withPropertyValues("endpoints." + id + ".enabled=false")
|
||||
.run((context) -> assertThat(context).doesNotHaveBean(webExtension));
|
||||
}
|
||||
|
||||
private ApplicationContextRunner contextRunner() {
|
||||
return new ApplicationContextRunner().withConfiguration(
|
||||
private WebApplicationContextRunner webContextRunner() {
|
||||
return new WebApplicationContextRunner().withConfiguration(
|
||||
AutoConfigurations.of(WebEndpointManagementContextConfiguration.class));
|
||||
}
|
||||
|
||||
private ReactiveWebApplicationContextRunner reactiveWebContextRunner() {
|
||||
return new ReactiveWebApplicationContextRunner().withConfiguration(
|
||||
AutoConfigurations.of(WebEndpointManagementContextConfiguration.class));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,501 +16,37 @@
|
|||
|
||||
package org.springframework.boot.actuate.autoconfigure.health;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import io.searchbox.client.JestClient;
|
||||
import org.assertj.core.api.Condition;
|
||||
import org.junit.Test;
|
||||
import org.neo4j.ogm.session.SessionFactory;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.web.ManagementServerProperties;
|
||||
import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.CassandraHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.CompositeHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.CouchbaseHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.DataSourceHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.DiskSpaceHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.ElasticsearchHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.ElasticsearchJestHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.actuate.health.JmsHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.LdapHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.MailHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.MongoHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.Neo4jHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.RabbitHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.RedisHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.SolrHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.RedisReactiveHealthIndicator;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
|
||||
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.boot.test.context.runner.ContextConsumer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.cassandra.core.CassandraOperations;
|
||||
import org.springframework.data.couchbase.core.CouchbaseOperations;
|
||||
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
|
||||
import org.springframework.ldap.core.LdapOperations;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link HealthIndicatorAutoConfiguration}.
|
||||
* Tests for {@link HealthIndicatorAutoConfiguration} that validates the outcome of
|
||||
* combining reactive and non reactive health indicators.
|
||||
*
|
||||
* @author Christian Dupuis
|
||||
* @author Stephane Nicoll
|
||||
* @author Andy Wilkinson
|
||||
* @author Eddú Meléndez
|
||||
* @author Eric Spiegelberg
|
||||
*/
|
||||
public class HealthIndicatorAutoConfigurationTests {
|
||||
|
||||
public final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(
|
||||
AutoConfigurations.of(HealthIndicatorAutoConfiguration.class,
|
||||
ManagementServerProperties.class));
|
||||
AutoConfigurations.of(HealthIndicatorAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
public void defaultHealthIndicator() {
|
||||
this.contextRunner.withPropertyValues("management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultHealthIndicatorsDisabled() {
|
||||
this.contextRunner.withPropertyValues("management.health.defaults.enabled:false")
|
||||
.run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultHealthIndicatorsDisabledWithCustomOne() {
|
||||
this.contextRunner.withUserConfiguration(CustomHealthIndicator.class)
|
||||
.withPropertyValues("management.health.defaults.enabled:false")
|
||||
.run((context) -> {
|
||||
Map<String, HealthIndicator> beans = context
|
||||
.getBeansOfType(HealthIndicator.class);
|
||||
assertThat(beans).hasSize(1);
|
||||
assertThat(context.getBean("customHealthIndicator"))
|
||||
.isSameAs(beans.values().iterator().next());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultHealthIndicatorsDisabledButOne() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("management.health.defaults.enabled:false",
|
||||
"management.health.diskspace.enabled:true")
|
||||
.run(hasSingleHealthIndicator(DiskSpaceHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void redisHealthIndicator() {
|
||||
public void reactiveRedisTakePrecedence() {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class))
|
||||
.withPropertyValues("management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(RedisHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notRedisHealthIndicator() {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class))
|
||||
.withPropertyValues("management.health.redis.enabled:false",
|
||||
"management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mongoHealthIndicator() {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(MongoAutoConfiguration.class,
|
||||
MongoDataAutoConfiguration.class))
|
||||
.withPropertyValues("management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(MongoHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notMongoHealthIndicator() {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(MongoAutoConfiguration.class,
|
||||
MongoDataAutoConfiguration.class))
|
||||
.withPropertyValues("management.health.mongo.enabled:false",
|
||||
"management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void combinedHealthIndicator() {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(MongoAutoConfiguration.class,
|
||||
RedisAutoConfiguration.class, MongoDataAutoConfiguration.class,
|
||||
SolrAutoConfiguration.class))
|
||||
.run((context) -> {
|
||||
Map<String, HealthIndicator> beans = context
|
||||
.getBeansOfType(HealthIndicator.class);
|
||||
assertThat(beans).hasSize(4);
|
||||
assertThat(context).hasSingleBean(ReactiveHealthIndicator.class);
|
||||
assertThat(context).getBean("redisHealthIndicator")
|
||||
.isInstanceOf(RedisReactiveHealthIndicator.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dataSourceHealthIndicator() {
|
||||
this.contextRunner
|
||||
.withConfiguration(
|
||||
AutoConfigurations.of(DataSourceAutoConfiguration.class))
|
||||
.withPropertyValues("management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(DataSourceHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dataSourceHealthIndicatorWithSeveralDataSources() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(EmbeddedDataSourceConfiguration.class,
|
||||
DataSourceConfig.class)
|
||||
.withPropertyValues("management.health.diskspace.enabled:false")
|
||||
.run((context) -> {
|
||||
Map<String, HealthIndicator> beans = context
|
||||
.getBeansOfType(HealthIndicator.class);
|
||||
assertThat(beans).hasSize(1);
|
||||
HealthIndicator bean = beans.values().iterator().next();
|
||||
assertThat(bean).isExactlyInstanceOf(CompositeHealthIndicator.class);
|
||||
assertThat(bean.health().getDetails()).containsOnlyKeys("dataSource",
|
||||
"testDataSource");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dataSourceHealthIndicatorWithAbstractRoutingDataSource() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(EmbeddedDataSourceConfiguration.class,
|
||||
RoutingDatasourceConfig.class)
|
||||
.withPropertyValues("management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(DataSourceHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dataSourceHealthIndicatorWithCustomValidationQuery() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(DataSourceConfig.class,
|
||||
DataSourcePoolMetadataProvidersConfiguration.class,
|
||||
HealthIndicatorAutoConfiguration.class)
|
||||
.withPropertyValues(
|
||||
"spring.datasource.test.validation-query:SELECT from FOOBAR",
|
||||
"management.health.diskspace.enabled:false")
|
||||
.run((context) -> {
|
||||
Map<String, HealthIndicator> beans = context
|
||||
.getBeansOfType(HealthIndicator.class);
|
||||
assertThat(beans).hasSize(1);
|
||||
HealthIndicator healthIndicator = beans.values().iterator().next();
|
||||
assertThat(healthIndicator.getClass())
|
||||
.isEqualTo(DataSourceHealthIndicator.class);
|
||||
DataSourceHealthIndicator dataSourceHealthIndicator = (DataSourceHealthIndicator) healthIndicator;
|
||||
assertThat(dataSourceHealthIndicator.getQuery())
|
||||
.isEqualTo("SELECT from FOOBAR");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notDataSourceHealthIndicator() {
|
||||
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
|
||||
.withPropertyValues("management.health.db.enabled:false",
|
||||
"management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rabbitHealthIndicator() {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(RabbitAutoConfiguration.class))
|
||||
.withPropertyValues("management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(RabbitHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notRabbitHealthIndicator() {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(RabbitAutoConfiguration.class))
|
||||
.withPropertyValues("management.health.rabbit.enabled:false",
|
||||
"management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void solrHealthIndicator() {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(SolrAutoConfiguration.class))
|
||||
.withPropertyValues("management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(SolrHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notSolrHealthIndicator() {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(SolrAutoConfiguration.class))
|
||||
.withPropertyValues("management.health.solr.enabled:false",
|
||||
"management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void diskSpaceHealthIndicator() {
|
||||
this.contextRunner.run(hasSingleHealthIndicator(DiskSpaceHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mailHealthIndicator() {
|
||||
this.contextRunner
|
||||
.withConfiguration(
|
||||
AutoConfigurations.of(MailSenderAutoConfiguration.class))
|
||||
.withPropertyValues("spring.mail.host:smtp.acme.org",
|
||||
"management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(MailHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notMailHealthIndicator() {
|
||||
this.contextRunner
|
||||
.withConfiguration(
|
||||
AutoConfigurations.of(MailSenderAutoConfiguration.class))
|
||||
.withPropertyValues("spring.mail.host:smtp.acme.org",
|
||||
"management.health.mail.enabled:false",
|
||||
"management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jmsHealthIndicator() {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(ActiveMQAutoConfiguration.class))
|
||||
.withPropertyValues("management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(JmsHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notJmsHealthIndicator() {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(ActiveMQAutoConfiguration.class))
|
||||
.withPropertyValues("management.health.jms.enabled:false",
|
||||
"management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void elasticsearchHealthIndicator() {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(JestClientConfiguration.class,
|
||||
JestAutoConfiguration.class,
|
||||
ElasticsearchAutoConfiguration.class))
|
||||
.withPropertyValues("spring.data.elasticsearch.cluster-nodes:localhost:0",
|
||||
"management.health.diskspace.enabled:false")
|
||||
.withSystemProperties("es.set.netty.runtime.available.processors=false")
|
||||
.run(hasSingleHealthIndicator(ElasticsearchHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void elasticsearchJestHealthIndicator() {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(JestClientConfiguration.class,
|
||||
JestAutoConfiguration.class))
|
||||
.withPropertyValues("management.health.diskspace.enabled:false")
|
||||
.withSystemProperties("es.set.netty.runtime.available.processors=false")
|
||||
.run(hasSingleHealthIndicator(ElasticsearchJestHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notElasticsearchHealthIndicator() {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(JestClientConfiguration.class,
|
||||
JestAutoConfiguration.class,
|
||||
ElasticsearchAutoConfiguration.class))
|
||||
.withPropertyValues("management.health.elasticsearch.enabled:false",
|
||||
"spring.data.elasticsearch.properties.path.home:target",
|
||||
"management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cassandraHealthIndicator() throws Exception {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(CassandraConfiguration.class))
|
||||
.withPropertyValues("management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(CassandraHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notCassandraHealthIndicator() throws Exception {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(CassandraConfiguration.class))
|
||||
.withPropertyValues("management.health.diskspace.enabled:false",
|
||||
"management.health.cassandra.enabled:false")
|
||||
.run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void couchbaseHealthIndicator() throws Exception {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(CouchbaseConfiguration.class))
|
||||
.withPropertyValues("management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(CouchbaseHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notCouchbaseHealthIndicator() throws Exception {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(CouchbaseConfiguration.class))
|
||||
.withPropertyValues("management.health.diskspace.enabled:false",
|
||||
"management.health.couchbase.enabled:false")
|
||||
.run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ldapHealthIndicator() throws Exception {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(LdapConfiguration.class))
|
||||
.withPropertyValues("management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(LdapHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notLdapHealthIndicator() throws Exception {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(LdapConfiguration.class))
|
||||
.withPropertyValues("management.health.diskspace.enabled:false",
|
||||
"management.health.ldap.enabled:false")
|
||||
.run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void neo4jHealthIndicator() throws Exception {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(Neo4jConfiguration.class))
|
||||
.withPropertyValues("management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(Neo4jHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notNeo4jHealthIndicator() throws Exception {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(Neo4jConfiguration.class))
|
||||
.withPropertyValues("management.health.diskspace.enabled:false",
|
||||
"management.health.neo4j.enabled:false")
|
||||
.run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
|
||||
}
|
||||
|
||||
private ContextConsumer<AssertableApplicationContext> hasSingleHealthIndicator(
|
||||
Class<? extends HealthIndicator> type) {
|
||||
return (context) -> assertThat(context).getBeans(HealthIndicator.class).hasSize(1)
|
||||
.hasValueSatisfying(
|
||||
new Condition<>((indicator) -> indicator.getClass().equals(type),
|
||||
"Wrong indicator type"));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties
|
||||
protected static class DataSourceConfig {
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "spring.datasource.test")
|
||||
public DataSource testDataSource() {
|
||||
return DataSourceBuilder.create()
|
||||
.type(org.apache.tomcat.jdbc.pool.DataSource.class)
|
||||
.driverClassName("org.hsqldb.jdbc.JDBCDriver")
|
||||
.url("jdbc:hsqldb:mem:test").username("sa").build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class RoutingDatasourceConfig {
|
||||
|
||||
@Bean
|
||||
AbstractRoutingDataSource routingDataSource() {
|
||||
return mock(AbstractRoutingDataSource.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class CustomHealthIndicator {
|
||||
|
||||
@Bean
|
||||
public HealthIndicator customHealthIndicator() {
|
||||
return () -> Health.down().build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@AutoConfigureBefore(HealthIndicatorAutoConfiguration.class)
|
||||
protected static class CassandraConfiguration {
|
||||
|
||||
@Bean
|
||||
public CassandraOperations cassandraOperations() {
|
||||
return mock(CassandraOperations.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@AutoConfigureBefore(HealthIndicatorAutoConfiguration.class)
|
||||
protected static class CouchbaseConfiguration {
|
||||
|
||||
@Bean
|
||||
public CouchbaseOperations couchbaseOperations() {
|
||||
return mock(CouchbaseOperations.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class JestClientConfiguration {
|
||||
|
||||
@Bean
|
||||
public JestClient jestClient() {
|
||||
return mock(JestClient.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@AutoConfigureBefore(HealthIndicatorAutoConfiguration.class)
|
||||
protected static class LdapConfiguration {
|
||||
|
||||
@Bean
|
||||
public LdapOperations ldapOperations() {
|
||||
return mock(LdapOperations.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@AutoConfigureBefore(HealthIndicatorAutoConfiguration.class)
|
||||
protected static class Neo4jConfiguration {
|
||||
|
||||
@Bean
|
||||
public SessionFactory sessionFactory() {
|
||||
return mock(SessionFactory.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,519 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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 javax.sql.DataSource;
|
||||
|
||||
import io.searchbox.client.JestClient;
|
||||
import org.assertj.core.api.Condition;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.neo4j.ogm.session.SessionFactory;
|
||||
|
||||
import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.CassandraHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.CompositeHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.CouchbaseHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.DataSourceHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.DiskSpaceHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.ElasticsearchHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.ElasticsearchJestHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.actuate.health.JmsHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.LdapHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.MailHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.MongoHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.Neo4jHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.RabbitHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.RedisHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.SolrHealthIndicator;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
|
||||
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.boot.test.context.runner.ContextConsumer;
|
||||
import org.springframework.boot.testsupport.runner.classpath.ClassPathExclusions;
|
||||
import org.springframework.boot.testsupport.runner.classpath.ModifiedClassPathRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.cassandra.core.CassandraOperations;
|
||||
import org.springframework.data.couchbase.core.CouchbaseOperations;
|
||||
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
|
||||
import org.springframework.ldap.core.LdapOperations;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link HealthIndicatorsConfiguration}.
|
||||
*
|
||||
* @author Christian Dupuis
|
||||
* @author Stephane Nicoll
|
||||
* @author Andy Wilkinson
|
||||
* @author Eddú Meléndez
|
||||
* @author Eric Spiegelberg
|
||||
*/
|
||||
@RunWith(ModifiedClassPathRunner.class)
|
||||
@ClassPathExclusions({ "reactor-core*.jar", "lettuce-core*.jar" })
|
||||
public class HealthIndicatorsConfigurationTests {
|
||||
|
||||
public final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(
|
||||
AutoConfigurations.of(HealthIndicatorAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
public void defaultHealthIndicator() {
|
||||
this.contextRunner.withPropertyValues("management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultHealthIndicatorsDisabled() {
|
||||
this.contextRunner.withPropertyValues("management.health.defaults.enabled:false")
|
||||
.run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultHealthIndicatorsDisabledWithCustomOne() {
|
||||
this.contextRunner.withUserConfiguration(CustomHealthIndicator.class)
|
||||
.withPropertyValues("management.health.defaults.enabled:false")
|
||||
.run((context) -> {
|
||||
Map<String, HealthIndicator> beans = context
|
||||
.getBeansOfType(HealthIndicator.class);
|
||||
assertThat(beans).hasSize(1);
|
||||
assertThat(context.getBean("customHealthIndicator"))
|
||||
.isSameAs(beans.values().iterator().next());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultHealthIndicatorsDisabledButOne() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("management.health.defaults.enabled:false",
|
||||
"management.health.diskspace.enabled:true")
|
||||
.run(hasSingleHealthIndicator(DiskSpaceHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void redisHealthIndicator() {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class))
|
||||
.withPropertyValues("management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(RedisHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notRedisHealthIndicator() {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class))
|
||||
.withPropertyValues("management.health.redis.enabled:false",
|
||||
"management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mongoHealthIndicator() {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(MongoAutoConfiguration.class,
|
||||
MongoDataAutoConfiguration.class))
|
||||
.withPropertyValues("management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(MongoHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notMongoHealthIndicator() {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(MongoAutoConfiguration.class,
|
||||
MongoDataAutoConfiguration.class))
|
||||
.withPropertyValues("management.health.mongo.enabled:false",
|
||||
"management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void combinedHealthIndicator() {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(MongoAutoConfiguration.class,
|
||||
RedisAutoConfiguration.class, MongoDataAutoConfiguration.class,
|
||||
SolrAutoConfiguration.class))
|
||||
.run((context) -> {
|
||||
Map<String, HealthIndicator> beans = context
|
||||
.getBeansOfType(HealthIndicator.class);
|
||||
assertThat(beans).hasSize(4);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dataSourceHealthIndicator() {
|
||||
this.contextRunner
|
||||
.withConfiguration(
|
||||
AutoConfigurations.of(DataSourceAutoConfiguration.class))
|
||||
.withPropertyValues("management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(DataSourceHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dataSourceHealthIndicatorWithSeveralDataSources() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(EmbeddedDataSourceConfiguration.class,
|
||||
DataSourceConfig.class)
|
||||
.withPropertyValues("management.health.diskspace.enabled:false")
|
||||
.run((context) -> {
|
||||
Map<String, HealthIndicator> beans = context
|
||||
.getBeansOfType(HealthIndicator.class);
|
||||
assertThat(beans).hasSize(1);
|
||||
HealthIndicator bean = beans.values().iterator().next();
|
||||
assertThat(bean).isExactlyInstanceOf(CompositeHealthIndicator.class);
|
||||
assertThat(bean.health().getDetails()).containsOnlyKeys("dataSource",
|
||||
"testDataSource");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dataSourceHealthIndicatorWithAbstractRoutingDataSource() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(EmbeddedDataSourceConfiguration.class,
|
||||
RoutingDatasourceConfig.class)
|
||||
.withPropertyValues("management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(DataSourceHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dataSourceHealthIndicatorWithCustomValidationQuery() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(DataSourceConfig.class,
|
||||
DataSourcePoolMetadataProvidersConfiguration.class,
|
||||
HealthIndicatorAutoConfiguration.class)
|
||||
.withPropertyValues(
|
||||
"spring.datasource.test.validation-query:SELECT from FOOBAR",
|
||||
"management.health.diskspace.enabled:false")
|
||||
.run((context) -> {
|
||||
Map<String, HealthIndicator> beans = context
|
||||
.getBeansOfType(HealthIndicator.class);
|
||||
assertThat(beans).hasSize(1);
|
||||
HealthIndicator healthIndicator = beans.values().iterator().next();
|
||||
assertThat(healthIndicator.getClass())
|
||||
.isEqualTo(DataSourceHealthIndicator.class);
|
||||
DataSourceHealthIndicator dataSourceHealthIndicator = (DataSourceHealthIndicator) healthIndicator;
|
||||
assertThat(dataSourceHealthIndicator.getQuery())
|
||||
.isEqualTo("SELECT from FOOBAR");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notDataSourceHealthIndicator() {
|
||||
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
|
||||
.withPropertyValues("management.health.db.enabled:false",
|
||||
"management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rabbitHealthIndicator() {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(RabbitAutoConfiguration.class))
|
||||
.withPropertyValues("management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(RabbitHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notRabbitHealthIndicator() {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(RabbitAutoConfiguration.class))
|
||||
.withPropertyValues("management.health.rabbit.enabled:false",
|
||||
"management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void solrHealthIndicator() {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(SolrAutoConfiguration.class))
|
||||
.withPropertyValues("management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(SolrHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notSolrHealthIndicator() {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(SolrAutoConfiguration.class))
|
||||
.withPropertyValues("management.health.solr.enabled:false",
|
||||
"management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void diskSpaceHealthIndicator() {
|
||||
this.contextRunner.run(hasSingleHealthIndicator(DiskSpaceHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mailHealthIndicator() {
|
||||
this.contextRunner
|
||||
.withConfiguration(
|
||||
AutoConfigurations.of(MailSenderAutoConfiguration.class))
|
||||
.withPropertyValues("spring.mail.host:smtp.acme.org",
|
||||
"management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(MailHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notMailHealthIndicator() {
|
||||
this.contextRunner
|
||||
.withConfiguration(
|
||||
AutoConfigurations.of(MailSenderAutoConfiguration.class))
|
||||
.withPropertyValues("spring.mail.host:smtp.acme.org",
|
||||
"management.health.mail.enabled:false",
|
||||
"management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jmsHealthIndicator() {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(ActiveMQAutoConfiguration.class))
|
||||
.withPropertyValues("management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(JmsHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notJmsHealthIndicator() {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(ActiveMQAutoConfiguration.class))
|
||||
.withPropertyValues("management.health.jms.enabled:false",
|
||||
"management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void elasticsearchHealthIndicator() {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(JestClientConfiguration.class,
|
||||
JestAutoConfiguration.class,
|
||||
ElasticsearchAutoConfiguration.class))
|
||||
.withPropertyValues("spring.data.elasticsearch.cluster-nodes:localhost:0",
|
||||
"management.health.diskspace.enabled:false")
|
||||
.withSystemProperties("es.set.netty.runtime.available.processors=false")
|
||||
.run(hasSingleHealthIndicator(ElasticsearchHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void elasticsearchJestHealthIndicator() {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(JestClientConfiguration.class,
|
||||
JestAutoConfiguration.class))
|
||||
.withPropertyValues("management.health.diskspace.enabled:false")
|
||||
.withSystemProperties("es.set.netty.runtime.available.processors=false")
|
||||
.run(hasSingleHealthIndicator(ElasticsearchJestHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notElasticsearchHealthIndicator() {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(JestClientConfiguration.class,
|
||||
JestAutoConfiguration.class,
|
||||
ElasticsearchAutoConfiguration.class))
|
||||
.withPropertyValues("management.health.elasticsearch.enabled:false",
|
||||
"spring.data.elasticsearch.properties.path.home:target",
|
||||
"management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cassandraHealthIndicator() throws Exception {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(CassandraConfiguration.class))
|
||||
.withPropertyValues("management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(CassandraHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notCassandraHealthIndicator() throws Exception {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(CassandraConfiguration.class))
|
||||
.withPropertyValues("management.health.diskspace.enabled:false",
|
||||
"management.health.cassandra.enabled:false")
|
||||
.run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void couchbaseHealthIndicator() throws Exception {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(CouchbaseConfiguration.class))
|
||||
.withPropertyValues("management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(CouchbaseHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notCouchbaseHealthIndicator() throws Exception {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(CouchbaseConfiguration.class))
|
||||
.withPropertyValues("management.health.diskspace.enabled:false",
|
||||
"management.health.couchbase.enabled:false")
|
||||
.run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ldapHealthIndicator() throws Exception {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(LdapConfiguration.class))
|
||||
.withPropertyValues("management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(LdapHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notLdapHealthIndicator() throws Exception {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(LdapConfiguration.class))
|
||||
.withPropertyValues("management.health.diskspace.enabled:false",
|
||||
"management.health.ldap.enabled:false")
|
||||
.run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void neo4jHealthIndicator() throws Exception {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(Neo4jConfiguration.class))
|
||||
.withPropertyValues("management.health.diskspace.enabled:false")
|
||||
.run(hasSingleHealthIndicator(Neo4jHealthIndicator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notNeo4jHealthIndicator() throws Exception {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(Neo4jConfiguration.class))
|
||||
.withPropertyValues("management.health.diskspace.enabled:false",
|
||||
"management.health.neo4j.enabled:false")
|
||||
.run(hasSingleHealthIndicator(ApplicationHealthIndicator.class));
|
||||
}
|
||||
|
||||
private ContextConsumer<AssertableApplicationContext> hasSingleHealthIndicator(
|
||||
Class<? extends HealthIndicator> type) {
|
||||
return (context) -> assertThat(context).getBeans(HealthIndicator.class).hasSize(1)
|
||||
.hasValueSatisfying(
|
||||
new Condition<>((indicator) -> indicator.getClass().equals(type),
|
||||
"Wrong indicator type"));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties
|
||||
protected static class DataSourceConfig {
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "spring.datasource.test")
|
||||
public DataSource testDataSource() {
|
||||
return DataSourceBuilder.create()
|
||||
.type(org.apache.tomcat.jdbc.pool.DataSource.class)
|
||||
.driverClassName("org.hsqldb.jdbc.JDBCDriver")
|
||||
.url("jdbc:hsqldb:mem:test").username("sa").build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class RoutingDatasourceConfig {
|
||||
|
||||
@Bean
|
||||
AbstractRoutingDataSource routingDataSource() {
|
||||
return mock(AbstractRoutingDataSource.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class CustomHealthIndicator {
|
||||
|
||||
@Bean
|
||||
public HealthIndicator customHealthIndicator() {
|
||||
return () -> Health.down().build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@AutoConfigureBefore(HealthIndicatorAutoConfiguration.class)
|
||||
protected static class CassandraConfiguration {
|
||||
|
||||
@Bean
|
||||
public CassandraOperations cassandraOperations() {
|
||||
return mock(CassandraOperations.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@AutoConfigureBefore(HealthIndicatorAutoConfiguration.class)
|
||||
protected static class CouchbaseConfiguration {
|
||||
|
||||
@Bean
|
||||
public CouchbaseOperations couchbaseOperations() {
|
||||
return mock(CouchbaseOperations.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class JestClientConfiguration {
|
||||
|
||||
@Bean
|
||||
public JestClient jestClient() {
|
||||
return mock(JestClient.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@AutoConfigureBefore(HealthIndicatorAutoConfiguration.class)
|
||||
protected static class LdapConfiguration {
|
||||
|
||||
@Bean
|
||||
public LdapOperations ldapOperations() {
|
||||
return mock(LdapOperations.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@AutoConfigureBefore(HealthIndicatorAutoConfiguration.class)
|
||||
protected static class Neo4jConfiguration {
|
||||
|
||||
@Bean
|
||||
public SessionFactory sessionFactory() {
|
||||
return mock(SessionFactory.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.assertj.core.api.Condition;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.RedisReactiveHealthIndicator;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.boot.test.context.runner.ContextConsumer;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link ReactiveHealthIndicatorsConfiguration}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class ReactiveHealthIndicatorsConfigurationTests {
|
||||
|
||||
public final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(
|
||||
AutoConfigurations.of(HealthIndicatorAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
public void redisHealthIndicator() {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(
|
||||
RedisAutoConfiguration.class))
|
||||
.withPropertyValues("management.health.diskspace.enabled:false")
|
||||
.run(hasSingleReactiveHealthIndicator(RedisReactiveHealthIndicator.class));
|
||||
}
|
||||
|
||||
private ContextConsumer<AssertableApplicationContext> hasSingleReactiveHealthIndicator(
|
||||
Class<? extends ReactiveHealthIndicator> type) {
|
||||
return (context) -> assertThat(context).getBeans(ReactiveHealthIndicator.class)
|
||||
.hasSize(1)
|
||||
.hasValueSatisfying(
|
||||
new Condition<>((indicator) -> indicator.getClass().equals(type),
|
||||
"Wrong indicator type"));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -21,9 +21,9 @@ import java.util.Map;
|
|||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.health.CompositeHealthIndicatorFactory;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.actuate.health.HealthIndicatorFactory;
|
||||
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
|
||||
import org.springframework.boot.actuate.health.Status;
|
||||
|
||||
|
|
@ -59,7 +59,7 @@ public class HealthEndpointTests {
|
|||
|
||||
private HealthIndicator createHealthIndicator(
|
||||
Map<String, HealthIndicator> healthIndicators) {
|
||||
return new HealthIndicatorFactory()
|
||||
return new CompositeHealthIndicatorFactory()
|
||||
.createHealthIndicator(new OrderedHealthAggregator(), healthIndicators);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,9 +21,9 @@ import java.util.Map;
|
|||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.health.CompositeHealthIndicatorFactory;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.actuate.health.HealthIndicatorFactory;
|
||||
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
|
||||
import org.springframework.boot.actuate.health.Status;
|
||||
|
||||
|
|
@ -52,7 +52,7 @@ public class StatusEndpointTests {
|
|||
|
||||
private HealthIndicator createHealthIndicator(
|
||||
Map<String, HealthIndicator> healthIndicators) {
|
||||
return new HealthIndicatorFactory()
|
||||
return new CompositeHealthIndicatorFactory()
|
||||
.createHealthIndicator(new OrderedHealthAggregator(), healthIndicators);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,9 +22,9 @@ import org.junit.Test;
|
|||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.HealthEndpoint;
|
||||
import org.springframework.boot.actuate.health.CompositeHealthIndicatorFactory;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.actuate.health.HealthIndicatorFactory;
|
||||
import org.springframework.boot.actuate.health.HealthStatusHttpMapper;
|
||||
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
|
@ -69,7 +69,7 @@ public class HealthEndpointWebIntegrationTests {
|
|||
@Bean
|
||||
public HealthEndpoint healthEndpoint(
|
||||
Map<String, HealthIndicator> healthIndicators) {
|
||||
return new HealthEndpoint(new HealthIndicatorFactory().createHealthIndicator(
|
||||
return new HealthEndpoint(new CompositeHealthIndicatorFactory().createHealthIndicator(
|
||||
new OrderedHealthAggregator(), healthIndicators));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,9 +22,9 @@ import org.junit.Test;
|
|||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.StatusEndpoint;
|
||||
import org.springframework.boot.actuate.health.CompositeHealthIndicatorFactory;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.actuate.health.HealthIndicatorFactory;
|
||||
import org.springframework.boot.actuate.health.HealthStatusHttpMapper;
|
||||
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
|
@ -67,7 +67,7 @@ public class StatusEndpointWebIntegrationTests {
|
|||
@Bean
|
||||
public StatusEndpoint statusEndpoint(
|
||||
Map<String, HealthIndicator> healthIndicators) {
|
||||
return new StatusEndpoint(new HealthIndicatorFactory().createHealthIndicator(
|
||||
return new StatusEndpoint(new CompositeHealthIndicatorFactory().createHealthIndicator(
|
||||
new OrderedHealthAggregator(), healthIndicators));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,13 +24,13 @@ import org.junit.Test;
|
|||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link HealthIndicatorFactory}.
|
||||
* Tests for {@link CompositeHealthIndicatorFactory}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Christian Dupuis
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class HealthIndicatorFactoryTests {
|
||||
public class CompositeHealthIndicatorFactoryTests {
|
||||
|
||||
@Test
|
||||
public void upAndUpIsAggregatedToUp() throws Exception {
|
||||
|
|
@ -62,7 +62,7 @@ public class HealthIndicatorFactoryTests {
|
|||
|
||||
private HealthIndicator createHealthIndicator(
|
||||
Map<String, HealthIndicator> healthIndicators) {
|
||||
return new HealthIndicatorFactory()
|
||||
return new CompositeHealthIndicatorFactory()
|
||||
.createHealthIndicator(new OrderedHealthAggregator(), healthIndicators);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* Tests for {@link CompositeReactiveHealthIndicatorFactory}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class CompositeReactiveHealthIndicatorFactoryTests {
|
||||
|
||||
private static final Health UP = new Health.Builder().status(Status.UP).build();
|
||||
|
||||
private static final Health DOWN = new Health.Builder().status(Status.DOWN).build();
|
||||
|
||||
@Test
|
||||
public void noHealthIndicator() {
|
||||
ReactiveHealthIndicator healthIndicator = createHealthIndicator(
|
||||
Collections.singletonMap("test", () -> Mono.just(UP)), null);
|
||||
StepVerifier.create(healthIndicator.health()).consumeNextWith(h -> {
|
||||
assertThat(h.getStatus()).isEqualTo(Status.UP);
|
||||
assertThat(h.getDetails()).containsOnlyKeys("test");
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultHealthIndicatorNameFactory() {
|
||||
ReactiveHealthIndicator healthIndicator = new CompositeReactiveHealthIndicatorFactory()
|
||||
.createReactiveHealthIndicator(new OrderedHealthAggregator(),
|
||||
Collections.singletonMap("myHealthIndicator", () -> Mono.just(UP)),
|
||||
null);
|
||||
StepVerifier.create(healthIndicator.health()).consumeNextWith(h -> {
|
||||
assertThat(h.getStatus()).isEqualTo(Status.UP);
|
||||
assertThat(h.getDetails()).containsOnlyKeys("my");
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void healthIndicatorIsAdapted() {
|
||||
ReactiveHealthIndicator healthIndicator = createHealthIndicator(
|
||||
Collections.singletonMap("test", () -> Mono.just(UP)),
|
||||
Collections.singletonMap("regular", () -> DOWN));
|
||||
StepVerifier.create(healthIndicator.health()).consumeNextWith(h -> {
|
||||
assertThat(h.getStatus()).isEqualTo(Status.DOWN);
|
||||
assertThat(h.getDetails()).containsOnlyKeys("test", "regular");
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void reactiveHealthIndicatorTakesPrecedence() {
|
||||
ReactiveHealthIndicator reactivehealthIndicator = mock(ReactiveHealthIndicator.class);
|
||||
given(reactivehealthIndicator.health()).willReturn(Mono.just(UP));
|
||||
HealthIndicator regularHealthIndicator = mock(HealthIndicator.class);
|
||||
given(regularHealthIndicator.health()).willReturn(UP);
|
||||
ReactiveHealthIndicator healthIndicator = createHealthIndicator(
|
||||
Collections.singletonMap("test", reactivehealthIndicator),
|
||||
Collections.singletonMap("test", regularHealthIndicator));
|
||||
StepVerifier.create(healthIndicator.health()).consumeNextWith(h -> {
|
||||
assertThat(h.getStatus()).isEqualTo(Status.UP);
|
||||
assertThat(h.getDetails()).containsOnlyKeys("test");
|
||||
}).verifyComplete();
|
||||
verify(reactivehealthIndicator, times(1)).health();
|
||||
verify(regularHealthIndicator, times(0)).health();
|
||||
}
|
||||
|
||||
private ReactiveHealthIndicator createHealthIndicator(
|
||||
Map<String, ReactiveHealthIndicator> reactiveHealthIndicators,
|
||||
Map<String, HealthIndicator> healthIndicators) {
|
||||
return new CompositeReactiveHealthIndicatorFactory(n -> n)
|
||||
.createReactiveHealthIndicator(new OrderedHealthAggregator(),
|
||||
reactiveHealthIndicators, healthIndicators);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.time.Duration;
|
||||
|
||||
import org.junit.Test;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link CompositeReactiveHealthIndicator}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class CompositeReactiveHealthIndicatorTests {
|
||||
|
||||
private static final Health UNKNOWN_HEALTH = Health.unknown()
|
||||
.withDetail("detail", "value").build();
|
||||
|
||||
private static final Health HEALTHY = Health.up().build();
|
||||
|
||||
private OrderedHealthAggregator healthAggregator = new OrderedHealthAggregator();
|
||||
|
||||
private CompositeReactiveHealthIndicator indicator =
|
||||
new CompositeReactiveHealthIndicator(this.healthAggregator);
|
||||
|
||||
@Test
|
||||
public void singleIndicator() {
|
||||
this.indicator.addHealthIndicator("test", () -> Mono.just(HEALTHY));
|
||||
StepVerifier.create(this.indicator.health()).consumeNextWith(h -> {
|
||||
assertThat(h.getStatus()).isEqualTo(Status.UP);
|
||||
assertThat(h.getDetails()).containsOnlyKeys("test");
|
||||
assertThat(h.getDetails().get("test")).isEqualTo(HEALTHY);
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void longHealth() {
|
||||
for (int i = 0; i < 50; i++) {
|
||||
this.indicator.addHealthIndicator(
|
||||
"test" + i, new TimeoutHealth(10000, Status.UP));
|
||||
}
|
||||
StepVerifier.withVirtualTime(this.indicator::health)
|
||||
.expectSubscription()
|
||||
.thenAwait(Duration.ofMillis(10000))
|
||||
.consumeNextWith(h -> {
|
||||
assertThat(h.getStatus()).isEqualTo(Status.UP);
|
||||
assertThat(h.getDetails()).hasSize(50);
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void timeoutReachedUsesFallback() {
|
||||
this.indicator.addHealthIndicator("slow", new TimeoutHealth(10000, Status.UP))
|
||||
.addHealthIndicator("fast", new TimeoutHealth(10, Status.UP))
|
||||
.timeoutStrategy(100, UNKNOWN_HEALTH);
|
||||
StepVerifier.create(this.indicator.health()).consumeNextWith(h -> {
|
||||
assertThat(h.getStatus()).isEqualTo(Status.UP);
|
||||
assertThat(h.getDetails()).containsOnlyKeys("slow", "fast");
|
||||
assertThat(h.getDetails().get("slow")).isEqualTo(UNKNOWN_HEALTH);
|
||||
assertThat(h.getDetails().get("fast")).isEqualTo(HEALTHY);
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void timeoutNotReached() {
|
||||
this.indicator.addHealthIndicator("slow", new TimeoutHealth(10000, Status.UP))
|
||||
.addHealthIndicator("fast", new TimeoutHealth(10, Status.UP))
|
||||
.timeoutStrategy(20000, null);
|
||||
StepVerifier.withVirtualTime(this.indicator::health)
|
||||
.expectSubscription()
|
||||
.thenAwait(Duration.ofMillis(10000))
|
||||
.consumeNextWith(h -> {
|
||||
assertThat(h.getStatus()).isEqualTo(Status.UP);
|
||||
assertThat(h.getDetails()).containsOnlyKeys("slow", "fast");
|
||||
assertThat(h.getDetails().get("slow")).isEqualTo(HEALTHY);
|
||||
assertThat(h.getDetails().get("fast")).isEqualTo(HEALTHY);
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
static class TimeoutHealth implements ReactiveHealthIndicator {
|
||||
|
||||
private final long timeout;
|
||||
|
||||
private final Status status;
|
||||
|
||||
TimeoutHealth(long timeout, Status status) {
|
||||
this.timeout = timeout;
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Health> health() {
|
||||
return Mono.delay(Duration.ofMillis(this.timeout))
|
||||
.map(l -> Health.status(this.status).build());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.junit.Test;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link HealthIndicatorReactiveAdapter}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class HealthIndicatorReactiveAdapterTests {
|
||||
|
||||
@Test
|
||||
public void delegateReturnsHealth() {
|
||||
HealthIndicator delegate = mock(HealthIndicator.class);
|
||||
HealthIndicatorReactiveAdapter adapter = new HealthIndicatorReactiveAdapter(delegate);
|
||||
Health status = Health.up().build();
|
||||
given(delegate.health()).willReturn(status);
|
||||
StepVerifier.create(adapter.health()).expectNext(status).verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void delegateThrowError() {
|
||||
HealthIndicator delegate = mock(HealthIndicator.class);
|
||||
HealthIndicatorReactiveAdapter adapter = new HealthIndicatorReactiveAdapter(delegate);
|
||||
given(delegate.health()).willThrow(new IllegalStateException("Expected"));
|
||||
StepVerifier.create(adapter.health()).expectError(IllegalStateException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void delegateRunsOnTheElasticScheduler() {
|
||||
String currentThread = Thread.currentThread().getName();
|
||||
HealthIndicator delegate = () -> Health.status(Thread.currentThread().getName()
|
||||
.equals(currentThread) ? Status.DOWN : Status.UP).build();
|
||||
HealthIndicatorReactiveAdapter adapter = new HealthIndicatorReactiveAdapter(delegate);
|
||||
StepVerifier.create(adapter.health()).expectNext(Health.status(Status.UP).build())
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -22,11 +22,6 @@ import java.util.Properties;
|
|||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.data.redis.RedisConnectionFailureException;
|
||||
import org.springframework.data.redis.connection.ClusterInfo;
|
||||
import org.springframework.data.redis.connection.RedisClusterConnection;
|
||||
|
|
@ -49,18 +44,6 @@ import static org.mockito.Mockito.verify;
|
|||
*/
|
||||
public class RedisHealthIndicatorTests {
|
||||
|
||||
@Test
|
||||
public void indicatorExists() {
|
||||
new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class,
|
||||
EndpointAutoConfiguration.class,
|
||||
HealthIndicatorAutoConfiguration.class))
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(RedisConnectionFactory.class);
|
||||
assertThat(context).hasSingleBean(RedisHealthIndicator.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void redisIsUp() throws Exception {
|
||||
Properties info = new Properties();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.Properties;
|
||||
|
||||
import org.junit.Test;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.data.redis.RedisConnectionFailureException;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.ReactiveServerCommands;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link RedisReactiveHealthIndicator}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class RedisReactiveHealthIndicatorTests {
|
||||
|
||||
@Test
|
||||
public void redisIsUp() throws Exception {
|
||||
Properties info = new Properties();
|
||||
info.put("redis_version", "2.8.9");
|
||||
ReactiveServerCommands commands = mock(ReactiveServerCommands.class);
|
||||
given(commands.info()).willReturn(Mono.just(info));
|
||||
RedisReactiveHealthIndicator healthIndicator = createHealthIndicator(commands);
|
||||
Mono<Health> health = healthIndicator.health();
|
||||
StepVerifier.create(health).consumeNextWith(h -> {
|
||||
assertThat(h.getStatus()).isEqualTo(Status.UP);
|
||||
assertThat(h.getDetails()).containsOnlyKeys("version");
|
||||
assertThat(h.getDetails().get("version")).isEqualTo("2.8.9");
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void redisIsDown() throws Exception {
|
||||
ReactiveServerCommands commands = mock(ReactiveServerCommands.class);
|
||||
given(commands.info()).willReturn(Mono.error(
|
||||
new RedisConnectionFailureException("Connection failed")));
|
||||
RedisReactiveHealthIndicator healthIndicator = createHealthIndicator(commands);
|
||||
Mono<Health> health = healthIndicator.health();
|
||||
StepVerifier.create(health)
|
||||
.expectError(RedisConnectionFailureException.class);
|
||||
}
|
||||
|
||||
private RedisReactiveHealthIndicator createHealthIndicator(
|
||||
ReactiveServerCommands serverCommands) {
|
||||
ReactiveRedisConnection redisConnection = mock(ReactiveRedisConnection.class);
|
||||
ReactiveRedisConnectionFactory redisConnectionFactory = mock(
|
||||
ReactiveRedisConnectionFactory.class);
|
||||
given(redisConnectionFactory.getReactiveConnection()).willReturn(redisConnection);
|
||||
given(redisConnection.serverCommands()).willReturn(serverCommands);
|
||||
return new RedisReactiveHealthIndicator(redisConnectionFactory);
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue