Support Tomcat's high-performance connection pool

In the process, refactored code towards allowing user-contributable
pool implementation.
This commit is contained in:
Ramnivas Laddad
2013-10-21 19:09:25 -07:00
parent 0ade97ee41
commit a1ec1f6134
8 changed files with 222 additions and 37 deletions

View File

@@ -84,6 +84,12 @@
<version>${tomcat.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-dbcp</artifactId>
<version>${tomcat.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>

View File

@@ -0,0 +1,31 @@
package org.springframework.cloud.service.relational;
import static org.springframework.cloud.service.Util.hasClass;
import javax.sql.DataSource;
import org.springframework.cloud.service.ServiceConnectorConfig;
import org.springframework.cloud.service.common.RelationalServiceInfo;
/**
*
* @author Ramnivas Laddad
*
* @param <SI>
*/
public class BasicDbcpPooledDataSourceCreator<SI extends RelationalServiceInfo> extends DbcpLikePooledDataSourceCreator<SI> {
@Override
public DataSource create(RelationalServiceInfo serviceInfo, ServiceConnectorConfig serviceConnectorConfig,
String driverClassName, String validationQuery) {
if (hasClass("org.apache.commons.dbcp.BasicDataSource")) {
logger.info("Found DBCP on the classpath. Using it for DataSource connection pooling.");
org.apache.commons.dbcp.BasicDataSource ds = new org.apache.commons.dbcp.BasicDataSource();
setBasicDataSourceProperties(ds, serviceInfo, serviceConnectorConfig, driverClassName, validationQuery);
return ds;
} else {
return null;
}
}
}

View File

@@ -1,15 +1,12 @@
package org.springframework.cloud.service.relational;
import static org.springframework.cloud.service.Util.hasClass;
import java.sql.DriverManager;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import javax.sql.DataSource;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.cloud.Cloud;
import org.springframework.cloud.service.AbstractServiceConnectorCreator;
import org.springframework.cloud.service.ServiceConnectorConfig;
import org.springframework.cloud.service.ServiceConnectorCreationException;
@@ -24,52 +21,41 @@ import org.springframework.jdbc.datasource.SimpleDriverDataSource;
*/
public abstract class DataSourceCreator<SI extends RelationalServiceInfo> extends AbstractServiceConnectorCreator<DataSource, SI> {
private static Logger logger = Logger.getLogger(DataSourceCreator.class.getName());
private DataSourceConfigurer configurer = new DataSourceConfigurer();
protected static Logger logger = Logger.getLogger(DataSourceCreator.class.getName());
public abstract String getDriverClassName();
public abstract String getValidationQuery();
private List<PooledDataSourceCreator<SI>> pooledDataSourceCreators = new ArrayList<PooledDataSourceCreator<SI>>();
public DataSourceCreator() {
if (pooledDataSourceCreators.size() == 0) {
pooledDataSourceCreators.add(new BasicDbcpPooledDataSourceCreator<SI>());
pooledDataSourceCreators.add(new TomcatDbcpPooledDataSourceCreator<SI>());
pooledDataSourceCreators.add(new TomcatHighPerformancePooledDataSourceCreator<SI>());
}
}
@Override
public DataSource create(SI serviceInfo, ServiceConnectorConfig serviceConnectorConfig) {
try {
Class.forName(getDriverClassName());
// Give first preference to user's DBCP datasource
if (hasClass("org.apache.commons.dbcp.BasicDataSource")) {
logger.info("Found DBCP on the classpath. Using it for DataSource connection pooling.");
org.apache.commons.dbcp.BasicDataSource ds = new org.apache.commons.dbcp.BasicDataSource();
setBasicDataSourceProperties(ds, serviceInfo, serviceConnectorConfig);
return ds;
// else, we have one from Tomcat
} else if (hasClass("org.apache.tomcat.jdbc.pool.DataSource")) {
logger.info("Found Tomcat connection pool on the classpath. Using it for DataSource connection pooling.");
org.apache.tomcat.jdbc.pool.DataSource ds = new org.apache.tomcat.jdbc.pool.DataSource();
setBasicDataSourceProperties(ds, serviceInfo, serviceConnectorConfig);
return ds;
} else {
// Only for testing outside Tomcat/CloudFoundry
logger.warning("Found neither DBCP nor Tomcat connection pool on the classpath (no pooling in effect)");
return new SimpleDriverDataSource(DriverManager.getDriver(serviceInfo.getJdbcUrl()),
serviceInfo.getJdbcUrl());
for (PooledDataSourceCreator<SI> delegate: pooledDataSourceCreators) {
DataSource ds = delegate.create(serviceInfo, serviceConnectorConfig, getDriverClassName(), getValidationQuery());
if (ds != null) {
return ds;
}
}
// Only for testing outside Tomcat/CloudFoundry
logger.warning("Found neither DBCP nor Tomcat connection pool on the classpath (no pooling is in effect).");
return new SimpleDriverDataSource(DriverManager.getDriver(serviceInfo.getJdbcUrl()), serviceInfo.getJdbcUrl());
} catch (Exception e) {
throw new ServiceConnectorCreationException(
"Failed to created cloud datasource for "
+ serviceInfo.getId() + " service", e);
}
}
private void setBasicDataSourceProperties(DataSource basicDataSource, RelationalServiceInfo serviceInfo,
ServiceConnectorConfig serviceConnectorConfig) {
BeanWrapper target = new BeanWrapperImpl(basicDataSource);
target.setPropertyValue("driverClassName", getDriverClassName());
target.setPropertyValue("url", serviceInfo.getJdbcUrl());
if (getValidationQuery() != null) {
target.setPropertyValue("validationQuery", getValidationQuery());
target.setPropertyValue("testOnBorrow", true);
}
configurer.configure(basicDataSource, (DataSourceConfig)serviceConnectorConfig);
}
}

View File

@@ -0,0 +1,38 @@
package org.springframework.cloud.service.relational;
import java.util.logging.Logger;
import javax.sql.DataSource;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.cloud.service.ServiceConnectorConfig;
import org.springframework.cloud.service.common.RelationalServiceInfo;
/**
* Common implementation that assumes DBCP connection pool properties.
*
* @author Ramnivas Laddad
*
* @param <SI>
*/
public abstract class DbcpLikePooledDataSourceCreator<SI extends RelationalServiceInfo> implements PooledDataSourceCreator<SI> {
protected static Logger logger = Logger.getLogger(PooledDataSourceCreator.class.getName());
private DataSourceConfigurer configurer = new DataSourceConfigurer();
protected void setBasicDataSourceProperties(DataSource basicDataSource, RelationalServiceInfo serviceInfo,
ServiceConnectorConfig serviceConnectorConfig,
String driverClassName, String validationQuery) {
BeanWrapper target = new BeanWrapperImpl(basicDataSource);
target.setPropertyValue("driverClassName", driverClassName);
target.setPropertyValue("url", serviceInfo.getJdbcUrl());
if (validationQuery != null) {
target.setPropertyValue("validationQuery", validationQuery);
target.setPropertyValue("testOnBorrow", true);
}
configurer.configure(basicDataSource, (DataSourceConfig)serviceConnectorConfig);
}
}

View File

@@ -0,0 +1,19 @@
package org.springframework.cloud.service.relational;
import javax.sql.DataSource;
import org.springframework.cloud.service.ServiceConnectorConfig;
import org.springframework.cloud.service.common.RelationalServiceInfo;
/**
* DataSource creator that produces a pooled connection
*
* @author Ramnivas Laddad
*
* @param <SI>
*/
public interface PooledDataSourceCreator<SI extends RelationalServiceInfo> {
public abstract DataSource create(RelationalServiceInfo serviceInfo, ServiceConnectorConfig serviceConnectorConfig,
String driverClassName, String validationQuery);
}

View File

@@ -0,0 +1,31 @@
package org.springframework.cloud.service.relational;
import static org.springframework.cloud.service.Util.hasClass;
import javax.sql.DataSource;
import org.springframework.cloud.service.ServiceConnectorConfig;
import org.springframework.cloud.service.common.RelationalServiceInfo;
/**
*
* @author Ramnivas Laddad
*
* @param <SI>
*/
public class TomcatDbcpPooledDataSourceCreator<SI extends RelationalServiceInfo> extends DbcpLikePooledDataSourceCreator<SI> {
@Override
public DataSource create(RelationalServiceInfo serviceInfo, ServiceConnectorConfig serviceConnectorConfig,
String driverClassName, String validationQuery) {
if (hasClass("org.apache.tomcat.dbcp.dbcp.BasicDataSource")) {
logger.info("Found Tomcat dbcp connection pool on the classpath. Using it for DataSource connection pooling.");
org.apache.tomcat.dbcp.dbcp.BasicDataSource ds = new org.apache.tomcat.dbcp.dbcp.BasicDataSource();
setBasicDataSourceProperties(ds, serviceInfo, serviceConnectorConfig, driverClassName, validationQuery);
return ds;
} else {
return null;
}
}
}

View File

@@ -0,0 +1,32 @@
package org.springframework.cloud.service.relational;
import static org.springframework.cloud.service.Util.hasClass;
import javax.sql.DataSource;
import org.springframework.cloud.service.ServiceConnectorConfig;
import org.springframework.cloud.service.common.RelationalServiceInfo;
/**
*
* @author Ramnivas Laddad
*
* @param <SI>
*/
public class TomcatHighPerformancePooledDataSourceCreator<SI extends RelationalServiceInfo>
extends DbcpLikePooledDataSourceCreator<SI> {
@Override
public DataSource create(RelationalServiceInfo serviceInfo, ServiceConnectorConfig serviceConnectorConfig,
String driverClassName, String validationQuery) {
if (hasClass("org.apache.tomcat.jdbc.pool.DataSource")) {
logger.info("Found Tomcat connection pool on the classpath. Using it for DataSource connection pooling.");
org.apache.tomcat.jdbc.pool.DataSource ds = new org.apache.tomcat.jdbc.pool.DataSource();
setBasicDataSourceProperties(ds, serviceInfo, serviceConnectorConfig, driverClassName, validationQuery);
return ds;
} else {
return null;
}
}
}

View File

@@ -0,0 +1,42 @@
package org.springframework.cloud.service.relational;
import java.util.ArrayList;
import java.util.List;
import javax.sql.DataSource;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.cloud.service.common.MysqlServiceInfo;
public class PooledDataSourceCreatorsTest {
@Mock private MysqlServiceInfo mockMysqlServiceInfo;
// Just to grab driver class name and validation query string
private MysqlDataSourceCreator mysqlDataSourceCreator = new MysqlDataSourceCreator();
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
@Test
public void pooledDataSourceCreation() {
List<PooledDataSourceCreator<MysqlServiceInfo>> pooledDataSourceCreators = new ArrayList<PooledDataSourceCreator<MysqlServiceInfo>>();
pooledDataSourceCreators.add(new BasicDbcpPooledDataSourceCreator<MysqlServiceInfo>());
pooledDataSourceCreators.add(new TomcatDbcpPooledDataSourceCreator<MysqlServiceInfo>());
pooledDataSourceCreators.add(new TomcatHighPerformancePooledDataSourceCreator<MysqlServiceInfo>());
for (PooledDataSourceCreator<MysqlServiceInfo> testCreator : pooledDataSourceCreators) {
DataSource ds = testCreator.create(mockMysqlServiceInfo, null,
mysqlDataSourceCreator.getDriverClassName(),
mysqlDataSourceCreator.getValidationQuery());
Assert.assertNotNull(ds);
}
}
}