All clients to customize the order of pooled DataSource implmentations in Spring connector.
This commit is contained in:
@@ -14,16 +14,18 @@ import org.springframework.cloud.service.common.RelationalServiceInfo;
|
||||
* @param <SI> the {@link RelationalServiceInfo} type for the underlying database service
|
||||
*/
|
||||
public class BasicDbcpPooledDataSourceCreator<SI extends RelationalServiceInfo> extends DbcpLikePooledDataSourceCreator<SI> {
|
||||
public static final String DBCP2_BASIC_DATASOURCE = "org.apache.commons.dbcp2.BasicDataSource";
|
||||
public static final String DBCP_BASIC_DATASOURCE = "org.apache.commons.dbcp.BasicDataSource";
|
||||
|
||||
@Override
|
||||
public DataSource create(RelationalServiceInfo serviceInfo, ServiceConnectorConfig serviceConnectorConfig,
|
||||
String driverClassName, String validationQuery) {
|
||||
if (hasClass("org.apache.commons.dbcp2.BasicDataSource")) {
|
||||
if (hasClass(DBCP2_BASIC_DATASOURCE)) {
|
||||
logger.info("Found DBCP2 on the classpath. Using it for DataSource connection pooling.");
|
||||
org.apache.commons.dbcp2.BasicDataSource ds = new org.apache.commons.dbcp2.BasicDataSource();
|
||||
setBasicDataSourceProperties(ds, serviceInfo, serviceConnectorConfig, driverClassName, validationQuery);
|
||||
return ds;
|
||||
} else if (hasClass("org.apache.commons.dbcp.BasicDataSource")) {
|
||||
} else if (hasClass(DBCP_BASIC_DATASOURCE)) {
|
||||
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);
|
||||
|
||||
@@ -2,23 +2,40 @@ package org.springframework.cloud.service.relational;
|
||||
|
||||
import org.springframework.cloud.service.PooledServiceConnectorConfig;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Ramnivas Laddad
|
||||
*
|
||||
*/
|
||||
public class DataSourceConfig extends PooledServiceConnectorConfig {
|
||||
private ConnectionConfig connectionConfig;
|
||||
private final ConnectionConfig connectionConfig;
|
||||
private final List<String> pooledDataSourceNames;
|
||||
|
||||
public DataSourceConfig(PoolConfig poolConfig, ConnectionConfig connectionConfig) {
|
||||
this(poolConfig, connectionConfig, null);
|
||||
}
|
||||
|
||||
public DataSourceConfig(List<String> pooledDataSourceNames) {
|
||||
this(null, null, pooledDataSourceNames);
|
||||
}
|
||||
|
||||
public DataSourceConfig(PoolConfig poolConfig, ConnectionConfig connectionConfig,
|
||||
List<String> pooledDataSourceNames) {
|
||||
super(poolConfig);
|
||||
this.connectionConfig = connectionConfig;
|
||||
this.pooledDataSourceNames = pooledDataSourceNames;
|
||||
}
|
||||
|
||||
|
||||
public ConnectionConfig getConnectionConfiguration() {
|
||||
return connectionConfig;
|
||||
}
|
||||
|
||||
|
||||
public List<String> getPooledDataSourceNames() {
|
||||
return pooledDataSourceNames;
|
||||
}
|
||||
|
||||
public static class ConnectionConfig {
|
||||
private String prop;
|
||||
|
||||
|
||||
@@ -2,7 +2,10 @@ package org.springframework.cloud.service.relational;
|
||||
|
||||
import java.sql.DriverManager;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
@@ -28,7 +31,8 @@ public abstract class DataSourceCreator<SI extends RelationalServiceInfo> extend
|
||||
private String[] driverClasses;
|
||||
private String validationQuery;
|
||||
|
||||
private List<PooledDataSourceCreator<SI>> pooledDataSourceCreators = new ArrayList<PooledDataSourceCreator<SI>>();
|
||||
private Map<String, PooledDataSourceCreator<SI>> pooledDataSourceCreators =
|
||||
new LinkedHashMap<String, PooledDataSourceCreator<SI>>();
|
||||
|
||||
public DataSourceCreator(String driverSystemPropKey, String[] driverClasses, String validationQuery) {
|
||||
this.driverSystemPropKey = driverSystemPropKey;
|
||||
@@ -36,33 +40,66 @@ public abstract class DataSourceCreator<SI extends RelationalServiceInfo> extend
|
||||
this.validationQuery = validationQuery;
|
||||
|
||||
if (pooledDataSourceCreators.size() == 0) {
|
||||
pooledDataSourceCreators.add(new BasicDbcpPooledDataSourceCreator<SI>());
|
||||
pooledDataSourceCreators.add(new TomcatDbcpPooledDataSourceCreator<SI>());
|
||||
pooledDataSourceCreators.add(new TomcatHighPerformancePooledDataSourceCreator<SI>());
|
||||
pooledDataSourceCreators.add(new HikariCpPooledDataSourceCreator<SI>());
|
||||
putPooledDataSourceCreator(new BasicDbcpPooledDataSourceCreator<SI>());
|
||||
putPooledDataSourceCreator(new TomcatDbcpPooledDataSourceCreator<SI>());
|
||||
putPooledDataSourceCreator(new TomcatJdbcPooledDataSourceCreator<SI>());
|
||||
putPooledDataSourceCreator(new HikariCpPooledDataSourceCreator<SI>());
|
||||
}
|
||||
}
|
||||
|
||||
private void putPooledDataSourceCreator(PooledDataSourceCreator<SI> pooledDataSourceCreator) {
|
||||
pooledDataSourceCreators.put(pooledDataSourceCreator.getClass().getSimpleName(), pooledDataSourceCreator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataSource create(SI serviceInfo, ServiceConnectorConfig serviceConnectorConfig) {
|
||||
try {
|
||||
for (PooledDataSourceCreator<SI> delegate: pooledDataSourceCreators) {
|
||||
DataSource ds = delegate.create(serviceInfo, serviceConnectorConfig, getDriverClassName(serviceInfo), validationQuery);
|
||||
|
||||
if (ds != null) {
|
||||
return ds;
|
||||
}
|
||||
DataSource ds = createPooledDataSource(serviceInfo, serviceConnectorConfig);
|
||||
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).");
|
||||
logger.warning("No connection pooling DataSource implementation found 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);
|
||||
"Failed to created cloud datasource for " + serviceInfo.getId() + " service", e);
|
||||
}
|
||||
}
|
||||
|
||||
private DataSource createPooledDataSource(SI serviceInfo, ServiceConnectorConfig serviceConnectorConfig) {
|
||||
Collection<PooledDataSourceCreator<SI>> delegates = filterPooledDataSourceCreators(serviceConnectorConfig);
|
||||
|
||||
for (PooledDataSourceCreator<SI> delegate : delegates) {
|
||||
DataSource ds = delegate.create(serviceInfo, serviceConnectorConfig, getDriverClassName(serviceInfo), validationQuery);
|
||||
if (ds != null) {
|
||||
return ds;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Collection<PooledDataSourceCreator<SI>> filterPooledDataSourceCreators(ServiceConnectorConfig serviceConnectorConfig) {
|
||||
if (serviceConnectorConfig != null) {
|
||||
List<String> pooledDataSourceNames = ((DataSourceConfig) serviceConnectorConfig).getPooledDataSourceNames();
|
||||
if (pooledDataSourceNames != null) {
|
||||
List<PooledDataSourceCreator<SI>> filtered = new ArrayList<PooledDataSourceCreator<SI>>();
|
||||
|
||||
for (String name : pooledDataSourceNames) {
|
||||
for (String key : pooledDataSourceCreators.keySet()) {
|
||||
if (key.contains(name)) {
|
||||
filtered.add(pooledDataSourceCreators.get(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return filtered;
|
||||
}
|
||||
}
|
||||
return pooledDataSourceCreators.values();
|
||||
}
|
||||
|
||||
public String getDriverClassName(SI serviceInfo) {
|
||||
String userSpecifiedDriver = System.getProperty(driverSystemPropKey);
|
||||
|
||||
|
||||
@@ -35,9 +35,9 @@ public abstract class DbcpLikePooledDataSourceCreator<SI extends RelationalServi
|
||||
}
|
||||
|
||||
if (serviceConnectorConfig == null) {
|
||||
// choose sensible values so that we set max connection pool size to what
|
||||
// free tier services on Cloud Foundry and Heroku allow
|
||||
serviceConnectorConfig = new DataSourceConfig(new PoolConfig(4, 30000), null);
|
||||
// choose sensible values so that we set max connection pool size to what
|
||||
// free tier services on Cloud Foundry and Heroku allow
|
||||
serviceConnectorConfig = new DataSourceConfig(new PoolConfig(4, 30000), null);
|
||||
}
|
||||
configurer.configure(basicDataSource, (DataSourceConfig)serviceConnectorConfig);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ public class HikariCpPooledDataSourceCreator<SI extends RelationalServiceInfo> i
|
||||
|
||||
protected static Logger logger = Logger.getLogger(PooledDataSourceCreator.class.getName());
|
||||
|
||||
private static final String HIKARI_CLASSNAME = "com.zaxxer.hikari.HikariDataSource";
|
||||
public static final String HIKARI_DATASOURCE = "com.zaxxer.hikari.HikariDataSource";
|
||||
|
||||
private DataSourceConfigurer configurer = new DataSourceConfigurer();
|
||||
|
||||
@@ -37,7 +37,7 @@ public class HikariCpPooledDataSourceCreator<SI extends RelationalServiceInfo> i
|
||||
@Override
|
||||
public DataSource create(RelationalServiceInfo serviceInfo, ServiceConnectorConfig serviceConnectorConfig,
|
||||
String driverClassName, String validationQuery) {
|
||||
if (hasClass(HIKARI_CLASSNAME)) {
|
||||
if (hasClass(HIKARI_DATASOURCE)) {
|
||||
logger.info("Found HikariCP on the classpath. Using it for DataSource connection pooling.");
|
||||
HikariDataSource ds = new HikariDataSource();
|
||||
setBasicDataSourceProperties(ds, serviceInfo, serviceConnectorConfig, driverClassName, validationQuery);
|
||||
|
||||
@@ -23,10 +23,10 @@ public class TomcatDbcpPooledDataSourceCreator<SI extends RelationalServiceInfo>
|
||||
public DataSource create(RelationalServiceInfo serviceInfo, ServiceConnectorConfig serviceConnectorConfig,
|
||||
String driverClassName, String validationQuery) {
|
||||
if (hasClass(TOMCAT_7_DBCP)) {
|
||||
logger.info("Found Tomcat 7 dbcp connection pool on the classpath. Using it for DataSource connection pooling.");
|
||||
logger.info("Found Tomcat 7 DBCP connection pool on the classpath. Using it for DataSource connection pooling.");
|
||||
return createDataSource(TOMCAT_7_DBCP, serviceInfo, serviceConnectorConfig, driverClassName, validationQuery);
|
||||
} else if (hasClass(TOMCAT_8_DBCP)) {
|
||||
logger.info("Found Tomcat 8 dbcp connection pool on the classpath. Using it for DataSource connection pooling.");
|
||||
logger.info("Found Tomcat 8 DBCP connection pool on the classpath. Using it for DataSource connection pooling.");
|
||||
return createDataSource(TOMCAT_8_DBCP, serviceInfo, serviceConnectorConfig, driverClassName, validationQuery);
|
||||
} else {
|
||||
return null;
|
||||
@@ -41,7 +41,7 @@ public class TomcatDbcpPooledDataSourceCreator<SI extends RelationalServiceInfo>
|
||||
setBasicDataSourceProperties(dataSource, serviceInfo, serviceConnectorConfig, driverClassName, validationQuery);
|
||||
return dataSource;
|
||||
} catch (Throwable e) {
|
||||
throw new ServiceConnectorCreationException("Error instantiating Tomcat dbcp connection pool", e);
|
||||
throw new ServiceConnectorCreationException("Error instantiating Tomcat DBCP connection pool", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,14 +11,16 @@ import org.springframework.cloud.service.common.RelationalServiceInfo;
|
||||
*
|
||||
* @author Ramnivas Laddad
|
||||
*/
|
||||
public class TomcatHighPerformancePooledDataSourceCreator<SI extends RelationalServiceInfo>
|
||||
public class TomcatJdbcPooledDataSourceCreator<SI extends RelationalServiceInfo>
|
||||
extends DbcpLikePooledDataSourceCreator<SI> {
|
||||
|
||||
public static final String TOMCAT_JDBC_DATASOURCE = "org.apache.tomcat.jdbc.pool.DataSource";
|
||||
|
||||
@Override
|
||||
public DataSource create(RelationalServiceInfo serviceInfo, ServiceConnectorConfig serviceConnectorConfig,
|
||||
String driverClassName, String validationQuery) {
|
||||
if (hasClass("org.apache.tomcat.jdbc.pool.DataSource")) {
|
||||
logger.info("Found Tomcat high-performance connection pool on the classpath. Using it for DataSource connection pooling.");
|
||||
if (hasClass(TOMCAT_JDBC_DATASOURCE)) {
|
||||
logger.info("Found Tomcat JDBC 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;
|
||||
@@ -2,19 +2,26 @@ package org.springframework.cloud.service.relational;
|
||||
|
||||
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.ServiceConnectorConfig;
|
||||
import org.springframework.cloud.service.common.MysqlServiceInfo;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.hamcrest.core.IsInstanceOf.instanceOf;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.cloud.service.Util.hasClass;
|
||||
import static org.springframework.cloud.service.relational.BasicDbcpPooledDataSourceCreator.DBCP2_BASIC_DATASOURCE;
|
||||
import static org.springframework.cloud.service.relational.BasicDbcpPooledDataSourceCreator.DBCP_BASIC_DATASOURCE;
|
||||
import static org.springframework.cloud.service.relational.HikariCpPooledDataSourceCreator.HIKARI_DATASOURCE;
|
||||
import static org.springframework.cloud.service.relational.TomcatDbcpPooledDataSourceCreator.TOMCAT_7_DBCP;
|
||||
import static org.springframework.cloud.service.relational.TomcatDbcpPooledDataSourceCreator.TOMCAT_8_DBCP;
|
||||
import static org.springframework.cloud.service.relational.TomcatJdbcPooledDataSourceCreator.TOMCAT_JDBC_DATASOURCE;
|
||||
|
||||
public class PooledDataSourceCreatorsTest {
|
||||
@Mock private MysqlServiceInfo mockMysqlServiceInfo;
|
||||
@@ -25,16 +32,45 @@ public class PooledDataSourceCreatorsTest {
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
when(mockMysqlServiceInfo.getJdbcUrl()).thenReturn("jdbc:mysql://myuser:mypassword@10.20.30.40:3306/database-123");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pooledDataSourceCreationDbcp() {
|
||||
assertPooledDataSource(new BasicDbcpPooledDataSourceCreator<MysqlServiceInfo>());
|
||||
public void pooledDataSourceCreationDefault() throws Exception {
|
||||
DataSource ds = createMysqlDataSource(null);
|
||||
assertBasicDbcpDataSource(ds);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pooledDataSourceCreationDbcp() throws Exception {
|
||||
DataSource ds = createMysqlDataSourceWithPooledName("BasicDbcp");
|
||||
assertBasicDbcpDataSource(ds);
|
||||
|
||||
ds = createMysqlDataSourceWithPooledName(BasicDbcpPooledDataSourceCreator.class.getSimpleName());
|
||||
assertBasicDbcpDataSource(ds);
|
||||
}
|
||||
|
||||
private void assertBasicDbcpDataSource(DataSource ds) throws ClassNotFoundException {
|
||||
assertTrue(hasClass(DBCP2_BASIC_DATASOURCE) || hasClass(DBCP_BASIC_DATASOURCE));
|
||||
|
||||
if (hasClass(DBCP2_BASIC_DATASOURCE)) {
|
||||
assertThat(ds, instanceOf(Class.forName(DBCP2_BASIC_DATASOURCE)));
|
||||
}
|
||||
if (hasClass(DBCP_BASIC_DATASOURCE) && !hasClass(DBCP2_BASIC_DATASOURCE)) {
|
||||
assertThat(ds, instanceOf(Class.forName(DBCP_BASIC_DATASOURCE)));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pooledDataSourceCreationTomcatDbcp() throws Exception {
|
||||
DataSource ds = assertPooledDataSource(new TomcatDbcpPooledDataSourceCreator<MysqlServiceInfo>());
|
||||
DataSource ds = createMysqlDataSourceWithPooledName("TomcatDbcp");
|
||||
assertTomcatDbcpDataSource(ds);
|
||||
|
||||
ds = createMysqlDataSourceWithPooledName(TomcatDbcpPooledDataSourceCreator.class.getSimpleName());
|
||||
assertTomcatDbcpDataSource(ds);
|
||||
}
|
||||
|
||||
private void assertTomcatDbcpDataSource(DataSource ds) throws ClassNotFoundException {
|
||||
assertTrue(hasClass(TOMCAT_7_DBCP) || hasClass(TOMCAT_8_DBCP));
|
||||
|
||||
if (hasClass(TOMCAT_7_DBCP)) {
|
||||
@@ -46,22 +82,35 @@ public class PooledDataSourceCreatorsTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pooledDataSourceCreationTomcatHighPerformance() {
|
||||
assertPooledDataSource(new TomcatHighPerformancePooledDataSourceCreator<MysqlServiceInfo>());
|
||||
public void pooledDataSourceCreationTomcatJdbc() throws Exception {
|
||||
DataSource ds = createMysqlDataSourceWithPooledName("TomcatJdbc");
|
||||
assertThat(ds, instanceOf(Class.forName(TOMCAT_JDBC_DATASOURCE)));
|
||||
|
||||
ds = createMysqlDataSourceWithPooledName(TomcatJdbcPooledDataSourceCreator.class.getSimpleName());
|
||||
assertThat(ds, instanceOf(Class.forName(TOMCAT_JDBC_DATASOURCE)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pooledDataSourceCreationHikariCP() {
|
||||
assertPooledDataSource(new HikariCpPooledDataSourceCreator<MysqlServiceInfo>());
|
||||
public void pooledDataSourceCreationHikariCP() throws Exception {
|
||||
DataSource ds = createMysqlDataSourceWithPooledName("HikariCp");
|
||||
assertThat(ds, instanceOf(Class.forName(HIKARI_DATASOURCE)));
|
||||
|
||||
ds = createMysqlDataSourceWithPooledName(HikariCpPooledDataSourceCreator.class.getSimpleName());
|
||||
assertThat(ds, instanceOf(Class.forName(HIKARI_DATASOURCE)));
|
||||
}
|
||||
|
||||
private DataSource assertPooledDataSource(PooledDataSourceCreator<MysqlServiceInfo> testCreator) {
|
||||
DataSource ds = testCreator.create(mockMysqlServiceInfo, null,
|
||||
mysqlDataSourceCreator.getDriverClassName(mockMysqlServiceInfo),
|
||||
"select 1");
|
||||
@Test
|
||||
public void pooledDataSourceCreationInvalid() throws Exception {
|
||||
DataSource ds = createMysqlDataSourceWithPooledName("Dummy");
|
||||
assertThat(ds, instanceOf(org.springframework.jdbc.datasource.SimpleDriverDataSource.class));
|
||||
}
|
||||
|
||||
Assert.assertNotNull("Failed to create datasource with " + testCreator.getClass().getSimpleName(), ds);
|
||||
private DataSource createMysqlDataSourceWithPooledName(String pooledDataSourceName) {
|
||||
DataSourceConfig config = new DataSourceConfig(Collections.singletonList(pooledDataSourceName));
|
||||
return createMysqlDataSource(config);
|
||||
}
|
||||
|
||||
return ds;
|
||||
private DataSource createMysqlDataSource(ServiceConnectorConfig config) {
|
||||
return mysqlDataSourceCreator.create(mockMysqlServiceInfo, config);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user