diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/MetricDataSourceAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/DataSourceMetricsAutoConfiguration.java similarity index 73% rename from spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/MetricDataSourceAutoConfiguration.java rename to spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/DataSourceMetricsAutoConfiguration.java index 3aa10bd868..4c88f7e501 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/MetricDataSourceAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/DataSourceMetricsAutoConfiguration.java @@ -24,27 +24,24 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; -import org.springframework.boot.autoconfigure.jdbc.DataSourceMetadataProvider; -import org.springframework.boot.autoconfigure.jdbc.DataSourceMetadataProvidersConfiguration; +import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvider; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Import; /** - * {@link EnableAutoConfiguration Auto-configuration} that provides - * metrics on dataSource usage. + * {@link EnableAutoConfiguration Auto-configuration} that provides metrics on dataSource + * usage. * * @author Stephane Nicoll * @since 1.2.0 */ @ConditionalOnBean(DataSource.class) @AutoConfigureAfter(DataSourceAutoConfiguration.class) -@Import(DataSourceMetadataProvidersConfiguration.class) -public class MetricDataSourceAutoConfiguration { +public class DataSourceMetricsAutoConfiguration { @Bean - @ConditionalOnBean(DataSourceMetadataProvider.class) - @ConditionalOnMissingBean(DataSourcePublicMetrics.class) - DataSourcePublicMetrics dataSourcePublicMetrics() { + @ConditionalOnBean(DataSourcePoolMetadataProvider.class) + @ConditionalOnMissingBean + public DataSourcePublicMetrics dataSourcePublicMetrics() { return new DataSourcePublicMetrics(); } 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 ec5b14844c..c6885e11d4 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 @@ -27,6 +27,7 @@ import org.apache.solr.client.solrj.SolrServer; 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.ApplicationHealthIndicator; import org.springframework.boot.actuate.health.CompositeHealthIndicator; import org.springframework.boot.actuate.health.DataSourceHealthIndicator; import org.springframework.boot.actuate.health.HealthAggregator; @@ -36,7 +37,6 @@ 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.SolrHealthIndicator; -import org.springframework.boot.actuate.health.ApplicationHealthIndicator; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -44,10 +44,10 @@ import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.jdbc.CompositeDataSourceMetadataProvider; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; -import org.springframework.boot.autoconfigure.jdbc.DataSourceMetadata; -import org.springframework.boot.autoconfigure.jdbc.DataSourceMetadataProvider; +import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadata; +import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvider; +import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProviders; import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; import org.springframework.boot.autoconfigure.mongo.MongoDataAutoConfiguration; import org.springframework.boot.autoconfigure.redis.RedisAutoConfiguration; @@ -103,33 +103,36 @@ public class HealthIndicatorAutoConfiguration { private Map dataSources; @Autowired(required = false) - private Collection metadataProviders = Collections.emptyList(); + private Collection metadataProviders = Collections + .emptyList(); @Bean @ConditionalOnMissingBean(name = "dbHealthIndicator") public HealthIndicator dbHealthIndicator() { - DataSourceMetadataProvider metadataProvider = - new CompositeDataSourceMetadataProvider(this.metadataProviders); + DataSourcePoolMetadataProvider metadataProvider = new DataSourcePoolMetadataProviders( + this.metadataProviders); if (this.dataSources.size() == 1) { - return createDataSourceHealthIndicator(metadataProvider, - this.dataSources.values().iterator().next()); + DataSource dataSource = this.dataSources.values().iterator().next(); + return createDataSourceHealthIndicator(metadataProvider, dataSource); } CompositeHealthIndicator composite = new CompositeHealthIndicator( this.healthAggregator); for (Map.Entry entry : this.dataSources.entrySet()) { - composite.addHealthIndicator(entry.getKey(), - createDataSourceHealthIndicator(metadataProvider, entry.getValue())); + String name = entry.getKey(); + DataSource dataSource = entry.getValue(); + composite.addHealthIndicator(name, + createDataSourceHealthIndicator(metadataProvider, dataSource)); } return composite; } - private DataSourceHealthIndicator createDataSourceHealthIndicator(DataSourceMetadataProvider provider, - DataSource dataSource) { - + private DataSourceHealthIndicator createDataSourceHealthIndicator( + DataSourcePoolMetadataProvider provider, DataSource dataSource) { String validationQuery = null; - DataSourceMetadata dataSourceMetadata = provider.getDataSourceMetadata(dataSource); - if (dataSourceMetadata != null) { - validationQuery = dataSourceMetadata.getValidationQuery(); + DataSourcePoolMetadata poolMetadata = provider + .getDataSourcePoolMetadata(dataSource); + if (poolMetadata != null) { + validationQuery = poolMetadata.getValidationQuery(); } return new DataSourceHealthIndicator(dataSource, validationQuery); } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/DataSourcePublicMetrics.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/DataSourcePublicMetrics.java index 7d57158ad1..029ee37ae8 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/DataSourcePublicMetrics.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/DataSourcePublicMetrics.java @@ -20,21 +20,22 @@ import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.Map; +import java.util.Set; + import javax.annotation.PostConstruct; import javax.sql.DataSource; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.boot.autoconfigure.jdbc.CompositeDataSourceMetadataProvider; -import org.springframework.boot.autoconfigure.jdbc.DataSourceMetadata; -import org.springframework.boot.autoconfigure.jdbc.DataSourceMetadataProvider; +import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadata; +import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvider; +import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProviders; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Primary; /** - * A {@link PublicMetrics} implementation that provides data source usage - * statistics. + * A {@link PublicMetrics} implementation that provides data source usage statistics. * * @author Stephane Nicoll * @since 1.2.0 @@ -47,81 +48,66 @@ public class DataSourcePublicMetrics implements PublicMetrics { private ApplicationContext applicationContext; @Autowired - private Collection dataSourceMetadataProviders; + private Collection providers; - private final Map dataSourceMetadataByPrefix - = new HashMap(); + private final Map metadataByPrefix = new HashMap(); @PostConstruct public void initialize() { - Map dataSources = this.applicationContext.getBeansOfType(DataSource.class); DataSource primaryDataSource = getPrimaryDataSource(); - - - DataSourceMetadataProvider provider = new CompositeDataSourceMetadataProvider(this.dataSourceMetadataProviders); - for (Map.Entry entry : dataSources.entrySet()) { - String prefix = createPrefix(entry.getKey(), entry.getValue(), entry.getValue().equals(primaryDataSource)); - DataSourceMetadata dataSourceMetadata = provider.getDataSourceMetadata(entry.getValue()); - if (dataSourceMetadata != null) { - dataSourceMetadataByPrefix.put(prefix, dataSourceMetadata); + DataSourcePoolMetadataProvider provider = new DataSourcePoolMetadataProviders( + this.providers); + for (Map.Entry entry : this.applicationContext + .getBeansOfType(DataSource.class).entrySet()) { + String beanName = entry.getKey(); + DataSource bean = entry.getValue(); + String prefix = createPrefix(beanName, bean, bean.equals(primaryDataSource)); + DataSourcePoolMetadata poolMetadata = provider + .getDataSourcePoolMetadata(bean); + if (poolMetadata != null) { + this.metadataByPrefix.put(prefix, poolMetadata); } } } @Override public Collection> metrics() { - Collection> result = new LinkedHashSet>(); - for (Map.Entry entry : dataSourceMetadataByPrefix.entrySet()) { + Set> metrics = new LinkedHashSet>(); + for (Map.Entry entry : this.metadataByPrefix + .entrySet()) { String prefix = entry.getKey(); - // Make sure the prefix ends with a dot - if (!prefix.endsWith(".")) { - prefix = prefix + "."; - } - DataSourceMetadata dataSourceMetadata = entry.getValue(); - Integer poolSize = dataSourceMetadata.getPoolSize(); - if (poolSize != null) { - result.add(new Metric(prefix + "active", poolSize)); - } - Float poolUsage = dataSourceMetadata.getPoolUsage(); - if (poolUsage != null) { - result.add(new Metric(prefix + "usage", poolUsage)); - } + prefix = (prefix.endsWith(".") ? prefix : prefix + "."); + DataSourcePoolMetadata metadata = entry.getValue(); + addMetric(metrics, prefix + "max", metadata.getMax()); + addMetric(metrics, prefix + "min", metadata.getMin()); + addMetric(metrics, prefix + "active", metadata.getActive()); + addMetric(metrics, prefix + "usage", metadata.getUsage()); + } + return metrics; + } + + private void addMetric(Set> metrics, String name, T value) { + if (value != null) { + metrics.add(new Metric(name, value)); } - return result; } /** - * Create the prefix to use for the metrics to associate with the given {@link DataSource}. - * @param dataSourceName the name of the data source bean + * Create the prefix to use for the metrics to associate with the given + * {@link DataSource}. + * @param name the name of the data source bean * @param dataSource the data source to configure * @param primary if this data source is the primary data source * @return a prefix for the given data source */ - protected String createPrefix(String dataSourceName, DataSource dataSource, boolean primary) { - StringBuilder sb = new StringBuilder("datasource."); + protected String createPrefix(String name, DataSource dataSource, boolean primary) { if (primary) { - sb.append("primary"); + return "datasource.primary"; } - else if (endWithDataSource(dataSourceName)) { // Strip the data source part out of the name - sb.append(dataSourceName.substring(0, dataSourceName.length() - DATASOURCE_SUFFIX.length())); + if (name.toLowerCase().endsWith(DATASOURCE_SUFFIX.toLowerCase())) { + name = name.substring(0, name.length() - DATASOURCE_SUFFIX.length()); } - else { - sb.append(dataSourceName); - } - return sb.toString(); - } - - /** - * Specify if the given value ends with {@value #DATASOURCE_SUFFIX}. - */ - protected boolean endWithDataSource(String value) { - int suffixLength = DATASOURCE_SUFFIX.length(); - int valueLength = value.length(); - if (valueLength > suffixLength) { - String suffix = value.substring(valueLength - suffixLength, valueLength); - return suffix.equalsIgnoreCase(DATASOURCE_SUFFIX); - } - return false; + return "datasource." + name; } /** @@ -131,9 +117,9 @@ public class DataSourcePublicMetrics implements PublicMetrics { */ private DataSource getPrimaryDataSource() { try { - return applicationContext.getBean(DataSource.class); + return this.applicationContext.getBean(DataSource.class); } - catch (NoSuchBeanDefinitionException e) { + catch (NoSuchBeanDefinitionException ex) { return null; } } diff --git a/spring-boot-actuator/src/main/resources/META-INF/spring.factories b/spring-boot-actuator/src/main/resources/META-INF/spring.factories index fc55c4cee0..aa77fbc180 100644 --- a/spring-boot-actuator/src/main/resources/META-INF/spring.factories +++ b/spring-boot-actuator/src/main/resources/META-INF/spring.factories @@ -1,6 +1,7 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.actuate.autoconfigure.AuditAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.CrshAutoConfiguration,\ +org.springframework.boot.actuate.autoconfigure.DataSourceMetricsAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.EndpointMBeanExportAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration,\ @@ -8,7 +9,6 @@ org.springframework.boot.actuate.autoconfigure.HealthIndicatorAutoConfiguration, org.springframework.boot.actuate.autoconfigure.JolokiaAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.ManagementSecurityAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.ManagementServerPropertiesAutoConfiguration,\ -org.springframework.boot.actuate.autoconfigure.MetricDataSourceAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.MetricFilterAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.MetricRepositoryAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.TraceRepositoryAutoConfiguration,\ diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/MetricDataSourceAutoConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/DataSourceMetricsAutoConfigurationTests.java similarity index 77% rename from spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/MetricDataSourceAutoConfigurationTests.java rename to spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/DataSourceMetricsAutoConfigurationTests.java index a9dc552eea..c627e68fb3 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/MetricDataSourceAutoConfigurationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/DataSourceMetricsAutoConfigurationTests.java @@ -16,8 +16,6 @@ package org.springframework.boot.actuate.autoconfigure; -import static org.junit.Assert.*; - import java.sql.Connection; import java.sql.SQLException; import java.util.Collection; @@ -26,16 +24,15 @@ import java.util.Map; import javax.sql.DataSource; -import com.zaxxer.hikari.HikariDataSource; import org.apache.commons.dbcp.BasicDataSource; import org.junit.After; import org.junit.Test; - import org.springframework.boot.actuate.endpoint.DataSourcePublicMetrics; import org.springframework.boot.actuate.endpoint.PublicMetrics; import org.springframework.boot.actuate.metrics.Metric; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder; +import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -44,11 +41,17 @@ import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.ConnectionCallback; import org.springframework.jdbc.core.JdbcTemplate; +import com.zaxxer.hikari.HikariDataSource; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + /** + * Tests for {@link DataSourceMetricsAutoConfiguration}. * * @author Stephane Nicoll */ -public class MetricDataSourceAutoConfigurationTests { +public class DataSourceMetricsAutoConfigurationTests { private AnnotationConfigApplicationContext context; @@ -78,24 +81,25 @@ public class MetricDataSourceAutoConfigurationTests { load(MultipleDataSourcesConfig.class); PublicMetrics bean = this.context.getBean(PublicMetrics.class); Collection> metrics = bean.metrics(); - assertMetrics(metrics, - "datasource.tomcat.active", "datasource.tomcat.usage", + assertMetrics(metrics, "datasource.tomcat.active", "datasource.tomcat.usage", "datasource.commonsDbcp.active", "datasource.commonsDbcp.usage"); // Hikari won't work unless a first connection has been retrieved - JdbcTemplate jdbcTemplate = new JdbcTemplate(context.getBean("hikariDS", DataSource.class)); + JdbcTemplate jdbcTemplate = new JdbcTemplate(this.context.getBean("hikariDS", + DataSource.class)); jdbcTemplate.execute(new ConnectionCallback() { @Override - public Void doInConnection(Connection connection) throws SQLException, DataAccessException { + public Void doInConnection(Connection connection) throws SQLException, + DataAccessException { return null; } }); Collection> anotherMetrics = bean.metrics(); - assertMetrics(anotherMetrics, - "datasource.tomcat.active", "datasource.tomcat.usage", - "datasource.hikariDS.active", "datasource.hikariDS.usage", - "datasource.commonsDbcp.active", "datasource.commonsDbcp.usage"); + assertMetrics(anotherMetrics, "datasource.tomcat.active", + "datasource.tomcat.usage", "datasource.hikariDS.active", + "datasource.hikariDS.usage", "datasource.commonsDbcp.active", + "datasource.commonsDbcp.usage"); } @Test @@ -103,19 +107,18 @@ public class MetricDataSourceAutoConfigurationTests { load(MultipleDataSourcesWithPrimaryConfig.class); PublicMetrics bean = this.context.getBean(PublicMetrics.class); Collection> metrics = bean.metrics(); - assertMetrics(metrics, - "datasource.primary.active", "datasource.primary.usage", + assertMetrics(metrics, "datasource.primary.active", "datasource.primary.usage", "datasource.commonsDbcp.active", "datasource.commonsDbcp.usage"); } @Test public void customPrefix() { - load(MultipleDataSourcesWithPrimaryConfig.class, CustomDataSourcePublicMetrics.class); + load(MultipleDataSourcesWithPrimaryConfig.class, + CustomDataSourcePublicMetrics.class); PublicMetrics bean = this.context.getBean(PublicMetrics.class); Collection> metrics = bean.metrics(); - assertMetrics(metrics, - "ds.first.active", "ds.first.usage", - "ds.second.active", "ds.second.usage"); + assertMetrics(metrics, "ds.first.active", "ds.first.usage", "ds.second.active", + "ds.second.usage"); } @@ -134,17 +137,18 @@ public class MetricDataSourceAutoConfigurationTests { if (config.length > 0) { this.context.register(config); } - this.context.register(MetricDataSourceAutoConfiguration.class); + this.context.register(DataSourcePoolMetadataProvidersConfiguration.class, + DataSourceMetricsAutoConfiguration.class); this.context.refresh(); } - @Configuration static class MultipleDataSourcesConfig { @Bean public DataSource tomcatDataSource() { - return initializeBuilder().type(org.apache.tomcat.jdbc.pool.DataSource.class).build(); + return initializeBuilder().type(org.apache.tomcat.jdbc.pool.DataSource.class) + .build(); } @Bean @@ -164,7 +168,8 @@ public class MetricDataSourceAutoConfigurationTests { @Bean @Primary public DataSource myDataSource() { - return initializeBuilder().type(org.apache.tomcat.jdbc.pool.DataSource.class).build(); + return initializeBuilder().type(org.apache.tomcat.jdbc.pool.DataSource.class) + .build(); } @Bean @@ -180,7 +185,8 @@ public class MetricDataSourceAutoConfigurationTests { public DataSourcePublicMetrics myDataSourcePublicMetrics() { return new DataSourcePublicMetrics() { @Override - protected String createPrefix(String dataSourceName, DataSource dataSource, boolean primary) { + protected String createPrefix(String dataSourceName, + DataSource dataSource, boolean primary) { return (primary ? "ds.first." : "ds.second"); } }; @@ -188,9 +194,8 @@ public class MetricDataSourceAutoConfigurationTests { } private static DataSourceBuilder initializeBuilder() { - return DataSourceBuilder.create() - .driverClassName("org.hsqldb.jdbc.JDBCDriver") - .url("jdbc:hsqldb:mem:test") - .username("sa"); + return DataSourceBuilder.create().driverClassName("org.hsqldb.jdbc.JDBCDriver") + .url("jdbc:hsqldb:mem:test").username("sa"); } + } 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 c0bac2d0dd..ff1b2d4464 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 @@ -33,9 +33,9 @@ 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; -import org.springframework.boot.autoconfigure.jdbc.DataSourceMetadataProvidersConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration; +import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration; import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; import org.springframework.boot.autoconfigure.mongo.MongoDataAutoConfiguration; import org.springframework.boot.autoconfigure.redis.RedisAutoConfiguration; @@ -167,7 +167,7 @@ public class HealthIndicatorAutoConfigurationTests { public void dataSourceHealthIndicatorWithCustomValidationQuery() { this.context = new AnnotationConfigApplicationContext(); this.context.register(PropertyPlaceholderAutoConfiguration.class, DataSourceProperties.class, - DataSourceConfig.class, DataSourceMetadataProvidersConfiguration.class, + DataSourceConfig.class, DataSourcePoolMetadataProvidersConfiguration.class, HealthIndicatorAutoConfiguration.class); EnvironmentTestUtils.addEnvironment(this.context, "spring.datasource.validation-query:SELECT from FOOBAR"); this.context.refresh(); diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/CompositeDataSourceMetadataProvider.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/CompositeDataSourceMetadataProvider.java deleted file mode 100644 index ffe98480fb..0000000000 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/CompositeDataSourceMetadataProvider.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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.autoconfigure.jdbc; - -import java.util.ArrayList; -import java.util.Collection; - -import javax.sql.DataSource; - -/** - * A {@link DataSourceMetadataProvider} implementation that returns the first - * {@link DataSourceMetadata} that is found by one of its delegate. - * - * @author Stephane Nicoll - * @since 1.2.0 - */ -public class CompositeDataSourceMetadataProvider implements DataSourceMetadataProvider { - - private final Collection providers; - - /** - * Create an instance with an initial collection of delegates to use. - */ - public CompositeDataSourceMetadataProvider(Collection providers) { - this.providers = providers; - } - - /** - * Create an instance with no delegate. - */ - public CompositeDataSourceMetadataProvider() { - this(new ArrayList()); - } - - @Override - public DataSourceMetadata getDataSourceMetadata(DataSource dataSource) { - for (DataSourceMetadataProvider provider : providers) { - DataSourceMetadata dataSourceMetadata = provider.getDataSourceMetadata(dataSource); - if (dataSourceMetadata != null) { - return dataSourceMetadata; - } - } - return null; - } - - /** - * Add a {@link DataSourceMetadataProvider} delegate to the list. - */ - public void addDataSourceMetadataProvider(DataSourceMetadataProvider provider) { - this.providers.add(provider); - } - -} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration.java index 5a68991a21..981397d438 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration.java @@ -30,6 +30,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.SpringBootCondition; import org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerPostProcessor.Registrar; +import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; @@ -54,7 +55,7 @@ import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; @Configuration @ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class }) @EnableConfigurationProperties(DataSourceProperties.class) -@Import(Registrar.class) +@Import({ Registrar.class, DataSourcePoolMetadataProvidersConfiguration.class }) public class DataSourceAutoConfiguration { /** diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceMetadata.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceMetadata.java deleted file mode 100644 index af4e9d1912..0000000000 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceMetadata.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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.autoconfigure.jdbc; - -import javax.sql.DataSource; - -/** - * Provide various metadata regarding a {@link DataSource} that - * are shared by most data source types but not accessible in a - * standard manner. - * - * @author Stephane Nicoll - * @since 1.2.0 - */ -public interface DataSourceMetadata { - - /** - * Return the usage of the pool as a double value between - * 0 and 1. - *
    - *
  • 1 means that the maximum number of connections - * have been allocated
  • - *
  • 0 means that no connection is currently active
  • - *
  • -1 means there is not limit to the number of connections - * that can be allocated
  • - *
- * This may also return {@code null} if the data source does - * not provide the necessary information to compute the poll usage. - */ - Float getPoolUsage(); - - /** - * Return the current number of active connections that - * have been allocated from the data source or {@code null} - * if that information is not available. - */ - Integer getPoolSize(); - - /** - * Return the maximum number of active connections that can be - * allocated at the same time or {@code -1} if there is no - * limit. Can also return {@code null} if that information is - * not available. - */ - Integer getMaxPoolSize(); - - /** - * Return the minimum number of idle connections in the pool - * or {@code null} if that information is not available. - */ - Integer getMinPoolSize(); - - /** - * Return the query to use to validate that a connection is - * valid or {@code null} if that information is not available. - */ - String getValidationQuery(); - -} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/HikariDataSourceMetadata.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/HikariDataSourceMetadata.java deleted file mode 100644 index cc2500ff1e..0000000000 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/HikariDataSourceMetadata.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * 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.autoconfigure.jdbc; - -import com.zaxxer.hikari.HikariDataSource; -import com.zaxxer.hikari.pool.HikariPool; - -import org.springframework.beans.BeansException; -import org.springframework.beans.DirectFieldAccessor; - -/** - * A {@link DataSourceMetadata} implementation for the hikari - * data source. - * - * @author Stephane Nicoll - * @since 1.2.0 - */ -public class HikariDataSourceMetadata extends AbstractDataSourceMetadata { - - - private final HikariPoolProvider hikariPoolProvider; - - public HikariDataSourceMetadata(HikariDataSource dataSource) { - super(dataSource); - this.hikariPoolProvider = new HikariPoolProvider(dataSource); - } - - @Override - public Integer getPoolSize() { - HikariPool hikariPool = hikariPoolProvider.getHikariPool(); - if (hikariPool != null) { - return hikariPool.getActiveConnections(); - } - return null; - } - - public Integer getMaxPoolSize() { - return getDataSource().getMaximumPoolSize(); - } - - @Override - public Integer getMinPoolSize() { - return getDataSource().getMinimumIdle(); - } - - @Override - public String getValidationQuery() { - return getDataSource().getConnectionTestQuery(); - } - - /** - * Provide the {@link HikariPool} instance managed internally by - * the {@link HikariDataSource} as there is no other way to retrieve - * that information except JMX access. - */ - private static class HikariPoolProvider { - private final HikariDataSource dataSource; - - private boolean poolAvailable; - - private HikariPoolProvider(HikariDataSource dataSource) { - this.dataSource = dataSource; - this.poolAvailable = isHikariPoolAvailable(); - } - - public HikariPool getHikariPool() { - if (!poolAvailable) { - return null; - } - - Object value = doGetValue(); - if (value instanceof HikariPool) { - return (HikariPool) value; - } - return null; - } - - private boolean isHikariPoolAvailable() { - try { - doGetValue(); - return true; - } - catch (BeansException e) { // No such field - return false; - } - catch (SecurityException e) { // Security manager prevents to read the value - return false; - } - } - - private Object doGetValue() { - DirectFieldAccessor accessor = new DirectFieldAccessor(this.dataSource); - return accessor.getPropertyValue("pool"); - } - } - -} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/AbstractDataSourceMetadata.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/metadata/AbstractDataSourcePoolMetadata.java similarity index 58% rename from spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/AbstractDataSourceMetadata.java rename to spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/metadata/AbstractDataSourcePoolMetadata.java index 9adf8267f7..c0f271d3af 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/AbstractDataSourceMetadata.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/metadata/AbstractDataSourcePoolMetadata.java @@ -14,48 +14,46 @@ * limitations under the License. */ -package org.springframework.boot.autoconfigure.jdbc; +package org.springframework.boot.autoconfigure.jdbc.metadata; import javax.sql.DataSource; /** - * A base {@link DataSourceMetadata} implementation. + * A base {@link DataSourcePoolMetadata} implementation. * * @author Stephane Nicoll * @since 1.2.0 */ -public abstract class AbstractDataSourceMetadata implements DataSourceMetadata { +public abstract class AbstractDataSourcePoolMetadata implements + DataSourcePoolMetadata { - private final D dataSource; + private final T dataSource; /** * Create an instance with the data source to use. */ - protected AbstractDataSourceMetadata(D dataSource) { + protected AbstractDataSourcePoolMetadata(T dataSource) { this.dataSource = dataSource; } @Override - public Float getPoolUsage() { - Integer max = getMaxPoolSize(); - if (max == null) { + public Float getUsage() { + Integer maxSize = getMax(); + Integer currentSize = getActive(); + if (maxSize == null || currentSize == null) { return null; } - if (max < 0) { + if (maxSize < 0) { return -1F; } - Integer current = getPoolSize(); - if (current == null) { - return null; - } - if (current == 0) { + if (currentSize == 0) { return 0F; } - return (float) current / max; // something like that + return (float) currentSize / (float) maxSize; } - protected final D getDataSource() { - return dataSource; + protected final T getDataSource() { + return this.dataSource; } } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/CommonsDbcpDataSourceMetadata.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/metadata/CommonsDbcpDataSourcePoolMetadata.java similarity index 70% rename from spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/CommonsDbcpDataSourceMetadata.java rename to spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/metadata/CommonsDbcpDataSourcePoolMetadata.java index 28343899bf..963564d5bf 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/CommonsDbcpDataSourceMetadata.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/metadata/CommonsDbcpDataSourcePoolMetadata.java @@ -14,35 +14,37 @@ * limitations under the License. */ -package org.springframework.boot.autoconfigure.jdbc; +package org.springframework.boot.autoconfigure.jdbc.metadata; + +import javax.sql.DataSource; import org.apache.commons.dbcp.BasicDataSource; /** - * A {@link DataSourceMetadata} implementation for the commons dbcp - * data source. + * {@link DataSourcePoolMetadata} for a Apache Commons DBCP {@link DataSource}. * * @author Stephane Nicoll * @since 1.2.0 */ -public class CommonsDbcpDataSourceMetadata extends AbstractDataSourceMetadata { +public class CommonsDbcpDataSourcePoolMetadata extends + AbstractDataSourcePoolMetadata { - public CommonsDbcpDataSourceMetadata(BasicDataSource dataSource) { + public CommonsDbcpDataSourcePoolMetadata(BasicDataSource dataSource) { super(dataSource); } @Override - public Integer getPoolSize() { + public Integer getActive() { return getDataSource().getNumActive(); } @Override - public Integer getMaxPoolSize() { + public Integer getMax() { return getDataSource().getMaxActive(); } @Override - public Integer getMinPoolSize() { + public Integer getMin() { return getDataSource().getMinIdle(); } @@ -50,4 +52,5 @@ public class CommonsDbcpDataSourceMetadata extends AbstractDataSourceMetadata + *
  • 1 means that the maximum number of connections have been allocated
  • + *
  • 0 means that no connection is currently active
  • + *
  • -1 means there is not limit to the number of connections that can be allocated
  • + * + * This may also return {@code null} if the data source does not provide the necessary + * information to compute the poll usage. + */ + Float getUsage(); + + /** + * Return the current number of active connections that have been allocated from the + * data source or {@code null} if that information is not available. + */ + Integer getActive(); + + /** + * Return the maximum number of active connections that can be allocated at the same + * time or {@code -1} if there is no limit. Can also return {@code null} if that + * information is not available. + */ + Integer getMax(); + + /** + * Return the minimum number of idle connections in the pool or {@code null} if that + * information is not available. + */ + Integer getMin(); + + /** + * Return the query to use to validate that a connection is valid or {@code null} if + * that information is not available. + */ + String getValidationQuery(); + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceMetadataProvider.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/metadata/DataSourcePoolMetadataProvider.java similarity index 62% rename from spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceMetadataProvider.java rename to spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/metadata/DataSourcePoolMetadataProvider.java index 14b9220478..95faec8c23 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceMetadataProvider.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/metadata/DataSourcePoolMetadataProvider.java @@ -14,23 +14,22 @@ * limitations under the License. */ -package org.springframework.boot.autoconfigure.jdbc; +package org.springframework.boot.autoconfigure.jdbc.metadata; import javax.sql.DataSource; /** - * Provide a {@link DataSourceMetadata} based on a {@link DataSource}. + * Provide a {@link DataSourcePoolMetadata} based on a {@link DataSource}. * * @author Stephane Nicoll * @since 1.2.0 */ -public interface DataSourceMetadataProvider { +public interface DataSourcePoolMetadataProvider { /** - * Return the {@link DataSourceMetadata} instance able to manage the - * specified {@link DataSource} or {@code null} if the given data - * source could not be handled. + * Return the {@link DataSourcePoolMetadata} instance able to manage the specified + * {@link DataSource} or {@code null} if the given data source could not be handled. */ - DataSourceMetadata getDataSourceMetadata(DataSource dataSource); + DataSourcePoolMetadata getDataSourcePoolMetadata(DataSource dataSource); } 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 new file mode 100644 index 0000000000..875ff14dd7 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/metadata/DataSourcePoolMetadataProviders.java @@ -0,0 +1,57 @@ +/* + * 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.autoconfigure.jdbc.metadata; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import javax.sql.DataSource; + +/** + * A {@link DataSourcePoolMetadataProvider} implementation that returns the first + * {@link DataSourcePoolMetadata} that is found by one of its delegate. + * + * @author Stephane Nicoll + * @since 1.2.0 + */ +public class DataSourcePoolMetadataProviders implements DataSourcePoolMetadataProvider { + + private final List providers; + + /** + * Create a {@link DataSourcePoolMetadataProviders} instance with an initial + * collection of delegates to use. + */ + public DataSourcePoolMetadataProviders( + Collection providers) { + this.providers = new ArrayList(providers); + } + + @Override + public DataSourcePoolMetadata getDataSourcePoolMetadata(DataSource dataSource) { + for (DataSourcePoolMetadataProvider provider : this.providers) { + DataSourcePoolMetadata metadata = provider + .getDataSourcePoolMetadata(dataSource); + if (metadata != null) { + return metadata; + } + } + return null; + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceMetadataProvidersConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/metadata/DataSourcePoolMetadataProvidersConfiguration.java similarity index 53% rename from spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceMetadataProvidersConfiguration.java rename to spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/metadata/DataSourcePoolMetadataProvidersConfiguration.java index d54a536d7b..28352ff655 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceMetadataProvidersConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/metadata/DataSourcePoolMetadataProvidersConfiguration.java @@ -14,79 +14,88 @@ * limitations under the License. */ -package org.springframework.boot.autoconfigure.jdbc; +package org.springframework.boot.autoconfigure.jdbc.metadata; import javax.sql.DataSource; -import com.zaxxer.hikari.HikariDataSource; import org.apache.commons.dbcp.BasicDataSource; - import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import com.zaxxer.hikari.HikariDataSource; + /** - * Register the {@link DataSourceMetadataProvider} instances for the supported - * data sources. + * Register the {@link DataSourcePoolMetadataProvider} instances for the supported data + * sources. * * @author Stephane Nicoll * @since 1.2.0 */ @Configuration -public class DataSourceMetadataProvidersConfiguration { +public class DataSourcePoolMetadataProvidersConfiguration { @Configuration @ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class) - static class TomcatDataSourceProviderConfiguration { + static class TomcatDataSourcePoolMetadataProviderConfiguration { @Bean - public DataSourceMetadataProvider tomcatDataSourceProvider() { - return new DataSourceMetadataProvider() { + public DataSourcePoolMetadataProvider tomcatPoolDataSourceMetadataProvider() { + return new DataSourcePoolMetadataProvider() { @Override - public DataSourceMetadata getDataSourceMetadata(DataSource dataSource) { + public DataSourcePoolMetadata getDataSourcePoolMetadata( + DataSource dataSource) { if (dataSource instanceof org.apache.tomcat.jdbc.pool.DataSource) { - return new TomcatDataSourceMetadata((org.apache.tomcat.jdbc.pool.DataSource) dataSource); + return new TomcatDataSourcePoolMetadata( + (org.apache.tomcat.jdbc.pool.DataSource) dataSource); } return null; } }; } + } @Configuration @ConditionalOnClass(HikariDataSource.class) - static class HikariDataSourceProviderConfiguration { + static class HikariPoolDataSourceMetadataProviderConfiguration { @Bean - public DataSourceMetadataProvider hikariDataSourceProvider() { - return new DataSourceMetadataProvider() { + public DataSourcePoolMetadataProvider hikariPoolDataSourceMetadataProvider() { + return new DataSourcePoolMetadataProvider() { @Override - public DataSourceMetadata getDataSourceMetadata(DataSource dataSource) { + public DataSourcePoolMetadata getDataSourcePoolMetadata( + DataSource dataSource) { if (dataSource instanceof HikariDataSource) { - return new HikariDataSourceMetadata((HikariDataSource) dataSource); + return new HikariDataSourcePoolMetadata( + (HikariDataSource) dataSource); } return null; } }; } + } @Configuration @ConditionalOnClass(BasicDataSource.class) - static class CommonsDbcpDataSourceProviderConfiguration { + static class CommonsDbcpPoolDataSourceMetadataProviderConfiguration { @Bean - public DataSourceMetadataProvider commonsDbcpDataSourceProvider() { - return new DataSourceMetadataProvider() { + public DataSourcePoolMetadataProvider commonsDbcpPoolDataSourceMetadataProvider() { + return new DataSourcePoolMetadataProvider() { @Override - public DataSourceMetadata getDataSourceMetadata(DataSource dataSource) { + public DataSourcePoolMetadata getDataSourcePoolMetadata( + DataSource dataSource) { if (dataSource instanceof BasicDataSource) { - return new CommonsDbcpDataSourceMetadata((BasicDataSource) dataSource); + return new CommonsDbcpDataSourcePoolMetadata( + (BasicDataSource) dataSource); } return null; } }; } + } } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/metadata/HikariDataSourcePoolMetadata.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/metadata/HikariDataSourcePoolMetadata.java new file mode 100644 index 0000000000..c656249d4a --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/metadata/HikariDataSourcePoolMetadata.java @@ -0,0 +1,69 @@ +/* + * 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.autoconfigure.jdbc.metadata; + +import javax.sql.DataSource; + +import org.springframework.beans.DirectFieldAccessor; + +import com.zaxxer.hikari.HikariDataSource; +import com.zaxxer.hikari.pool.HikariPool; + +/** + * {@link DataSourcePoolMetadata} for a Hikari {@link DataSource}. + * + * @author Stephane Nicoll + * @since 1.2.0 + */ +public class HikariDataSourcePoolMetadata extends + AbstractDataSourcePoolMetadata { + + public HikariDataSourcePoolMetadata(HikariDataSource dataSource) { + super(dataSource); + } + + @Override + public Integer getActive() { + try { + return getHikariPool().getActiveConnections(); + } + catch (Exception ex) { + return null; + } + } + + private HikariPool getHikariPool() { + return (HikariPool) new DirectFieldAccessor(getDataSource()) + .getPropertyValue("pool"); + } + + @Override + public Integer getMax() { + return getDataSource().getMaximumPoolSize(); + } + + @Override + public Integer getMin() { + return getDataSource().getMinimumIdle(); + } + + @Override + public String getValidationQuery() { + return getDataSource().getConnectionTestQuery(); + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/TomcatDataSourceMetadata.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/metadata/TomcatDataSourcePoolMetadata.java similarity index 74% rename from spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/TomcatDataSourceMetadata.java rename to spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/metadata/TomcatDataSourcePoolMetadata.java index 689a82dcea..6b69b54dd6 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/TomcatDataSourceMetadata.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/metadata/TomcatDataSourcePoolMetadata.java @@ -14,37 +14,35 @@ * limitations under the License. */ -package org.springframework.boot.autoconfigure.jdbc; +package org.springframework.boot.autoconfigure.jdbc.metadata; import org.apache.tomcat.jdbc.pool.ConnectionPool; import org.apache.tomcat.jdbc.pool.DataSource; /** - * - * A {@link DataSourceMetadata} implementation for the tomcat - * data source. + * {@link DataSourcePoolMetadata} for a Tomcat {@link DataSource}. * * @author Stephane Nicoll */ -public class TomcatDataSourceMetadata extends AbstractDataSourceMetadata { +public class TomcatDataSourcePoolMetadata extends AbstractDataSourcePoolMetadata { - public TomcatDataSourceMetadata(DataSource dataSource) { + public TomcatDataSourcePoolMetadata(DataSource dataSource) { super(dataSource); } @Override - public Integer getPoolSize() { + public Integer getActive() { ConnectionPool pool = getDataSource().getPool(); return (pool == null ? 0 : pool.getActive()); } @Override - public Integer getMaxPoolSize() { + public Integer getMax() { return getDataSource().getMaxActive(); } @Override - public Integer getMinPoolSize() { + public Integer getMin() { return getDataSource().getMinIdle(); } @@ -52,4 +50,5 @@ public class TomcatDataSourceMetadata extends AbstractDataSourceMetadata { +public abstract class AbstractDataSourcePoolMetadataTests> { /** * Return a data source metadata instance with a min size of 0 and max size of 2. @@ -40,36 +41,40 @@ public abstract class AbstractDataSourceMetadataTests() { @Override - public Void doInConnection(Connection connection) throws SQLException, DataAccessException { + public Void doInConnection(Connection connection) throws SQLException, + DataAccessException { return null; } }); - assertEquals(Integer.valueOf(0), getDataSourceMetadata().getPoolSize()); - assertEquals(Float.valueOf(0), getDataSourceMetadata().getPoolUsage()); + assertEquals(Integer.valueOf(0), getDataSourceMetadata().getActive()); + assertEquals(Float.valueOf(0), getDataSourceMetadata().getUsage()); } @Test public void getPoolSizeOneConnection() { - JdbcTemplate jdbcTemplate = new JdbcTemplate(getDataSourceMetadata().getDataSource()); + JdbcTemplate jdbcTemplate = new JdbcTemplate(getDataSourceMetadata() + .getDataSource()); jdbcTemplate.execute(new ConnectionCallback() { @Override - public Void doInConnection(Connection connection) throws SQLException, DataAccessException { - assertEquals(Integer.valueOf(1), getDataSourceMetadata().getPoolSize()); - assertEquals(Float.valueOf(0.5F), getDataSourceMetadata().getPoolUsage()); + public Void doInConnection(Connection connection) throws SQLException, + DataAccessException { + assertEquals(Integer.valueOf(1), getDataSourceMetadata().getActive()); + assertEquals(Float.valueOf(0.5F), getDataSourceMetadata().getUsage()); return null; } }); @@ -77,15 +82,20 @@ public abstract class AbstractDataSourceMetadataTests() { @Override - public Void doInConnection(Connection connection) throws SQLException, DataAccessException { + public Void doInConnection(Connection connection) throws SQLException, + DataAccessException { jdbcTemplate.execute(new ConnectionCallback() { @Override - public Void doInConnection(Connection connection) throws SQLException, DataAccessException { - assertEquals(Integer.valueOf(2), getDataSourceMetadata().getPoolSize()); - assertEquals(Float.valueOf(1F), getDataSourceMetadata().getPoolUsage()); + public Void doInConnection(Connection connection) + throws SQLException, DataAccessException { + assertEquals(Integer.valueOf(2), getDataSourceMetadata() + .getActive()); + assertEquals(Float.valueOf(1F), getDataSourceMetadata() + .getUsage()); return null; } }); @@ -98,10 +108,8 @@ public abstract class AbstractDataSourceMetadataTests { +public class CommonsDbcpDataSourcePoolMetadataTests extends + AbstractDataSourcePoolMetadataTests { - private CommonsDbcpDataSourceMetadata dataSourceMetadata; + private CommonsDbcpDataSourcePoolMetadata dataSourceMetadata; @Before public void setup() { @@ -36,50 +41,54 @@ public class CommonsDbcpDataSourceMetadataTests extends AbstractDataSourceMetada } @Override - protected CommonsDbcpDataSourceMetadata getDataSourceMetadata() { + protected CommonsDbcpDataSourcePoolMetadata getDataSourceMetadata() { return this.dataSourceMetadata; } @Test public void getPoolUsageWithNoCurrent() { - CommonsDbcpDataSourceMetadata dsm = new CommonsDbcpDataSourceMetadata(createDataSource()) { + CommonsDbcpDataSourcePoolMetadata dsm = new CommonsDbcpDataSourcePoolMetadata( + createDataSource()) { @Override - public Integer getPoolSize() { + public Integer getActive() { return null; } }; - assertNull(dsm.getPoolUsage()); + assertNull(dsm.getUsage()); } @Test public void getPoolUsageWithNoMax() { - CommonsDbcpDataSourceMetadata dsm = new CommonsDbcpDataSourceMetadata(createDataSource()) { + CommonsDbcpDataSourcePoolMetadata dsm = new CommonsDbcpDataSourcePoolMetadata( + createDataSource()) { @Override - public Integer getMaxPoolSize() { + public Integer getMax() { return null; } }; - assertNull(dsm.getPoolUsage()); + assertNull(dsm.getUsage()); } @Test public void getPoolUsageWithUnlimitedPool() { - DataSourceMetadata unlimitedDataSource = createDataSourceMetadata(0, -1); - assertEquals(Float.valueOf(-1F), unlimitedDataSource.getPoolUsage()); + DataSourcePoolMetadata unlimitedDataSource = createDataSourceMetadata(0, -1); + assertEquals(Float.valueOf(-1F), unlimitedDataSource.getUsage()); } @Override public void getValidationQuery() { BasicDataSource dataSource = createDataSource(); dataSource.setValidationQuery("SELECT FROM FOO"); - assertEquals("SELECT FROM FOO", new CommonsDbcpDataSourceMetadata(dataSource).getValidationQuery()); + assertEquals("SELECT FROM FOO", + new CommonsDbcpDataSourcePoolMetadata(dataSource).getValidationQuery()); } - private CommonsDbcpDataSourceMetadata createDataSourceMetadata(int minSize, int maxSize) { + private CommonsDbcpDataSourcePoolMetadata createDataSourceMetadata(int minSize, + int maxSize) { BasicDataSource dataSource = createDataSource(); dataSource.setMinIdle(minSize); dataSource.setMaxActive(maxSize); - return new CommonsDbcpDataSourceMetadata(dataSource); + return new CommonsDbcpDataSourcePoolMetadata(dataSource); } private BasicDataSource createDataSource() { diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/metadata/DataSourcePoolMetadataProvidersTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/metadata/DataSourcePoolMetadataProvidersTests.java new file mode 100644 index 0000000000..6fd985cbf9 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/metadata/DataSourcePoolMetadataProvidersTests.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.autoconfigure.jdbc.metadata; + +import java.util.Arrays; + +import javax.sql.DataSource; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.mockito.BDDMockito.given; + +/** + * Tests for {@link DataSourcePoolMetadataProviders}. + * + * @author Stephane Nicoll + */ +public class DataSourcePoolMetadataProvidersTests { + + @Mock + private DataSourcePoolMetadataProvider firstProvider; + + @Mock + private DataSourcePoolMetadata first; + + @Mock + private DataSource firstDataSource; + + @Mock + private DataSourcePoolMetadataProvider secondProvider; + + @Mock + private DataSourcePoolMetadata second; + + @Mock + private DataSource secondDataSource; + + @Mock + private DataSource unknownDataSource; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + given(this.firstProvider.getDataSourcePoolMetadata(this.firstDataSource)) + .willReturn(this.first); + given(this.firstProvider.getDataSourcePoolMetadata(this.secondDataSource)) + .willReturn(this.second); + } + + @Test + public void createWithProviders() { + DataSourcePoolMetadataProviders provider = new DataSourcePoolMetadataProviders( + Arrays.asList(this.firstProvider, this.secondProvider)); + assertSame(this.first, provider.getDataSourcePoolMetadata(this.firstDataSource)); + assertSame(this.second, provider.getDataSourcePoolMetadata(this.secondDataSource)); + assertNull(provider.getDataSourcePoolMetadata(this.unknownDataSource)); + } + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/HikariDataSourceMetadataTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/metadata/HikariDataSourcePoolMetadataTests.java similarity index 64% rename from spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/HikariDataSourceMetadataTests.java rename to spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/metadata/HikariDataSourcePoolMetadataTests.java index 95b1c96dd9..fabfb4295a 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/HikariDataSourceMetadataTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/metadata/HikariDataSourcePoolMetadataTests.java @@ -14,27 +14,32 @@ * limitations under the License. */ -package org.springframework.boot.autoconfigure.jdbc; +package org.springframework.boot.autoconfigure.jdbc.metadata; + +import org.junit.Before; +import org.springframework.boot.autoconfigure.jdbc.metadata.HikariDataSourcePoolMetadata; import com.zaxxer.hikari.HikariDataSource; -import org.junit.Before; import static org.junit.Assert.assertEquals; /** + * Tests for {@link HikariDataSourcePoolMetadata}. + * * @author Stephane Nicoll */ -public class HikariDataSourceMetadataTests extends AbstractDataSourceMetadataTests { +public class HikariDataSourcePoolMetadataTests extends + AbstractDataSourcePoolMetadataTests { - private HikariDataSourceMetadata dataSourceMetadata; + private HikariDataSourcePoolMetadata dataSourceMetadata; @Before public void setup() { - this.dataSourceMetadata = new HikariDataSourceMetadata(createDataSource(0, 2)); + this.dataSourceMetadata = new HikariDataSourcePoolMetadata(createDataSource(0, 2)); } @Override - protected HikariDataSourceMetadata getDataSourceMetadata() { + protected HikariDataSourcePoolMetadata getDataSourceMetadata() { return this.dataSourceMetadata; } @@ -42,11 +47,13 @@ public class HikariDataSourceMetadataTests extends AbstractDataSourceMetadataTes public void getValidationQuery() { HikariDataSource dataSource = createDataSource(0, 4); dataSource.setConnectionTestQuery("SELECT FROM FOO"); - assertEquals("SELECT FROM FOO", new HikariDataSourceMetadata(dataSource).getValidationQuery()); + assertEquals("SELECT FROM FOO", + new HikariDataSourcePoolMetadata(dataSource).getValidationQuery()); } private HikariDataSource createDataSource(int minSize, int maxSize) { - HikariDataSource dataSource = (HikariDataSource) initializeBuilder().type(HikariDataSource.class).build(); + HikariDataSource dataSource = (HikariDataSource) initializeBuilder().type( + HikariDataSource.class).build(); dataSource.setMinimumIdle(minSize); dataSource.setMaximumPoolSize(maxSize); return dataSource; diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/TomcatDataSourceMetadataTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/metadata/TomcatDataSourcePoolMetadataTests.java similarity index 66% rename from spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/TomcatDataSourceMetadataTests.java rename to spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/metadata/TomcatDataSourcePoolMetadataTests.java index f07784137f..86b7a27ac2 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/TomcatDataSourceMetadataTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/metadata/TomcatDataSourcePoolMetadataTests.java @@ -14,28 +14,31 @@ * limitations under the License. */ -package org.springframework.boot.autoconfigure.jdbc; +package org.springframework.boot.autoconfigure.jdbc.metadata; import org.apache.tomcat.jdbc.pool.DataSource; import org.junit.Before; +import org.springframework.boot.autoconfigure.jdbc.metadata.TomcatDataSourcePoolMetadata; import static org.junit.Assert.assertEquals; /** + * Tests for {@link TomcatDataSourcePoolMetadata}. * * @author Stephane Nicoll */ -public class TomcatDataSourceMetadataTests extends AbstractDataSourceMetadataTests { +public class TomcatDataSourcePoolMetadataTests extends + AbstractDataSourcePoolMetadataTests { - private TomcatDataSourceMetadata dataSourceMetadata; + private TomcatDataSourcePoolMetadata dataSourceMetadata; @Before public void setup() { - this.dataSourceMetadata = new TomcatDataSourceMetadata(createDataSource(0, 2)); + this.dataSourceMetadata = new TomcatDataSourcePoolMetadata(createDataSource(0, 2)); } @Override - protected TomcatDataSourceMetadata getDataSourceMetadata() { + protected TomcatDataSourcePoolMetadata getDataSourceMetadata() { return this.dataSourceMetadata; } @@ -43,11 +46,13 @@ public class TomcatDataSourceMetadataTests extends AbstractDataSourceMetadataTes public void getValidationQuery() { DataSource dataSource = createDataSource(0, 4); dataSource.setValidationQuery("SELECT FROM FOO"); - assertEquals("SELECT FROM FOO", new TomcatDataSourceMetadata(dataSource).getValidationQuery()); + assertEquals("SELECT FROM FOO", + new TomcatDataSourcePoolMetadata(dataSource).getValidationQuery()); } private DataSource createDataSource(int minSize, int maxSize) { - DataSource dataSource = (DataSource) initializeBuilder().type(DataSource.class).build(); + DataSource dataSource = (DataSource) initializeBuilder().type(DataSource.class) + .build(); dataSource.setMinIdle(minSize); dataSource.setMaxActive(maxSize); 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 aeb26bbe8c..334f27a879 100644 --- a/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc +++ b/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc @@ -634,26 +634,35 @@ The `gauge` shows the last response time for a request. So the last request to ` NOTE: In this example we are actually accessing the endpoint over HTTP using the `/metrics` URL, this explains why `metrics` appears in the response. + + [[production-ready-datasource-metrics]] === DataSource metrics +The following metrics are exposed for each supported `DataSource` defined in your +application: -The following metrics are available for each data source defined in the application: the -number of allocated connection(s) (`.active`) and the current usage of the connection -pool (`.usage`). +* The maximum number connections (`datasource.xxx.max`). +* The minimum number of connections (`datasource.xxx.min`). +* The number of active connections (`datasource.xxx.active`) +* The current usage of the connection pool (`datasource.xxx.usage`). -All data source metrics share the `datasource.` prefix. The prefix is further qualified for -each data source: +All data source metrics share the `datasource.` prefix. The prefix is further qualified +for each data source: * If the data source is the primary data source (that is either the only available data - source or the one flagged `@Primary` amongst the existing ones), the prefix is `datasource.primary` -* If the data source bean name ends with `dataSource`, the prefix is the name of the bean without - it (i.e. `datasource.batch` for `batchDataSource`) -* In all other cases, the name of the bean is used + source or the one flagged `@Primary` amongst the existing ones), the prefix is + `datasource.primary`. +* If the data source bean name ends with `dataSource`, the prefix is the name of the bean + without `dataSource` (i.e. `datasource.batch` for `batchDataSource`). +* In all other cases, the name of the bean is used. + +It is possible to override part or all of those defaults by registering a bean with a +customized version of `DataSourcePublicMetrics`. By default, Spring Boot provides metadata +for all supported datasources; you can add additional `DataSourcePoolMetadataProvider` +beans if your favorite data source isn't supported out of the box. See +`DataSourcePoolMetadataProvidersConfiguration` for examples. + -It is possible to override part or all of those defaults by registering a bean with a customized -version of `DataSourcePublicMetrics`. Spring Boot provides those metadata for all supported -datasource; you can provide a `DataSourceMetadata` implementation for your favorite data source, -check `DatasourceMetadataProvidersConfiguration` for more details. [[production-ready-recording-metrics]] === Recording your own metrics