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 192be3221e6..7a1790e17f6 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 @@ -17,7 +17,6 @@ package org.springframework.boot.actuate.autoconfigure; import java.util.Collection; -import java.util.Collections; import java.util.Map; import javax.jms.ConnectionFactory; @@ -25,6 +24,7 @@ import javax.sql.DataSource; import org.apache.solr.client.solrj.SolrServer; import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.health.ApplicationHealthIndicator; import org.springframework.boot.actuate.health.CompositeHealthIndicator; @@ -60,6 +60,7 @@ import org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.ResolvableType; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.mail.javamail.JavaMailSenderImpl; @@ -70,6 +71,7 @@ import org.springframework.mail.javamail.JavaMailSenderImpl; * @author Christian Dupuis * @author Andy Wilkinson * @author Stephane Nicoll + * @author Phillip Webb * @since 1.1.0 */ @Configuration @@ -100,60 +102,93 @@ public class HealthIndicatorAutoConfiguration { return new ApplicationHealthIndicator(); } - @Configuration - @ConditionalOnBean(DataSource.class) - @ConditionalOnProperty(prefix = "management.health.db", name = "enabled", matchIfMissing = true) - public static class DataSourcesHealthIndicatorConfiguration { + /** + * Base class for configurations that can combine source beans using a + * {@link CompositeHealthIndicator}. + * @param The health indicator type + * @param The bean source type + */ + protected static abstract class CompositeHealthIndicatorConfiguration { @Autowired private HealthAggregator healthAggregator; + protected HealthIndicator createHealthIndicator(Map beans) { + if (beans.size() == 1) { + return createHealthIndicator(beans.values().iterator().next()); + } + CompositeHealthIndicator composite = new CompositeHealthIndicator( + this.healthAggregator); + for (Map.Entry entry : beans.entrySet()) { + composite.addHealthIndicator(entry.getKey(), + createHealthIndicator(entry.getValue())); + } + return composite; + } + + @SuppressWarnings("unchecked") + protected H createHealthIndicator(S source) { + Class[] generics = ResolvableType.forClass( + CompositeHealthIndicatorConfiguration.class, getClass()) + .resolveGenerics(); + Class indicatorClass = (Class) generics[0]; + Class sourceClass = (Class) generics[1]; + try { + return indicatorClass.getConstructor(sourceClass).newInstance(source); + } + catch (Exception ex) { + throw new IllegalStateException("Unable to create indicator " + + indicatorClass + " for source " + sourceClass, ex); + } + } + + } + + @Configuration + @ConditionalOnBean(DataSource.class) + @ConditionalOnProperty(prefix = "management.health.db", name = "enabled", matchIfMissing = true) + public static class DataSourcesHealthIndicatorConfiguration extends + CompositeHealthIndicatorConfiguration + implements InitializingBean { + @Autowired(required = false) private Map dataSources; @Autowired(required = false) - private Collection metadataProviders = Collections - .emptyList(); + private Collection metadataProviders; + + private DataSourcePoolMetadataProvider poolMetadataProvider; + + @Override + public void afterPropertiesSet() throws Exception { + this.poolMetadataProvider = new DataSourcePoolMetadataProviders( + this.metadataProviders); + } @Bean @ConditionalOnMissingBean(name = "dbHealthIndicator") public HealthIndicator dbHealthIndicator() { - DataSourcePoolMetadataProvider metadataProvider = new DataSourcePoolMetadataProviders( - this.metadataProviders); - if (this.dataSources.size() == 1) { - DataSource dataSource = this.dataSources.values().iterator().next(); - return createDataSourceHealthIndicator(metadataProvider, dataSource); - } - CompositeHealthIndicator composite = new CompositeHealthIndicator( - this.healthAggregator); - for (Map.Entry entry : this.dataSources.entrySet()) { - String name = entry.getKey(); - DataSource dataSource = entry.getValue(); - composite.addHealthIndicator(name, - createDataSourceHealthIndicator(metadataProvider, dataSource)); - } - return composite; + return createHealthIndicator(this.dataSources); } - private DataSourceHealthIndicator createDataSourceHealthIndicator( - DataSourcePoolMetadataProvider provider, DataSource dataSource) { - String validationQuery = null; - DataSourcePoolMetadata poolMetadata = provider - .getDataSourcePoolMetadata(dataSource); - if (poolMetadata != null) { - validationQuery = poolMetadata.getValidationQuery(); - } - return new DataSourceHealthIndicator(dataSource, validationQuery); + @Override + protected DataSourceHealthIndicator createHealthIndicator(DataSource source) { + return new DataSourceHealthIndicator(source, getValidationQuery(source)); } + + private String getValidationQuery(DataSource source) { + DataSourcePoolMetadata poolMetadata = this.poolMetadataProvider + .getDataSourcePoolMetadata(source); + return (poolMetadata == null ? null : poolMetadata.getValidationQuery()); + } + } @Configuration @ConditionalOnBean(MongoTemplate.class) @ConditionalOnProperty(prefix = "management.health.mongo", name = "enabled", matchIfMissing = true) - public static class MongoHealthIndicatorConfiguration { - - @Autowired - private HealthAggregator healthAggregator; + public static class MongoHealthIndicatorConfiguration extends + CompositeHealthIndicatorConfiguration { @Autowired private Map mongoTemplates; @@ -161,27 +196,17 @@ public class HealthIndicatorAutoConfiguration { @Bean @ConditionalOnMissingBean(name = "mongoHealthIndicator") public HealthIndicator mongoHealthIndicator() { - if (this.mongoTemplates.size() == 1) { - return new MongoHealthIndicator(this.mongoTemplates.values().iterator() - .next()); - } - CompositeHealthIndicator composite = new CompositeHealthIndicator( - this.healthAggregator); - for (Map.Entry entry : this.mongoTemplates.entrySet()) { - composite.addHealthIndicator(entry.getKey(), new MongoHealthIndicator( - entry.getValue())); - } - return composite; + return createHealthIndicator(this.mongoTemplates); } + } @Configuration @ConditionalOnBean(RedisConnectionFactory.class) @ConditionalOnProperty(prefix = "management.health.redis", name = "enabled", matchIfMissing = true) - public static class RedisHealthIndicatorConfiguration { - - @Autowired - private HealthAggregator healthAggregator; + public static class RedisHealthIndicatorConfiguration + extends + CompositeHealthIndicatorConfiguration { @Autowired private Map redisConnectionFactories; @@ -189,29 +214,16 @@ public class HealthIndicatorAutoConfiguration { @Bean @ConditionalOnMissingBean(name = "redisHealthIndicator") public HealthIndicator redisHealthIndicator() { - if (this.redisConnectionFactories.size() == 1) { - return new RedisHealthIndicator(this.redisConnectionFactories.values() - .iterator().next()); - } - - CompositeHealthIndicator composite = new CompositeHealthIndicator( - this.healthAggregator); - for (Map.Entry entry : this.redisConnectionFactories - .entrySet()) { - composite.addHealthIndicator(entry.getKey(), new RedisHealthIndicator( - entry.getValue())); - } - return composite; + return createHealthIndicator(this.redisConnectionFactories); } + } @Configuration @ConditionalOnBean(RabbitTemplate.class) @ConditionalOnProperty(prefix = "management.health.rabbit", name = "enabled", matchIfMissing = true) - public static class RabbitHealthIndicatorConfiguration { - - @Autowired - private HealthAggregator healthAggregator; + public static class RabbitHealthIndicatorConfiguration extends + CompositeHealthIndicatorConfiguration { @Autowired private Map rabbitTemplates; @@ -219,28 +231,16 @@ public class HealthIndicatorAutoConfiguration { @Bean @ConditionalOnMissingBean(name = "rabbitHealthIndicator") public HealthIndicator rabbitHealthIndicator() { - if (this.rabbitTemplates.size() == 1) { - return new RabbitHealthIndicator(this.rabbitTemplates.values().iterator() - .next()); - } - CompositeHealthIndicator composite = new CompositeHealthIndicator( - this.healthAggregator); - for (Map.Entry entry : this.rabbitTemplates - .entrySet()) { - composite.addHealthIndicator(entry.getKey(), new RabbitHealthIndicator( - entry.getValue())); - } - return composite; + return createHealthIndicator(this.rabbitTemplates); } + } @Configuration @ConditionalOnBean(SolrServer.class) @ConditionalOnProperty(prefix = "management.health.solr", name = "enabled", matchIfMissing = true) - public static class SolrHealthIndicatorConfiguration { - - @Autowired - private HealthAggregator healthAggregator; + public static class SolrHealthIndicatorConfiguration extends + CompositeHealthIndicatorConfiguration { @Autowired private Map solrServers; @@ -248,18 +248,9 @@ public class HealthIndicatorAutoConfiguration { @Bean @ConditionalOnMissingBean(name = "solrHealthIndicator") public HealthIndicator solrHealthIndicator() { - if (this.solrServers.size() == 1) { - return new SolrHealthIndicator(this.solrServers.entrySet().iterator() - .next().getValue()); - } - CompositeHealthIndicator composite = new CompositeHealthIndicator( - this.healthAggregator); - for (Map.Entry entry : this.solrServers.entrySet()) { - composite.addHealthIndicator(entry.getKey(), new SolrHealthIndicator( - entry.getValue())); - } - return composite; + return createHealthIndicator(this.solrServers); } + } @Configuration @@ -283,10 +274,9 @@ public class HealthIndicatorAutoConfiguration { @Configuration @ConditionalOnBean(JavaMailSenderImpl.class) @ConditionalOnProperty(prefix = "management.health.mail", name = "enabled", matchIfMissing = true) - public static class MailHealthIndicatorConfiguration { - - @Autowired - private HealthAggregator healthAggregator; + public static class MailHealthIndicatorConfiguration + extends + CompositeHealthIndicatorConfiguration { @Autowired(required = false) private Map mailSenders; @@ -294,28 +284,16 @@ public class HealthIndicatorAutoConfiguration { @Bean @ConditionalOnMissingBean(name = "mailHealthIndicator") public HealthIndicator mailHealthIndicator() { - if (this.mailSenders.size() == 1) { - return new MailHealthIndicator(this.mailSenders.values().iterator() - .next()); - } - CompositeHealthIndicator composite = new CompositeHealthIndicator( - this.healthAggregator); - for (Map.Entry entry : this.mailSenders - .entrySet()) { - composite.addHealthIndicator(entry.getKey(), new MailHealthIndicator( - entry.getValue())); - } - return composite; + return createHealthIndicator(this.mailSenders); } + } @Configuration @ConditionalOnBean(ConnectionFactory.class) @ConditionalOnProperty(prefix = "management.health.jms", name = "enabled", matchIfMissing = true) - public static class JmsHealthIndicatorConfiguration { - - @Autowired - private HealthAggregator healthAggregator; + public static class JmsHealthIndicatorConfiguration extends + CompositeHealthIndicatorConfiguration { @Autowired(required = false) private Map connectionFactories; @@ -323,19 +301,9 @@ public class HealthIndicatorAutoConfiguration { @Bean @ConditionalOnMissingBean(name = "jmsHealthIndicator") public HealthIndicator jmsHealthIndicator() { - if (this.connectionFactories.size() == 1) { - return new JmsHealthIndicator(this.connectionFactories.values() - .iterator().next()); - } - CompositeHealthIndicator composite = new CompositeHealthIndicator( - this.healthAggregator); - for (Map.Entry entry : this.connectionFactories - .entrySet()) { - composite.addHealthIndicator(entry.getKey(), - new JmsHealthIndicator(entry.getValue())); - } - return composite; + return createHealthIndicator(this.connectionFactories); } + } } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/metadata/DataSourcePoolMetadataProviders.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/metadata/DataSourcePoolMetadataProviders.java index e218177d292..e503d4f7118 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/metadata/DataSourcePoolMetadataProviders.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/metadata/DataSourcePoolMetadataProviders.java @@ -18,6 +18,7 @@ package org.springframework.boot.autoconfigure.jdbc.metadata; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; import javax.sql.DataSource; @@ -40,7 +41,9 @@ public class DataSourcePoolMetadataProviders implements DataSourcePoolMetadataPr */ public DataSourcePoolMetadataProviders( Collection providers) { - this.providers = new ArrayList(providers); + this.providers = (providers == null ? Collections + . emptyList() + : new ArrayList(providers)); } @Override