diff --git a/spring-boot-actuator/pom.xml b/spring-boot-actuator/pom.xml
index eecfd9a32d..496c39fa43 100644
--- a/spring-boot-actuator/pom.xml
+++ b/spring-boot-actuator/pom.xml
@@ -131,6 +131,21 @@
spring-rabbit
true
+
+ org.apache.tomcat
+ tomcat-jdbc
+ true
+
+
+ com.zaxxer
+ HikariCP
+ true
+
+
+ commons-dbcp
+ commons-dbcp
+ true
+
ch.qos.logback
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/MetricDataSourceAutoConfiguration.java
new file mode 100644
index 0000000000..7e6030261c
--- /dev/null
+++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/MetricDataSourceAutoConfiguration.java
@@ -0,0 +1,51 @@
+/*
+ * 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.actuate.autoconfigure;
+
+import javax.sql.DataSource;
+
+import org.springframework.boot.actuate.endpoint.DataSourcePublicMetrics;
+import org.springframework.boot.actuate.metrics.jdbc.DataSourceMetadataProvider;
+import org.springframework.boot.actuate.metrics.jdbc.DataSourceMetadataProvidersConfiguration;
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+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.context.annotation.Bean;
+import org.springframework.context.annotation.Import;
+
+/**
+ * {@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 {
+
+ @Bean
+ @ConditionalOnBean(DataSourceMetadataProvider.class)
+ @ConditionalOnMissingBean(DataSourcePublicMetrics.class)
+ DataSourcePublicMetrics dataSourcePublicMetrics() {
+ return new DataSourcePublicMetrics();
+ }
+
+}
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
new file mode 100644
index 0000000000..b860f9c475
--- /dev/null
+++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/DataSourcePublicMetrics.java
@@ -0,0 +1,141 @@
+/*
+ * 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.actuate.endpoint;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+
+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.actuate.metrics.jdbc.CompositeDataSourceMetadataProvider;
+import org.springframework.boot.actuate.metrics.jdbc.DataSourceMetadata;
+import org.springframework.boot.actuate.metrics.jdbc.DataSourceMetadataProvider;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Primary;
+
+/**
+ * A {@link PublicMetrics} implementation that provides data source usage
+ * statistics.
+ *
+ * @author Stephane Nicoll
+ * @since 1.2.0
+ */
+public class DataSourcePublicMetrics implements PublicMetrics {
+
+ private static final String DATASOURCE_SUFFIX = "dataSource";
+
+ @Autowired
+ private ApplicationContext applicationContext;
+
+ @Autowired
+ private Collection dataSourceMetadataProviders;
+
+ private final Map dataSourceMetadataByPrefix
+ = 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);
+ }
+ }
+ }
+
+ @Override
+ public Collection> metrics() {
+ Collection> result = new LinkedHashSet>();
+ for (Map.Entry entry : dataSourceMetadataByPrefix.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));
+ }
+ }
+ 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
+ * @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.");
+ if (primary) {
+ sb.append("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 {
+ 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;
+ }
+
+ /**
+ * Attempt to locate the primary {@link DataSource} (i.e. either the only data source
+ * available or the one amongst the candidates marked as {@link Primary}. Return
+ * {@code null} if there no primary data source could be found.
+ */
+ private DataSource getPrimaryDataSource() {
+ try {
+ return applicationContext.getBean(DataSource.class);
+ }
+ catch (NoSuchBeanDefinitionException e) {
+ return null;
+ }
+ }
+}
diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/jdbc/AbstractDataSourceMetadata.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/jdbc/AbstractDataSourceMetadata.java
new file mode 100644
index 0000000000..92b1587ebe
--- /dev/null
+++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/jdbc/AbstractDataSourceMetadata.java
@@ -0,0 +1,61 @@
+/*
+ * 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.actuate.metrics.jdbc;
+
+import javax.sql.DataSource;
+
+/**
+ * A base {@link DataSourceMetadata} implementation.
+ *
+ * @author Stephane Nicoll
+ * @since 1.2.0
+ */
+public abstract class AbstractDataSourceMetadata implements DataSourceMetadata {
+
+ private final D dataSource;
+
+ /**
+ * Create an instance with the data source to use.
+ */
+ protected AbstractDataSourceMetadata(D dataSource) {
+ this.dataSource = dataSource;
+ }
+
+ @Override
+ public Float getPoolUsage() {
+ Integer max = getMaxPoolSize();
+ if (max == null) {
+ return null;
+ }
+ if (max < 0) {
+ return -1F;
+ }
+ Integer current = getPoolSize();
+ if (current == null) {
+ return null;
+ }
+ if (current == 0) {
+ return 0F;
+ }
+ return (float) current / max; // something like that
+ }
+
+ protected final D getDataSource() {
+ return dataSource;
+ }
+
+}
diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/jdbc/CommonsDbcpDataSourceMetadata.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/jdbc/CommonsDbcpDataSourceMetadata.java
new file mode 100644
index 0000000000..49f67955e2
--- /dev/null
+++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/jdbc/CommonsDbcpDataSourceMetadata.java
@@ -0,0 +1,49 @@
+/*
+ * 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.actuate.metrics.jdbc;
+
+import org.apache.commons.dbcp.BasicDataSource;
+
+/**
+ * A {@link DataSourceMetadata} implementation for the commons dbcp
+ * data source.
+ *
+ * @author Stephane Nicoll
+ * @since 1.2.0
+ */
+public class CommonsDbcpDataSourceMetadata extends AbstractDataSourceMetadata {
+
+ public CommonsDbcpDataSourceMetadata(BasicDataSource dataSource) {
+ super(dataSource);
+ }
+
+ @Override
+ public Integer getPoolSize() {
+ return getDataSource().getNumActive();
+ }
+
+ @Override
+ public Integer getMaxPoolSize() {
+ return getDataSource().getMaxActive();
+ }
+
+ @Override
+ public Integer getMinPoolSize() {
+ return getDataSource().getMinIdle();
+ }
+
+}
diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/jdbc/CompositeDataSourceMetadataProvider.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/jdbc/CompositeDataSourceMetadataProvider.java
new file mode 100644
index 0000000000..cf75b800dc
--- /dev/null
+++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/jdbc/CompositeDataSourceMetadataProvider.java
@@ -0,0 +1,67 @@
+/*
+ * 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.actuate.metrics.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-actuator/src/main/java/org/springframework/boot/actuate/metrics/jdbc/DataSourceMetadata.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/jdbc/DataSourceMetadata.java
new file mode 100644
index 0000000000..b3e67ab301
--- /dev/null
+++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/jdbc/DataSourceMetadata.java
@@ -0,0 +1,67 @@
+/*
+ * 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.actuate.metrics.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();
+
+}
diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/jdbc/DataSourceMetadataProvider.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/jdbc/DataSourceMetadataProvider.java
new file mode 100644
index 0000000000..6d78238ed7
--- /dev/null
+++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/jdbc/DataSourceMetadataProvider.java
@@ -0,0 +1,36 @@
+/*
+ * 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.actuate.metrics.jdbc;
+
+import javax.sql.DataSource;
+
+/**
+ * Provide a {@link DataSourceMetadata} based on a {@link DataSource}.
+ *
+ * @author Stephane Nicoll
+ * @since 1.2.0
+ */
+public interface DataSourceMetadataProvider {
+
+ /**
+ * Return the {@link DataSourceMetadata} instance able to manage the
+ * specified {@link DataSource} or {@code null} if the given data
+ * source could not be handled.
+ */
+ DataSourceMetadata getDataSourceMetadata(DataSource dataSource);
+
+}
diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/jdbc/DataSourceMetadataProvidersConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/jdbc/DataSourceMetadataProvidersConfiguration.java
new file mode 100644
index 0000000000..156fc25a59
--- /dev/null
+++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/jdbc/DataSourceMetadataProvidersConfiguration.java
@@ -0,0 +1,92 @@
+/*
+ * 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.actuate.metrics.jdbc;
+
+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;
+
+/**
+ * Register the {@link DataSourceMetadataProvider} instances for the supported
+ * data sources.
+ *
+ * @author Stephane Nicoll
+ * @since 1.2.0
+ */
+@Configuration
+public class DataSourceMetadataProvidersConfiguration {
+
+ @Configuration
+ @ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class)
+ static class TomcatDataSourceProviderConfiguration {
+
+ @Bean
+ public DataSourceMetadataProvider tomcatDataSourceProvider() {
+ return new DataSourceMetadataProvider() {
+ @Override
+ public DataSourceMetadata getDataSourceMetadata(DataSource dataSource) {
+ if (dataSource instanceof org.apache.tomcat.jdbc.pool.DataSource) {
+ return new TomcatDataSourceMetadata((org.apache.tomcat.jdbc.pool.DataSource) dataSource);
+ }
+ return null;
+ }
+ };
+ }
+ }
+
+ @Configuration
+ @ConditionalOnClass(HikariDataSource.class)
+ static class HikariDataSourceProviderConfiguration {
+
+ @Bean
+ public DataSourceMetadataProvider hikariDataSourceProvider() {
+ return new DataSourceMetadataProvider() {
+ @Override
+ public DataSourceMetadata getDataSourceMetadata(DataSource dataSource) {
+ if (dataSource instanceof HikariDataSource) {
+ return new HikariDataSourceMetadata((HikariDataSource) dataSource);
+ }
+ return null;
+ }
+ };
+ }
+ }
+
+ @Configuration
+ @ConditionalOnClass(BasicDataSource.class)
+ static class CommonsDbcpDataSourceProviderConfiguration {
+
+ @Bean
+ public DataSourceMetadataProvider commonsDbcpDataSourceProvider() {
+ return new DataSourceMetadataProvider() {
+ @Override
+ public DataSourceMetadata getDataSourceMetadata(DataSource dataSource) {
+ if (dataSource instanceof BasicDataSource) {
+ return new CommonsDbcpDataSourceMetadata((BasicDataSource) dataSource);
+ }
+ return null;
+ }
+ };
+ }
+ }
+
+}
diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/jdbc/HikariDataSourceMetadata.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/jdbc/HikariDataSourceMetadata.java
new file mode 100644
index 0000000000..d2aaef2c80
--- /dev/null
+++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/jdbc/HikariDataSourceMetadata.java
@@ -0,0 +1,106 @@
+/*
+ * 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.actuate.metrics.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();
+ }
+
+ /**
+ * 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-actuator/src/main/java/org/springframework/boot/actuate/metrics/jdbc/TomcatDataSourceMetadata.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/jdbc/TomcatDataSourceMetadata.java
new file mode 100644
index 0000000000..7b3adcf109
--- /dev/null
+++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/jdbc/TomcatDataSourceMetadata.java
@@ -0,0 +1,51 @@
+/*
+ * 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.actuate.metrics.jdbc;
+
+import org.apache.tomcat.jdbc.pool.ConnectionPool;
+import org.apache.tomcat.jdbc.pool.DataSource;
+
+/**
+ *
+ * A {@link DataSourceMetadata} implementation for the tomcat
+ * data source.
+ *
+ * @author Stephane Nicoll
+ */
+public class TomcatDataSourceMetadata extends AbstractDataSourceMetadata {
+
+ public TomcatDataSourceMetadata(DataSource dataSource) {
+ super(dataSource);
+ }
+
+ @Override
+ public Integer getPoolSize() {
+ ConnectionPool pool = getDataSource().getPool();
+ return (pool == null ? 0 : pool.getActive());
+ }
+
+ @Override
+ public Integer getMaxPoolSize() {
+ return getDataSource().getMaxActive();
+ }
+
+ @Override
+ public Integer getMinPoolSize() {
+ return getDataSource().getMinIdle();
+ }
+
+}
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 59e69a2deb..fc55c4cee0 100644
--- a/spring-boot-actuator/src/main/resources/META-INF/spring.factories
+++ b/spring-boot-actuator/src/main/resources/META-INF/spring.factories
@@ -8,6 +8,7 @@ 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/MetricDataSourceAutoConfigurationTests.java
new file mode 100644
index 0000000000..a9dc552eea
--- /dev/null
+++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/MetricDataSourceAutoConfigurationTests.java
@@ -0,0 +1,196 @@
+/*
+ * 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.actuate.autoconfigure;
+
+import static org.junit.Assert.*;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Collection;
+import java.util.HashMap;
+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.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.dao.DataAccessException;
+import org.springframework.jdbc.core.ConnectionCallback;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+/**
+ *
+ * @author Stephane Nicoll
+ */
+public class MetricDataSourceAutoConfigurationTests {
+
+ private AnnotationConfigApplicationContext context;
+
+ @After
+ public void after() {
+ if (this.context != null) {
+ this.context.close();
+ }
+ }
+
+ @Test
+ public void noDataSource() {
+ load();
+ assertEquals(0, this.context.getBeansOfType(PublicMetrics.class).size());
+ }
+
+ @Test
+ public void autoDataSource() {
+ load(DataSourceAutoConfiguration.class);
+ PublicMetrics bean = this.context.getBean(PublicMetrics.class);
+ Collection> metrics = bean.metrics();
+ assertMetrics(metrics, "datasource.primary.active", "datasource.primary.usage");
+ }
+
+ @Test
+ public void multipleDataSources() {
+ load(MultipleDataSourcesConfig.class);
+ PublicMetrics bean = this.context.getBean(PublicMetrics.class);
+ Collection> metrics = bean.metrics();
+ 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.execute(new ConnectionCallback() {
+ @Override
+ 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");
+ }
+
+ @Test
+ public void multipleDataSourcesWithPrimary() {
+ load(MultipleDataSourcesWithPrimaryConfig.class);
+ PublicMetrics bean = this.context.getBean(PublicMetrics.class);
+ Collection> metrics = bean.metrics();
+ assertMetrics(metrics,
+ "datasource.primary.active", "datasource.primary.usage",
+ "datasource.commonsDbcp.active", "datasource.commonsDbcp.usage");
+ }
+
+ @Test
+ public void customPrefix() {
+ 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");
+
+ }
+
+ private void assertMetrics(Collection> metrics, String... keys) {
+ Map content = new HashMap();
+ for (Metric> metric : metrics) {
+ content.put(metric.getName(), metric.getValue());
+ }
+ for (String key : keys) {
+ assertTrue("Key '" + key + "' was not found", content.containsKey(key));
+ }
+ }
+
+ private void load(Class>... config) {
+ this.context = new AnnotationConfigApplicationContext();
+ if (config.length > 0) {
+ this.context.register(config);
+ }
+ this.context.register(MetricDataSourceAutoConfiguration.class);
+ this.context.refresh();
+ }
+
+
+ @Configuration
+ static class MultipleDataSourcesConfig {
+
+ @Bean
+ public DataSource tomcatDataSource() {
+ return initializeBuilder().type(org.apache.tomcat.jdbc.pool.DataSource.class).build();
+ }
+
+ @Bean
+ public DataSource hikariDS() {
+ return initializeBuilder().type(HikariDataSource.class).build();
+ }
+
+ @Bean
+ public DataSource commonsDbcpDataSource() {
+ return initializeBuilder().type(BasicDataSource.class).build();
+ }
+ }
+
+ @Configuration
+ static class MultipleDataSourcesWithPrimaryConfig {
+
+ @Bean
+ @Primary
+ public DataSource myDataSource() {
+ return initializeBuilder().type(org.apache.tomcat.jdbc.pool.DataSource.class).build();
+ }
+
+ @Bean
+ public DataSource commonsDbcpDataSource() {
+ return initializeBuilder().type(BasicDataSource.class).build();
+ }
+ }
+
+ @Configuration
+ static class CustomDataSourcePublicMetrics {
+
+ @Bean
+ public DataSourcePublicMetrics myDataSourcePublicMetrics() {
+ return new DataSourcePublicMetrics() {
+ @Override
+ protected String createPrefix(String dataSourceName, DataSource dataSource, boolean primary) {
+ return (primary ? "ds.first." : "ds.second");
+ }
+ };
+ }
+ }
+
+ private static DataSourceBuilder initializeBuilder() {
+ 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/metrics/jdbc/AbstractDataSourceMetadataTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/jdbc/AbstractDataSourceMetadataTests.java
new file mode 100644
index 0000000000..43cd8f8a76
--- /dev/null
+++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/jdbc/AbstractDataSourceMetadataTests.java
@@ -0,0 +1,105 @@
+/*
+ * 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.actuate.metrics.jdbc;
+
+import static org.junit.Assert.*;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import org.junit.Test;
+
+import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
+import org.springframework.dao.DataAccessException;
+import org.springframework.jdbc.core.ConnectionCallback;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+/**
+ *
+ * @author Stephane Nicoll
+ */
+public abstract class AbstractDataSourceMetadataTests {
+
+ /**
+ * Return a data source metadata instance with a min size of 0 and max size of 2.
+ */
+ protected abstract D getDataSourceMetadata();
+
+ @Test
+ public void getMaxPoolSize() {
+ assertEquals(Integer.valueOf(2), getDataSourceMetadata().getMaxPoolSize());
+ }
+
+ @Test
+ public void getMinPoolSize() {
+ assertEquals(Integer.valueOf(0), getDataSourceMetadata().getMinPoolSize());
+ }
+
+ @Test
+ public void getPoolSizeNoConnection() {
+ // Make sure the pool is initialized
+ JdbcTemplate jdbcTemplate = new JdbcTemplate(getDataSourceMetadata().getDataSource());
+ jdbcTemplate.execute(new ConnectionCallback() {
+ @Override
+ public Void doInConnection(Connection connection) throws SQLException, DataAccessException {
+ return null;
+ }
+ });
+ assertEquals(Integer.valueOf(0), getDataSourceMetadata().getPoolSize());
+ assertEquals(Float.valueOf(0), getDataSourceMetadata().getPoolUsage());
+ }
+
+ @Test
+ public void getPoolSizeOneConnection() {
+ 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());
+ return null;
+ }
+ });
+ }
+
+ @Test
+ public void getPoolSizeTwoConnections() {
+ final JdbcTemplate jdbcTemplate = new JdbcTemplate(getDataSourceMetadata().getDataSource());
+ jdbcTemplate.execute(new ConnectionCallback() {
+ @Override
+ 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());
+ return null;
+ }
+ });
+ return null;
+ }
+ });
+ }
+
+ protected DataSourceBuilder initializeBuilder() {
+ 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/metrics/jdbc/CommonsDbcpDataSourceMetadataTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/jdbc/CommonsDbcpDataSourceMetadataTests.java
new file mode 100644
index 0000000000..bb823ee117
--- /dev/null
+++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/jdbc/CommonsDbcpDataSourceMetadataTests.java
@@ -0,0 +1,82 @@
+/*
+ * 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.actuate.metrics.jdbc;
+
+import static org.junit.Assert.*;
+
+import org.apache.commons.dbcp.BasicDataSource;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ * @author Stephane Nicoll
+ */
+public class CommonsDbcpDataSourceMetadataTests extends AbstractDataSourceMetadataTests {
+
+ private CommonsDbcpDataSourceMetadata dataSourceMetadata;
+
+ @Before
+ public void setup() {
+ this.dataSourceMetadata = createDataSourceMetadata(0, 2);
+ }
+
+ @Override
+ protected CommonsDbcpDataSourceMetadata getDataSourceMetadata() {
+ return this.dataSourceMetadata;
+ }
+
+ @Test
+ public void getPoolUsageWithNoCurrent() {
+ CommonsDbcpDataSourceMetadata dsm = new CommonsDbcpDataSourceMetadata(createDataSource()) {
+ @Override
+ public Integer getPoolSize() {
+ return null;
+ }
+ };
+ assertNull(dsm.getPoolUsage());
+ }
+
+ @Test
+ public void getPoolUsageWithNoMax() {
+ CommonsDbcpDataSourceMetadata dsm = new CommonsDbcpDataSourceMetadata(createDataSource()) {
+ @Override
+ public Integer getMaxPoolSize() {
+ return null;
+ }
+ };
+ assertNull(dsm.getPoolUsage());
+ }
+
+ @Test
+ public void getPoolUsageWithUnlimitedPool() {
+ DataSourceMetadata unlimitedDataSource = createDataSourceMetadata(0, -1);
+ assertEquals(Float.valueOf(-1F), unlimitedDataSource.getPoolUsage());
+ }
+
+ private CommonsDbcpDataSourceMetadata createDataSourceMetadata(int minSize, int maxSize) {
+ BasicDataSource dataSource = createDataSource();
+ dataSource.setMinIdle(minSize);
+ dataSource.setMaxActive(maxSize);
+ return new CommonsDbcpDataSourceMetadata(dataSource);
+ }
+
+ private BasicDataSource createDataSource() {
+ return (BasicDataSource) initializeBuilder().type(BasicDataSource.class).build();
+ }
+
+}
diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/jdbc/CompositeDataSourceMetadataProviderTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/jdbc/CompositeDataSourceMetadataProviderTests.java
new file mode 100644
index 0000000000..f0a16231e1
--- /dev/null
+++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/jdbc/CompositeDataSourceMetadataProviderTests.java
@@ -0,0 +1,84 @@
+/*
+ * 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.actuate.metrics.jdbc;
+
+import static org.junit.Assert.*;
+import static org.mockito.BDDMockito.*;
+
+import java.util.Arrays;
+
+import javax.sql.DataSource;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ *
+ * @author Stephane Nicoll
+ */
+public class CompositeDataSourceMetadataProviderTests {
+
+ @Mock
+ private DataSourceMetadataProvider firstProvider;
+
+ @Mock
+ private DataSourceMetadata first;
+
+ @Mock
+ private DataSource firstDataSource;
+
+ @Mock
+ private DataSourceMetadataProvider secondProvider;
+
+ @Mock
+ private DataSourceMetadata second;
+
+ @Mock
+ private DataSource secondDataSource;
+
+ @Mock
+ private DataSource unknownDataSource;
+
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ given(firstProvider.getDataSourceMetadata(firstDataSource)).willReturn(first);
+ given(firstProvider.getDataSourceMetadata(secondDataSource)).willReturn(second);
+ }
+
+ @Test
+ public void createWithProviders() {
+ CompositeDataSourceMetadataProvider provider =
+ new CompositeDataSourceMetadataProvider(Arrays.asList(firstProvider, secondProvider));
+ assertSame(first, provider.getDataSourceMetadata(firstDataSource));
+ assertSame(second, provider.getDataSourceMetadata(secondDataSource));
+ assertNull(provider.getDataSourceMetadata(unknownDataSource));
+ }
+
+ @Test
+ public void addProvider() {
+ CompositeDataSourceMetadataProvider provider =
+ new CompositeDataSourceMetadataProvider();
+ assertNull(provider.getDataSourceMetadata(firstDataSource));
+ provider.addDataSourceMetadataProvider(firstProvider);
+ assertSame(first, provider.getDataSourceMetadata(firstDataSource));
+ }
+
+}
diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/jdbc/HikariDataSourceMetadataTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/jdbc/HikariDataSourceMetadataTests.java
new file mode 100644
index 0000000000..287b93396a
--- /dev/null
+++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/jdbc/HikariDataSourceMetadataTests.java
@@ -0,0 +1,47 @@
+/*
+ * 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.actuate.metrics.jdbc;
+
+import com.zaxxer.hikari.HikariDataSource;
+import org.junit.Before;
+
+/**
+ *
+ * @author Stephane Nicoll
+ */
+public class HikariDataSourceMetadataTests extends AbstractDataSourceMetadataTests {
+
+ private HikariDataSourceMetadata dataSourceMetadata;
+
+ @Before
+ public void setup() {
+ this.dataSourceMetadata = createDataSourceMetadata(0, 2);
+ }
+
+ @Override
+ protected HikariDataSourceMetadata getDataSourceMetadata() {
+ return this.dataSourceMetadata;
+ }
+
+ private HikariDataSourceMetadata createDataSourceMetadata(int minSize, int maxSize) {
+ HikariDataSource dataSource = (HikariDataSource) initializeBuilder().type(HikariDataSource.class).build();
+ dataSource.setMinimumIdle(minSize);
+ dataSource.setMaximumPoolSize(maxSize);
+
+ return new HikariDataSourceMetadata(dataSource);
+ }
+}
diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/jdbc/TomcatDataSourceMetadataTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/jdbc/TomcatDataSourceMetadataTests.java
new file mode 100644
index 0000000000..095fdb81fd
--- /dev/null
+++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/jdbc/TomcatDataSourceMetadataTests.java
@@ -0,0 +1,51 @@
+/*
+ * 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.actuate.metrics.jdbc;
+
+import org.apache.tomcat.jdbc.pool.DataSource;
+import org.junit.Before;
+
+/**
+ *
+ * @author Stephane Nicoll
+ */
+public class TomcatDataSourceMetadataTests extends AbstractDataSourceMetadataTests {
+
+ private TomcatDataSourceMetadata dataSourceMetadata;
+
+ @Before
+ public void setup() {
+ this.dataSourceMetadata = createDataSourceMetadata(0, 2);
+ }
+
+ @Override
+ protected TomcatDataSourceMetadata getDataSourceMetadata() {
+ return this.dataSourceMetadata;
+ }
+
+ private TomcatDataSourceMetadata createDataSourceMetadata(int minSize, int maxSize) {
+ DataSource dataSource = (DataSource) initializeBuilder().type(DataSource.class).build();
+ dataSource.setMinIdle(minSize);
+ dataSource.setMaxActive(maxSize);
+
+ // Avoid warnings
+ dataSource.setInitialSize(minSize);
+ dataSource.setMaxIdle(maxSize);
+ return new TomcatDataSourceMetadata(dataSource);
+ }
+
+}
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 5f518295d1..00d66a0d9d 100644
--- a/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc
+++ b/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc
@@ -615,7 +615,10 @@ endpoint you should see a response similar to this:
"threads": 15,
"threads.daemon": 11,
"threads.peak": 15,
- "uptime": 494836
+ "uptime": 494836,
+ "instance.uptime": 489782,
+ "datasource.primary.active": 5,
+ "datasource.primary.usage": 0.25
}
----
@@ -631,7 +634,26 @@ 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 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`).
+
+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
+
+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