Commit b6f38669 authored by Christian Dupuis's avatar Christian Dupuis

Merge branch 'rework-health-support'

parents d59cbc83 0c2bc99a
......@@ -37,6 +37,8 @@ import org.springframework.boot.actuate.endpoint.RequestMappingEndpoint;
import org.springframework.boot.actuate.endpoint.ShutdownEndpoint;
import org.springframework.boot.actuate.endpoint.TraceEndpoint;
import org.springframework.boot.actuate.endpoint.VanillaPublicMetrics;
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
import org.springframework.boot.actuate.health.HealthAggregator;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.metrics.reader.MetricReader;
import org.springframework.boot.actuate.metrics.repository.InMemoryMetricRepository;
......@@ -74,7 +76,10 @@ public class EndpointAutoConfiguration {
private InfoPropertiesConfiguration properties;
@Autowired(required = false)
Map<String, HealthIndicator<? extends Object>> healthIndicators = new HashMap<String, HealthIndicator<? extends Object>>();
private HealthAggregator healthAggregator = new OrderedHealthAggregator();
@Autowired(required = false)
Map<String, HealthIndicator> healthIndicators = new HashMap<String, HealthIndicator>();
@Autowired(required = false)
private MetricReader metricRepository = new InMemoryMetricRepository();
......@@ -97,7 +102,7 @@ public class EndpointAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public HealthEndpoint healthEndpoint() {
return new HealthEndpoint(this.healthIndicators);
return new HealthEndpoint(this.healthAggregator, this.healthIndicators);
}
@Bean
......
......@@ -33,10 +33,12 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.actuate.endpoint.EnvironmentEndpoint;
import org.springframework.boot.actuate.endpoint.HealthEndpoint;
import org.springframework.boot.actuate.endpoint.MetricsEndpoint;
import org.springframework.boot.actuate.endpoint.ShutdownEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping;
import org.springframework.boot.actuate.endpoint.mvc.EnvironmentMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints;
import org.springframework.boot.actuate.endpoint.mvc.ShutdownMvcEndpoint;
......@@ -54,6 +56,7 @@ import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
import org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext;
import org.springframework.boot.context.embedded.EmbeddedServletContainerException;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
......@@ -72,16 +75,18 @@ import org.springframework.web.servlet.DispatcherServlet;
* different port to {@link ServerProperties} a new child context is created, otherwise it
* is assumed that endpoint requests will be mapped and handled via an already registered
* {@link DispatcherServlet}.
*
*
* @author Dave Syer
* @author Phillip Webb
* @author Christian Dupuis
*/
@Configuration
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class })
@ConditionalOnWebApplication
@AutoConfigureAfter({ PropertyPlaceholderAutoConfiguration.class,
EmbeddedServletContainerAutoConfiguration.class, WebMvcAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class })
EmbeddedServletContainerAutoConfiguration.class, WebMvcAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class })
@EnableConfigurationProperties(HealthMvcEndpointProperties.class)
public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware,
ApplicationListener<ContextRefreshedEvent> {
......@@ -89,6 +94,9 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware,
private ApplicationContext applicationContext;
@Autowired
private HealthMvcEndpointProperties healthMvcEndpointProperties;
@Autowired
private ManagementServerProperties managementServerProperties;
......@@ -101,8 +109,8 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware,
@Bean
@ConditionalOnMissingBean
public EndpointHandlerMapping endpointHandlerMapping() {
EndpointHandlerMapping mapping = new EndpointHandlerMapping(mvcEndpoints()
.getEndpoints());
EndpointHandlerMapping mapping = new EndpointHandlerMapping(
mvcEndpoints().getEndpoints());
boolean disabled = ManagementServerPort.get(this.applicationContext) != ManagementServerPort.SAME;
mapping.setDisabled(disabled);
if (!disabled) {
......@@ -125,10 +133,12 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware,
// instantiation of ManagementServerProperties.
@Configuration
protected static class ApplicationContextFilterConfiguration {
@Bean
public Filter applicationContextIdFilter(ApplicationContext context) {
final String id = context.getId();
return new OncePerRequestFilter() {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
......@@ -153,6 +163,17 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware,
return new EnvironmentMvcEndpoint(delegate);
}
@Bean
@ConditionalOnBean(HealthEndpoint.class)
@ConditionalOnExpression("${endpoints.health.enabled:true}")
public HealthMvcEndpoint healthMvcEndpoint(HealthEndpoint delegate) {
HealthMvcEndpoint healthMvcEndpoint = new HealthMvcEndpoint(delegate);
if (this.healthMvcEndpointProperties.getMapping() != null) {
healthMvcEndpoint.setStatusMapping(this.healthMvcEndpointProperties.getMapping());
}
return healthMvcEndpoint;
}
@Bean
@ConditionalOnBean(MetricsEndpoint.class)
@ConditionalOnExpression("${endpoints.metrics.enabled:true}")
......@@ -184,27 +205,25 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware,
// Ensure close on the parent also closes the child
if (this.applicationContext instanceof ConfigurableApplicationContext) {
((ConfigurableApplicationContext) this.applicationContext)
.addApplicationListener(new ApplicationListener<ContextClosedEvent>() {
@Override
public void onApplicationEvent(ContextClosedEvent event) {
if (event.getApplicationContext() == EndpointWebMvcAutoConfiguration.this.applicationContext) {
childContext.close();
}
}
});
((ConfigurableApplicationContext) this.applicationContext).addApplicationListener(new ApplicationListener<ContextClosedEvent>() {
@Override
public void onApplicationEvent(ContextClosedEvent event) {
if (event.getApplicationContext() == EndpointWebMvcAutoConfiguration.this.applicationContext) {
childContext.close();
}
}
});
}
try {
childContext.refresh();
}
catch (RuntimeException ex) {
} catch (RuntimeException ex) {
// No support currently for deploying a war with management.port=<different>,
// and this is the signature of that happening
if (ex instanceof EmbeddedServletContainerException
|| ex.getCause() instanceof EmbeddedServletContainerException) {
logger.warn("Could not start embedded container (management endpoints are still available through JMX)");
}
else {
} else {
throw ex;
}
}
......@@ -219,17 +238,14 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware,
ServerProperties serverProperties;
try {
serverProperties = beanFactory.getBean(ServerProperties.class);
}
catch (NoSuchBeanDefinitionException ex) {
} catch (NoSuchBeanDefinitionException ex) {
serverProperties = new ServerProperties();
}
ManagementServerProperties managementServerProperties;
try {
managementServerProperties = beanFactory
.getBean(ManagementServerProperties.class);
}
catch (NoSuchBeanDefinitionException ex) {
managementServerProperties = beanFactory.getBean(ManagementServerProperties.class);
} catch (NoSuchBeanDefinitionException ex) {
managementServerProperties = new ManagementServerProperties();
}
......
......@@ -16,15 +16,19 @@
package org.springframework.boot.actuate.autoconfigure;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.health.CompositeHealthIndicator;
import org.springframework.boot.actuate.health.HealthAggregator;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.MongoHealthIndicator;
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.SimpleDataSourceHealthIndicator;
......@@ -58,9 +62,22 @@ import org.springframework.data.redis.connection.RedisConnectionFactory;
RabbitAutoConfiguration.class })
public class HealthIndicatorAutoConfiguration {
@Value("${health.status.order:}")
private List<String> statusOrder = null;
@Bean
@ConditionalOnMissingBean
public HealthAggregator healthAggregator() {
OrderedHealthAggregator healthAggregator = new OrderedHealthAggregator();
if (this.statusOrder != null) {
healthAggregator.setStatusOrder(this.statusOrder);
}
return healthAggregator;
}
@Bean
@ConditionalOnMissingBean(HealthIndicator.class)
public HealthIndicator<?> statusHealthIndicator() {
public HealthIndicator statusHealthIndicator() {
return new VanillaHealthIndicator();
}
......@@ -69,18 +86,22 @@ public class HealthIndicatorAutoConfiguration {
@ConditionalOnExpression("${health.db.enabled:true}")
public static class DataSourcesHealthIndicatorConfiguration {
@Autowired
private HealthAggregator healthAggregator;
@Autowired(required = false)
private Map<String, DataSource> dataSources;
@Bean
@ConditionalOnMissingBean(name = "dbHealthIndicator")
public HealthIndicator<? extends Object> dbHealthIndicator() {
public HealthIndicator dbHealthIndicator() {
if (this.dataSources.size() == 1) {
return new SimpleDataSourceHealthIndicator(this.dataSources.values()
.iterator().next());
}
CompositeHealthIndicator composite = new CompositeHealthIndicator();
CompositeHealthIndicator composite = new CompositeHealthIndicator(
this.healthAggregator);
for (Map.Entry<String, DataSource> entry : this.dataSources.entrySet()) {
composite.addHealthIndicator(entry.getKey(),
new SimpleDataSourceHealthIndicator(entry.getValue()));
......@@ -94,18 +115,22 @@ public class HealthIndicatorAutoConfiguration {
@ConditionalOnExpression("${health.mongo.enabled:true}")
public static class MongoHealthIndicatorConfiguration {
@Autowired
private HealthAggregator healthAggregator;
@Autowired
private Map<String, MongoTemplate> mongoTemplates;
@Bean
@ConditionalOnMissingBean(name = "mongoHealthIndicator")
public HealthIndicator<?> mongoHealthIndicator() {
public HealthIndicator mongoHealthIndicator() {
if (this.mongoTemplates.size() == 1) {
return new MongoHealthIndicator(this.mongoTemplates.values().iterator()
.next());
}
CompositeHealthIndicator composite = new CompositeHealthIndicator();
CompositeHealthIndicator composite = new CompositeHealthIndicator(
this.healthAggregator);
for (Map.Entry<String, MongoTemplate> entry : this.mongoTemplates.entrySet()) {
composite.addHealthIndicator(entry.getKey(), new MongoHealthIndicator(
entry.getValue()));
......@@ -119,18 +144,22 @@ public class HealthIndicatorAutoConfiguration {
@ConditionalOnExpression("${health.redis.enabled:true}")
public static class RedisHealthIndicatorConfiguration {
@Autowired
private HealthAggregator healthAggregator;
@Autowired
private Map<String, RedisConnectionFactory> redisConnectionFactories;
@Bean
@ConditionalOnMissingBean(name = "redisHealthIndicator")
public HealthIndicator<?> redisHealthIndicator() {
public HealthIndicator redisHealthIndicator() {
if (this.redisConnectionFactories.size() == 1) {
return new RedisHealthIndicator(this.redisConnectionFactories.values()
.iterator().next());
}
CompositeHealthIndicator composite = new CompositeHealthIndicator();
CompositeHealthIndicator composite = new CompositeHealthIndicator(
this.healthAggregator);
for (Map.Entry<String, RedisConnectionFactory> entry : this.redisConnectionFactories
.entrySet()) {
composite.addHealthIndicator(entry.getKey(), new RedisHealthIndicator(
......@@ -145,18 +174,22 @@ public class HealthIndicatorAutoConfiguration {
@ConditionalOnExpression("${health.rabbit.enabled:true}")
public static class RabbitHealthIndicatorConfiguration {
@Autowired
private HealthAggregator healthAggregator;
@Autowired
private Map<String, RabbitTemplate> rabbitTemplates;
@Bean
@ConditionalOnMissingBean(name = "rabbitHealthIndicator")
public HealthIndicator<?> rabbitHealthIndicator() {
public HealthIndicator rabbitHealthIndicator() {
if (this.rabbitTemplates.size() == 1) {
return new RabbitHealthIndicator(this.rabbitTemplates.values().iterator()
.next());
}
CompositeHealthIndicator composite = new CompositeHealthIndicator();
CompositeHealthIndicator composite = new CompositeHealthIndicator(
this.healthAggregator);
for (Map.Entry<String, RabbitTemplate> entry : this.rabbitTemplates
.entrySet()) {
composite.addHealthIndicator(entry.getKey(), new RabbitHealthIndicator(
......
/*
* Copyright 2012-2014 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;
import java.util.HashMap;
import java.util.Map;
import org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.http.HttpStatus;
/**
* Configuration properties for the {@link HealthMvcEndpoint}.
*
* @author Christian Dupuis
* @since 1.1.0
*/
@ConfigurationProperties(prefix = "endpoints.health")
public class HealthMvcEndpointProperties {
private Map<String, HttpStatus> mapping = new HashMap<String, HttpStatus>();
public Map<String, HttpStatus> getMapping() {
return this.mapping;
}
public void setMapping(Map<String, HttpStatus> mapping) {
this.mapping = mapping;
}
}
\ No newline at end of file
......@@ -16,10 +16,11 @@
package org.springframework.boot.actuate.endpoint;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.springframework.boot.actuate.health.CompositeHealthIndicator;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthAggregator;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.util.Assert;
......@@ -30,30 +31,39 @@ import org.springframework.util.Assert;
* @author Dave Syer
* @author Christian Dupuis
*/
@ConfigurationProperties(prefix = "endpoints.health", ignoreUnknownFields = false)
public class HealthEndpoint extends AbstractEndpoint<Map<String, Object>> {
@ConfigurationProperties(prefix = "endpoints.health", ignoreUnknownFields = true)
public class HealthEndpoint extends AbstractEndpoint<Health> {
private final Map<String, HealthIndicator<? extends Object>> healthIndicators;
private final HealthIndicator healthIndicator;
/**
* Create a new {@link HealthIndicator} instance.
*/
public HealthEndpoint(Map<String, HealthIndicator<? extends Object>> healthIndicators) {
public HealthEndpoint(HealthAggregator healthAggregator,
Map<String, HealthIndicator> healthIndicators) {
super("health", false, true);
Assert.notNull(healthIndicators, "HealthIndicator must not be null");
this.healthIndicators = healthIndicators;
Assert.notNull(healthAggregator, "HealthAggregator must not be null");
Assert.notNull(healthIndicators, "HealthIndicators must not be null");
if (healthIndicators.size() == 1) {
this.healthIndicator = healthIndicators.values().iterator().next();
} else {
CompositeHealthIndicator healthIndicator = new CompositeHealthIndicator(
healthAggregator);
for (Map.Entry<String, HealthIndicator> h : healthIndicators.entrySet()) {
healthIndicator.addHealthIndicator(getKey(h.getKey()), h.getValue());
}
this.healthIndicator = healthIndicator;
}
}
/**
* Invoke all {@link HealthIndicator} delegates and collect their health information.
*/
@Override
public Map<String, Object> invoke() {
Map<String, Object> health = new LinkedHashMap<String, Object>();
for (Entry<String, HealthIndicator<?>> entry : this.healthIndicators.entrySet()) {
health.put(getKey(entry.getKey()), entry.getValue().health());
}
return health;
public Health invoke() {
return this.healthIndicator.health();
}
/**
......
/*
* Copyright 2012-2014 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.mvc;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.springframework.boot.actuate.endpoint.HealthEndpoint;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.Status;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* Adapter to expose {@link HealthEndpoint} as an {@link MvcEndpoint}.
*
* @author Christian Dupuis
* @since 1.1.0
*/
public class HealthMvcEndpoint extends EndpointMvcAdapter {
private Map<String, HttpStatus> statusMapping;
public HealthMvcEndpoint(HealthEndpoint delegate) {
super(delegate);
setupDefaultStatusMapping();
}
public void setStatusMapping(Map<String, HttpStatus> statusMapping) {
Assert.notNull(statusMapping, "StatusMapping must not be null");
this.statusMapping = statusMapping;
}
@RequestMapping
@ResponseBody
@Override
public Object invoke() {
if (!this.getDelegate().isEnabled()) {
// Shouldn't happen
return new ResponseEntity<Map<String, String>>(Collections.singletonMap(
"message", "This endpoint is disabled"), HttpStatus.NOT_FOUND);
}
Health health = (Health) getDelegate().invoke();
Status status = health.getStatus();
if (this.statusMapping.containsKey(status.getCode())) {
return new ResponseEntity<Health>(health,
this.statusMapping.get(status.getCode()));
}
return health;
}
private void setupDefaultStatusMapping() {
this.statusMapping = new HashMap<String, HttpStatus>();
this.statusMapping.put(Status.DOWN.getCode(), HttpStatus.SERVICE_UNAVAILABLE);
this.statusMapping.put(Status.OUT_OF_SERVICE.getCode(),
HttpStatus.SERVICE_UNAVAILABLE);
}
}
......@@ -19,22 +19,27 @@ package org.springframework.boot.actuate.health;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.util.Assert;
/**
* {@link HealthIndicator} that returns health indications from all registered delegates.
*
*
* @author Tyler J. Frederick
* @author Phillip Webb
* @author Christian Dupuis
* @since 1.1.0
*/
public class CompositeHealthIndicator implements HealthIndicator<Map<String, Object>> {
public class CompositeHealthIndicator implements HealthIndicator {
private final Map<String, HealthIndicator> indicators;
private final Map<String, HealthIndicator<?>> indicators;
private final HealthAggregator healthAggregator;
/**
* Create a new {@link CompositeHealthIndicator}.
*/
public CompositeHealthIndicator() {
this.indicators = new LinkedHashMap<String, HealthIndicator<?>>();
public CompositeHealthIndicator(HealthAggregator healthAggregator) {
this(healthAggregator, new LinkedHashMap<String, HealthIndicator>());
}
/**
......@@ -42,21 +47,25 @@ public class CompositeHealthIndicator implements HealthIndicator<Map<String, Obj
* @param indicators a map of {@link HealthIndicator}s with the key being used as an
* indicator name.
*/
public CompositeHealthIndicator(Map<String, HealthIndicator<?>> indicators) {
this.indicators = new LinkedHashMap<String, HealthIndicator<?>>(indicators);
public CompositeHealthIndicator(HealthAggregator healthAggregator,
Map<String, HealthIndicator> indicators) {
Assert.notNull(healthAggregator, "HealthAggregator must not be null");
Assert.notNull(healthAggregator, "Indicators must not be null");
this.indicators = new LinkedHashMap<String, HealthIndicator>(indicators);
this.healthAggregator = healthAggregator;
}
public void addHealthIndicator(String name, HealthIndicator<?> indicator) {
public void addHealthIndicator(String name, HealthIndicator indicator) {
this.indicators.put(name, indicator);
}
@Override
public Map<String, Object> health() {
Map<String, Object> health = new LinkedHashMap<String, Object>();
for (Map.Entry<String, HealthIndicator<?>> entry : this.indicators.entrySet()) {
health.put(entry.getKey(), entry.getValue().health());
public Health health() {
Map<String, Health> healths = new LinkedHashMap<String, Health>();
for (Map.Entry<String, HealthIndicator> entry : this.indicators.entrySet()) {
healths.put(entry.getKey(), entry.getValue().health());
}
return health;
return this.healthAggregator.aggregate(healths);
}
}
/*
* Copyright 2012-2014 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 org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
/**
* Value object used to carry information about the health information of a component or
* subsystem.
*
* <p>
* {@link Health} contains a {@link Status} to express the state of a component or
* subsystem and some additional details to carry some contextual information.
*
* <p>
* {@link Health} has a fluent API to make it easy to construct instances. Typical usage
* in a {@link HealthIndicator} would be:
*
* <code>
* Health health = new Health();
* try {
* // do some test to determine state of component
*
* health.up().withDetail("version", "1.1.2");
* }
* catch (Exception ex) {
* health.down().withException(ex);
* }
* return health;
* </code>
*
* @author Christian Dupuis
* @since 1.1.0
*/
@JsonInclude(Include.NON_EMPTY)
public class Health {
private Status status;
private Map<String, Object> details;
public Health() {
this(Status.UNKOWN);
}
public Health(Status status) {
this.status = status;
this.details = new LinkedHashMap<String, Object>();
}
public Health status(Status status) {
Assert.notNull(status, "Status must not be null");
this.status = status;
return this;
}
public Health up() {
return status(Status.UP);
}
public Health down() {
return status(Status.DOWN);
}
public Health withException(Exception ex) {
Assert.notNull(ex, "Exception must not be null");
return withDetail("error", ex.getClass().getName() + ": " + ex.getMessage());
}
@JsonAnySetter
public Health withDetail(String key, Object data) {
Assert.notNull(key, "Key must not be null");
Assert.notNull(data, "Data must not be null");
this.details.put(key, data);
return this;
}
@JsonUnwrapped
public Status getStatus() {
return this.status;
}
@JsonAnyGetter
public Map<String, Object> getDetails() {
return this.details;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj != null && obj instanceof Health) {
return ObjectUtils.nullSafeEquals(this.status, ((Health) obj).status)
&& ObjectUtils.nullSafeEquals(this.details, ((Health) obj).details);
}
return false;
}
@Override
public int hashCode() {
int hashCode = 0;
if (this.status != null) {
hashCode = this.status.hashCode();
}
return 13 * hashCode + this.details.hashCode();
}
}
/*
* Copyright 2012-2014 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.Map;
/**
* Strategy interface used by {@link CompositeHealthIndicator} to aggregate {@link Health}
* instances into a final one.
*
* <p>
* This is especially useful to combine subsystem states expressed through
* {@link Health#getStatus()} into one state for the entire system. The default
* implementation {@link OrderedHealthAggregator} sorts {@link Status} instances based on
* a priority list.
*
* <p>
* It is possible to add more complex {@link Status} types to the system. In that case
* either the {@link OrderedHealthAggregator} needs to be properly configured or users
* need to register a custom {@link HealthAggregator} as bean.
*
* @author Christian Dupuis
* @since 1.1.0
*/
public interface HealthAggregator {
/**
* Aggregate several given {@link Health} instances into one.
*/
Health aggregate(Map<String, Health> healths);
}
/*
* Copyright 2012-2013 the original author or authors.
* Copyright 2012-2014 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.
......@@ -18,15 +18,15 @@ package org.springframework.boot.actuate.health;
/**
* Strategy interface used to provide an indication of application health.
*
*
* @author Dave Syer
* @see VanillaHealthIndicator
*/
public interface HealthIndicator<T> {
public interface HealthIndicator {
/**
* @return an indication of health
*/
T health();
Health health();
}
......@@ -16,9 +16,6 @@
package org.springframework.boot.actuate.health;
import java.util.HashMap;
import java.util.Map;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.util.Assert;
......@@ -27,11 +24,11 @@ import com.mongodb.CommandResult;
/**
* Simple implementation of a {@link HealthIndicator} returning status information for
* Mongo data stores.
*
*
* @author Christian Dupuis
* @since 1.1.0
*/
public class MongoHealthIndicator implements HealthIndicator<Map<String, Object>> {
public class MongoHealthIndicator implements HealthIndicator {
private final MongoTemplate mongoTemplate;
......@@ -41,17 +38,15 @@ public class MongoHealthIndicator implements HealthIndicator<Map<String, Object>
}
@Override
public Map<String, Object> health() {
Map<String, Object> health = new HashMap<String, Object>();
public Health health() {
Health health = new Health();
try {
CommandResult result = this.mongoTemplate
.executeCommand("{ serverStatus: 1 }");
health.put("status", "ok");
health.put("version", result.getString("version"));
health.up().withDetail("version", result.getString("version"));
}
catch (Exception ex) {
health.put("status", "error");
health.put("error", ex.getClass().getName() + ": " + ex.getMessage());
health.down().withException(ex);
}
return health;
}
......
/*
* Copyright 2012-2014 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.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
/**
* Default {@link HealthAggregator} implementation that aggregates {@link Health}
* instances and determines the final system state based on a simple ordered list.
*
* <p>
* If a different order is required or a new {@link Status} type will be used, the order
* can be set by calling {@link #setStatusOrder(List)}.
*
* @author Christian Dupuis
* @since 1.1.0
*/
public class OrderedHealthAggregator implements HealthAggregator {
private List<String> statusOrder = Arrays.asList("DOWN", "OUT_OF_SERVICE", "UP",
"UNKOWN");
@Override
public Health aggregate(Map<String, Health> healths) {
Health health = new Health();
List<Status> status = new ArrayList<Status>();
for (Map.Entry<String, Health> h : healths.entrySet()) {
health.withDetail(h.getKey(), h.getValue());
status.add(h.getValue().getStatus());
}
health.status(aggregateStatus(status));
return health;
}
public void setStatusOrder(List<String> statusOrder) {
this.statusOrder = statusOrder;
}
protected Status aggregateStatus(List<Status> status) {
if (status.size() == 0) {
return Status.UNKOWN;
}
status.sort(new Comparator<Status>() {
@Override
public int compare(Status s1, Status s2) {
return Integer.valueOf(
OrderedHealthAggregator.this.statusOrder.indexOf(s1.getCode())).compareTo(
Integer.valueOf(OrderedHealthAggregator.this.statusOrder.indexOf(s2.getCode())));
}
});
return status.get(0);
}
}
......@@ -16,7 +16,6 @@
package org.springframework.boot.actuate.health;
import java.util.HashMap;
import java.util.Map;
import org.springframework.amqp.rabbit.core.ChannelCallback;
......@@ -28,11 +27,11 @@ import com.rabbitmq.client.Channel;
/**
* Simple implementation of a {@link HealthIndicator} returning status information for the
* RabbitMQ messaging system.
*
*
* @author Christian Dupuis
* @since 1.1.0
*/
public class RabbitHealthIndicator implements HealthIndicator<Map<String, Object>> {
public class RabbitHealthIndicator implements HealthIndicator {
private final RabbitTemplate rabbitTemplate;
......@@ -42,10 +41,10 @@ public class RabbitHealthIndicator implements HealthIndicator<Map<String, Object
}
@Override
public Map<String, Object> health() {
Map<String, Object> health = new HashMap<String, Object>();
public Health health() {
Health health = new Health();
try {
health.put("version",
health.up().withDetail("version",
this.rabbitTemplate.execute(new ChannelCallback<String>() {
@Override
......@@ -55,12 +54,9 @@ public class RabbitHealthIndicator implements HealthIndicator<Map<String, Object
return serverProperties.get("version").toString();
}
}));
health.put("status", "ok");
}
catch (Exception ex) {
health.put("status", "error");
health.put("error", ex.getClass().getName() + ": " + ex.getMessage());
health.down().withException(ex);
}
return health;
}
......
......@@ -16,8 +16,6 @@
package org.springframework.boot.actuate.health;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.springframework.data.redis.connection.RedisConnection;
......@@ -28,11 +26,11 @@ import org.springframework.util.Assert;
/**
* Simple implementation of a {@link HealthIndicator} returning status information for
* Redis data stores.
*
*
* @author Christian Dupuis
* @since 1.1.0
*/
public class RedisHealthIndicator implements HealthIndicator<Map<String, Object>> {
public class RedisHealthIndicator implements HealthIndicator {
private final RedisConnectionFactory redisConnectionFactory;
......@@ -42,19 +40,17 @@ public class RedisHealthIndicator implements HealthIndicator<Map<String, Object>
}
@Override
public Map<String, Object> health() {
Map<String, Object> health = new HashMap<String, Object>();
public Health health() {
Health health = new Health();
RedisConnection connection = null;
try {
connection = RedisConnectionUtils.getConnection(this.redisConnectionFactory);
Properties info = connection.info();
health.put("status", "ok");
health.put("version", info.getProperty("redis_version"));
health.up().withDetail("version", info.getProperty("redis_version"));
}
catch (Exception ex) {
health.put("status", "error");
health.put("error", ex.getClass().getName() + ": " + ex.getMessage());
health.down().withException(ex);
}
finally {
RedisConnectionUtils.releaseConnection(connection,
......
......@@ -19,7 +19,6 @@ package org.springframework.boot.actuate.health;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.sql.DataSource;
......@@ -32,8 +31,9 @@ import org.springframework.util.StringUtils;
/**
* Simple implementation of {@link HealthIndicator} that returns a status and also
* attempts a simple database test.
*
*
* @author Dave Syer
* @author Christian Dupuis
*/
public class SimpleDataSourceHealthIndicator implements
HealthIndicator<Map<String, Object>> {
......@@ -72,9 +72,10 @@ public class SimpleDataSourceHealthIndicator implements
}
@Override
public Map<String, Object> health() {
LinkedHashMap<String, Object> health = new LinkedHashMap<String, Object>();
health.put("status", "ok");
public Health health() {
Health health = new Health();
health.up();
String product = "unknown";
if (this.dataSource != null) {
try {
......@@ -85,21 +86,19 @@ public class SimpleDataSourceHealthIndicator implements
return connection.getMetaData().getDatabaseProductName();
}
});
health.put("database", product);
health.withDetail("database", product);
}
catch (DataAccessException ex) {
health.put("status", "error");
health.put("error", ex.getClass().getName() + ": " + ex.getMessage());
health.down().withException(ex);
}
String query = detectQuery(product);
if (StringUtils.hasText(query)) {
try {
health.put("hello",
health.withDetail("hello",
this.jdbcTemplate.queryForObject(query, Object.class));
}
catch (Exception ex) {
health.put("status", "error");
health.put("error", ex.getClass().getName() + ": " + ex.getMessage());
health.down().withException(ex);
}
}
}
......
/*
* Copyright 2012-2014 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.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Value object to express state of a component or subsystem.
*
* <p>
* Status provides convenient constants for commonly used states like {@link #UP},
* {@link #DOWN} or {@link #OUT_OF_SERVICE}.
*
* <p>
* Custom states can also be created and used throughout the Spring Boot Health subsystem.
*
* @author Christian Dupuis
* @since 1.1.0
*/
@JsonInclude(Include.NON_EMPTY)
public class Status {
/**
* Convenient constant value representing unknown state
*/
public static final Status UNKOWN = new Status("UNKOWN");
/**
* Convenient constant value representing up state
*/
public static final Status UP = new Status("UP");
/**
* Convenient constant value representing down state
*/
public static final Status DOWN = new Status("DOWN");
/**
* Convenient constant value representing out-of-service state
*/
public static final Status OUT_OF_SERVICE = new Status("OUT_OF_SERVICE");
private final String code;
private final String description;
public Status(String code) {
this(code, "");
}
public Status(String code, String description) {
Assert.notNull(code, "Code must not be null");
Assert.notNull(description, "Description must not be null");
this.code = code;
this.description = description;
}
@JsonProperty("status")
public String getCode() {
return this.code;
}
@JsonInclude(Include.NON_EMPTY)
public String getDescription() {
return this.description;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj != null && obj instanceof Status) {
return ObjectUtils.nullSafeEquals(this.code, ((Status) obj).code);
}
return false;
}
@Override
public int hashCode() {
return this.code.hashCode();
}
}
\ No newline at end of file
......@@ -16,16 +16,18 @@
package org.springframework.boot.actuate.health;
/**
* Default implementation of {@link HealthIndicator} that simply returns {@literal "ok"}.
*
*
* @author Dave Syer
* @author Christian Dupuis
*/
public class VanillaHealthIndicator implements HealthIndicator<String> {
public class VanillaHealthIndicator implements HealthIndicator {
@Override
public String health() {
return "ok";
public Health health() {
return new Health(Status.UP);
}
}
......@@ -16,8 +16,6 @@
package org.springframework.boot.actuate.autoconfigure;
import java.util.Map;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
......@@ -31,6 +29,7 @@ import org.springframework.boot.actuate.endpoint.MetricsEndpoint;
import org.springframework.boot.actuate.endpoint.RequestMappingEndpoint;
import org.springframework.boot.actuate.endpoint.ShutdownEndpoint;
import org.springframework.boot.actuate.endpoint.TraceEndpoint;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport;
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
import org.springframework.boot.test.EnvironmentTestUtils;
......@@ -43,10 +42,11 @@ import static org.junit.Assert.assertTrue;
/**
* Tests for {@link EndpointAutoConfiguration}.
*
*
* @author Dave Syer
* @author Phillip Webb
* @author Greg Turnquist
* @author Christian Dupuis
*/
public class EndpointAutoConfigurationTests {
......@@ -80,7 +80,6 @@ public class EndpointAutoConfigurationTests {
}
@Test
@SuppressWarnings("unchecked")
public void healthEndpoint() {
this.context = new AnnotationConfigApplicationContext();
this.context.register(EmbeddedDataSourceConfiguration.class,
......@@ -88,12 +87,9 @@ public class EndpointAutoConfigurationTests {
this.context.refresh();
HealthEndpoint bean = this.context.getBean(HealthEndpoint.class);
assertNotNull(bean);
Map<String, Object> result = bean.invoke();
Health result = bean.invoke();
assertNotNull(result);
assertTrue("Wrong result: " + result,
((Map<String, Object>) result.get("db")).containsKey("status"));
assertTrue("Wrong result: " + result,
((Map<String, Object>) result.get("db")).containsKey("database"));
assertTrue("Wrong result: " + result, result.getDetails().containsKey("database"));
}
@Test
......@@ -104,9 +100,8 @@ public class EndpointAutoConfigurationTests {
this.context.refresh();
HealthEndpoint bean = this.context.getBean(HealthEndpoint.class);
assertNotNull(bean);
Map<String, Object> result = bean.invoke();
Health result = bean.invoke();
assertNotNull(result);
assertTrue("Wrong result: " + result, result.containsKey("status"));
}
@Test
......
......@@ -60,7 +60,6 @@ public class HealthIndicatorAutoConfigurationTests {
}
}
@SuppressWarnings("rawtypes")
@Test
public void defaultHealthIndicator() {
this.context = new AnnotationConfigApplicationContext();
......@@ -73,7 +72,6 @@ public class HealthIndicatorAutoConfigurationTests {
.getClass());
}
@SuppressWarnings("rawtypes")
@Test
public void redisHealthIndicator() {
this.context = new AnnotationConfigApplicationContext();
......@@ -87,7 +85,6 @@ public class HealthIndicatorAutoConfigurationTests {
.getClass());
}
@SuppressWarnings("rawtypes")
@Test
public void notRedisHealthIndicator() {
this.context = new AnnotationConfigApplicationContext();
......@@ -102,7 +99,6 @@ public class HealthIndicatorAutoConfigurationTests {
.getClass());
}
@SuppressWarnings("rawtypes")
@Test
public void mongoHealthIndicator() {
this.context = new AnnotationConfigApplicationContext();
......@@ -116,7 +112,6 @@ public class HealthIndicatorAutoConfigurationTests {
.getClass());
}
@SuppressWarnings("rawtypes")
@Test
public void notMongoHealthIndicator() {
this.context = new AnnotationConfigApplicationContext();
......@@ -131,7 +126,6 @@ public class HealthIndicatorAutoConfigurationTests {
.getClass());
}
@SuppressWarnings("rawtypes")
@Test
public void combinedHealthIndicator() {
this.context = new AnnotationConfigApplicationContext();
......@@ -143,7 +137,6 @@ public class HealthIndicatorAutoConfigurationTests {
assertEquals(2, beans.size());
}
@SuppressWarnings("rawtypes")
@Test
public void dataSourceHealthIndicator() {
this.context = new AnnotationConfigApplicationContext();
......@@ -157,7 +150,6 @@ public class HealthIndicatorAutoConfigurationTests {
.next().getClass());
}
@SuppressWarnings("rawtypes")
@Test
public void notDataSourceHealthIndicator() {
this.context = new AnnotationConfigApplicationContext();
......@@ -172,7 +164,6 @@ public class HealthIndicatorAutoConfigurationTests {
.getClass());
}
@SuppressWarnings("rawtypes")
@Test
public void rabbitHealthIndicator() {
this.context = new AnnotationConfigApplicationContext();
......@@ -186,7 +177,6 @@ public class HealthIndicatorAutoConfigurationTests {
.getClass());
}
@SuppressWarnings("rawtypes")
@Test
public void notRabbitHealthIndicator() {
this.context = new AnnotationConfigApplicationContext();
......
......@@ -16,11 +16,14 @@
package org.springframework.boot.actuate.endpoint;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthAggregator;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
import org.springframework.boot.actuate.health.Status;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
......@@ -32,6 +35,7 @@ import static org.junit.Assert.assertThat;
* Tests for {@link HealthEndpoint}.
*
* @author Phillip Webb
* @author Christian Dupuis
*/
public class HealthEndpointTests extends AbstractEndpointTests<HealthEndpoint> {
......@@ -41,9 +45,8 @@ public class HealthEndpointTests extends AbstractEndpointTests<HealthEndpoint> {
@Test
public void invoke() throws Exception {
Map<String, Object> result = new HashMap<String, Object>();
result.put("status", "fine");
assertThat(getEndpointBean().invoke(), equalTo(result));
Status result = new Status("FINE");
assertThat(getEndpointBean().invoke().getStatus(), equalTo(result));
}
@Configuration
......@@ -51,18 +54,25 @@ public class HealthEndpointTests extends AbstractEndpointTests<HealthEndpoint> {
public static class Config {
@Bean
public HealthEndpoint endpoint(Map<String, HealthIndicator<?>> healthIndicators) {
return new HealthEndpoint(healthIndicators);
public HealthEndpoint endpoint(HealthAggregator healthAggregator,
Map<String, HealthIndicator> healthIndicators) {
return new HealthEndpoint(healthAggregator, healthIndicators);
}
@Bean
public HealthIndicator<String> statusHealthIndicator() {
return new HealthIndicator<String>() {
public HealthIndicator statusHealthIndicator() {
return new HealthIndicator() {
@Override
public String health() {
return "fine";
public Health health() {
return new Health().status(new Status("FINE"));
}
};
}
@Bean
public HealthAggregator healthAggregator() {
return new OrderedHealthAggregator();
}
}
}
/*
* Copyright 2012-2014 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.mvc;
import java.util.Collections;
import org.junit.Before;
import org.junit.Test;
import org.springframework.boot.actuate.endpoint.HealthEndpoint;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.Status;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* Tests for {@link HealthMvcEndpoint}.
*
* @author Christian Dupuis
*/
public class HealthMvcEndpointTests {
private HealthEndpoint endpoint = null;
private HealthMvcEndpoint mvc = null;
@Before
public void init() {
this.endpoint = mock(HealthEndpoint.class);
when(this.endpoint.isEnabled()).thenReturn(true);
this.mvc = new HealthMvcEndpoint(this.endpoint);
}
@Test
public void up() {
when(this.endpoint.invoke()).thenReturn(new Health().up());
Object result = this.mvc.invoke();
assertTrue(result instanceof Health);
assertTrue(((Health) result).getStatus() == Status.UP);
}
@SuppressWarnings("unchecked")
@Test
public void down() {
when(this.endpoint.invoke()).thenReturn(new Health().down());
Object result = this.mvc.invoke();
assertTrue(result instanceof ResponseEntity);
ResponseEntity<Health> response = (ResponseEntity<Health>) result;
assertTrue(response.getBody().getStatus() == Status.DOWN);
assertEquals(HttpStatus.SERVICE_UNAVAILABLE, response.getStatusCode());
}
@SuppressWarnings("unchecked")
@Test
public void customMapping() {
when(this.endpoint.invoke()).thenReturn(new Health().status(new Status("OK")));
this.mvc.setStatusMapping(Collections.singletonMap("OK",
HttpStatus.INTERNAL_SERVER_ERROR));
Object result = this.mvc.invoke();
assertTrue(result instanceof ResponseEntity);
ResponseEntity<Health> response = (ResponseEntity<Health>) result;
assertTrue(response.getBody().getStatus().equals(new Status("OK")));
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
}
}
......@@ -24,71 +24,108 @@ import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import com.fasterxml.jackson.databind.ObjectMapper;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasEntry;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.mockito.BDDMockito.given;
/**
* Tests for {@link CompositeHealthIndicator}
*
*
* @author Tyler J. Frederick
* @author Phillip Webb
* @author Christian Dupuis
*/
public class CompositeHealthIndicatorTests {
private HealthAggregator healthAggregator;
@Mock
private HealthIndicator<String> one;
private HealthIndicator one;
@Mock
private HealthIndicator<String> two;
private HealthIndicator two;
@Mock
private HealthIndicator<String> three;
private HealthIndicator three;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
given(this.one.health()).willReturn("1");
given(this.two.health()).willReturn("2");
given(this.three.health()).willReturn("3");
given(this.one.health()).willReturn(new Health().withDetail("1", "1"));
given(this.two.health()).willReturn(new Health().withDetail("2", "2"));
given(this.three.health()).willReturn(new Health().withDetail("3", "3"));
this.healthAggregator = new OrderedHealthAggregator();
}
@Test
public void createWithIndicators() throws Exception {
Map<String, HealthIndicator<?>> indicators = new HashMap<String, HealthIndicator<?>>();
Map<String, HealthIndicator> indicators = new HashMap<String, HealthIndicator>();
indicators.put("one", this.one);
indicators.put("two", this.two);
CompositeHealthIndicator composite = new CompositeHealthIndicator(indicators);
Map<String, Object> result = composite.health();
assertThat(result.size(), equalTo(2));
assertThat(result, hasEntry("one", (Object) "1"));
assertThat(result, hasEntry("two", (Object) "2"));
CompositeHealthIndicator composite = new CompositeHealthIndicator(
this.healthAggregator, indicators);
Health result = composite.health();
assertThat(result.getDetails().size(), equalTo(2));
assertThat(result.getDetails(),
hasEntry("one", (Object) new Health().withDetail("1", "1")));
assertThat(result.getDetails(),
hasEntry("two", (Object) new Health().withDetail("2", "2")));
}
@Test
public void createWithIndicatorsAndAdd() throws Exception {
Map<String, HealthIndicator<?>> indicators = new HashMap<String, HealthIndicator<?>>();
Map<String, HealthIndicator> indicators = new HashMap<String, HealthIndicator>();
indicators.put("one", this.one);
indicators.put("two", this.two);
CompositeHealthIndicator composite = new CompositeHealthIndicator(indicators);
CompositeHealthIndicator composite = new CompositeHealthIndicator(
this.healthAggregator, indicators);
composite.addHealthIndicator("three", this.three);
Map<String, Object> result = composite.health();
assertThat(result.size(), equalTo(3));
assertThat(result, hasEntry("one", (Object) "1"));
assertThat(result, hasEntry("two", (Object) "2"));
assertThat(result, hasEntry("three", (Object) "3"));
Health result = composite.health();
assertThat(result.getDetails().size(), equalTo(3));
assertThat(result.getDetails(),
hasEntry("one", (Object) new Health().withDetail("1", "1")));
assertThat(result.getDetails(),
hasEntry("two", (Object) new Health().withDetail("2", "2")));
assertThat(result.getDetails(),
hasEntry("three", (Object) new Health().withDetail("3", "3")));
}
@Test
public void createWithoutAndAdd() throws Exception {
CompositeHealthIndicator composite = new CompositeHealthIndicator();
CompositeHealthIndicator composite = new CompositeHealthIndicator(
this.healthAggregator);
composite.addHealthIndicator("one", this.one);
composite.addHealthIndicator("two", this.two);
Map<String, Object> result = composite.health();
assertThat(result.size(), equalTo(2));
assertThat(result, hasEntry("one", (Object) "1"));
assertThat(result, hasEntry("two", (Object) "2"));
Health result = composite.health();
assertThat(result.getDetails().size(), equalTo(2));
assertThat(result.getDetails(),
hasEntry("one", (Object) new Health().withDetail("1", "1")));
assertThat(result.getDetails(),
hasEntry("two", (Object) new Health().withDetail("2", "2")));
}
@Test
public void testSerialization() throws Exception {
Map<String, HealthIndicator> indicators = new HashMap<String, HealthIndicator>();
indicators.put("db1", this.one);
indicators.put("db2", this.two);
CompositeHealthIndicator innerComposite = new CompositeHealthIndicator(
this.healthAggregator, indicators);
CompositeHealthIndicator composite = new CompositeHealthIndicator(
this.healthAggregator);
composite.addHealthIndicator("db", innerComposite);
Health result = composite.health();
ObjectMapper mapper = new ObjectMapper();
assertEquals(
"{\"status\":\"UNKOWN\",\"db\":{\"status\":\"UNKOWN\",\"db1\":{\"status\":\"UNKOWN\",\"1\":\"1\"},\"db2\":{\"status\":\"UNKOWN\",\"2\":\"2\"}}}",
mapper.writeValueAsString(result));
}
}
......@@ -16,8 +16,6 @@
package org.springframework.boot.actuate.health;
import java.util.Map;
import org.junit.After;
import org.junit.Test;
import org.mockito.Mockito;
......@@ -38,7 +36,7 @@ import static org.junit.Assert.assertTrue;
/**
* Tests for {@link MongoHealthIndicator}.
*
*
* @author Christian Dupuis
*/
public class MongoHealthIndicatorTests {
......@@ -73,9 +71,9 @@ public class MongoHealthIndicatorTests {
commandResult);
MongoHealthIndicator healthIndicator = new MongoHealthIndicator(mongoTemplate);
Map<String, Object> health = healthIndicator.health();
assertEquals("ok", health.get("status"));
assertEquals("2.6.4", health.get("version"));
Health health = healthIndicator.health();
assertEquals(Status.UP, health.getStatus());
assertEquals("2.6.4", health.getDetails().get("version"));
Mockito.verify(commandResult).getString("version");
Mockito.verify(mongoTemplate).executeCommand("{ serverStatus: 1 }");
......@@ -88,9 +86,10 @@ public class MongoHealthIndicatorTests {
new MongoException("Connection failed"));
MongoHealthIndicator healthIndicator = new MongoHealthIndicator(mongoTemplate);
Map<String, Object> health = healthIndicator.health();
assertEquals("error", health.get("status"));
assertTrue(((String) health.get("error")).contains("Connection failed"));
Health health = healthIndicator.health();
assertEquals(Status.DOWN, health.getStatus());
assertTrue(((String) health.getDetails().get("error"))
.contains("Connection failed"));
Mockito.verify(mongoTemplate).executeCommand("{ serverStatus: 1 }");
}
......
/*
* Copyright 2012-2014 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.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* Tests for {@link OrderedHealthAggregator}.
*
* @author Christian Dupuis
*/
public class OrderedHealthAggregatorTests {
private OrderedHealthAggregator healthAggregator;
@Before
public void setup() {
this.healthAggregator = new OrderedHealthAggregator();
}
@Test
public void testDefaultOrdering() {
Map<String, Health> healths = new HashMap<String, Health>();
healths.put("h1", new Health(Status.DOWN));
healths.put("h2", new Health(Status.UP));
healths.put("h3", new Health(Status.UNKOWN));
healths.put("h4", new Health(Status.OUT_OF_SERVICE));
assertEquals(Status.DOWN, this.healthAggregator.aggregate(healths).getStatus());
}
@Test
public void testDefaultOrderingWithCustomStatus() {
Map<String, Health> healths = new HashMap<String, Health>();
healths.put("h1", new Health(Status.DOWN));
healths.put("h2", new Health(Status.UP));
healths.put("h3", new Health(Status.UNKOWN));
healths.put("h4", new Health(Status.OUT_OF_SERVICE));
healths.put("h5", new Health(new Status("CUSTOM")));
assertEquals(new Status("CUSTOM"),
this.healthAggregator.aggregate(healths).getStatus());
}
@Test
public void testDefaultOrderingWithCustomStatusAndOrder() {
this.healthAggregator.setStatusOrder(Arrays.asList("DOWN", "OUT_OF_SERVICE",
"UP", "UNKOWN", "CUSTOM"));
Map<String, Health> healths = new HashMap<String, Health>();
healths.put("h1", new Health(Status.DOWN));
healths.put("h2", new Health(Status.UP));
healths.put("h3", new Health(Status.UNKOWN));
healths.put("h4", new Health(Status.OUT_OF_SERVICE));
healths.put("h5", new Health(new Status("CUSTOM")));
assertEquals(Status.DOWN, this.healthAggregator.aggregate(healths).getStatus());
}
}
......@@ -16,7 +16,6 @@
package org.springframework.boot.actuate.health;
import java.util.Map;
import java.util.Properties;
import org.junit.After;
......@@ -37,7 +36,7 @@ import static org.junit.Assert.assertTrue;
/**
* Tests for {@link RedisHealthIndicator}.
*
*
* @author Christian Dupuis
*/
public class RedisHealthIndicatorTests {
......@@ -76,9 +75,9 @@ public class RedisHealthIndicatorTests {
RedisHealthIndicator healthIndicator = new RedisHealthIndicator(
redisConnectionFactory);
Map<String, Object> health = healthIndicator.health();
assertEquals("ok", health.get("status"));
assertEquals("2.8.9", health.get("version"));
Health health = healthIndicator.health();
assertEquals(Status.UP, health.getStatus());
assertEquals("2.8.9", health.getDetails().get("version"));
Mockito.verify(redisConnectionFactory).getConnection();
Mockito.verify(redisConnection).info();
......@@ -95,9 +94,10 @@ public class RedisHealthIndicatorTests {
RedisHealthIndicator healthIndicator = new RedisHealthIndicator(
redisConnectionFactory);
Map<String, Object> health = healthIndicator.health();
assertEquals("error", health.get("status"));
assertTrue(((String) health.get("error")).contains("Connection failed"));
Health health = healthIndicator.health();
assertEquals(Status.DOWN, health.getStatus());
assertTrue(((String) health.getDetails().get("error"))
.contains("Connection failed"));
Mockito.verify(redisConnectionFactory).getConnection();
Mockito.verify(redisConnection).info();
......
......@@ -17,7 +17,6 @@
package org.springframework.boot.actuate.health;
import java.sql.Connection;
import java.util.Map;
import javax.sql.DataSource;
......@@ -37,7 +36,7 @@ import static org.mockito.Mockito.when;
/**
* Tests for {@link SimpleDataSourceHealthIndicator}.
*
*
* @author Dave Syer
*/
public class SimpleDataSourceHealthIndicatorTests {
......@@ -55,31 +54,31 @@ public class SimpleDataSourceHealthIndicatorTests {
@Test
public void database() {
this.indicator.setDataSource(this.dataSource);
Map<String, Object> health = this.indicator.health();
assertNotNull(health.get("database"));
assertNotNull(health.get("hello"));
Health health = this.indicator.health();
assertNotNull(health.getDetails().get("database"));
assertNotNull(health.getDetails().get("hello"));
}
@Test
public void customQuery() {
this.indicator.setDataSource(this.dataSource);
new JdbcTemplate(this.dataSource)
.execute("CREATE TABLE FOO (id INTEGER IDENTITY PRIMARY KEY)");
.execute("CREATE TABLE FOO (id INTEGER IDENTITY PRIMARY KEY)");
this.indicator.setQuery("SELECT COUNT(*) from FOO");
Map<String, Object> health = this.indicator.health();
Health health = this.indicator.health();
System.err.println(health);
assertNotNull(health.get("database"));
assertEquals("ok", health.get("status"));
assertNotNull(health.get("hello"));
assertNotNull(health.getDetails().get("database"));
assertEquals(Status.UP, health.getStatus());
assertNotNull(health.getDetails().get("hello"));
}
@Test
public void error() {
this.indicator.setDataSource(this.dataSource);
this.indicator.setQuery("SELECT COUNT(*) from BAR");
Map<String, Object> health = this.indicator.health();
assertNotNull(health.get("database"));
assertEquals("error", health.get("status"));
Health health = this.indicator.health();
assertNotNull(health.getDetails().get("database"));
assertEquals(Status.DOWN, health.getStatus());
}
@Test
......@@ -90,8 +89,8 @@ public class SimpleDataSourceHealthIndicatorTests {
this.dataSource.getConnection().getMetaData());
when(dataSource.getConnection()).thenReturn(connection);
this.indicator.setDataSource(dataSource);
Map<String, Object> health = this.indicator.health();
assertNotNull(health.get("database"));
Health health = this.indicator.health();
assertNotNull(health.getDetails().get("database"));
verify(connection, times(2)).close();
}
......
......@@ -18,12 +18,11 @@ package org.springframework.boot.actuate.health;
import org.junit.Test;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertEquals;
/**
* Tests for {@link VanillaHealthIndicator}.
*
*
* @author Phillip Webb
*/
public class VanillaHealthIndicatorTests {
......@@ -31,7 +30,7 @@ public class VanillaHealthIndicatorTests {
@Test
public void ok() throws Exception {
VanillaHealthIndicator healthIndicator = new VanillaHealthIndicator();
assertThat(healthIndicator.health(), equalTo("ok"));
assertEquals(Status.UP, healthIndicator.health().getStatus());
}
}
......@@ -75,7 +75,7 @@ public class SampleActuatorUiApplicationPortTests {
ResponseEntity<String> entity = new TestRestTemplate().getForEntity(
"http://localhost:" + this.managementPort + "/health", String.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
assertEquals("{\"status\":\"ok\"}", entity.getBody());
assertEquals("{\"status\":\"UP\"}", entity.getBody());
}
}
......@@ -68,6 +68,6 @@ public class EndpointsPropertiesSampleActuatorApplicationTests {
"http://localhost:" + this.port + "/admin/health", String.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
assertTrue("Wrong body: " + entity.getBody(),
entity.getBody().contains("\"status\":\"ok\""));
entity.getBody().contains("\"status\":\"UP\""));
}
}
......@@ -73,7 +73,7 @@ public class ManagementAddressActuatorApplicationTests {
String.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
assertTrue("Wrong body: " + entity.getBody(),
entity.getBody().contains("\"status\":\"ok\""));
entity.getBody().contains("\"status\":\"UP\""));
}
}
......@@ -82,7 +82,7 @@ public class ManagementPortSampleActuatorApplicationTests {
"http://localhost:" + this.managementPort + "/health", String.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
assertTrue("Wrong body: " + entity.getBody(),
entity.getBody().contains("\"status\":\"ok\""));
entity.getBody().contains("\"status\":\"UP\""));
}
@Test
......
......@@ -131,7 +131,7 @@ public class SampleActuatorApplicationTests {
"http://localhost:" + this.port + "/health", String.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
assertTrue("Wrong body: " + entity.getBody(),
entity.getBody().contains("\"status\":\"ok\""));
entity.getBody().contains("\"status\":\"UP\""));
}
@Test
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment