diff --git a/gradle.properties b/gradle.properties index 1c405b7..f3e3b15 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ -version=1.1.2.BUILD-SNAPSHOT +version=1.1.2.AL2-SNAPSHOT group=org.springframework.cloud diff --git a/spring-cloud-core/src/main/java/org/springframework/cloud/Cloud.java b/spring-cloud-core/src/main/java/org/springframework/cloud/Cloud.java index 82aafeb..109f3f1 100644 --- a/spring-cloud-core/src/main/java/org/springframework/cloud/Cloud.java +++ b/spring-cloud-core/src/main/java/org/springframework/cloud/Cloud.java @@ -202,7 +202,7 @@ public class Cloud { * * *

- * If a there is only a single service of a given type (as defined by the {@link ServiceInfo.ServiceLabel} + * If a there is only a single service of a given type (as defined by the {link ServiceInfo.ServiceLabel} * annoation's value of the corresponding {@link ServiceInfo} class), that service is aliased * to the service type. Keys for such properties start in cloud.services.<service-type>. * For example, if there is only a single MySQL service bound to the application, the service properties diff --git a/spring-cloud-spring-service-connector/build.gradle b/spring-cloud-spring-service-connector/build.gradle index 26caf24..ca7d8a3 100644 --- a/spring-cloud-spring-service-connector/build.gradle +++ b/spring-cloud-spring-service-connector/build.gradle @@ -22,6 +22,7 @@ dependencies { exclude(group: 'commons-logging', module: 'commons-logging') } optional("commons-dbcp:commons-dbcp:$commonDbcpVersion") + optional("com.zaxxer:HikariCP-java6:2.1.0") optional("org.springframework.amqp:spring-rabbit:$springAmqpVersion") optional("org.springframework.data:spring-data-redis:$springDataRedisVersion") { exclude(group: 'org.springframework', module: 'spring-context-support') 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 654799f..56cbc01 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 @@ -22,23 +22,24 @@ import org.springframework.jdbc.datasource.SimpleDriverDataSource; */ public abstract class DataSourceCreator extends AbstractServiceConnectorCreator { - protected static Logger logger = Logger.getLogger(DataSourceCreator.class.getName()); + protected static Logger logger = Logger.getLogger(DataSourceCreator.class.getName()); - private String driverSystemPropKey; - private String[] driverClasses; - private String validationQuery; + private String driverSystemPropKey; + private String[] driverClasses; + private String validationQuery; - private List> pooledDataSourceCreators = new ArrayList>(); + private List> pooledDataSourceCreators = new ArrayList>(); public DataSourceCreator(String driverSystemPropKey, String[] driverClasses, String validationQuery) { - this.driverSystemPropKey = driverSystemPropKey; - this.driverClasses = driverClasses; - this.validationQuery = validationQuery; + this.driverSystemPropKey = driverSystemPropKey; + this.driverClasses = driverClasses; + this.validationQuery = validationQuery; if (pooledDataSourceCreators.size() == 0) { pooledDataSourceCreators.add(new BasicDbcpPooledDataSourceCreator()); pooledDataSourceCreators.add(new TomcatDbcpPooledDataSourceCreator()); pooledDataSourceCreators.add(new TomcatHighPerformancePooledDataSourceCreator()); + pooledDataSourceCreators.add(new HikariCpPooledDataSourceCreator()); } } @@ -62,21 +63,21 @@ public abstract class DataSourceCreator extend } } - public String getDriverClassName(SI serviceInfo) { - String userSpecifiedDriver = System.getProperty(driverSystemPropKey); + public String getDriverClassName(SI serviceInfo) { + String userSpecifiedDriver = System.getProperty(driverSystemPropKey); - if (userSpecifiedDriver != null && !userSpecifiedDriver.isEmpty()) { - return userSpecifiedDriver; - } else { - for (String driver : driverClasses) { - try { - Class.forName(driver); - return driver; - } catch (ClassNotFoundException ex) { - // continue... - } - } - } - throw new CloudException("No suitable database driver found for " + serviceInfo.getId() + " service "); - } + if (userSpecifiedDriver != null && !userSpecifiedDriver.isEmpty()) { + return userSpecifiedDriver; + } else { + for (String driver : driverClasses) { + try { + Class.forName(driver); + return driver; + } catch (ClassNotFoundException ex) { + // continue... + } + } + } + throw new CloudException("No suitable database driver found for " + serviceInfo.getId() + " service "); + } } diff --git a/spring-cloud-spring-service-connector/src/main/java/org/springframework/cloud/service/relational/HikariCpPooledDataSourceCreator.java b/spring-cloud-spring-service-connector/src/main/java/org/springframework/cloud/service/relational/HikariCpPooledDataSourceCreator.java new file mode 100644 index 0000000..a48406b --- /dev/null +++ b/spring-cloud-spring-service-connector/src/main/java/org/springframework/cloud/service/relational/HikariCpPooledDataSourceCreator.java @@ -0,0 +1,56 @@ +package org.springframework.cloud.service.relational; + +import static org.springframework.cloud.service.Util.*; + +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; + +import com.zaxxer.hikari.HikariDataSource; + +public class HikariCpPooledDataSourceCreator implements PooledDataSourceCreator { + + protected static Logger logger = Logger.getLogger(PooledDataSourceCreator.class.getName()); + + private static final String HIKARI_CLASSNAME = "com.zaxxer.hikari.HikariDataSource"; + + 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("jdbcUrl", serviceInfo.getJdbcUrl()); + if (validationQuery != null) { + target.setPropertyValue("connectionTestQuery", validationQuery); + } + + 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 + //target.setPropertyValue("maximumPoolSize", 4); + //target.setPropertyValue("idleTimeout", 55000); + } + configurer.configure(basicDataSource, (DataSourceConfig)serviceConnectorConfig); + } + + @Override + public DataSource create(RelationalServiceInfo serviceInfo, ServiceConnectorConfig serviceConnectorConfig, + String driverClassName, String validationQuery) { + if (hasClass(HIKARI_CLASSNAME)) { + logger.info("Found HikariCP on the classpath. Using it for DataSource connection pooling."); + HikariDataSource ds = new HikariDataSource(); + setBasicDataSourceProperties(ds, serviceInfo, serviceConnectorConfig, driverClassName, validationQuery); + return ds; + } else { + return null; + } + } + +} 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 5791980..4456a56 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 @@ -14,7 +14,7 @@ public class PooledDataSourceCreatorsTest { // Just to grab driver class name and validation query string private MysqlDataSourceCreator mysqlDataSourceCreator = new MysqlDataSourceCreator(); - + @Before public void setup() { MockitoAnnotations.initMocks(this); @@ -22,25 +22,30 @@ public class PooledDataSourceCreatorsTest { @Test public void pooledDataSourceCreationDbcp() { - assertPooledDataSource(new BasicDbcpPooledDataSourceCreator()); + assertPooledDataSource(new BasicDbcpPooledDataSourceCreator()); } - @Test - public void pooledDataSourceCreationTomcatDbcp() { - assertPooledDataSource(new TomcatDbcpPooledDataSourceCreator()); - } + @Test + public void pooledDataSourceCreationTomcatDbcp() { + assertPooledDataSource(new TomcatDbcpPooledDataSourceCreator()); + } + + @Test + public void pooledDataSourceCreationTomcatHighPerformance() { + assertPooledDataSource(new TomcatHighPerformancePooledDataSourceCreator()); + } + + @Test + public void pooledDataSourceCreationHikariCP() { + assertPooledDataSource(new HikariCpPooledDataSourceCreator()); + } - @Test - public void pooledDataSourceCreationTomcatHighPerformance() { - assertPooledDataSource(new TomcatHighPerformancePooledDataSourceCreator()); - } - private void assertPooledDataSource(PooledDataSourceCreator testCreator) { - DataSource ds = testCreator.create(mockMysqlServiceInfo, null, - mysqlDataSourceCreator.getDriverClassName(mockMysqlServiceInfo), - "select 1"); + DataSource ds = testCreator.create(mockMysqlServiceInfo, null, + mysqlDataSourceCreator.getDriverClassName(mockMysqlServiceInfo), + "select 1"); - Assert.assertNotNull("Failed to create datasource with " + testCreator.getClass().getSimpleName(), ds); + Assert.assertNotNull("Failed to create datasource with " + testCreator.getClass().getSimpleName(), ds); } }