Merge pull request #819 from cdupuis/health-indicator

Rework HealthIndicator support
This commit is contained in:
Christian Dupuis 2014-05-08 16:40:44 +02:00
commit dfd6f91aef
16 changed files with 680 additions and 73 deletions

View File

@ -76,6 +76,11 @@
<artifactId>spring-data-redis</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>

View File

@ -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<? extends Object> healthIndicator;
@Autowired(required = false)
private Map<String, DataSource> 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<Object>(this.healthIndicator);
}
private HealthIndicator<? extends Object> 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<String, DataSource> entry : this.dataSources.entrySet()) {
composite.addHealthIndicator(entry.getKey(),
new SimpleHealthIndicator(entry.getValue()));
}
return composite;
public HealthEndpoint healthEndpoint() {
return new HealthEndpoint();
}
@Bean

View File

@ -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<String, DataSource> dataSources;
@Bean
@ConditionalOnMissingBean(name = "dbHealthIndicator")
public HealthIndicator<? extends Object> dbHealthIndicator() {
if (this.dataSources.size() == 1) {
return new SimpleDataSourceHealthIndicator(this.dataSources.values().iterator()
.next());
}
CompositeHealthIndicator composite = new CompositeHealthIndicator();
for (Map.Entry<String, DataSource> 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<String, MongoTemplate> 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<String, MongoTemplate> 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<String, RedisConnectionFactory> 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<String, RedisConnectionFactory> entry : this.redisConnectionFactories
.entrySet()) {
composite.addHealthIndicator(entry.getKey(), new RedisHealthIndicator(
entry.getValue()));
}
return composite;
}
}
}

View File

@ -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<T> extends AbstractEndpoint<T> {
public class HealthEndpoint extends AbstractEndpoint<Map<String, Object>> {
private final HealthIndicator<? extends T> indicator;
@Autowired(required = false)
private Map<String, HealthIndicator<? extends Object>> healthIndicators;
/**
* Create a new {@link HealthIndicator} instance.
*
* @param indicator the health indicator
*/
public HealthEndpoint(HealthIndicator<? extends T> 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<String, Object> invoke() {
Map<String, Object> health = new LinkedHashMap<String, Object>();
for (Entry<String, HealthIndicator<?>> entry : this.healthIndicators.entrySet()) {
health.put(getKey(entry.getKey()), entry.getValue().health());
}
return health;
}
/**
* 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;
}
}

View File

@ -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<Map<String, Object>> {
private final MongoTemplate mongoTemplate;
public MongoHealthIndicator(MongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
}
@Override
public Map<String, Object> health() {
Map<String, Object> health = new HashMap<String, Object>();
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;
}
}

View File

@ -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<Map<String, Object>> {
private final RedisConnectionFactory redisConnectionFactory;
public RedisHealthIndicator(RedisConnectionFactory connectionFactory) {
this.redisConnectionFactory = connectionFactory;
}
@Override
public Map<String, Object> health() {
Map<String, Object> health = new HashMap<String, Object>();
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;
}
}

View File

@ -35,7 +35,7 @@ import org.springframework.util.StringUtils;
*
* @author Dave Syer
*/
public class SimpleHealthIndicator implements HealthIndicator<Map<String, Object>> {
public class SimpleDataSourceHealthIndicator implements HealthIndicator<Map<String, Object>> {
private DataSource dataSource;
@ -55,16 +55,16 @@ public class SimpleHealthIndicator implements HealthIndicator<Map<String, Object
private String query = null;
/**
* Create a new {@link SimpleHealthIndicator} instance.
* Create a new {@link SimpleDataSourceHealthIndicator} instance.
*/
public SimpleHealthIndicator() {
public SimpleDataSourceHealthIndicator() {
}
/**
* Create a new {@link SimpleHealthIndicator} using the specified datasource.
* Create a new {@link SimpleDataSourceHealthIndicator} using the specified datasource.
* @param dataSource the data source
*/
public SimpleHealthIndicator(DataSource dataSource) {
public SimpleDataSourceHealthIndicator(DataSource dataSource) {
this.dataSource = dataSource;
this.jdbcTemplate = new JdbcTemplate(dataSource);
}

View File

@ -4,6 +4,7 @@ org.springframework.boot.actuate.autoconfigure.CrshAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.EndpointMBeanExportAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.HealthIndicatorAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.JolokiaAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.ManagementServerPropertiesAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.MetricFilterAutoConfiguration,\

View File

@ -43,7 +43,7 @@ import static org.junit.Assert.assertTrue;
/**
* Tests for {@link EndpointAutoConfiguration}.
*
*
* @author Dave Syer
* @author Phillip Webb
* @author Greg Turnquist
@ -80,18 +80,33 @@ public class EndpointAutoConfigurationTests {
}
@Test
@SuppressWarnings("unchecked")
public void healthEndpoint() {
this.context = new AnnotationConfigApplicationContext();
this.context.register(EndpointAutoConfiguration.class,
EmbeddedDataSourceConfiguration.class);
this.context.register(EmbeddedDataSourceConfiguration.class,
EndpointAutoConfiguration.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<String, Object> result = (Map<String, Object>) bean.invoke();
Map<String, Object> result = bean.invoke();
assertNotNull(result);
assertTrue("Wrong result: " + result,
((Map<String, Object>) result.get("db")).containsKey("status"));
assertTrue("Wrong result: " + result,
((Map<String, Object>) result.get("db")).containsKey("database"));
}
@Test
public void healthEndpointWithDefaultHealthIndicator() {
this.context = new AnnotationConfigApplicationContext();
this.context.register(EndpointAutoConfiguration.class,
HealthIndicatorAutoConfiguration.class);
this.context.refresh();
HealthEndpoint bean = this.context.getBean(HealthEndpoint.class);
assertNotNull(bean);
Map<String, Object> result = bean.invoke();
assertNotNull(result);
assertTrue("Wrong result: " + result, result.containsKey("status"));
assertTrue("Wrong result: " + result, result.containsKey("database"));
}
@Test

View File

@ -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<String, HealthIndicator> 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<String, HealthIndicator> 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<String, HealthIndicator> 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<String, HealthIndicator> 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<String, HealthIndicator> beans = this.context
.getBeansOfType(HealthIndicator.class);
assertEquals(1, beans.size());
assertEquals(SimpleDataSourceHealthIndicator.class, beans.values().iterator().next()
.getClass());
}
}

View File

@ -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<HealthEndpoint<String>> {
public class HealthEndpointTests extends AbstractEndpointTests<HealthEndpoint> {
public HealthEndpointTests() {
super(Config.class, HealthEndpoint.class, "health", false, "endpoints.health");
@ -38,7 +41,9 @@ public class HealthEndpointTests extends AbstractEndpointTests<HealthEndpoint<St
@Test
public void invoke() throws Exception {
assertThat(getEndpointBean().invoke(), equalTo("fine"));
Map<String, Object> result = new HashMap<String, Object>();
result.put("status", "fine");
assertThat(getEndpointBean().invoke(), equalTo(result));
}
@Configuration
@ -46,14 +51,18 @@ public class HealthEndpointTests extends AbstractEndpointTests<HealthEndpoint<St
public static class Config {
@Bean
public HealthEndpoint<String> endpoint() {
return new HealthEndpoint<String>(new HealthIndicator<String>() {
public HealthEndpoint endpoint() {
return new HealthEndpoint();
}
@Bean
public HealthIndicator<String> statusHealthIndicator() {
return new HealthIndicator<String>() {
@Override
public String health() {
return "fine";
}
});
};
}
}
}

View File

@ -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<String, Object> 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<String, Object> health = healthIndicator.health();
assertEquals("error", health.get("status"));
assertTrue(((String) health.get("error")).contains("Connection failed"));
Mockito.verify(mongoTemplate).executeCommand("{ serverStatus: 1 }");
}
}

View File

@ -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<String, Object> 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<String, Object> health = healthIndicator.health();
assertEquals("error", health.get("status"));
assertTrue(((String) health.get("error")).contains("Connection failed"));
Mockito.verify(redisConnectionFactory).getConnection();
Mockito.verify(redisConnection).info();
}
}

View File

@ -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

View File

@ -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

View File

@ -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`.