diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointAutoConfiguration.java index 680861092e..06ecc4230c 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointAutoConfiguration.java @@ -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> healthIndicators = new HashMap>(); + private HealthAggregator healthAggregator = new OrderedHealthAggregator(); + + @Autowired(required = false) + Map healthIndicators = new HashMap(); @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 diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/HealthIndicatorAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/HealthIndicatorAutoConfiguration.java index 1c70ad1ef4..55d697d6b9 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/HealthIndicatorAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/HealthIndicatorAutoConfiguration.java @@ -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 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 dataSources; @Bean @ConditionalOnMissingBean(name = "dbHealthIndicator") - public HealthIndicator 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 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 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 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 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 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 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 entry : this.rabbitTemplates .entrySet()) { composite.addHealthIndicator(entry.getKey(), new RabbitHealthIndicator( diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/HealthEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/HealthEndpoint.java index 6f44146b89..ed43df33b4 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/HealthEndpoint.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/HealthEndpoint.java @@ -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; @@ -31,29 +32,39 @@ import org.springframework.util.Assert; * @author Christian Dupuis */ @ConfigurationProperties(prefix = "endpoints.health", ignoreUnknownFields = false) -public class HealthEndpoint extends AbstractEndpoint> { +public class HealthEndpoint extends AbstractEndpoint { - private final Map> healthIndicators; + private final HealthIndicator healthIndicator; /** * Create a new {@link HealthIndicator} instance. */ - public HealthEndpoint(Map> healthIndicators) { + public HealthEndpoint(HealthAggregator healthAggregator, + Map 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 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 invoke() { - Map health = new LinkedHashMap(); - for (Entry> entry : this.healthIndicators.entrySet()) { - health.put(getKey(entry.getKey()), entry.getValue().health()); - } - return health; + public Health invoke() { + return this.healthIndicator.health(); } /** diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/CompositeHealthIndicator.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/CompositeHealthIndicator.java index bc3ffa9c14..4de09d7228 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/CompositeHealthIndicator.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/CompositeHealthIndicator.java @@ -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> { +public class CompositeHealthIndicator implements HealthIndicator { - private final Map> indicators; + private final Map indicators; + + private final HealthAggregator healthAggregator; /** * Create a new {@link CompositeHealthIndicator}. */ - public CompositeHealthIndicator() { - this.indicators = new LinkedHashMap>(); + public CompositeHealthIndicator(HealthAggregator healthAggregator) { + this(healthAggregator, new LinkedHashMap()); } /** @@ -42,21 +47,25 @@ public class CompositeHealthIndicator implements HealthIndicator> indicators) { - this.indicators = new LinkedHashMap>(indicators); + public CompositeHealthIndicator(HealthAggregator healthAggregator, + Map indicators) { + Assert.notNull(healthAggregator, "HealthAggregator must not be null"); + Assert.notNull(healthAggregator, "Indicators must not be null"); + this.indicators = new LinkedHashMap(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 health() { - Map health = new LinkedHashMap(); - for (Map.Entry> entry : this.indicators.entrySet()) { - health.put(entry.getKey(), entry.getValue().health()); + public Health health() { + Map healths = new LinkedHashMap(); + for (Map.Entry entry : this.indicators.entrySet()) { + healths.put(entry.getKey(), entry.getValue().health()); } - return health; + return this.healthAggregator.aggregate(healths); } } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/Health.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/Health.java new file mode 100644 index 0000000000..e9540c7c42 --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/Health.java @@ -0,0 +1,133 @@ +/* + * 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. + * + *

+ * {@link Health} contains a {@link Status} to express the state of a component or + * subsystem and some additional details to carry some contextual information. + * + *

+ * {@link Health} has a fluent API to make it easy to construct instances. Typical usage + * in a {@link HealthIndicator} would be: + * + * + * 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; + * + * + * @author Christian Dupuis + * @since 1.1.0 + */ +@JsonInclude(Include.NON_EMPTY) +public class Health { + + private Status status; + + private Map details; + + public Health() { + this(Status.UNKOWN); + } + + public Health(Status status) { + this.status = status; + this.details = new LinkedHashMap(); + } + + 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 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(); + } + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthAggregator.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthAggregator.java new file mode 100644 index 0000000000..0a0283d4cc --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthAggregator.java @@ -0,0 +1,45 @@ +/* + * 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. + * + *

+ * 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. + * + *

+ * 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 healths); +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthIndicator.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthIndicator.java index 6e82b451a8..a39f572498 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthIndicator.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthIndicator.java @@ -1,5 +1,5 @@ /* - * 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 { +public interface HealthIndicator { /** * @return an indication of health */ - T health(); + Health health(); } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/MongoHealthIndicator.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/MongoHealthIndicator.java index 1e89500a9c..e6364923fa 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/MongoHealthIndicator.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/MongoHealthIndicator.java @@ -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> { +public class MongoHealthIndicator implements HealthIndicator { private final MongoTemplate mongoTemplate; @@ -41,17 +38,15 @@ public class MongoHealthIndicator implements HealthIndicator } @Override - public Map health() { - Map health = new HashMap(); + 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; } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/OrderedHealthAggregator.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/OrderedHealthAggregator.java new file mode 100644 index 0000000000..9cdbea476b --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/OrderedHealthAggregator.java @@ -0,0 +1,78 @@ +/* + * 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. + * + *

+ * 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 statusOrder = Arrays.asList("DOWN", "OUT_OF_SERVICE", "UP", + "UNKOWN"); + + @Override + public Health aggregate(Map healths) { + Health health = new Health(); + + List status = new ArrayList(); + for (Map.Entry h : healths.entrySet()) { + health.withDetail(h.getKey(), h.getValue()); + status.add(h.getValue().getStatus()); + } + health.status(aggregateStatus(status)); + return health; + } + + public void setStatusOrder(List statusOrder) { + this.statusOrder = statusOrder; + } + + protected Status aggregateStatus(List status) { + + if (status.size() == 0) { + return Status.UNKOWN; + } + + status.sort(new Comparator() { + + @Override + public int compare(Status s1, Status s2) { + return Integer.valueOf( + OrderedHealthAggregator.this.statusOrder.indexOf(s1.getStatus())).compareTo( + Integer.valueOf(OrderedHealthAggregator.this.statusOrder.indexOf(s2.getStatus()))); + + } + }); + + return status.get(0); + } + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/RabbitHealthIndicator.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/RabbitHealthIndicator.java index bc7f54387e..771d856398 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/RabbitHealthIndicator.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/RabbitHealthIndicator.java @@ -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> { +public class RabbitHealthIndicator implements HealthIndicator { private final RabbitTemplate rabbitTemplate; @@ -42,10 +41,10 @@ public class RabbitHealthIndicator implements HealthIndicator health() { - Map health = new HashMap(); + public Health health() { + Health health = new Health(); try { - health.put("version", + health.up().withDetail("version", this.rabbitTemplate.execute(new ChannelCallback() { @Override @@ -55,12 +54,9 @@ public class RabbitHealthIndicator implements HealthIndicator> { +public class RedisHealthIndicator implements HealthIndicator { private final RedisConnectionFactory redisConnectionFactory; @@ -42,19 +40,17 @@ public class RedisHealthIndicator implements HealthIndicator } @Override - public Map health() { - Map health = new HashMap(); + 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, diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/SimpleDataSourceHealthIndicator.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/SimpleDataSourceHealthIndicator.java index 383ccf7d30..ead2847f33 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/SimpleDataSourceHealthIndicator.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/SimpleDataSourceHealthIndicator.java @@ -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> { @@ -72,9 +72,10 @@ public class SimpleDataSourceHealthIndicator implements } @Override - public Map health() { - LinkedHashMap health = new LinkedHashMap(); - 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); } } } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/Status.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/Status.java new file mode 100644 index 0000000000..5a9e790bc9 --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/Status.java @@ -0,0 +1,100 @@ +/* + * 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; + +/** + * Value object to express state of a component or subsystem. + * + *

+ * Status provides convenient constants for commonly used states like {@link #UP}, + * {@link #DOWN} or {@link #OUT_OF_SERVICE}. + * + *

+ * 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 status; + + private final String description; + + public Status(String code) { + this(code, ""); + } + + public Status(String code, String description) { + Assert.notNull(code, "Status must not be null"); + Assert.notNull(description, "Description must not be null"); + this.status = code; + this.description = description; + } + + public String getStatus() { + return this.status; + } + + @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.status, ((Status) obj).status); + } + return false; + } + + @Override + public int hashCode() { + return this.status.hashCode(); + } +} \ No newline at end of file diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/VanillaHealthIndicator.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/VanillaHealthIndicator.java index fa07500c82..d2370a18c7 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/VanillaHealthIndicator.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/VanillaHealthIndicator.java @@ -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 { +public class VanillaHealthIndicator implements HealthIndicator { @Override - public String health() { - return "ok"; + public Health health() { + return new Health(Status.UP); } } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointAutoConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointAutoConfigurationTests.java index b77fa0d4be..a17d0a7d2f 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointAutoConfigurationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointAutoConfigurationTests.java @@ -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 result = bean.invoke(); + Health result = bean.invoke(); assertNotNull(result); - assertTrue("Wrong result: " + result, - ((Map) result.get("db")).containsKey("status")); - assertTrue("Wrong result: " + result, - ((Map) 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 result = bean.invoke(); + Health result = bean.invoke(); assertNotNull(result); - assertTrue("Wrong result: " + result, result.containsKey("status")); } @Test diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/HealthIndicatorAutoConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/HealthIndicatorAutoConfigurationTests.java index 39ee99bd3a..f859eb467d 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/HealthIndicatorAutoConfigurationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/HealthIndicatorAutoConfigurationTests.java @@ -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(); diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/HealthEndpointTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/HealthEndpointTests.java index 75169ded60..86678506d7 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/HealthEndpointTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/HealthEndpointTests.java @@ -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 { @@ -41,9 +45,8 @@ public class HealthEndpointTests extends AbstractEndpointTests { @Test public void invoke() throws Exception { - Map result = new HashMap(); - 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 { public static class Config { @Bean - public HealthEndpoint endpoint(Map> healthIndicators) { - return new HealthEndpoint(healthIndicators); + public HealthEndpoint endpoint(HealthAggregator healthAggregator, + Map healthIndicators) { + return new HealthEndpoint(healthAggregator, healthIndicators); } @Bean - public HealthIndicator statusHealthIndicator() { - return new HealthIndicator() { + 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(); + } } } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/CompositeHealthIndicatorTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/CompositeHealthIndicatorTests.java index 03da219085..fa210dce59 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/CompositeHealthIndicatorTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/CompositeHealthIndicatorTests.java @@ -20,10 +20,14 @@ import java.util.HashMap; import java.util.Map; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; + import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasEntry; import static org.junit.Assert.assertThat; @@ -31,64 +35,99 @@ import static org.mockito.BDDMockito.given; /** * Tests for {@link CompositeHealthIndicator} - * + * * @author Tyler J. Frederick * @author Phillip Webb + * @author Christian Dupuis */ public class CompositeHealthIndicatorTests { - @Mock - private HealthIndicator one; + private HealthAggregator healthAggregator; @Mock - private HealthIndicator two; + private HealthIndicator one; @Mock - private HealthIndicator three; + private HealthIndicator two; + + @Mock + 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> indicators = new HashMap>(); + Map indicators = new HashMap(); indicators.put("one", this.one); indicators.put("two", this.two); - CompositeHealthIndicator composite = new CompositeHealthIndicator(indicators); - Map 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> indicators = new HashMap>(); + Map indicators = new HashMap(); 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 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 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 + @Ignore + public void testSerialization() throws Exception { + Map indicators = new HashMap(); + 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(); + mapper.configure(SerializationFeature.INDENT_OUTPUT, true); + String test = mapper.writeValueAsString(result); + System.out.println(test); } } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/MongoHealthIndicatorTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/MongoHealthIndicatorTests.java index ba69ef7c9d..16b1d15937 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/MongoHealthIndicatorTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/MongoHealthIndicatorTests.java @@ -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 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 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 }"); } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/OrderedHealthAggregatorTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/OrderedHealthAggregatorTests.java new file mode 100644 index 0000000000..e18d4e14b4 --- /dev/null +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/OrderedHealthAggregatorTests.java @@ -0,0 +1,77 @@ +/* + * 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 healths = new HashMap(); + 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 healths = new HashMap(); + 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 healths = new HashMap(); + 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()); + } + +} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/RedisHealthIndicatorTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/RedisHealthIndicatorTests.java index 957a0e0a02..839bd57e6c 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/RedisHealthIndicatorTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/RedisHealthIndicatorTests.java @@ -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 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 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(); diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/SimpleDataSourceHealthIndicatorTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/SimpleDataSourceHealthIndicatorTests.java index 80452543f0..641396534c 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/SimpleDataSourceHealthIndicatorTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/SimpleDataSourceHealthIndicatorTests.java @@ -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 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 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 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 health = this.indicator.health(); - assertNotNull(health.get("database")); + Health health = this.indicator.health(); + assertNotNull(health.getDetails().get("database")); verify(connection, times(2)).close(); } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/VanillaHealthIndicatorTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/VanillaHealthIndicatorTests.java index 13debb5edf..2813b64333 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/VanillaHealthIndicatorTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/VanillaHealthIndicatorTests.java @@ -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()); } } diff --git a/spring-boot-samples/spring-boot-sample-actuator-ui/src/test/java/sample/actuator/ui/SampleActuatorUiApplicationPortTests.java b/spring-boot-samples/spring-boot-sample-actuator-ui/src/test/java/sample/actuator/ui/SampleActuatorUiApplicationPortTests.java index 5afad5416e..1eba39ebd1 100644 --- a/spring-boot-samples/spring-boot-sample-actuator-ui/src/test/java/sample/actuator/ui/SampleActuatorUiApplicationPortTests.java +++ b/spring-boot-samples/spring-boot-sample-actuator-ui/src/test/java/sample/actuator/ui/SampleActuatorUiApplicationPortTests.java @@ -75,7 +75,7 @@ public class SampleActuatorUiApplicationPortTests { ResponseEntity 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()); } } diff --git a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/sample/actuator/EndpointsPropertiesSampleActuatorApplicationTests.java b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/sample/actuator/EndpointsPropertiesSampleActuatorApplicationTests.java index 02701b4621..f604b3e1e9 100644 --- a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/sample/actuator/EndpointsPropertiesSampleActuatorApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/sample/actuator/EndpointsPropertiesSampleActuatorApplicationTests.java @@ -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\"")); } } diff --git a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/sample/actuator/ManagementAddressActuatorApplicationTests.java b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/sample/actuator/ManagementAddressActuatorApplicationTests.java index 848b95ffc3..71bb34079d 100644 --- a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/sample/actuator/ManagementAddressActuatorApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/sample/actuator/ManagementAddressActuatorApplicationTests.java @@ -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\"")); } } diff --git a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/sample/actuator/ManagementPortSampleActuatorApplicationTests.java b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/sample/actuator/ManagementPortSampleActuatorApplicationTests.java index e959ddc85e..111f533b1a 100644 --- a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/sample/actuator/ManagementPortSampleActuatorApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/sample/actuator/ManagementPortSampleActuatorApplicationTests.java @@ -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 diff --git a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/sample/actuator/SampleActuatorApplicationTests.java b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/sample/actuator/SampleActuatorApplicationTests.java index a662172a97..d7cd5a6d6d 100644 --- a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/sample/actuator/SampleActuatorApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/sample/actuator/SampleActuatorApplicationTests.java @@ -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