diff --git a/spring-boot-actuator/pom.xml b/spring-boot-actuator/pom.xml index 37338e0b8b..1ecb5e8bc6 100644 --- a/spring-boot-actuator/pom.xml +++ b/spring-boot-actuator/pom.xml @@ -76,6 +76,11 @@ spring-data-redis true + + org.springframework.data + spring-data-mongodb + true + org.springframework.security spring-security-web 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 c06da53e27..5d588c7db7 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 @@ -20,8 +20,6 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.Properties; -import javax.sql.DataSource; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.actuate.endpoint.AutoConfigurationReportEndpoint; @@ -38,10 +36,6 @@ 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.CompositeHealthIndicator; -import org.springframework.boot.actuate.health.HealthIndicator; -import org.springframework.boot.actuate.health.SimpleHealthIndicator; -import org.springframework.boot.actuate.health.VanillaHealthIndicator; import org.springframework.boot.actuate.metrics.reader.MetricReader; import org.springframework.boot.actuate.metrics.repository.InMemoryMetricRepository; import org.springframework.boot.actuate.trace.InMemoryTraceRepository; @@ -64,20 +58,15 @@ import org.springframework.web.servlet.handler.AbstractHandlerMethodMapping; /** * {@link EnableAutoConfiguration Auto-configuration} for common management * {@link Endpoint}s. - * + * * @author Dave Syer * @author Phillip Webb * @author Greg Turnquist + * @author Chiristian Dupuis */ @Configuration public class EndpointAutoConfiguration { - @Autowired(required = false) - private HealthIndicator healthIndicator; - - @Autowired(required = false) - private Map dataSources; - @Autowired private InfoPropertiesConfiguration properties; @@ -98,28 +87,8 @@ public class EndpointAutoConfiguration { @Bean @ConditionalOnMissingBean - public HealthEndpoint healthEndpoint() { - if (this.healthIndicator == null) { - this.healthIndicator = createHealthIndicator(); - } - return new HealthEndpoint(this.healthIndicator); - } - - private HealthIndicator createHealthIndicator() { - if (this.dataSources == null || this.dataSources.isEmpty()) { - return new VanillaHealthIndicator(); - } - - if (this.dataSources.size() == 1) { - return new SimpleHealthIndicator(this.dataSources.values().iterator().next()); - } - - CompositeHealthIndicator composite = new CompositeHealthIndicator(); - for (Map.Entry entry : this.dataSources.entrySet()) { - composite.addHealthIndicator(entry.getKey(), - new SimpleHealthIndicator(entry.getValue())); - } - return composite; + public HealthEndpoint healthEndpoint() { + return new HealthEndpoint(); } @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 new file mode 100644 index 0000000000..067aedfe6d --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/HealthIndicatorAutoConfiguration.java @@ -0,0 +1,137 @@ +/* + * Copyright 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.Map; + +import javax.sql.DataSource; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.health.CompositeHealthIndicator; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.boot.actuate.health.MongoHealthIndicator; +import org.springframework.boot.actuate.health.RedisHealthIndicator; +import org.springframework.boot.actuate.health.SimpleDataSourceHealthIndicator; +import org.springframework.boot.actuate.health.VanillaHealthIndicator; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.jdbc.CommonsDataSourceConfiguration; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration; +import org.springframework.boot.autoconfigure.jdbc.HikariDataSourceConfiguration; +import org.springframework.boot.autoconfigure.jdbc.TomcatDataSourceConfiguration; +import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; +import org.springframework.boot.autoconfigure.mongo.MongoDataAutoConfiguration; +import org.springframework.boot.autoconfigure.redis.RedisAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.redis.connection.RedisConnectionFactory; + +/** + * @author Christian Dupuis + * @since 1.1.0 + */ +@Configuration +@AutoConfigureAfter({ DataSourceAutoConfiguration.class, + EmbeddedDataSourceConfiguration.class, CommonsDataSourceConfiguration.class, + HikariDataSourceConfiguration.class, TomcatDataSourceConfiguration.class, + MongoAutoConfiguration.class, MongoDataAutoConfiguration.class, + RedisAutoConfiguration.class }) +public class HealthIndicatorAutoConfiguration { + + @Bean + @ConditionalOnMissingBean(HealthIndicator.class) + public HealthIndicator statusHealthIndicator() { + return new VanillaHealthIndicator(); + } + + @Configuration + @ConditionalOnBean(DataSource.class) + public static class DataSourcesHealthIndicatorConfiguration { + + @Autowired(required = false) + private Map dataSources; + + @Bean + @ConditionalOnMissingBean(name = "dbHealthIndicator") + public HealthIndicator dbHealthIndicator() { + if (this.dataSources.size() == 1) { + return new SimpleDataSourceHealthIndicator(this.dataSources.values().iterator() + .next()); + } + + CompositeHealthIndicator composite = new CompositeHealthIndicator(); + for (Map.Entry entry : this.dataSources.entrySet()) { + composite.addHealthIndicator(entry.getKey(), new SimpleDataSourceHealthIndicator( + entry.getValue())); + } + return composite; + } + } + + @Configuration + @ConditionalOnBean(MongoTemplate.class) + public static class MongoHealthIndicatorConfiguration { + + @Autowired + private Map mongoTemplates; + + @Bean + @ConditionalOnMissingBean(name = "mongoHealthIndicator") + public HealthIndicator mongoHealthIndicator() { + if (this.mongoTemplates.size() == 1) { + return new MongoHealthIndicator(this.mongoTemplates.values().iterator() + .next()); + } + + CompositeHealthIndicator composite = new CompositeHealthIndicator(); + for (Map.Entry entry : this.mongoTemplates.entrySet()) { + composite.addHealthIndicator(entry.getKey(), new MongoHealthIndicator( + entry.getValue())); + } + return composite; + } + } + + @Configuration + @ConditionalOnBean(RedisConnectionFactory.class) + public static class RedisHealthIndicatorConfiguration { + + @Autowired + private Map redisConnectionFactories; + + @Bean + @ConditionalOnMissingBean(name = "redisHealthIndicator") + public HealthIndicator redisHealthIndicator() { + if (this.redisConnectionFactories.size() == 1) { + return new RedisHealthIndicator(this.redisConnectionFactories.values() + .iterator().next()); + } + + CompositeHealthIndicator composite = new CompositeHealthIndicator(); + for (Map.Entry entry : this.redisConnectionFactories + .entrySet()) { + composite.addHealthIndicator(entry.getKey(), new RedisHealthIndicator( + entry.getValue())); + } + return composite; + } + } + +} 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 51bbcef8b2..b680cfb01b 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,34 +16,53 @@ package org.springframework.boot.actuate.endpoint; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; + +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.util.Assert; /** * {@link Endpoint} to expose application health. - * + * * @author Dave Syer + * @author Christian Dupuis */ @ConfigurationProperties(prefix = "endpoints.health", ignoreUnknownFields = false) -public class HealthEndpoint extends AbstractEndpoint { +public class HealthEndpoint extends AbstractEndpoint> { - private final HealthIndicator indicator; + @Autowired(required = false) + private Map> healthIndicators; /** * Create a new {@link HealthIndicator} instance. - * - * @param indicator the health indicator */ - public HealthEndpoint(HealthIndicator indicator) { + public HealthEndpoint() { super("health", false, true); - Assert.notNull(indicator, "Indicator must not be null"); - this.indicator = indicator; } + /** + * Invoke all {@link HealthIndicator} delegates and collect their health information. + */ @Override - public T invoke() { - return this.indicator.health(); + public Map invoke() { + Map health = new LinkedHashMap(); + for (Entry> entry : this.healthIndicators.entrySet()) { + health.put(getKey(entry.getKey()), entry.getValue().health()); + } + return health; } + /** + * Turns the bean name into a key that can be used in the map of health information. + */ + private String getKey(String name) { + int x = name.toLowerCase().indexOf("healthindicator"); + if (x > 0) { + return name.substring(0, x); + } + return name; + } } 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 new file mode 100644 index 0000000000..080602e6c3 --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/MongoHealthIndicator.java @@ -0,0 +1,57 @@ +/* + * Copyright 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.HashMap; +import java.util.Map; + +import org.springframework.data.mongodb.core.MongoTemplate; + +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> { + + private final MongoTemplate mongoTemplate; + + public MongoHealthIndicator(MongoTemplate mongoTemplate) { + this.mongoTemplate = mongoTemplate; + } + + @Override + public Map health() { + Map health = new HashMap(); + try { + CommandResult result = this.mongoTemplate + .executeCommand("{ serverStatus: 1 }"); + health.put("status", "ok"); + health.put("version", result.getString("version")); + } + catch (Exception ex) { + health.put("status", "error"); + health.put("error", ex.getClass().getName() + ": " + ex.getMessage()); + } + return health; + } + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/RedisHealthIndicator.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/RedisHealthIndicator.java new file mode 100644 index 0000000000..fe5e342524 --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/RedisHealthIndicator.java @@ -0,0 +1,63 @@ +/* + * Copyright 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.HashMap; +import java.util.Map; +import java.util.Properties; + +import org.springframework.data.redis.connection.RedisConnection; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisConnectionUtils; + +/** + * 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> { + + private final RedisConnectionFactory redisConnectionFactory; + + public RedisHealthIndicator(RedisConnectionFactory connectionFactory) { + this.redisConnectionFactory = connectionFactory; + } + + @Override + public Map health() { + Map health = new HashMap(); + + RedisConnection connection = null; + try { + connection = RedisConnectionUtils.getConnection(this.redisConnectionFactory); + Properties info = connection.info(); + health.put("status", "ok"); + health.put("version", info.getProperty("redis_version")); + } + catch (Exception ex) { + health.put("status", "error"); + health.put("error", ex.getClass().getName() + ": " + ex.getMessage()); + } + finally { + RedisConnectionUtils.releaseConnection(connection, + this.redisConnectionFactory); + } + return health; + } +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/SimpleHealthIndicator.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/SimpleDataSourceHealthIndicator.java similarity index 90% rename from spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/SimpleHealthIndicator.java rename to spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/SimpleDataSourceHealthIndicator.java index 4c185c4a5b..181480c9ad 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/SimpleHealthIndicator.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/SimpleDataSourceHealthIndicator.java @@ -35,7 +35,7 @@ import org.springframework.util.StringUtils; * * @author Dave Syer */ -public class SimpleHealthIndicator implements HealthIndicator> { +public class SimpleDataSourceHealthIndicator implements HealthIndicator> { private DataSource dataSource; @@ -55,16 +55,16 @@ public class SimpleHealthIndicator implements HealthIndicator 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")); + } + + @Test + public void healthEndpointWithDefaultHealthIndicator() { this.context = new AnnotationConfigApplicationContext(); this.context.register(EndpointAutoConfiguration.class, - EmbeddedDataSourceConfiguration.class); + HealthIndicatorAutoConfiguration.class); this.context.refresh(); - HealthEndpoint bean = this.context.getBean(HealthEndpoint.class); + HealthEndpoint bean = this.context.getBean(HealthEndpoint.class); assertNotNull(bean); - @SuppressWarnings("unchecked") - Map result = (Map) bean.invoke(); + Map result = bean.invoke(); assertNotNull(result); assertTrue("Wrong result: " + result, result.containsKey("status")); - assertTrue("Wrong result: " + result, result.containsKey("database")); } @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 new file mode 100644 index 0000000000..9abc4b39e9 --- /dev/null +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/HealthIndicatorAutoConfigurationTests.java @@ -0,0 +1,126 @@ +/* + * Copyright 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.Map; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.boot.actuate.health.MongoHealthIndicator; +import org.springframework.boot.actuate.health.RedisHealthIndicator; +import org.springframework.boot.actuate.health.SimpleDataSourceHealthIndicator; +import org.springframework.boot.actuate.health.VanillaHealthIndicator; +import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration; +import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; +import org.springframework.boot.autoconfigure.mongo.MongoDataAutoConfiguration; +import org.springframework.boot.autoconfigure.redis.RedisAutoConfiguration; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +import static org.junit.Assert.assertEquals; + +/** + * Tests for {@link HealthIndicatorAutoConfiguration}. + * + * @author Christian Dupuis + */ +public class HealthIndicatorAutoConfigurationTests { + + private AnnotationConfigApplicationContext context; + + @Before + public void setup() { + this.context = new AnnotationConfigApplicationContext(); + this.context.register(EndpointAutoConfiguration.class); + this.context.refresh(); + } + + @After + public void close() { + if (this.context != null) { + this.context.close(); + } + } + + @SuppressWarnings("rawtypes") + @Test + public void defaultHealthIndicator() { + this.context = new AnnotationConfigApplicationContext(); + this.context.register(HealthIndicatorAutoConfiguration.class); + this.context.refresh(); + Map beans = this.context + .getBeansOfType(HealthIndicator.class); + assertEquals(1, beans.size()); + assertEquals(VanillaHealthIndicator.class, beans.values().iterator().next() + .getClass()); + } + + @SuppressWarnings("rawtypes") + @Test + public void redisHealthIndicator() { + this.context = new AnnotationConfigApplicationContext(); + this.context.register(RedisAutoConfiguration.class, + HealthIndicatorAutoConfiguration.class); + this.context.refresh(); + Map beans = this.context + .getBeansOfType(HealthIndicator.class); + assertEquals(1, beans.size()); + assertEquals(RedisHealthIndicator.class, beans.values().iterator().next() + .getClass()); + } + + @SuppressWarnings("rawtypes") + @Test + public void mongoHealthIndicator() { + this.context = new AnnotationConfigApplicationContext(); + this.context.register(MongoAutoConfiguration.class, + MongoDataAutoConfiguration.class, HealthIndicatorAutoConfiguration.class); + this.context.refresh(); + Map beans = this.context + .getBeansOfType(HealthIndicator.class); + assertEquals(1, beans.size()); + assertEquals(MongoHealthIndicator.class, beans.values().iterator().next() + .getClass()); + } + + @SuppressWarnings("rawtypes") + @Test + public void combinedHealthIndicator() { + this.context = new AnnotationConfigApplicationContext(); + this.context.register(MongoAutoConfiguration.class, RedisAutoConfiguration.class, + MongoDataAutoConfiguration.class, HealthIndicatorAutoConfiguration.class); + this.context.refresh(); + Map beans = this.context + .getBeansOfType(HealthIndicator.class); + assertEquals(2, beans.size()); + } + + @SuppressWarnings("rawtypes") + @Test + public void dataSourceHealthIndicator() { + this.context = new AnnotationConfigApplicationContext(); + this.context.register(EmbeddedDataSourceConfiguration.class, + HealthIndicatorAutoConfiguration.class); + this.context.refresh(); + Map beans = this.context + .getBeansOfType(HealthIndicator.class); + assertEquals(1, beans.size()); + assertEquals(SimpleDataSourceHealthIndicator.class, beans.values().iterator().next() + .getClass()); + } +} \ No newline at end of file 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 e56f77380c..7be942a0fd 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 @@ -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. @@ -16,6 +16,9 @@ package org.springframework.boot.actuate.endpoint; +import java.util.HashMap; +import java.util.Map; + import org.junit.Test; import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -27,10 +30,10 @@ import static org.junit.Assert.assertThat; /** * Tests for {@link HealthEndpoint}. - * + * * @author Phillip Webb */ -public class HealthEndpointTests extends AbstractEndpointTests> { +public class HealthEndpointTests extends AbstractEndpointTests { public HealthEndpointTests() { super(Config.class, HealthEndpoint.class, "health", false, "endpoints.health"); @@ -38,7 +41,9 @@ public class HealthEndpointTests extends AbstractEndpointTests result = new HashMap(); + result.put("status", "fine"); + assertThat(getEndpointBean().invoke(), equalTo(result)); } @Configuration @@ -46,14 +51,18 @@ public class HealthEndpointTests extends AbstractEndpointTests endpoint() { - return new HealthEndpoint(new HealthIndicator() { + public HealthEndpoint endpoint() { + return new HealthEndpoint(); + } + + @Bean + public HealthIndicator statusHealthIndicator() { + return new HealthIndicator() { @Override public String health() { return "fine"; } - }); + }; } - } } 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 new file mode 100644 index 0000000000..eebf168bff --- /dev/null +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/MongoHealthIndicatorTests.java @@ -0,0 +1,97 @@ +/* + * Copyright 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; + +import org.junit.After; +import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.HealthIndicatorAutoConfiguration; +import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; +import org.springframework.boot.autoconfigure.mongo.MongoDataAutoConfiguration; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.data.mongodb.core.MongoTemplate; + +import com.mongodb.CommandResult; +import com.mongodb.MongoException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * Tests for {@link MongoHealthIndicator}. + * + * @author Christian Dupuis + */ +public class MongoHealthIndicatorTests { + + private AnnotationConfigApplicationContext context; + + @After + public void close() { + if (this.context != null) { + this.context.close(); + } + } + + @Test + public void indicatorExists() { + this.context = new AnnotationConfigApplicationContext( + PropertyPlaceholderAutoConfiguration.class, MongoAutoConfiguration.class, + MongoDataAutoConfiguration.class, EndpointAutoConfiguration.class, + HealthIndicatorAutoConfiguration.class); + assertEquals(1, this.context.getBeanNamesForType(MongoTemplate.class).length); + MongoHealthIndicator healthIndicator = this.context + .getBean(MongoHealthIndicator.class); + assertNotNull(healthIndicator); + } + + @Test + public void mongoIsUp() throws Exception { + CommandResult commandResult = Mockito.mock(CommandResult.class); + Mockito.when(commandResult.getString("version")).thenReturn("2.6.4"); + MongoTemplate mongoTemplate = Mockito.mock(MongoTemplate.class); + Mockito.when(mongoTemplate.executeCommand("{ serverStatus: 1 }")).thenReturn( + commandResult); + MongoHealthIndicator healthIndicator = new MongoHealthIndicator(mongoTemplate); + + Map health = healthIndicator.health(); + assertEquals("ok", health.get("status")); + assertEquals("2.6.4", health.get("version")); + + Mockito.verify(commandResult).getString("version"); + Mockito.verify(mongoTemplate).executeCommand("{ serverStatus: 1 }"); + } + + @Test + public void mongoIsDown() throws Exception { + MongoTemplate mongoTemplate = Mockito.mock(MongoTemplate.class); + Mockito.when(mongoTemplate.executeCommand("{ serverStatus: 1 }")).thenThrow( + 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")); + + Mockito.verify(mongoTemplate).executeCommand("{ serverStatus: 1 }"); + } +} 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 new file mode 100644 index 0000000000..c599920f13 --- /dev/null +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/RedisHealthIndicatorTests.java @@ -0,0 +1,105 @@ +/* + * Copyright 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; +import java.util.Properties; + +import org.junit.After; +import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.HealthIndicatorAutoConfiguration; +import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.autoconfigure.redis.RedisAutoConfiguration; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.data.redis.RedisConnectionFailureException; +import org.springframework.data.redis.connection.RedisConnection; +import org.springframework.data.redis.connection.RedisConnectionFactory; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * Tests for {@link RedisHealthIndicator}. + * + * @author Christian Dupuis + */ +public class RedisHealthIndicatorTests { + + private AnnotationConfigApplicationContext context; + + @After + public void close() { + if (this.context != null) { + this.context.close(); + } + } + + @Test + public void indicatorExists() { + this.context = new AnnotationConfigApplicationContext( + PropertyPlaceholderAutoConfiguration.class, RedisAutoConfiguration.class, + EndpointAutoConfiguration.class, HealthIndicatorAutoConfiguration.class); + assertEquals(1, + this.context.getBeanNamesForType(RedisConnectionFactory.class).length); + RedisHealthIndicator healthIndicator = this.context + .getBean(RedisHealthIndicator.class); + assertNotNull(healthIndicator); + } + + @Test + public void redisIsUp() throws Exception { + Properties info = new Properties(); + info.put("redis_version", "2.8.9"); + + RedisConnection redisConnection = Mockito.mock(RedisConnection.class); + RedisConnectionFactory redisConnectionFactory = Mockito + .mock(RedisConnectionFactory.class); + Mockito.when(redisConnectionFactory.getConnection()).thenReturn(redisConnection); + Mockito.when(redisConnection.info()).thenReturn(info); + RedisHealthIndicator healthIndicator = new RedisHealthIndicator( + redisConnectionFactory); + + Map health = healthIndicator.health(); + assertEquals("ok", health.get("status")); + assertEquals("2.8.9", health.get("version")); + + Mockito.verify(redisConnectionFactory).getConnection(); + Mockito.verify(redisConnection).info(); + } + + @Test + public void redisIsDown() throws Exception { + RedisConnection redisConnection = Mockito.mock(RedisConnection.class); + RedisConnectionFactory redisConnectionFactory = Mockito + .mock(RedisConnectionFactory.class); + Mockito.when(redisConnectionFactory.getConnection()).thenReturn(redisConnection); + Mockito.when(redisConnection.info()).thenThrow( + new RedisConnectionFailureException("Connection failed")); + RedisHealthIndicator healthIndicator = new RedisHealthIndicator( + redisConnectionFactory); + + Map health = healthIndicator.health(); + assertEquals("error", health.get("status")); + assertTrue(((String) health.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/SimpleHealthIndicatorTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/SimpleDataSourceHealthIndicatorTests.java similarity index 93% rename from spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/SimpleHealthIndicatorTests.java rename to spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/SimpleDataSourceHealthIndicatorTests.java index 1afb28c78e..80452543f0 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/SimpleHealthIndicatorTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/SimpleDataSourceHealthIndicatorTests.java @@ -36,13 +36,13 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; /** - * Tests for {@link SimpleHealthIndicator}. + * Tests for {@link SimpleDataSourceHealthIndicator}. * * @author Dave Syer */ -public class SimpleHealthIndicatorTests { +public class SimpleDataSourceHealthIndicatorTests { - private final SimpleHealthIndicator indicator = new SimpleHealthIndicator(); + private final SimpleDataSourceHealthIndicator indicator = new SimpleDataSourceHealthIndicator(); private DriverManagerDataSource dataSource; @Before diff --git a/spring-boot-docs/src/main/asciidoc/index.adoc b/spring-boot-docs/src/main/asciidoc/index.adoc index b8df190aae..68dcd5addd 100644 --- a/spring-boot-docs/src/main/asciidoc/index.adoc +++ b/spring-boot-docs/src/main/asciidoc/index.adoc @@ -1,5 +1,5 @@ = Spring Boot Reference Guide -Phillip Webb; Dave Syer; Josh Long; Stéphane Nicoll; Rob Winch; Andy Wilkinson; Marcel Overdijk; +Phillip Webb; Dave Syer; Josh Long; Stéphane Nicoll; Rob Winch; Andy Wilkinson; Marcel Overdijk; Christian Dupuis; :doctype: book :toc: :toclevels: 4 diff --git a/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc b/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc index d29a8ccf30..3a214e2399 100644 --- a/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc +++ b/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc @@ -162,9 +162,13 @@ To provide custom health information you can register a Spring bean that impleme } ---- -Spring Boot also provides a -{sc-spring-boot-actuator}/health/SimpleHealthIndicator.{sc-ext}[`SimpleHealthIndicator`] -implementation that attempts a simple database test. +Spring Boot provides a +{sc-spring-boot-actuator}/health/SimpleDatabaseHealthIndicator.{sc-ext}[`SimpleDatabaseHealthIndicator`] +implementation that attempts a simple database test as well as implementations for +Redis and MongoDB. + +Spring Boot adds the `HealthIndicator` instances automatically if beans of type `DataSource`, +`MongoTemplate` or `RedisConnectionFactory` are present in the `ApplicationContext`.