Commit 2262a3ba authored by Phillip Webb's avatar Phillip Webb

Polish DataSource metrics

parents 53c4859a 5001b038
...@@ -24,27 +24,24 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; ...@@ -24,27 +24,24 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceMetadataProvider; import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvider;
import org.springframework.boot.autoconfigure.jdbc.DataSourceMetadataProvidersConfiguration;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
/** /**
* {@link EnableAutoConfiguration Auto-configuration} that provides * {@link EnableAutoConfiguration Auto-configuration} that provides metrics on dataSource
* metrics on dataSource usage. * usage.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
* @since 1.2.0 * @since 1.2.0
*/ */
@ConditionalOnBean(DataSource.class) @ConditionalOnBean(DataSource.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class) @AutoConfigureAfter(DataSourceAutoConfiguration.class)
@Import(DataSourceMetadataProvidersConfiguration.class) public class DataSourceMetricsAutoConfiguration {
public class MetricDataSourceAutoConfiguration {
@Bean @Bean
@ConditionalOnBean(DataSourceMetadataProvider.class) @ConditionalOnBean(DataSourcePoolMetadataProvider.class)
@ConditionalOnMissingBean(DataSourcePublicMetrics.class) @ConditionalOnMissingBean
DataSourcePublicMetrics dataSourcePublicMetrics() { public DataSourcePublicMetrics dataSourcePublicMetrics() {
return new DataSourcePublicMetrics(); return new DataSourcePublicMetrics();
} }
......
...@@ -27,6 +27,7 @@ import org.apache.solr.client.solrj.SolrServer; ...@@ -27,6 +27,7 @@ import org.apache.solr.client.solrj.SolrServer;
import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; 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.CompositeHealthIndicator;
import org.springframework.boot.actuate.health.DataSourceHealthIndicator; import org.springframework.boot.actuate.health.DataSourceHealthIndicator;
import org.springframework.boot.actuate.health.HealthAggregator; import org.springframework.boot.actuate.health.HealthAggregator;
...@@ -36,7 +37,6 @@ import org.springframework.boot.actuate.health.OrderedHealthAggregator; ...@@ -36,7 +37,6 @@ import org.springframework.boot.actuate.health.OrderedHealthAggregator;
import org.springframework.boot.actuate.health.RabbitHealthIndicator; import org.springframework.boot.actuate.health.RabbitHealthIndicator;
import org.springframework.boot.actuate.health.RedisHealthIndicator; import org.springframework.boot.actuate.health.RedisHealthIndicator;
import org.springframework.boot.actuate.health.SolrHealthIndicator; 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.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
...@@ -44,10 +44,10 @@ import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration; ...@@ -44,10 +44,10 @@ import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 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.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceMetadata; import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadata;
import org.springframework.boot.autoconfigure.jdbc.DataSourceMetadataProvider; 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.MongoAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoDataAutoConfiguration; import org.springframework.boot.autoconfigure.mongo.MongoDataAutoConfiguration;
import org.springframework.boot.autoconfigure.redis.RedisAutoConfiguration; import org.springframework.boot.autoconfigure.redis.RedisAutoConfiguration;
...@@ -103,33 +103,36 @@ public class HealthIndicatorAutoConfiguration { ...@@ -103,33 +103,36 @@ public class HealthIndicatorAutoConfiguration {
private Map<String, DataSource> dataSources; private Map<String, DataSource> dataSources;
@Autowired(required = false) @Autowired(required = false)
private Collection<DataSourceMetadataProvider> metadataProviders = Collections.emptyList(); private Collection<DataSourcePoolMetadataProvider> metadataProviders = Collections
.emptyList();
@Bean @Bean
@ConditionalOnMissingBean(name = "dbHealthIndicator") @ConditionalOnMissingBean(name = "dbHealthIndicator")
public HealthIndicator dbHealthIndicator() { public HealthIndicator dbHealthIndicator() {
DataSourceMetadataProvider metadataProvider = DataSourcePoolMetadataProvider metadataProvider = new DataSourcePoolMetadataProviders(
new CompositeDataSourceMetadataProvider(this.metadataProviders); this.metadataProviders);
if (this.dataSources.size() == 1) { if (this.dataSources.size() == 1) {
return createDataSourceHealthIndicator(metadataProvider, DataSource dataSource = this.dataSources.values().iterator().next();
this.dataSources.values().iterator().next()); return createDataSourceHealthIndicator(metadataProvider, dataSource);
} }
CompositeHealthIndicator composite = new CompositeHealthIndicator( CompositeHealthIndicator composite = new CompositeHealthIndicator(
this.healthAggregator); this.healthAggregator);
for (Map.Entry<String, DataSource> entry : this.dataSources.entrySet()) { for (Map.Entry<String, DataSource> entry : this.dataSources.entrySet()) {
composite.addHealthIndicator(entry.getKey(), String name = entry.getKey();
createDataSourceHealthIndicator(metadataProvider, entry.getValue())); DataSource dataSource = entry.getValue();
composite.addHealthIndicator(name,
createDataSourceHealthIndicator(metadataProvider, dataSource));
} }
return composite; return composite;
} }
private DataSourceHealthIndicator createDataSourceHealthIndicator(DataSourceMetadataProvider provider, private DataSourceHealthIndicator createDataSourceHealthIndicator(
DataSource dataSource) { DataSourcePoolMetadataProvider provider, DataSource dataSource) {
String validationQuery = null; String validationQuery = null;
DataSourceMetadata dataSourceMetadata = provider.getDataSourceMetadata(dataSource); DataSourcePoolMetadata poolMetadata = provider
if (dataSourceMetadata != null) { .getDataSourcePoolMetadata(dataSource);
validationQuery = dataSourceMetadata.getValidationQuery(); if (poolMetadata != null) {
validationQuery = poolMetadata.getValidationQuery();
} }
return new DataSourceHealthIndicator(dataSource, validationQuery); return new DataSourceHealthIndicator(dataSource, validationQuery);
} }
......
...@@ -20,21 +20,22 @@ import java.util.Collection; ...@@ -20,21 +20,22 @@ import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.Map; import java.util.Map;
import java.util.Set;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.sql.DataSource; import javax.sql.DataSource;
import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.metrics.Metric; import org.springframework.boot.actuate.metrics.Metric;
import org.springframework.boot.autoconfigure.jdbc.CompositeDataSourceMetadataProvider; import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadata;
import org.springframework.boot.autoconfigure.jdbc.DataSourceMetadata; import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvider;
import org.springframework.boot.autoconfigure.jdbc.DataSourceMetadataProvider; import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProviders;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Primary;
/** /**
* A {@link PublicMetrics} implementation that provides data source usage * A {@link PublicMetrics} implementation that provides data source usage statistics.
* statistics.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
* @since 1.2.0 * @since 1.2.0
...@@ -47,81 +48,66 @@ public class DataSourcePublicMetrics implements PublicMetrics { ...@@ -47,81 +48,66 @@ public class DataSourcePublicMetrics implements PublicMetrics {
private ApplicationContext applicationContext; private ApplicationContext applicationContext;
@Autowired @Autowired
private Collection<DataSourceMetadataProvider> dataSourceMetadataProviders; private Collection<DataSourcePoolMetadataProvider> providers;
private final Map<String, DataSourceMetadata> dataSourceMetadataByPrefix private final Map<String, DataSourcePoolMetadata> metadataByPrefix = new HashMap<String, DataSourcePoolMetadata>();
= new HashMap<String, DataSourceMetadata>();
@PostConstruct @PostConstruct
public void initialize() { public void initialize() {
Map<String, DataSource> dataSources = this.applicationContext.getBeansOfType(DataSource.class);
DataSource primaryDataSource = getPrimaryDataSource(); DataSource primaryDataSource = getPrimaryDataSource();
DataSourcePoolMetadataProvider provider = new DataSourcePoolMetadataProviders(
this.providers);
DataSourceMetadataProvider provider = new CompositeDataSourceMetadataProvider(this.dataSourceMetadataProviders); for (Map.Entry<String, DataSource> entry : this.applicationContext
for (Map.Entry<String, DataSource> entry : dataSources.entrySet()) { .getBeansOfType(DataSource.class).entrySet()) {
String prefix = createPrefix(entry.getKey(), entry.getValue(), entry.getValue().equals(primaryDataSource)); String beanName = entry.getKey();
DataSourceMetadata dataSourceMetadata = provider.getDataSourceMetadata(entry.getValue()); DataSource bean = entry.getValue();
if (dataSourceMetadata != null) { String prefix = createPrefix(beanName, bean, bean.equals(primaryDataSource));
dataSourceMetadataByPrefix.put(prefix, dataSourceMetadata); DataSourcePoolMetadata poolMetadata = provider
.getDataSourcePoolMetadata(bean);
if (poolMetadata != null) {
this.metadataByPrefix.put(prefix, poolMetadata);
} }
} }
} }
@Override @Override
public Collection<Metric<?>> metrics() { public Collection<Metric<?>> metrics() {
Collection<Metric<?>> result = new LinkedHashSet<Metric<?>>(); Set<Metric<?>> metrics = new LinkedHashSet<Metric<?>>();
for (Map.Entry<String, DataSourceMetadata> entry : dataSourceMetadataByPrefix.entrySet()) { for (Map.Entry<String, DataSourcePoolMetadata> entry : this.metadataByPrefix
.entrySet()) {
String prefix = entry.getKey(); String prefix = entry.getKey();
// Make sure the prefix ends with a dot prefix = (prefix.endsWith(".") ? prefix : prefix + ".");
if (!prefix.endsWith(".")) { DataSourcePoolMetadata metadata = entry.getValue();
prefix = prefix + "."; addMetric(metrics, prefix + "max", metadata.getMax());
} addMetric(metrics, prefix + "min", metadata.getMin());
DataSourceMetadata dataSourceMetadata = entry.getValue(); addMetric(metrics, prefix + "active", metadata.getActive());
Integer poolSize = dataSourceMetadata.getPoolSize(); addMetric(metrics, prefix + "usage", metadata.getUsage());
if (poolSize != null) { }
result.add(new Metric<Integer>(prefix + "active", poolSize)); return metrics;
} }
Float poolUsage = dataSourceMetadata.getPoolUsage();
if (poolUsage != null) { private <T extends Number> void addMetric(Set<Metric<?>> metrics, String name, T value) {
result.add(new Metric<Float>(prefix + "usage", poolUsage)); if (value != null) {
} metrics.add(new Metric<T>(name, value));
} }
return result;
} }
/** /**
* Create the prefix to use for the metrics to associate with the given {@link DataSource}. * Create the prefix to use for the metrics to associate with the given
* @param dataSourceName the name of the data source bean * {@link DataSource}.
* @param name the name of the data source bean
* @param dataSource the data source to configure * @param dataSource the data source to configure
* @param primary if this data source is the primary data source * @param primary if this data source is the primary data source
* @return a prefix for the given data source * @return a prefix for the given data source
*/ */
protected String createPrefix(String dataSourceName, DataSource dataSource, boolean primary) { protected String createPrefix(String name, DataSource dataSource, boolean primary) {
StringBuilder sb = new StringBuilder("datasource.");
if (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()));
} }
else { if (name.toLowerCase().endsWith(DATASOURCE_SUFFIX.toLowerCase())) {
sb.append(dataSourceName); name = name.substring(0, name.length() - DATASOURCE_SUFFIX.length());
}
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 { ...@@ -131,9 +117,9 @@ public class DataSourcePublicMetrics implements PublicMetrics {
*/ */
private DataSource getPrimaryDataSource() { private DataSource getPrimaryDataSource() {
try { try {
return applicationContext.getBean(DataSource.class); return this.applicationContext.getBean(DataSource.class);
} }
catch (NoSuchBeanDefinitionException e) { catch (NoSuchBeanDefinitionException ex) {
return null; return null;
} }
} }
......
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.actuate.autoconfigure.AuditAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.AuditAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.CrshAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.CrshAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.DataSourceMetricsAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.EndpointMBeanExportAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.EndpointMBeanExportAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration,\
...@@ -8,7 +9,6 @@ org.springframework.boot.actuate.autoconfigure.HealthIndicatorAutoConfiguration, ...@@ -8,7 +9,6 @@ org.springframework.boot.actuate.autoconfigure.HealthIndicatorAutoConfiguration,
org.springframework.boot.actuate.autoconfigure.JolokiaAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.JolokiaAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.ManagementSecurityAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.ManagementSecurityAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.ManagementServerPropertiesAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.ManagementServerPropertiesAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.MetricDataSourceAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.MetricFilterAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.MetricFilterAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.MetricRepositoryAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.MetricRepositoryAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.TraceRepositoryAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.TraceRepositoryAutoConfiguration,\
......
...@@ -16,8 +16,6 @@ ...@@ -16,8 +16,6 @@
package org.springframework.boot.actuate.autoconfigure; package org.springframework.boot.actuate.autoconfigure;
import static org.junit.Assert.*;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Collection; import java.util.Collection;
...@@ -26,16 +24,15 @@ import java.util.Map; ...@@ -26,16 +24,15 @@ import java.util.Map;
import javax.sql.DataSource; import javax.sql.DataSource;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.commons.dbcp.BasicDataSource; import org.apache.commons.dbcp.BasicDataSource;
import org.junit.After; import org.junit.After;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.actuate.endpoint.DataSourcePublicMetrics; import org.springframework.boot.actuate.endpoint.DataSourcePublicMetrics;
import org.springframework.boot.actuate.endpoint.PublicMetrics; import org.springframework.boot.actuate.endpoint.PublicMetrics;
import org.springframework.boot.actuate.metrics.Metric; import org.springframework.boot.actuate.metrics.Metric;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder; 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.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
...@@ -44,11 +41,17 @@ import org.springframework.dao.DataAccessException; ...@@ -44,11 +41,17 @@ import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.ConnectionCallback; import org.springframework.jdbc.core.ConnectionCallback;
import org.springframework.jdbc.core.JdbcTemplate; 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 * @author Stephane Nicoll
*/ */
public class MetricDataSourceAutoConfigurationTests { public class DataSourceMetricsAutoConfigurationTests {
private AnnotationConfigApplicationContext context; private AnnotationConfigApplicationContext context;
...@@ -78,24 +81,25 @@ public class MetricDataSourceAutoConfigurationTests { ...@@ -78,24 +81,25 @@ public class MetricDataSourceAutoConfigurationTests {
load(MultipleDataSourcesConfig.class); load(MultipleDataSourcesConfig.class);
PublicMetrics bean = this.context.getBean(PublicMetrics.class); PublicMetrics bean = this.context.getBean(PublicMetrics.class);
Collection<Metric<?>> metrics = bean.metrics(); Collection<Metric<?>> metrics = bean.metrics();
assertMetrics(metrics, assertMetrics(metrics, "datasource.tomcat.active", "datasource.tomcat.usage",
"datasource.tomcat.active", "datasource.tomcat.usage",
"datasource.commonsDbcp.active", "datasource.commonsDbcp.usage"); "datasource.commonsDbcp.active", "datasource.commonsDbcp.usage");
// Hikari won't work unless a first connection has been retrieved // 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<Void>() { jdbcTemplate.execute(new ConnectionCallback<Void>() {
@Override @Override
public Void doInConnection(Connection connection) throws SQLException, DataAccessException { public Void doInConnection(Connection connection) throws SQLException,
DataAccessException {
return null; return null;
} }
}); });
Collection<Metric<?>> anotherMetrics = bean.metrics(); Collection<Metric<?>> anotherMetrics = bean.metrics();
assertMetrics(anotherMetrics, assertMetrics(anotherMetrics, "datasource.tomcat.active",
"datasource.tomcat.active", "datasource.tomcat.usage", "datasource.tomcat.usage", "datasource.hikariDS.active",
"datasource.hikariDS.active", "datasource.hikariDS.usage", "datasource.hikariDS.usage", "datasource.commonsDbcp.active",
"datasource.commonsDbcp.active", "datasource.commonsDbcp.usage"); "datasource.commonsDbcp.usage");
} }
@Test @Test
...@@ -103,19 +107,18 @@ public class MetricDataSourceAutoConfigurationTests { ...@@ -103,19 +107,18 @@ public class MetricDataSourceAutoConfigurationTests {
load(MultipleDataSourcesWithPrimaryConfig.class); load(MultipleDataSourcesWithPrimaryConfig.class);
PublicMetrics bean = this.context.getBean(PublicMetrics.class); PublicMetrics bean = this.context.getBean(PublicMetrics.class);
Collection<Metric<?>> metrics = bean.metrics(); Collection<Metric<?>> metrics = bean.metrics();
assertMetrics(metrics, assertMetrics(metrics, "datasource.primary.active", "datasource.primary.usage",
"datasource.primary.active", "datasource.primary.usage",
"datasource.commonsDbcp.active", "datasource.commonsDbcp.usage"); "datasource.commonsDbcp.active", "datasource.commonsDbcp.usage");
} }
@Test @Test
public void customPrefix() { public void customPrefix() {
load(MultipleDataSourcesWithPrimaryConfig.class, CustomDataSourcePublicMetrics.class); load(MultipleDataSourcesWithPrimaryConfig.class,
CustomDataSourcePublicMetrics.class);
PublicMetrics bean = this.context.getBean(PublicMetrics.class); PublicMetrics bean = this.context.getBean(PublicMetrics.class);
Collection<Metric<?>> metrics = bean.metrics(); Collection<Metric<?>> metrics = bean.metrics();
assertMetrics(metrics, assertMetrics(metrics, "ds.first.active", "ds.first.usage", "ds.second.active",
"ds.first.active", "ds.first.usage", "ds.second.usage");
"ds.second.active", "ds.second.usage");
} }
...@@ -134,17 +137,18 @@ public class MetricDataSourceAutoConfigurationTests { ...@@ -134,17 +137,18 @@ public class MetricDataSourceAutoConfigurationTests {
if (config.length > 0) { if (config.length > 0) {
this.context.register(config); this.context.register(config);
} }
this.context.register(MetricDataSourceAutoConfiguration.class); this.context.register(DataSourcePoolMetadataProvidersConfiguration.class,
DataSourceMetricsAutoConfiguration.class);
this.context.refresh(); this.context.refresh();
} }
@Configuration @Configuration
static class MultipleDataSourcesConfig { static class MultipleDataSourcesConfig {
@Bean @Bean
public DataSource tomcatDataSource() { 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 @Bean
...@@ -164,7 +168,8 @@ public class MetricDataSourceAutoConfigurationTests { ...@@ -164,7 +168,8 @@ public class MetricDataSourceAutoConfigurationTests {
@Bean @Bean
@Primary @Primary
public DataSource myDataSource() { 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 @Bean
...@@ -180,7 +185,8 @@ public class MetricDataSourceAutoConfigurationTests { ...@@ -180,7 +185,8 @@ public class MetricDataSourceAutoConfigurationTests {
public DataSourcePublicMetrics myDataSourcePublicMetrics() { public DataSourcePublicMetrics myDataSourcePublicMetrics() {
return new DataSourcePublicMetrics() { return new DataSourcePublicMetrics() {
@Override @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"); return (primary ? "ds.first." : "ds.second");
} }
}; };
...@@ -188,9 +194,8 @@ public class MetricDataSourceAutoConfigurationTests { ...@@ -188,9 +194,8 @@ public class MetricDataSourceAutoConfigurationTests {
} }
private static DataSourceBuilder initializeBuilder() { private static DataSourceBuilder initializeBuilder() {
return DataSourceBuilder.create() return DataSourceBuilder.create().driverClassName("org.hsqldb.jdbc.JDBCDriver")
.driverClassName("org.hsqldb.jdbc.JDBCDriver") .url("jdbc:hsqldb:mem:test").username("sa");
.url("jdbc:hsqldb:mem:test")
.username("sa");
} }
} }
...@@ -33,9 +33,9 @@ import org.springframework.boot.actuate.health.ApplicationHealthIndicator; ...@@ -33,9 +33,9 @@ import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration; import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder; 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.DataSourceProperties;
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration; 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.MongoAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoDataAutoConfiguration; import org.springframework.boot.autoconfigure.mongo.MongoDataAutoConfiguration;
import org.springframework.boot.autoconfigure.redis.RedisAutoConfiguration; import org.springframework.boot.autoconfigure.redis.RedisAutoConfiguration;
...@@ -167,7 +167,7 @@ public class HealthIndicatorAutoConfigurationTests { ...@@ -167,7 +167,7 @@ public class HealthIndicatorAutoConfigurationTests {
public void dataSourceHealthIndicatorWithCustomValidationQuery() { public void dataSourceHealthIndicatorWithCustomValidationQuery() {
this.context = new AnnotationConfigApplicationContext(); this.context = new AnnotationConfigApplicationContext();
this.context.register(PropertyPlaceholderAutoConfiguration.class, DataSourceProperties.class, this.context.register(PropertyPlaceholderAutoConfiguration.class, DataSourceProperties.class,
DataSourceConfig.class, DataSourceMetadataProvidersConfiguration.class, DataSourceConfig.class, DataSourcePoolMetadataProvidersConfiguration.class,
HealthIndicatorAutoConfiguration.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(); this.context.refresh();
......
...@@ -30,6 +30,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; ...@@ -30,6 +30,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition; import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerPostProcessor.Registrar; 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.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
...@@ -54,7 +55,7 @@ import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; ...@@ -54,7 +55,7 @@ import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
@Configuration @Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class }) @ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class) @EnableConfigurationProperties(DataSourceProperties.class)
@Import(Registrar.class) @Import({ Registrar.class, DataSourcePoolMetadataProvidersConfiguration.class })
public class DataSourceAutoConfiguration { public class DataSourceAutoConfiguration {
/** /**
......
/*
* 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<HikariDataSource> {
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");
}
}
}
...@@ -14,48 +14,46 @@ ...@@ -14,48 +14,46 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.autoconfigure.jdbc; package org.springframework.boot.autoconfigure.jdbc.metadata;
import javax.sql.DataSource; import javax.sql.DataSource;
/** /**
* A base {@link DataSourceMetadata} implementation. * A base {@link DataSourcePoolMetadata} implementation.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
* @since 1.2.0 * @since 1.2.0
*/ */
public abstract class AbstractDataSourceMetadata<D extends DataSource> implements DataSourceMetadata { public abstract class AbstractDataSourcePoolMetadata<T extends DataSource> implements
DataSourcePoolMetadata {
private final D dataSource; private final T dataSource;
/** /**
* Create an instance with the data source to use. * Create an instance with the data source to use.
*/ */
protected AbstractDataSourceMetadata(D dataSource) { protected AbstractDataSourcePoolMetadata(T dataSource) {
this.dataSource = dataSource; this.dataSource = dataSource;
} }
@Override @Override
public Float getPoolUsage() { public Float getUsage() {
Integer max = getMaxPoolSize(); Integer maxSize = getMax();
if (max == null) { Integer currentSize = getActive();
if (maxSize == null || currentSize == null) {
return null; return null;
} }
if (max < 0) { if (maxSize < 0) {
return -1F; return -1F;
} }
Integer current = getPoolSize(); if (currentSize == 0) {
if (current == null) {
return null;
}
if (current == 0) {
return 0F; return 0F;
} }
return (float) current / max; // something like that return (float) currentSize / (float) maxSize;
} }
protected final D getDataSource() { protected final T getDataSource() {
return dataSource; return this.dataSource;
} }
} }
...@@ -14,35 +14,37 @@ ...@@ -14,35 +14,37 @@
* limitations under the License. * 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; import org.apache.commons.dbcp.BasicDataSource;
/** /**
* A {@link DataSourceMetadata} implementation for the commons dbcp * {@link DataSourcePoolMetadata} for a Apache Commons DBCP {@link DataSource}.
* data source.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
* @since 1.2.0 * @since 1.2.0
*/ */
public class CommonsDbcpDataSourceMetadata extends AbstractDataSourceMetadata<BasicDataSource> { public class CommonsDbcpDataSourcePoolMetadata extends
AbstractDataSourcePoolMetadata<BasicDataSource> {
public CommonsDbcpDataSourceMetadata(BasicDataSource dataSource) { public CommonsDbcpDataSourcePoolMetadata(BasicDataSource dataSource) {
super(dataSource); super(dataSource);
} }
@Override @Override
public Integer getPoolSize() { public Integer getActive() {
return getDataSource().getNumActive(); return getDataSource().getNumActive();
} }
@Override @Override
public Integer getMaxPoolSize() { public Integer getMax() {
return getDataSource().getMaxActive(); return getDataSource().getMaxActive();
} }
@Override @Override
public Integer getMinPoolSize() { public Integer getMin() {
return getDataSource().getMinIdle(); return getDataSource().getMinIdle();
} }
...@@ -50,4 +52,5 @@ public class CommonsDbcpDataSourceMetadata extends AbstractDataSourceMetadata<Ba ...@@ -50,4 +52,5 @@ public class CommonsDbcpDataSourceMetadata extends AbstractDataSourceMetadata<Ba
public String getValidationQuery() { public String getValidationQuery() {
return getDataSource().getValidationQuery(); return getDataSource().getValidationQuery();
} }
} }
...@@ -14,59 +14,54 @@ ...@@ -14,59 +14,54 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.autoconfigure.jdbc; package org.springframework.boot.autoconfigure.jdbc.metadata;
import javax.sql.DataSource; import javax.sql.DataSource;
/** /**
* Provide various metadata regarding a {@link DataSource} that * Provides access meta-data that is commonly available from most polled
* are shared by most data source types but not accessible in a * {@link DataSource} implementations.
* standard manner.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
* @since 1.2.0 * @since 1.2.0
*/ */
public interface DataSourceMetadata { public interface DataSourcePoolMetadata {
/** /**
* Return the usage of the pool as a double value between * Return the usage of the pool as value between 0 and 1 (or -1 if the pool is not
* 0 and 1. * limited).
* <ul> * <ul>
* <li>1 means that the maximum number of connections * <li>1 means that the maximum number of connections have been allocated</li>
* have been allocated</li>
* <li>0 means that no connection is currently active</li> * <li>0 means that no connection is currently active</li>
* <li>-1 means there is not limit to the number of connections * <li>-1 means there is not limit to the number of connections that can be allocated</li>
* that can be allocated</li>
* </ul> * </ul>
* This may also return {@code null} if the data source does * This may also return {@code null} if the data source does not provide the necessary
* not provide the necessary information to compute the poll usage. * information to compute the poll usage.
*/ */
Float getPoolUsage(); Float getUsage();
/** /**
* Return the current number of active connections that * Return the current number of active connections that have been allocated from the
* have been allocated from the data source or {@code null} * data source or {@code null} if that information is not available.
* if that information is not available.
*/ */
Integer getPoolSize(); Integer getActive();
/** /**
* Return the maximum number of active connections that can be * Return the maximum number of active connections that can be allocated at the same
* allocated at the same time or {@code -1} if there is no * time or {@code -1} if there is no limit. Can also return {@code null} if that
* limit. Can also return {@code null} if that information is * information is not available.
* not available.
*/ */
Integer getMaxPoolSize(); Integer getMax();
/** /**
* Return the minimum number of idle connections in the pool * Return the minimum number of idle connections in the pool or {@code null} if that
* or {@code null} if that information is not available. * information is not available.
*/ */
Integer getMinPoolSize(); Integer getMin();
/** /**
* Return the query to use to validate that a connection is * Return the query to use to validate that a connection is valid or {@code null} if
* valid or {@code null} if that information is not available. * that information is not available.
*/ */
String getValidationQuery(); String getValidationQuery();
......
...@@ -14,23 +14,22 @@ ...@@ -14,23 +14,22 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.autoconfigure.jdbc; package org.springframework.boot.autoconfigure.jdbc.metadata;
import javax.sql.DataSource; 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 * @author Stephane Nicoll
* @since 1.2.0 * @since 1.2.0
*/ */
public interface DataSourceMetadataProvider { public interface DataSourcePoolMetadataProvider {
/** /**
* Return the {@link DataSourceMetadata} instance able to manage the * Return the {@link DataSourcePoolMetadata} instance able to manage the specified
* specified {@link DataSource} or {@code null} if the given data * {@link DataSource} or {@code null} if the given data source could not be handled.
* source could not be handled.
*/ */
DataSourceMetadata getDataSourceMetadata(DataSource dataSource); DataSourcePoolMetadata getDataSourcePoolMetadata(DataSource dataSource);
} }
...@@ -14,54 +14,44 @@ ...@@ -14,54 +14,44 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.autoconfigure.jdbc; package org.springframework.boot.autoconfigure.jdbc.metadata;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import javax.sql.DataSource; import javax.sql.DataSource;
/** /**
* A {@link DataSourceMetadataProvider} implementation that returns the first * A {@link DataSourcePoolMetadataProvider} implementation that returns the first
* {@link DataSourceMetadata} that is found by one of its delegate. * {@link DataSourcePoolMetadata} that is found by one of its delegate.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
* @since 1.2.0 * @since 1.2.0
*/ */
public class CompositeDataSourceMetadataProvider implements DataSourceMetadataProvider { public class DataSourcePoolMetadataProviders implements DataSourcePoolMetadataProvider {
private final Collection<DataSourceMetadataProvider> providers; private final List<DataSourcePoolMetadataProvider> providers;
/** /**
* Create an instance with an initial collection of delegates to use. * Create a {@link DataSourcePoolMetadataProviders} instance with an initial
* collection of delegates to use.
*/ */
public CompositeDataSourceMetadataProvider(Collection<DataSourceMetadataProvider> providers) { public DataSourcePoolMetadataProviders(
this.providers = providers; Collection<? extends DataSourcePoolMetadataProvider> providers) {
} this.providers = new ArrayList<DataSourcePoolMetadataProvider>(providers);
/**
* Create an instance with no delegate.
*/
public CompositeDataSourceMetadataProvider() {
this(new ArrayList<DataSourceMetadataProvider>());
} }
@Override @Override
public DataSourceMetadata getDataSourceMetadata(DataSource dataSource) { public DataSourcePoolMetadata getDataSourcePoolMetadata(DataSource dataSource) {
for (DataSourceMetadataProvider provider : providers) { for (DataSourcePoolMetadataProvider provider : this.providers) {
DataSourceMetadata dataSourceMetadata = provider.getDataSourceMetadata(dataSource); DataSourcePoolMetadata metadata = provider
if (dataSourceMetadata != null) { .getDataSourcePoolMetadata(dataSource);
return dataSourceMetadata; if (metadata != null) {
return metadata;
} }
} }
return null; return null;
} }
/**
* Add a {@link DataSourceMetadataProvider} delegate to the list.
*/
public void addDataSourceMetadataProvider(DataSourceMetadataProvider provider) {
this.providers.add(provider);
}
} }
...@@ -14,79 +14,88 @@ ...@@ -14,79 +14,88 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.autoconfigure.jdbc; package org.springframework.boot.autoconfigure.jdbc.metadata;
import javax.sql.DataSource; import javax.sql.DataSource;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.commons.dbcp.BasicDataSource; import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import com.zaxxer.hikari.HikariDataSource;
/** /**
* Register the {@link DataSourceMetadataProvider} instances for the supported * Register the {@link DataSourcePoolMetadataProvider} instances for the supported data
* data sources. * sources.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
* @since 1.2.0 * @since 1.2.0
*/ */
@Configuration @Configuration
public class DataSourceMetadataProvidersConfiguration { public class DataSourcePoolMetadataProvidersConfiguration {
@Configuration @Configuration
@ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class) @ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class)
static class TomcatDataSourceProviderConfiguration { static class TomcatDataSourcePoolMetadataProviderConfiguration {
@Bean @Bean
public DataSourceMetadataProvider tomcatDataSourceProvider() { public DataSourcePoolMetadataProvider tomcatPoolDataSourceMetadataProvider() {
return new DataSourceMetadataProvider() { return new DataSourcePoolMetadataProvider() {
@Override @Override
public DataSourceMetadata getDataSourceMetadata(DataSource dataSource) { public DataSourcePoolMetadata getDataSourcePoolMetadata(
DataSource dataSource) {
if (dataSource instanceof org.apache.tomcat.jdbc.pool.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; return null;
} }
}; };
} }
} }
@Configuration @Configuration
@ConditionalOnClass(HikariDataSource.class) @ConditionalOnClass(HikariDataSource.class)
static class HikariDataSourceProviderConfiguration { static class HikariPoolDataSourceMetadataProviderConfiguration {
@Bean @Bean
public DataSourceMetadataProvider hikariDataSourceProvider() { public DataSourcePoolMetadataProvider hikariPoolDataSourceMetadataProvider() {
return new DataSourceMetadataProvider() { return new DataSourcePoolMetadataProvider() {
@Override @Override
public DataSourceMetadata getDataSourceMetadata(DataSource dataSource) { public DataSourcePoolMetadata getDataSourcePoolMetadata(
DataSource dataSource) {
if (dataSource instanceof HikariDataSource) { if (dataSource instanceof HikariDataSource) {
return new HikariDataSourceMetadata((HikariDataSource) dataSource); return new HikariDataSourcePoolMetadata(
(HikariDataSource) dataSource);
} }
return null; return null;
} }
}; };
} }
} }
@Configuration @Configuration
@ConditionalOnClass(BasicDataSource.class) @ConditionalOnClass(BasicDataSource.class)
static class CommonsDbcpDataSourceProviderConfiguration { static class CommonsDbcpPoolDataSourceMetadataProviderConfiguration {
@Bean @Bean
public DataSourceMetadataProvider commonsDbcpDataSourceProvider() { public DataSourcePoolMetadataProvider commonsDbcpPoolDataSourceMetadataProvider() {
return new DataSourceMetadataProvider() { return new DataSourcePoolMetadataProvider() {
@Override @Override
public DataSourceMetadata getDataSourceMetadata(DataSource dataSource) { public DataSourcePoolMetadata getDataSourcePoolMetadata(
DataSource dataSource) {
if (dataSource instanceof BasicDataSource) { if (dataSource instanceof BasicDataSource) {
return new CommonsDbcpDataSourceMetadata((BasicDataSource) dataSource); return new CommonsDbcpDataSourcePoolMetadata(
(BasicDataSource) dataSource);
} }
return null; return null;
} }
}; };
} }
} }
} }
/*
* 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<HikariDataSource> {
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();
}
}
...@@ -14,37 +14,35 @@ ...@@ -14,37 +14,35 @@
* limitations under the License. * 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.ConnectionPool;
import org.apache.tomcat.jdbc.pool.DataSource; import org.apache.tomcat.jdbc.pool.DataSource;
/** /**
* * {@link DataSourcePoolMetadata} for a Tomcat {@link DataSource}.
* A {@link DataSourceMetadata} implementation for the tomcat
* data source.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
*/ */
public class TomcatDataSourceMetadata extends AbstractDataSourceMetadata<DataSource> { public class TomcatDataSourcePoolMetadata extends AbstractDataSourcePoolMetadata<DataSource> {
public TomcatDataSourceMetadata(DataSource dataSource) { public TomcatDataSourcePoolMetadata(DataSource dataSource) {
super(dataSource); super(dataSource);
} }
@Override @Override
public Integer getPoolSize() { public Integer getActive() {
ConnectionPool pool = getDataSource().getPool(); ConnectionPool pool = getDataSource().getPool();
return (pool == null ? 0 : pool.getActive()); return (pool == null ? 0 : pool.getActive());
} }
@Override @Override
public Integer getMaxPoolSize() { public Integer getMax() {
return getDataSource().getMaxActive(); return getDataSource().getMaxActive();
} }
@Override @Override
public Integer getMinPoolSize() { public Integer getMin() {
return getDataSource().getMinIdle(); return getDataSource().getMinIdle();
} }
...@@ -52,4 +50,5 @@ public class TomcatDataSourceMetadata extends AbstractDataSourceMetadata<DataSou ...@@ -52,4 +50,5 @@ public class TomcatDataSourceMetadata extends AbstractDataSourceMetadata<DataSou
public String getValidationQuery() { public String getValidationQuery() {
return getDataSource().getValidationQuery(); return getDataSource().getValidationQuery();
} }
} }
...@@ -14,24 +14,25 @@ ...@@ -14,24 +14,25 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.autoconfigure.jdbc; package org.springframework.boot.autoconfigure.jdbc.metadata;
import static org.junit.Assert.*;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.ConnectionCallback; import org.springframework.jdbc.core.ConnectionCallback;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
import static org.junit.Assert.assertEquals;
/** /**
* Abstract base class for {@link DataSourcePoolMetadata} tests.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
*/ */
public abstract class AbstractDataSourceMetadataTests<D extends AbstractDataSourceMetadata> { public abstract class AbstractDataSourcePoolMetadataTests<D extends AbstractDataSourcePoolMetadata<?>> {
/** /**
* Return a data source metadata instance with a min size of 0 and max size of 2. * 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<D extends AbstractDataSour ...@@ -40,36 +41,40 @@ public abstract class AbstractDataSourceMetadataTests<D extends AbstractDataSour
@Test @Test
public void getMaxPoolSize() { public void getMaxPoolSize() {
assertEquals(Integer.valueOf(2), getDataSourceMetadata().getMaxPoolSize()); assertEquals(Integer.valueOf(2), getDataSourceMetadata().getMax());
} }
@Test @Test
public void getMinPoolSize() { public void getMinPoolSize() {
assertEquals(Integer.valueOf(0), getDataSourceMetadata().getMinPoolSize()); assertEquals(Integer.valueOf(0), getDataSourceMetadata().getMin());
} }
@Test @Test
public void getPoolSizeNoConnection() { public void getPoolSizeNoConnection() {
// Make sure the pool is initialized // Make sure the pool is initialized
JdbcTemplate jdbcTemplate = new JdbcTemplate(getDataSourceMetadata().getDataSource()); JdbcTemplate jdbcTemplate = new JdbcTemplate(getDataSourceMetadata()
.getDataSource());
jdbcTemplate.execute(new ConnectionCallback<Void>() { jdbcTemplate.execute(new ConnectionCallback<Void>() {
@Override @Override
public Void doInConnection(Connection connection) throws SQLException, DataAccessException { public Void doInConnection(Connection connection) throws SQLException,
DataAccessException {
return null; return null;
} }
}); });
assertEquals(Integer.valueOf(0), getDataSourceMetadata().getPoolSize()); assertEquals(Integer.valueOf(0), getDataSourceMetadata().getActive());
assertEquals(Float.valueOf(0), getDataSourceMetadata().getPoolUsage()); assertEquals(Float.valueOf(0), getDataSourceMetadata().getUsage());
} }
@Test @Test
public void getPoolSizeOneConnection() { public void getPoolSizeOneConnection() {
JdbcTemplate jdbcTemplate = new JdbcTemplate(getDataSourceMetadata().getDataSource()); JdbcTemplate jdbcTemplate = new JdbcTemplate(getDataSourceMetadata()
.getDataSource());
jdbcTemplate.execute(new ConnectionCallback<Void>() { jdbcTemplate.execute(new ConnectionCallback<Void>() {
@Override @Override
public Void doInConnection(Connection connection) throws SQLException, DataAccessException { public Void doInConnection(Connection connection) throws SQLException,
assertEquals(Integer.valueOf(1), getDataSourceMetadata().getPoolSize()); DataAccessException {
assertEquals(Float.valueOf(0.5F), getDataSourceMetadata().getPoolUsage()); assertEquals(Integer.valueOf(1), getDataSourceMetadata().getActive());
assertEquals(Float.valueOf(0.5F), getDataSourceMetadata().getUsage());
return null; return null;
} }
}); });
...@@ -77,15 +82,20 @@ public abstract class AbstractDataSourceMetadataTests<D extends AbstractDataSour ...@@ -77,15 +82,20 @@ public abstract class AbstractDataSourceMetadataTests<D extends AbstractDataSour
@Test @Test
public void getPoolSizeTwoConnections() { public void getPoolSizeTwoConnections() {
final JdbcTemplate jdbcTemplate = new JdbcTemplate(getDataSourceMetadata().getDataSource()); final JdbcTemplate jdbcTemplate = new JdbcTemplate(getDataSourceMetadata()
.getDataSource());
jdbcTemplate.execute(new ConnectionCallback<Void>() { jdbcTemplate.execute(new ConnectionCallback<Void>() {
@Override @Override
public Void doInConnection(Connection connection) throws SQLException, DataAccessException { public Void doInConnection(Connection connection) throws SQLException,
DataAccessException {
jdbcTemplate.execute(new ConnectionCallback<Void>() { jdbcTemplate.execute(new ConnectionCallback<Void>() {
@Override @Override
public Void doInConnection(Connection connection) throws SQLException, DataAccessException { public Void doInConnection(Connection connection)
assertEquals(Integer.valueOf(2), getDataSourceMetadata().getPoolSize()); throws SQLException, DataAccessException {
assertEquals(Float.valueOf(1F), getDataSourceMetadata().getPoolUsage()); assertEquals(Integer.valueOf(2), getDataSourceMetadata()
.getActive());
assertEquals(Float.valueOf(1F), getDataSourceMetadata()
.getUsage());
return null; return null;
} }
}); });
...@@ -98,10 +108,8 @@ public abstract class AbstractDataSourceMetadataTests<D extends AbstractDataSour ...@@ -98,10 +108,8 @@ public abstract class AbstractDataSourceMetadataTests<D extends AbstractDataSour
public abstract void getValidationQuery(); public abstract void getValidationQuery();
protected DataSourceBuilder initializeBuilder() { protected DataSourceBuilder initializeBuilder() {
return DataSourceBuilder.create() return DataSourceBuilder.create().driverClassName("org.hsqldb.jdbc.JDBCDriver")
.driverClassName("org.hsqldb.jdbc.JDBCDriver") .url("jdbc:hsqldb:mem:test").username("sa");
.url("jdbc:hsqldb:mem:test")
.username("sa");
} }
} }
...@@ -14,21 +14,26 @@ ...@@ -14,21 +14,26 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.autoconfigure.jdbc; package org.springframework.boot.autoconfigure.jdbc.metadata;
import static org.junit.Assert.*;
import org.apache.commons.dbcp.BasicDataSource; import org.apache.commons.dbcp.BasicDataSource;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.autoconfigure.jdbc.metadata.CommonsDbcpDataSourcePoolMetadata;
import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadata;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
/** /**
* Tests for {@link CommonsDbcpDataSourcePoolMetadata}.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
*/ */
public class CommonsDbcpDataSourceMetadataTests extends AbstractDataSourceMetadataTests<CommonsDbcpDataSourceMetadata> { public class CommonsDbcpDataSourcePoolMetadataTests extends
AbstractDataSourcePoolMetadataTests<CommonsDbcpDataSourcePoolMetadata> {
private CommonsDbcpDataSourceMetadata dataSourceMetadata; private CommonsDbcpDataSourcePoolMetadata dataSourceMetadata;
@Before @Before
public void setup() { public void setup() {
...@@ -36,50 +41,54 @@ public class CommonsDbcpDataSourceMetadataTests extends AbstractDataSourceMetada ...@@ -36,50 +41,54 @@ public class CommonsDbcpDataSourceMetadataTests extends AbstractDataSourceMetada
} }
@Override @Override
protected CommonsDbcpDataSourceMetadata getDataSourceMetadata() { protected CommonsDbcpDataSourcePoolMetadata getDataSourceMetadata() {
return this.dataSourceMetadata; return this.dataSourceMetadata;
} }
@Test @Test
public void getPoolUsageWithNoCurrent() { public void getPoolUsageWithNoCurrent() {
CommonsDbcpDataSourceMetadata dsm = new CommonsDbcpDataSourceMetadata(createDataSource()) { CommonsDbcpDataSourcePoolMetadata dsm = new CommonsDbcpDataSourcePoolMetadata(
createDataSource()) {
@Override @Override
public Integer getPoolSize() { public Integer getActive() {
return null; return null;
} }
}; };
assertNull(dsm.getPoolUsage()); assertNull(dsm.getUsage());
} }
@Test @Test
public void getPoolUsageWithNoMax() { public void getPoolUsageWithNoMax() {
CommonsDbcpDataSourceMetadata dsm = new CommonsDbcpDataSourceMetadata(createDataSource()) { CommonsDbcpDataSourcePoolMetadata dsm = new CommonsDbcpDataSourcePoolMetadata(
createDataSource()) {
@Override @Override
public Integer getMaxPoolSize() { public Integer getMax() {
return null; return null;
} }
}; };
assertNull(dsm.getPoolUsage()); assertNull(dsm.getUsage());
} }
@Test @Test
public void getPoolUsageWithUnlimitedPool() { public void getPoolUsageWithUnlimitedPool() {
DataSourceMetadata unlimitedDataSource = createDataSourceMetadata(0, -1); DataSourcePoolMetadata unlimitedDataSource = createDataSourceMetadata(0, -1);
assertEquals(Float.valueOf(-1F), unlimitedDataSource.getPoolUsage()); assertEquals(Float.valueOf(-1F), unlimitedDataSource.getUsage());
} }
@Override @Override
public void getValidationQuery() { public void getValidationQuery() {
BasicDataSource dataSource = createDataSource(); BasicDataSource dataSource = createDataSource();
dataSource.setValidationQuery("SELECT FROM FOO"); 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(); BasicDataSource dataSource = createDataSource();
dataSource.setMinIdle(minSize); dataSource.setMinIdle(minSize);
dataSource.setMaxActive(maxSize); dataSource.setMaxActive(maxSize);
return new CommonsDbcpDataSourceMetadata(dataSource); return new CommonsDbcpDataSourcePoolMetadata(dataSource);
} }
private BasicDataSource createDataSource() { private BasicDataSource createDataSource() {
......
...@@ -14,10 +14,7 @@ ...@@ -14,10 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.autoconfigure.jdbc; package org.springframework.boot.autoconfigure.jdbc.metadata;
import static org.junit.Assert.*;
import static org.mockito.BDDMockito.*;
import java.util.Arrays; import java.util.Arrays;
...@@ -28,26 +25,31 @@ import org.junit.Test; ...@@ -28,26 +25,31 @@ import org.junit.Test;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockitoAnnotations; 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 * @author Stephane Nicoll
*/ */
public class CompositeDataSourceMetadataProviderTests { public class DataSourcePoolMetadataProvidersTests {
@Mock @Mock
private DataSourceMetadataProvider firstProvider; private DataSourcePoolMetadataProvider firstProvider;
@Mock @Mock
private DataSourceMetadata first; private DataSourcePoolMetadata first;
@Mock @Mock
private DataSource firstDataSource; private DataSource firstDataSource;
@Mock @Mock
private DataSourceMetadataProvider secondProvider; private DataSourcePoolMetadataProvider secondProvider;
@Mock @Mock
private DataSourceMetadata second; private DataSourcePoolMetadata second;
@Mock @Mock
private DataSource secondDataSource; private DataSource secondDataSource;
...@@ -55,30 +57,22 @@ public class CompositeDataSourceMetadataProviderTests { ...@@ -55,30 +57,22 @@ public class CompositeDataSourceMetadataProviderTests {
@Mock @Mock
private DataSource unknownDataSource; private DataSource unknownDataSource;
@Before @Before
public void setup() { public void setup() {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
given(firstProvider.getDataSourceMetadata(firstDataSource)).willReturn(first); given(this.firstProvider.getDataSourcePoolMetadata(this.firstDataSource))
given(firstProvider.getDataSourceMetadata(secondDataSource)).willReturn(second); .willReturn(this.first);
given(this.firstProvider.getDataSourcePoolMetadata(this.secondDataSource))
.willReturn(this.second);
} }
@Test @Test
public void createWithProviders() { public void createWithProviders() {
CompositeDataSourceMetadataProvider provider = DataSourcePoolMetadataProviders provider = new DataSourcePoolMetadataProviders(
new CompositeDataSourceMetadataProvider(Arrays.asList(firstProvider, secondProvider)); Arrays.asList(this.firstProvider, this.secondProvider));
assertSame(first, provider.getDataSourceMetadata(firstDataSource)); assertSame(this.first, provider.getDataSourcePoolMetadata(this.firstDataSource));
assertSame(second, provider.getDataSourceMetadata(secondDataSource)); assertSame(this.second, provider.getDataSourcePoolMetadata(this.secondDataSource));
assertNull(provider.getDataSourceMetadata(unknownDataSource)); assertNull(provider.getDataSourcePoolMetadata(this.unknownDataSource));
}
@Test
public void addProvider() {
CompositeDataSourceMetadataProvider provider =
new CompositeDataSourceMetadataProvider();
assertNull(provider.getDataSourceMetadata(firstDataSource));
provider.addDataSourceMetadataProvider(firstProvider);
assertSame(first, provider.getDataSourceMetadata(firstDataSource));
} }
} }
...@@ -14,27 +14,32 @@ ...@@ -14,27 +14,32 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.autoconfigure.jdbc; package org.springframework.boot.autoconfigure.jdbc.metadata;
import com.zaxxer.hikari.HikariDataSource;
import org.junit.Before; import org.junit.Before;
import org.springframework.boot.autoconfigure.jdbc.metadata.HikariDataSourcePoolMetadata;
import com.zaxxer.hikari.HikariDataSource;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
/** /**
* Tests for {@link HikariDataSourcePoolMetadata}.
*
* @author Stephane Nicoll * @author Stephane Nicoll
*/ */
public class HikariDataSourceMetadataTests extends AbstractDataSourceMetadataTests<HikariDataSourceMetadata> { public class HikariDataSourcePoolMetadataTests extends
AbstractDataSourcePoolMetadataTests<HikariDataSourcePoolMetadata> {
private HikariDataSourceMetadata dataSourceMetadata; private HikariDataSourcePoolMetadata dataSourceMetadata;
@Before @Before
public void setup() { public void setup() {
this.dataSourceMetadata = new HikariDataSourceMetadata(createDataSource(0, 2)); this.dataSourceMetadata = new HikariDataSourcePoolMetadata(createDataSource(0, 2));
} }
@Override @Override
protected HikariDataSourceMetadata getDataSourceMetadata() { protected HikariDataSourcePoolMetadata getDataSourceMetadata() {
return this.dataSourceMetadata; return this.dataSourceMetadata;
} }
...@@ -42,11 +47,13 @@ public class HikariDataSourceMetadataTests extends AbstractDataSourceMetadataTes ...@@ -42,11 +47,13 @@ public class HikariDataSourceMetadataTests extends AbstractDataSourceMetadataTes
public void getValidationQuery() { public void getValidationQuery() {
HikariDataSource dataSource = createDataSource(0, 4); HikariDataSource dataSource = createDataSource(0, 4);
dataSource.setConnectionTestQuery("SELECT FROM FOO"); 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) { 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.setMinimumIdle(minSize);
dataSource.setMaximumPoolSize(maxSize); dataSource.setMaximumPoolSize(maxSize);
return dataSource; return dataSource;
......
...@@ -14,28 +14,31 @@ ...@@ -14,28 +14,31 @@
* limitations under the License. * 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.apache.tomcat.jdbc.pool.DataSource;
import org.junit.Before; import org.junit.Before;
import org.springframework.boot.autoconfigure.jdbc.metadata.TomcatDataSourcePoolMetadata;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
/** /**
* Tests for {@link TomcatDataSourcePoolMetadata}.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
*/ */
public class TomcatDataSourceMetadataTests extends AbstractDataSourceMetadataTests<TomcatDataSourceMetadata> { public class TomcatDataSourcePoolMetadataTests extends
AbstractDataSourcePoolMetadataTests<TomcatDataSourcePoolMetadata> {
private TomcatDataSourceMetadata dataSourceMetadata; private TomcatDataSourcePoolMetadata dataSourceMetadata;
@Before @Before
public void setup() { public void setup() {
this.dataSourceMetadata = new TomcatDataSourceMetadata(createDataSource(0, 2)); this.dataSourceMetadata = new TomcatDataSourcePoolMetadata(createDataSource(0, 2));
} }
@Override @Override
protected TomcatDataSourceMetadata getDataSourceMetadata() { protected TomcatDataSourcePoolMetadata getDataSourceMetadata() {
return this.dataSourceMetadata; return this.dataSourceMetadata;
} }
...@@ -43,11 +46,13 @@ public class TomcatDataSourceMetadataTests extends AbstractDataSourceMetadataTes ...@@ -43,11 +46,13 @@ public class TomcatDataSourceMetadataTests extends AbstractDataSourceMetadataTes
public void getValidationQuery() { public void getValidationQuery() {
DataSource dataSource = createDataSource(0, 4); DataSource dataSource = createDataSource(0, 4);
dataSource.setValidationQuery("SELECT FROM FOO"); 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) { 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.setMinIdle(minSize);
dataSource.setMaxActive(maxSize); dataSource.setMaxActive(maxSize);
......
...@@ -634,26 +634,35 @@ The `gauge` shows the last response time for a request. So the last request to ` ...@@ -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 NOTE: In this example we are actually accessing the endpoint over HTTP using the
`/metrics` URL, this explains why `metrics` appears in the response. `/metrics` URL, this explains why `metrics` appears in the response.
[[production-ready-datasource-metrics]] [[production-ready-datasource-metrics]]
=== 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 * The maximum number connections (`datasource.xxx.max`).
number of allocated connection(s) (`.active`) and the current usage of the connection * The minimum number of connections (`datasource.xxx.min`).
pool (`.usage`). * 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 All data source metrics share the `datasource.` prefix. The prefix is further qualified
each data source: for each data source:
* If the data source is the primary data source (that is either the only available data * 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` source or the one flagged `@Primary` amongst the existing ones), the prefix is
* If the data source bean name ends with `dataSource`, the prefix is the name of the bean without `datasource.primary`.
it (i.e. `datasource.batch` for `batchDataSource`) * If the data source bean name ends with `dataSource`, the prefix is the name of the bean
* In all other cases, the name of the bean is used 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`. Spring Boot provides those metadata for all supported It is possible to override part or all of those defaults by registering a bean with a
datasource; you can provide a `DataSourceMetadata` implementation for your favorite data source, customized version of `DataSourcePublicMetrics`. By default, Spring Boot provides metadata
check `DatasourceMetadataProvidersConfiguration` for more details. 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.
[[production-ready-recording-metrics]] [[production-ready-recording-metrics]]
=== Recording your own metrics === Recording your own metrics
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment