Polish DataSourceMetrics code

This commit is contained in:
Phillip Webb 2014-09-01 10:40:16 -07:00
parent 169a46b1aa
commit e17769fc2f
13 changed files with 89 additions and 145 deletions

View File

@ -43,8 +43,8 @@ public class DataSourceMetricsAutoConfiguration {
@Bean
@ConditionalOnBean(DataSourceMetadataProvider.class)
@ConditionalOnMissingBean(DataSourcePublicMetrics.class)
DataSourcePublicMetrics dataSourcePublicMetrics() {
@ConditionalOnMissingBean
public DataSourcePublicMetrics dataSourcePublicMetrics() {
return new DataSourcePublicMetrics();
}

View File

@ -20,6 +20,7 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import javax.annotation.PostConstruct;
import javax.sql.DataSource;
@ -47,88 +48,63 @@ public class DataSourcePublicMetrics implements PublicMetrics {
private ApplicationContext applicationContext;
@Autowired
private Collection<DataSourceMetadataProvider> dataSourceMetadataProviders;
private Collection<DataSourceMetadataProvider> providers;
private final Map<String, DataSourceMetadata> dataSourceMetadataByPrefix = new HashMap<String, DataSourceMetadata>();
private final Map<String, DataSourceMetadata> metadataByPrefix = new HashMap<String, DataSourceMetadata>();
@PostConstruct
public void initialize() {
Map<String, DataSource> dataSources = this.applicationContext
.getBeansOfType(DataSource.class);
DataSource primaryDataSource = getPrimaryDataSource();
DataSourceMetadataProvider provider = new CompositeDataSourceMetadataProvider(
this.dataSourceMetadataProviders);
for (Map.Entry<String, DataSource> entry : dataSources.entrySet()) {
String prefix = createPrefix(entry.getKey(), entry.getValue(), entry
.getValue().equals(primaryDataSource));
DataSourceMetadata dataSourceMetadata = provider.getDataSourceMetadata(entry
.getValue());
this.providers);
for (Map.Entry<String, DataSource> entry : this.applicationContext
.getBeansOfType(DataSource.class).entrySet()) {
String beanName = entry.getKey();
DataSource bean = entry.getValue();
String prefix = createPrefix(beanName, bean, bean.equals(primaryDataSource));
DataSourceMetadata dataSourceMetadata = provider.getDataSourceMetadata(bean);
if (dataSourceMetadata != null) {
this.dataSourceMetadataByPrefix.put(prefix, dataSourceMetadata);
this.metadataByPrefix.put(prefix, dataSourceMetadata);
}
}
}
@Override
public Collection<Metric<?>> metrics() {
Collection<Metric<?>> result = new LinkedHashSet<Metric<?>>();
for (Map.Entry<String, DataSourceMetadata> entry : this.dataSourceMetadataByPrefix
Set<Metric<?>> metrics = new LinkedHashSet<Metric<?>>();
for (Map.Entry<String, DataSourceMetadata> entry : this.metadataByPrefix
.entrySet()) {
String prefix = entry.getKey();
// Make sure the prefix ends with a dot
if (!prefix.endsWith(".")) {
prefix = prefix + ".";
}
prefix = (prefix.endsWith(".") ? prefix : prefix + ".");
DataSourceMetadata dataSourceMetadata = entry.getValue();
Integer poolSize = dataSourceMetadata.getPoolSize();
if (poolSize != null) {
result.add(new Metric<Integer>(prefix + "active", poolSize));
}
Float poolUsage = dataSourceMetadata.getPoolUsage();
if (poolUsage != null) {
result.add(new Metric<Float>(prefix + "usage", poolUsage));
}
addMetric(metrics, prefix + "active", dataSourceMetadata.getPoolSize());
addMetric(metrics, prefix + "usage", dataSourceMetadata.getPoolUsage());
}
return metrics;
}
private <T extends Number> void addMetric(Set<Metric<?>> metrics, String name, T value) {
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}.
* @param dataSourceName the name of the data source bean
* @param name the name of the data source bean
* @param dataSource the data source to configure
* @param primary if this data source is the primary data source
* @return a prefix for the given data source
*/
protected String createPrefix(String dataSourceName, DataSource dataSource,
boolean primary) {
StringBuilder sb = new StringBuilder("datasource.");
protected String createPrefix(String name, DataSource dataSource, boolean primary) {
if (primary) {
sb.append("primary");
return "datasource.primary";
}
else if (endWithDataSource(dataSourceName)) { // Strip the data source part out of
// the name
sb.append(dataSourceName.substring(0, dataSourceName.length()
- DATASOURCE_SUFFIX.length()));
if (name.toLowerCase().endsWith(DATASOURCE_SUFFIX.toLowerCase())) {
name = name.substring(0, name.length() - DATASOURCE_SUFFIX.length());
}
else {
sb.append(dataSourceName);
}
return sb.toString();
}
/**
* Specify if the given value ends with {@code dataSource}.
*/
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;
}
/**
@ -140,7 +116,7 @@ public class DataSourcePublicMetrics implements PublicMetrics {
try {
return this.applicationContext.getBean(DataSource.class);
}
catch (NoSuchBeanDefinitionException e) {
catch (NoSuchBeanDefinitionException ex) {
return null;
}
}

View File

@ -24,38 +24,35 @@ import javax.sql.DataSource;
* @author Stephane Nicoll
* @since 1.2.0
*/
public abstract class AbstractDataSourceMetadata<D extends DataSource> implements
public abstract class AbstractDataSourceMetadata<T extends DataSource> implements
DataSourceMetadata {
private final D dataSource;
private final T dataSource;
/**
* Create an instance with the data source to use.
*/
protected AbstractDataSourceMetadata(D dataSource) {
protected AbstractDataSourceMetadata(T dataSource) {
this.dataSource = dataSource;
}
@Override
public Float getPoolUsage() {
Integer max = getMaxPoolSize();
if (max == null) {
Integer maxSize = getMaxPoolSize();
Integer currentSize = getPoolSize();
if (maxSize == null || currentSize == null) {
return null;
}
if (max < 0) {
if (maxSize < 0) {
return -1F;
}
Integer current = getPoolSize();
if (current == null) {
return null;
}
if (current == 0) {
if (currentSize == 0) {
return 0F;
}
return (float) current / max; // something like that
return (float) currentSize / (float) maxSize;
}
protected final D getDataSource() {
protected final T getDataSource() {
return this.dataSource;
}

View File

@ -16,10 +16,12 @@
package org.springframework.boot.autoconfigure.jdbc;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
/**
* A {@link DataSourceMetadata} implementation for the commons dbcp data source.
* {@link DataSourceMetadata} for a Apache Commons DBCP {@link DataSource}.
*
* @author Stephane Nicoll
* @since 1.2.0

View File

@ -18,6 +18,7 @@ package org.springframework.boot.autoconfigure.jdbc;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.sql.DataSource;
@ -30,30 +31,30 @@ import javax.sql.DataSource;
*/
public class CompositeDataSourceMetadataProvider implements DataSourceMetadataProvider {
private final Collection<DataSourceMetadataProvider> providers;
private final List<DataSourceMetadataProvider> providers;
/**
* Create an instance with an initial collection of delegates to use.
*/
public CompositeDataSourceMetadataProvider(
Collection<DataSourceMetadataProvider> providers) {
this.providers = providers;
}
/**
* Create an instance with no delegate.
* Create a {@link CompositeDataSourceMetadataProvider} instance with no delegate.
*/
public CompositeDataSourceMetadataProvider() {
this(new ArrayList<DataSourceMetadataProvider>());
}
/**
* Create a {@link CompositeDataSourceMetadataProvider} instance with an initial
* collection of delegates to use.
*/
public CompositeDataSourceMetadataProvider(
Collection<? extends DataSourceMetadataProvider> providers) {
this.providers = new ArrayList<DataSourceMetadataProvider>(providers);
}
@Override
public DataSourceMetadata getDataSourceMetadata(DataSource dataSource) {
for (DataSourceMetadataProvider provider : this.providers) {
DataSourceMetadata dataSourceMetadata = provider
.getDataSourceMetadata(dataSource);
if (dataSourceMetadata != null) {
return dataSourceMetadata;
DataSourceMetadata metadata = provider.getDataSourceMetadata(dataSource);
if (metadata != null) {
return metadata;
}
}
return null;

View File

@ -19,8 +19,8 @@ package org.springframework.boot.autoconfigure.jdbc;
import javax.sql.DataSource;
/**
* Provide various metadata regarding a {@link DataSource} that are shared by most data
* source types but not accessible in a standard manner.
* Provides access meta-data that is commonly available from most {@link DataSource}
* implementations.
*
* @author Stephane Nicoll
* @since 1.2.0
@ -28,7 +28,8 @@ import javax.sql.DataSource;
public interface DataSourceMetadata {
/**
* Return the usage of the pool as a double value between 0 and 1.
* Return the usage of the pool as value between 0 and 1 (or -1 if the pool is not
* limited).
* <ul>
* <li>1 means that the maximum number of connections have been allocated</li>
* <li>0 means that no connection is currently active</li>

View File

@ -16,14 +16,15 @@
package org.springframework.boot.autoconfigure.jdbc;
import org.springframework.beans.BeansException;
import javax.sql.DataSource;
import org.springframework.beans.DirectFieldAccessor;
import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.pool.HikariPool;
/**
* A {@link DataSourceMetadata} implementation for the hikari data source.
* {@link DataSourceMetadata} for a Hikari {@link DataSource}.
*
* @author Stephane Nicoll
* @since 1.2.0
@ -31,20 +32,23 @@ import com.zaxxer.hikari.pool.HikariPool;
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 = this.hikariPoolProvider.getHikariPool();
if (hikariPool != null) {
return hikariPool.getActiveConnections();
try {
return getHikariPool().getActiveConnections();
}
return null;
catch (Exception ex) {
return null;
}
}
private HikariPool getHikariPool() {
return (HikariPool) new DirectFieldAccessor(getDataSource())
.getPropertyValue("pool");
}
@Override
@ -62,50 +66,4 @@ public class HikariDataSourceMetadata extends
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 (!this.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");
}
}
}

View File

@ -20,8 +20,7 @@ import org.apache.tomcat.jdbc.pool.ConnectionPool;
import org.apache.tomcat.jdbc.pool.DataSource;
/**
*
* A {@link DataSourceMetadata} implementation for the tomcat data source.
* {@link DataSourceMetadata} for a Tomcat {@link DataSource}.
*
* @author Stephane Nicoll
*/

View File

@ -27,9 +27,11 @@ import org.springframework.jdbc.core.JdbcTemplate;
import static org.junit.Assert.assertEquals;
/**
* Abstract base class for {@link DataSourceMetadata} tests.
*
* @author Stephane Nicoll
*/
public abstract class AbstractDataSourceMetadataTests<D extends AbstractDataSourceMetadata> {
public abstract class AbstractDataSourceMetadataTests<D extends AbstractDataSourceMetadata<?>> {
/**
* Return a data source metadata instance with a min size of 0 and max size of 2.

View File

@ -24,6 +24,8 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
/**
* Tests for {@link CommonsDbcpDataSourceMetadata}.
*
* @author Stephane Nicoll
*/
public class CommonsDbcpDataSourceMetadataTests extends

View File

@ -30,6 +30,8 @@ import static org.junit.Assert.assertSame;
import static org.mockito.BDDMockito.given;
/**
* Tests for {@link CompositeDataSourceMetadataProvider}.
*
* @author Stephane Nicoll
*/
public class CompositeDataSourceMetadataProviderTests {

View File

@ -23,6 +23,8 @@ import com.zaxxer.hikari.HikariDataSource;
import static org.junit.Assert.assertEquals;
/**
* Tests for {@link HikariDataSourceMetadata}.
*
* @author Stephane Nicoll
*/
public class HikariDataSourceMetadataTests extends

View File

@ -22,6 +22,8 @@ import org.junit.Before;
import static org.junit.Assert.assertEquals;
/**
* Tests for {@link TomcatDataSourceMetadata}.
*
* @author Stephane Nicoll
*/
public class TomcatDataSourceMetadataTests extends