diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/DataSourceHealthIndicator.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/DataSourceHealthIndicator.java index 67ed12e262b..c8506ab1c45 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/DataSourceHealthIndicator.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/DataSourceHealthIndicator.java @@ -20,12 +20,14 @@ import java.sql.Connection; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.sql.DataSource; +import org.springframework.beans.factory.InitializingBean; import org.springframework.dao.DataAccessException; import org.springframework.dao.support.DataAccessUtils; import org.springframework.jdbc.IncorrectResultSetColumnCountException; @@ -33,6 +35,7 @@ import org.springframework.jdbc.core.ConnectionCallback; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.support.JdbcUtils; +import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** @@ -45,24 +48,26 @@ import org.springframework.util.StringUtils; * @author Stephane Nicoll * @since 1.1.0 */ -public class DataSourceHealthIndicator extends AbstractHealthIndicator { - - private DataSource dataSource; - - private JdbcTemplate jdbcTemplate; - - private static final Map queries = new HashMap(); +public class DataSourceHealthIndicator extends AbstractHealthIndicator implements + InitializingBean { + private static final Map PRODUCT_SPECIFIC_QUERIES; static { + Map queries = new HashMap(); queries.put("HSQL Database Engine", "SELECT COUNT(*) FROM " + "INFORMATION_SCHEMA.SYSTEM_USERS"); queries.put("Oracle", "SELECT 'Hello' from DUAL"); queries.put("Apache Derby", "SELECT 1 FROM SYSIBM.SYSDUMMY1"); + PRODUCT_SPECIFIC_QUERIES = Collections.unmodifiableMap(queries); } private static final String DEFAULT_QUERY = "SELECT 1"; - private String query = null; + private DataSource dataSource; + + private String query; + + private JdbcTemplate jdbcTemplate; /** * Create a new {@link DataSourceHealthIndicator} instance. @@ -71,7 +76,17 @@ public class DataSourceHealthIndicator extends AbstractHealthIndicator { } /** - * Create a new {@link DataSourceHealthIndicator} using the specified datasource. + * Create a new {@link DataSourceHealthIndicator} using the specified + * {@link DataSource}. + * @param dataSource the data source + */ + public DataSourceHealthIndicator(DataSource dataSource) { + this(dataSource, null); + } + + /** + * Create a new {@link DataSourceHealthIndicator} using the specified + * {@link DataSource} and validation query. * @param dataSource the data source * @param query the validation query to use (can be {@code null}) */ @@ -81,12 +96,10 @@ public class DataSourceHealthIndicator extends AbstractHealthIndicator { this.jdbcTemplate = new JdbcTemplate(dataSource); } - /** - * Create a new {@link DataSourceHealthIndicator} using the specified datasource. - * @param dataSource the data source - */ - public DataSourceHealthIndicator(DataSource dataSource) { - this(dataSource, null); + @Override + public void afterPropertiesSet() throws Exception { + Assert.state(this.dataSource != null, + "DataSource for DataSourceHealthIndicator must be specified"); } @Override @@ -102,11 +115,11 @@ public class DataSourceHealthIndicator extends AbstractHealthIndicator { private void doDataSourceHealthCheck(Health.Builder builder) throws Exception { String product = getProduct(); builder.up().withDetail("database", product); - String query = detectQuery(product); - if (StringUtils.hasText(query)) { + String validationQuery = getValidationQuery(product); + if (StringUtils.hasText(validationQuery)) { try { // Avoid calling getObject as it breaks MySQL on Java 7 - List results = this.jdbcTemplate.query(query, + List results = this.jdbcTemplate.query(validationQuery, new SingleColumnRowMapper()); Object result = DataAccessUtils.requiredSingleResult(results); builder.withDetail("hello", result); @@ -127,10 +140,10 @@ public class DataSourceHealthIndicator extends AbstractHealthIndicator { }); } - protected String detectQuery(String product) { + protected String getValidationQuery(String product) { String query = this.query; if (!StringUtils.hasText(query)) { - query = queries.get(product); + query = PRODUCT_SPECIFIC_QUERIES.get(product); } if (!StringUtils.hasText(query)) { query = DEFAULT_QUERY; @@ -147,18 +160,18 @@ public class DataSourceHealthIndicator extends AbstractHealthIndicator { } /** - * Set a specific validation query to use to validate a connection. If - * none is set, a default validation query is used. + * Set a specific validation query to use to validate a connection. If none is set, a + * default validation query is used. */ public void setQuery(String query) { this.query = query; } /** - * Return the specific validation query, if any. + * Return the validation query or {@code null}. */ public String getQuery() { - return query; + return this.query; } /** 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 ff1b2d44647..e307b8a2e4e 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 @@ -23,13 +23,13 @@ import javax.sql.DataSource; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.springframework.boot.actuate.health.ApplicationHealthIndicator; import org.springframework.boot.actuate.health.DataSourceHealthIndicator; import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.boot.actuate.health.MongoHealthIndicator; import org.springframework.boot.actuate.health.RabbitHealthIndicator; import org.springframework.boot.actuate.health.RedisHealthIndicator; import org.springframework.boot.actuate.health.SolrHealthIndicator; -import org.springframework.boot.actuate.health.ApplicationHealthIndicator; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder; @@ -166,12 +166,15 @@ public class HealthIndicatorAutoConfigurationTests { @Test public void dataSourceHealthIndicatorWithCustomValidationQuery() { this.context = new AnnotationConfigApplicationContext(); - this.context.register(PropertyPlaceholderAutoConfiguration.class, DataSourceProperties.class, - DataSourceConfig.class, DataSourcePoolMetadataProvidersConfiguration.class, + this.context.register(PropertyPlaceholderAutoConfiguration.class, + DataSourceProperties.class, DataSourceConfig.class, + DataSourcePoolMetadataProvidersConfiguration.class, HealthIndicatorAutoConfiguration.class); - EnvironmentTestUtils.addEnvironment(this.context, "spring.datasource.validation-query:SELECT from FOOBAR"); + EnvironmentTestUtils.addEnvironment(this.context, + "spring.datasource.validation-query:SELECT from FOOBAR"); this.context.refresh(); - Map beans = this.context.getBeansOfType(HealthIndicator.class); + Map beans = this.context + .getBeansOfType(HealthIndicator.class); assertEquals(1, beans.size()); HealthIndicator healthIndicator = beans.values().iterator().next(); assertEquals(DataSourceHealthIndicator.class, healthIndicator.getClass()); @@ -247,7 +250,6 @@ public class HealthIndicatorAutoConfigurationTests { .getClass()); } - @Configuration @EnableConfigurationProperties protected static class DataSourceConfig { @@ -257,8 +259,9 @@ public class HealthIndicatorAutoConfigurationTests { public DataSource dataSource() { return DataSourceBuilder.create() .driverClassName("org.hsqldb.jdbc.JDBCDriver") - .url("jdbc:hsqldb:mem:test") - .username("sa").build(); + .url("jdbc:hsqldb:mem:test").username("sa").build(); } + } + }