diff --git a/spring-cloud-spring-service-connector/src/main/java/org/springframework/cloud/service/relational/BasicDbcpPooledDataSourceCreator.java b/spring-cloud-spring-service-connector/src/main/java/org/springframework/cloud/service/relational/BasicDbcpPooledDataSourceCreator.java index 0eb5d37..2e78dc4 100644 --- a/spring-cloud-spring-service-connector/src/main/java/org/springframework/cloud/service/relational/BasicDbcpPooledDataSourceCreator.java +++ b/spring-cloud-spring-service-connector/src/main/java/org/springframework/cloud/service/relational/BasicDbcpPooledDataSourceCreator.java @@ -14,16 +14,18 @@ import org.springframework.cloud.service.common.RelationalServiceInfo; * @param the {@link RelationalServiceInfo} type for the underlying database service */ public class BasicDbcpPooledDataSourceCreator extends DbcpLikePooledDataSourceCreator { + 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); diff --git a/spring-cloud-spring-service-connector/src/main/java/org/springframework/cloud/service/relational/DataSourceConfig.java b/spring-cloud-spring-service-connector/src/main/java/org/springframework/cloud/service/relational/DataSourceConfig.java index ac118a7..b19d538 100644 --- a/spring-cloud-spring-service-connector/src/main/java/org/springframework/cloud/service/relational/DataSourceConfig.java +++ b/spring-cloud-spring-service-connector/src/main/java/org/springframework/cloud/service/relational/DataSourceConfig.java @@ -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 pooledDataSourceNames; public DataSourceConfig(PoolConfig poolConfig, ConnectionConfig connectionConfig) { + this(poolConfig, connectionConfig, null); + } + + public DataSourceConfig(List pooledDataSourceNames) { + this(null, null, pooledDataSourceNames); + } + + public DataSourceConfig(PoolConfig poolConfig, ConnectionConfig connectionConfig, + List pooledDataSourceNames) { super(poolConfig); this.connectionConfig = connectionConfig; + this.pooledDataSourceNames = pooledDataSourceNames; } - + public ConnectionConfig getConnectionConfiguration() { return connectionConfig; } - + + public List getPooledDataSourceNames() { + return pooledDataSourceNames; + } + public static class ConnectionConfig { private String prop; diff --git a/spring-cloud-spring-service-connector/src/main/java/org/springframework/cloud/service/relational/DataSourceCreator.java b/spring-cloud-spring-service-connector/src/main/java/org/springframework/cloud/service/relational/DataSourceCreator.java index 56cbc01..b5fb936 100644 --- a/spring-cloud-spring-service-connector/src/main/java/org/springframework/cloud/service/relational/DataSourceCreator.java +++ b/spring-cloud-spring-service-connector/src/main/java/org/springframework/cloud/service/relational/DataSourceCreator.java @@ -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 extend private String[] driverClasses; private String validationQuery; - private List> pooledDataSourceCreators = new ArrayList>(); + private Map> pooledDataSourceCreators = + new LinkedHashMap>(); public DataSourceCreator(String driverSystemPropKey, String[] driverClasses, String validationQuery) { this.driverSystemPropKey = driverSystemPropKey; @@ -36,33 +40,66 @@ public abstract class DataSourceCreator extend this.validationQuery = validationQuery; if (pooledDataSourceCreators.size() == 0) { - pooledDataSourceCreators.add(new BasicDbcpPooledDataSourceCreator()); - pooledDataSourceCreators.add(new TomcatDbcpPooledDataSourceCreator()); - pooledDataSourceCreators.add(new TomcatHighPerformancePooledDataSourceCreator()); - pooledDataSourceCreators.add(new HikariCpPooledDataSourceCreator()); + putPooledDataSourceCreator(new BasicDbcpPooledDataSourceCreator()); + putPooledDataSourceCreator(new TomcatDbcpPooledDataSourceCreator()); + putPooledDataSourceCreator(new TomcatJdbcPooledDataSourceCreator()); + putPooledDataSourceCreator(new HikariCpPooledDataSourceCreator()); } } + private void putPooledDataSourceCreator(PooledDataSourceCreator pooledDataSourceCreator) { + pooledDataSourceCreators.put(pooledDataSourceCreator.getClass().getSimpleName(), pooledDataSourceCreator); + } + @Override public DataSource create(SI serviceInfo, ServiceConnectorConfig serviceConnectorConfig) { try { - for (PooledDataSourceCreator 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> delegates = filterPooledDataSourceCreators(serviceConnectorConfig); + + for (PooledDataSourceCreator delegate : delegates) { + DataSource ds = delegate.create(serviceInfo, serviceConnectorConfig, getDriverClassName(serviceInfo), validationQuery); + if (ds != null) { + return ds; + } + } + + return null; + } + + private Collection> filterPooledDataSourceCreators(ServiceConnectorConfig serviceConnectorConfig) { + if (serviceConnectorConfig != null) { + List pooledDataSourceNames = ((DataSourceConfig) serviceConnectorConfig).getPooledDataSourceNames(); + if (pooledDataSourceNames != null) { + List> filtered = new ArrayList>(); + + 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); diff --git a/spring-cloud-spring-service-connector/src/main/java/org/springframework/cloud/service/relational/DbcpLikePooledDataSourceCreator.java b/spring-cloud-spring-service-connector/src/main/java/org/springframework/cloud/service/relational/DbcpLikePooledDataSourceCreator.java index 06177b5..c933973 100644 --- a/spring-cloud-spring-service-connector/src/main/java/org/springframework/cloud/service/relational/DbcpLikePooledDataSourceCreator.java +++ b/spring-cloud-spring-service-connector/src/main/java/org/springframework/cloud/service/relational/DbcpLikePooledDataSourceCreator.java @@ -35,9 +35,9 @@ public abstract class DbcpLikePooledDataSourceCreator 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 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); diff --git a/spring-cloud-spring-service-connector/src/main/java/org/springframework/cloud/service/relational/TomcatDbcpPooledDataSourceCreator.java b/spring-cloud-spring-service-connector/src/main/java/org/springframework/cloud/service/relational/TomcatDbcpPooledDataSourceCreator.java index 0cad9df..90951b3 100644 --- a/spring-cloud-spring-service-connector/src/main/java/org/springframework/cloud/service/relational/TomcatDbcpPooledDataSourceCreator.java +++ b/spring-cloud-spring-service-connector/src/main/java/org/springframework/cloud/service/relational/TomcatDbcpPooledDataSourceCreator.java @@ -23,10 +23,10 @@ public class TomcatDbcpPooledDataSourceCreator 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 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); } } } diff --git a/spring-cloud-spring-service-connector/src/main/java/org/springframework/cloud/service/relational/TomcatHighPerformancePooledDataSourceCreator.java b/spring-cloud-spring-service-connector/src/main/java/org/springframework/cloud/service/relational/TomcatJdbcPooledDataSourceCreator.java similarity index 71% rename from spring-cloud-spring-service-connector/src/main/java/org/springframework/cloud/service/relational/TomcatHighPerformancePooledDataSourceCreator.java rename to spring-cloud-spring-service-connector/src/main/java/org/springframework/cloud/service/relational/TomcatJdbcPooledDataSourceCreator.java index b68a647..a006610 100644 --- a/spring-cloud-spring-service-connector/src/main/java/org/springframework/cloud/service/relational/TomcatHighPerformancePooledDataSourceCreator.java +++ b/spring-cloud-spring-service-connector/src/main/java/org/springframework/cloud/service/relational/TomcatJdbcPooledDataSourceCreator.java @@ -11,14 +11,16 @@ import org.springframework.cloud.service.common.RelationalServiceInfo; * * @author Ramnivas Laddad */ -public class TomcatHighPerformancePooledDataSourceCreator +public class TomcatJdbcPooledDataSourceCreator extends DbcpLikePooledDataSourceCreator { + 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; diff --git a/spring-cloud-spring-service-connector/src/test/java/org/springframework/cloud/service/relational/PooledDataSourceCreatorsTest.java b/spring-cloud-spring-service-connector/src/test/java/org/springframework/cloud/service/relational/PooledDataSourceCreatorsTest.java index 09f79fa..ac619d9 100644 --- a/spring-cloud-spring-service-connector/src/test/java/org/springframework/cloud/service/relational/PooledDataSourceCreatorsTest.java +++ b/spring-cloud-spring-service-connector/src/test/java/org/springframework/cloud/service/relational/PooledDataSourceCreatorsTest.java @@ -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()); + 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()); + 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()); + 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()); + 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 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); } }