diff --git a/pom.xml b/pom.xml index 19595b5..8cb767e 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ org.activestack syncengine - 1.1.58-SNAPSHOT + 1.1.59-SNAPSHOT 3.2.4.RELEASE @@ -335,6 +335,21 @@ logback-ext-loggly 0.1.2 + + + + com.zaxxer + HikariCP + 2.4.1 + + + + + com.h2database + h2 + 1.3.167 + test + diff --git a/src/main/java/com/percero/agents/sync/jobs/UpdateTableConnectionFactory.java b/src/main/java/com/percero/agents/sync/jobs/UpdateTableConnectionFactory.java index 8576242..4e0192a 100644 --- a/src/main/java/com/percero/agents/sync/jobs/UpdateTableConnectionFactory.java +++ b/src/main/java/com/percero/agents/sync/jobs/UpdateTableConnectionFactory.java @@ -1,51 +1,12 @@ package com.percero.agents.sync.jobs; -import com.mchange.v2.c3p0.ComboPooledDataSource; -import org.apache.log4j.Logger; - -import java.beans.PropertyVetoException; -import java.sql.Connection; -import java.sql.SQLException; +import com.percero.datasource.BaseConnectionFactory; /** * Created by jonnysamps on 9/2/15. */ -//@Component -public class UpdateTableConnectionFactory { - - private static Logger logger = Logger.getLogger(UpdateTableConnectionFactory.class); - - private String driverClassName; - public void setDriverClassName(String val){ - this.driverClassName = val; - } - public String getDriverClassName(){ - return driverClassName; - } +public class UpdateTableConnectionFactory extends BaseConnectionFactory { - private String username; - public void setUsername(String val){ - this.username = val; - } - public String getUsername(){ - return username; - } - - private String password; - public void setPassword(String val){ - this.password = val; - } - public String getPassword(){ - return password; - } - - private String jdbcUrl; - public void setJdbcUrl(String val){ - this.jdbcUrl = val; - } - public String getJdbcUrl(){ - return jdbcUrl; - } private String[] tableNames; public void setTableNames(String[] val) { @@ -117,42 +78,4 @@ public int getWeight(){ return this.weight; } - private ComboPooledDataSource cpds; - - public void init() throws PropertyVetoException{ - try { - cpds = new ComboPooledDataSource(); - cpds.setDriverClass(driverClassName); //loads the jdbc driver - cpds.setJdbcUrl(jdbcUrl); - cpds.setUser(username); - cpds.setPassword(password); - - // the settings below are optional -- c3p0 can work with defaults - cpds.setMinPoolSize(10); - cpds.setAcquireIncrement(5); - cpds.setMaxPoolSize(this.weight); - cpds.setTestConnectionOnCheckout(true); - - }catch(PropertyVetoException pve){ - logger.error(pve.getMessage(), pve); - throw pve; - } - } - - public Connection getConnection() throws SQLException{ - try{ - if (cpds == null) { - init(); - } - return cpds.getConnection(); - } - catch(PropertyVetoException e){ - logger.error(e.getMessage(), e); - throw new SQLException(e); - } - catch(SQLException e){ - logger.error(e.getMessage(), e); - throw e; - } - } } diff --git a/src/main/java/com/percero/datasource/BaseConnectionFactory.java b/src/main/java/com/percero/datasource/BaseConnectionFactory.java new file mode 100644 index 0000000..440725e --- /dev/null +++ b/src/main/java/com/percero/datasource/BaseConnectionFactory.java @@ -0,0 +1,227 @@ +package com.percero.datasource; + +import java.beans.PropertyVetoException; +import java.sql.Connection; +import java.sql.SQLException; + +import javax.sql.DataSource; + +import org.apache.log4j.Logger; +import org.springframework.util.StringUtils; + +import com.mchange.v2.c3p0.ComboPooledDataSource; +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; + +public class BaseConnectionFactory implements IConnectionFactory { + + private static Logger logger = Logger.getLogger(BaseConnectionFactory.class); + + public static final String HIKARI_CONNECTION_POOL = "hikari"; + public static final String C3P0_CONNECTION_POOL = "c3p0"; + + private String name; + private String preferredConnectionPool = HIKARI_CONNECTION_POOL; + private Integer acquireIncrement = 4; + private Integer minPoolSize = 4; + private Integer maxPoolSize = 52; + private Integer maxIdleTime = 60 * 30; // 30 Minutes + private String testQuery = "SELECT 1 FROM dual"; + private Integer fetchSize = 100; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPreferredConnectionPool() { + return preferredConnectionPool; + } + + public void setPreferredConnectionPool(String preferredConnectionPool) { + this.preferredConnectionPool = preferredConnectionPool; + } + + public Integer getAcquireIncrement() { + return acquireIncrement; + } + + public void setAcquireIncrement(Integer acquireIncrement) { + this.acquireIncrement = acquireIncrement; + } + + public Integer getMinPoolSize() { + return minPoolSize; + } + + public void setMinPoolSize(Integer minPoolSize) { + this.minPoolSize = minPoolSize; + } + + public Integer getMaxPoolSize() { + return maxPoolSize; + } + + public void setMaxPoolSize(Integer maxPoolSize) { + this.maxPoolSize = maxPoolSize; + } + + public Integer getMaxIdleTime() { + return maxIdleTime; + } + + public void setMaxIdleTime(Integer maxIdleTime) { + this.maxIdleTime = maxIdleTime; + } + + public String getTestQuery() { + return testQuery; + } + + public void setTestQuery(String testQuery) { + this.testQuery = testQuery; + } + + public Integer getFetchSize() { + // Default to 100; + if (fetchSize == null || fetchSize <= 0) { + return 100; + } + return fetchSize; + } + + public void setFetchSize(Integer fetchSize) { + this.fetchSize = fetchSize; + } + + private String driverClassName; + public void setDriverClassName(String val){ + this.driverClassName = val; + } + public String getDriverClassName(){ + return driverClassName; + } + + private String username; + public void setUsername(String val){ + this.username = val; + } + public String getUsername(){ + return username; + } + + private String password; + public void setPassword(String val){ + this.password = val; + } + public String getPassword(){ + return password; + } + + private String jdbcUrl; + public void setJdbcUrl(String val){ + this.jdbcUrl = val; + } + public String getJdbcUrl(){ + return jdbcUrl; + } + + private DataSource ds; + public DataSource getDataSource() { + return ds; + } + + public void init() throws PropertyVetoException{ + try { + if (C3P0_CONNECTION_POOL.equalsIgnoreCase(preferredConnectionPool)) { + ComboPooledDataSource cpds = new ComboPooledDataSource(); + cpds.setDriverClass(driverClassName); // loads the jdbc driver + cpds.setJdbcUrl(jdbcUrl); + cpds.setUser(username); + cpds.setPassword(password); + + // the settings below are optional -- c3p0 can work with + // defaults + if (minPoolSize != null) { + cpds.setMinPoolSize(minPoolSize); + } + if (acquireIncrement != null) { + cpds.setAcquireIncrement(acquireIncrement); + } + if (maxPoolSize != null) { + cpds.setMaxPoolSize(maxPoolSize); + } + if (maxIdleTime != null) { + cpds.setMaxIdleTime(maxIdleTime); + cpds.setIdleConnectionTestPeriod(maxIdleTime); + } + cpds.setNumHelperThreads(30); + cpds.setTestConnectionOnCheckout(true); + if (StringUtils.hasText(testQuery)) { + cpds.setPreferredTestQuery(testQuery); + } + + ds = cpds; + } else { + // Default to Hikari Connection Pool. + HikariConfig config = new HikariConfig(); + config.setDriverClassName(driverClassName); + config.setJdbcUrl(jdbcUrl); + config.setUsername(username); + config.setPassword(password); + config.addDataSourceProperty("cachePrepStmts", "true"); + config.addDataSourceProperty("prepStmtCacheSize", "250"); + config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048"); + + if (StringUtils.hasText(name)) { + config.setPoolName(name); + } + if (minPoolSize != null) { + config.setMinimumIdle(minPoolSize); + } + // if (acquireIncrement != null) { + // config.setAcquireIncrement(acquireIncrement); + // } + if (maxPoolSize != null) { + config.setMaximumPoolSize(maxPoolSize); + } + if (maxIdleTime != null) { + config.setIdleTimeout(maxIdleTime * 1000); // Convert to + // milliseconds + } + // config.setNumHelperThreads(30); + // config.setTestConnectionOnCheckout(true); + if (StringUtils.hasText(testQuery)) { + config.setConnectionTestQuery(testQuery); + } + + ds = new HikariDataSource(config); + } + + }catch(PropertyVetoException pve){ + logger.error(pve.getMessage(), pve); + throw pve; + } + } + + public Connection getConnection() throws SQLException{ + try{ + if (ds == null) { + init(); + } + return getDataSource().getConnection(); + } + catch(PropertyVetoException e){ + logger.error(e.getMessage(), e); + throw new SQLException(e); + } + catch(SQLException e){ + logger.error(e.getMessage(), e); + throw e; + } + } + +} diff --git a/src/main/java/com/percero/datasource/IConnectionFactory.java b/src/main/java/com/percero/datasource/IConnectionFactory.java new file mode 100644 index 0000000..62d71bd --- /dev/null +++ b/src/main/java/com/percero/datasource/IConnectionFactory.java @@ -0,0 +1,13 @@ +package com.percero.datasource; + +import java.sql.Connection; +import java.sql.SQLException; + +public interface IConnectionFactory { + + String getName(); + void setName(String name); + Connection getConnection() throws SQLException; + + Integer getFetchSize(); +} diff --git a/src/test/java/com/percero/datasource/TestBaseConnectionFactory.java b/src/test/java/com/percero/datasource/TestBaseConnectionFactory.java new file mode 100644 index 0000000..5bf0beb --- /dev/null +++ b/src/test/java/com/percero/datasource/TestBaseConnectionFactory.java @@ -0,0 +1,161 @@ +package com.percero.datasource; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.beans.PropertyVetoException; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.net.URL; +import java.sql.Connection; +import java.sql.SQLException; + +import javax.sql.DataSource; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.Mockito; + +import com.esotericsoftware.yamlbeans.YamlException; +import com.esotericsoftware.yamlbeans.YamlReader; +import com.mchange.v2.c3p0.ComboPooledDataSource; +import com.zaxxer.hikari.HikariDataSource; + +public class TestBaseConnectionFactory { + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + } + + @AfterClass + public static void tearDownAfterClass() throws Exception { + } + + BaseConnectionFactory bcf = null; + @Before + public void setUp() throws Exception { + } + + @After + public void tearDown() throws Exception { + bcf = null; + } + + @Test + public void testInit() { + BaseConnectionFactory bcf = new BaseConnectionFactory(); + bcf.setJdbcUrl("jdbc:h2:~/test"); + bcf.setDriverClassName("org.h2.Driver"); + bcf.setName("TESTS"); + bcf.setUsername("sa"); + bcf.setPassword(""); + + String testQuery = "SELECT 1 FROM dual"; + bcf.setTestQuery(testQuery); + bcf.setMaxIdleTime(1000); + + try { + // Validate data source type. + bcf.init(); + assertTrue(bcf.getDataSource() instanceof HikariDataSource); + HikariDataSource hds = (HikariDataSource) bcf.getDataSource(); + assertEquals(bcf.getDriverClassName(), hds.getDriverClassName()); + assertEquals(bcf.getJdbcUrl(), hds.getJdbcUrl()); + assertEquals(bcf.getUsername(), hds.getUsername()); + assertEquals(bcf.getPassword(), hds.getPassword()); + assertEquals(bcf.getName(), hds.getPoolName()); + assertEquals(bcf.getMinPoolSize().intValue(), hds.getMinimumIdle()); + assertEquals(bcf.getMaxPoolSize().longValue(), hds.getMaximumPoolSize()); + assertEquals(bcf.getMaxIdleTime().longValue() * 1000, hds.getIdleTimeout()); + assertEquals(testQuery, hds.getConnectionTestQuery()); + + assertEquals("true", hds.getDataSourceProperties().getProperty("cachePrepStmts")); + assertEquals("250", hds.getDataSourceProperties().getProperty("prepStmtCacheSize")); + assertEquals("2048", hds.getDataSourceProperties().getProperty("prepStmtCacheSqlLimit")); + + // C3PO. + bcf.setPreferredConnectionPool(BaseConnectionFactory.C3P0_CONNECTION_POOL); + bcf.init(); + assertTrue(bcf.getDataSource() instanceof ComboPooledDataSource); + ComboPooledDataSource cpds = (ComboPooledDataSource) bcf.getDataSource(); + assertEquals(bcf.getDriverClassName(), cpds.getDriverClass()); + assertEquals(bcf.getJdbcUrl(), cpds.getJdbcUrl()); + assertEquals(bcf.getUsername(), cpds.getUser()); + assertEquals(bcf.getPassword(), cpds.getPassword()); + assertEquals(bcf.getMinPoolSize().intValue(), cpds.getMinPoolSize()); + assertEquals(bcf.getAcquireIncrement().intValue(), cpds.getAcquireIncrement()); + assertEquals(bcf.getMaxPoolSize().intValue(), cpds.getMaxPoolSize()); + assertEquals(bcf.getMaxIdleTime().intValue(), cpds.getMaxIdleTime()); + assertEquals(bcf.getMaxIdleTime().intValue(), cpds.getIdleConnectionTestPeriod()); + assertEquals(30, cpds.getNumHelperThreads()); + assertEquals(testQuery, cpds.getPreferredTestQuery()); + + // Hikari. + bcf.setPreferredConnectionPool(BaseConnectionFactory.HIKARI_CONNECTION_POOL); + bcf.init(); + assertTrue(bcf.getDataSource() instanceof HikariDataSource); + } catch (PropertyVetoException e) { + // TODO Auto-generated catch block + fail("Error with property: " + e.getMessage()); + } + } + + @Test + public void testGetConnection() { + + DataSource mockDataSource = Mockito.mock(DataSource.class); + Connection connection = Mockito.mock(Connection.class); + + try { + Mockito.when(mockDataSource.getConnection()).thenReturn(connection); + } catch (SQLException e) { + fail("Failed to Mock DataSource.getConnection(): " + e.getMessage()); + } + + bcf = Mockito.mock(BaseConnectionFactory.class); + Mockito.when(bcf.getDataSource()).thenReturn(mockDataSource); + + try { + Mockito.when(bcf.getConnection()).thenCallRealMethod(); + assertSame(connection, bcf.getConnection()); + } catch (SQLException e) { + fail("Failed to get Connection from DataSource: " + e.getMessage()); + } + } + + @Test + public void testYmlImport() { + try { + URL ymlUrl = TestBaseConnectionFactory.class.getClassLoader().getResource("baseconnectionfactories.yml"); + if (ymlUrl == null) { + fail("No configuration file found: baseconnectionFactories.yml"); + } + File configFile = new File(ymlUrl.getFile()); + YamlReader reader = new YamlReader(new FileReader(configFile)); + + BaseConnectionFactory connectionFactory = reader.read(BaseConnectionFactory.class); + assertNotNull(connectionFactory); + assertEquals("org.h2.Driver", connectionFactory.getDriverClassName()); + assertEquals("jdbc:h2:~/test", connectionFactory.getJdbcUrl()); + assertEquals("sa", connectionFactory.getUsername()); + assertEquals("pass", connectionFactory.getPassword()); + assertEquals("TEST", connectionFactory.getName()); + assertEquals(new Integer(5), connectionFactory.getMinPoolSize()); + assertEquals(new Integer(25), connectionFactory.getMaxPoolSize()); + assertEquals(new Integer(1000), connectionFactory.getMaxIdleTime()); + assertEquals("SELECT 1 FROM dual", connectionFactory.getTestQuery()); + } + catch (FileNotFoundException e) { + fail("Error processing config file baseconnectionfactories.yml: " + e.getMessage()); + } catch (YamlException e) { + fail("YAML Error processing config file baseconnectionfactories.yml: " + e.getMessage()); + } + } +} diff --git a/src/test/java/com/percero/datasource/TestUpdateTablesConnectionFactory.java b/src/test/java/com/percero/datasource/TestUpdateTablesConnectionFactory.java new file mode 100644 index 0000000..2a225b5 --- /dev/null +++ b/src/test/java/com/percero/datasource/TestUpdateTablesConnectionFactory.java @@ -0,0 +1,94 @@ +package com.percero.datasource; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.net.URL; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.esotericsoftware.yamlbeans.YamlException; +import com.esotericsoftware.yamlbeans.YamlReader; +import com.percero.agents.sync.jobs.UpdateTableConnectionFactory; + +public class TestUpdateTablesConnectionFactory { + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + } + + @AfterClass + public static void tearDownAfterClass() throws Exception { + } + + @Before + public void setUp() throws Exception { + } + + @After + public void tearDown() throws Exception { + } + + @Test + public void testYmlImport() { + try { + URL ymlUrl = TestUpdateTablesConnectionFactory.class.getClassLoader().getResource("updatetablesconnectionfactories.yml"); + if (ymlUrl == null) { + fail("No configuration file found: updatetablesconnectionfactories.yml"); + } + File configFile = new File(ymlUrl.getFile()); + YamlReader reader = new YamlReader(new FileReader(configFile)); + + // First UpdateTableConnectionFactory + UpdateTableConnectionFactory connectionFactory = reader.read(UpdateTableConnectionFactory.class); + assertNotNull(connectionFactory); + assertEquals("org.h2.Driver", connectionFactory.getDriverClassName()); + assertEquals("jdbc:h2:~/test", connectionFactory.getJdbcUrl()); + assertEquals("sa", connectionFactory.getUsername()); + assertEquals("pass", connectionFactory.getPassword()); + assertEquals("TEST", connectionFactory.getName()); + assertEquals(new Integer(5), connectionFactory.getMinPoolSize()); + assertEquals(new Integer(25), connectionFactory.getMaxPoolSize()); + assertEquals(new Integer(1000), connectionFactory.getMaxIdleTime()); + assertEquals("SELECT 1 FROM dual", connectionFactory.getTestQuery()); + + assertEquals(50, connectionFactory.getWeight()); + assertEquals("UPDATE_TABLE_LOCK_BATCH", connectionFactory.getStoredProcedureName()); + assertEquals("My Stored Proc", connectionFactory.getStoredProcedureDefinition()); + + assertNotNull(connectionFactory.getTableNames()); + assertEquals(2, connectionFactory.getTableNames().length); + assertEquals("update_table_1", connectionFactory.getTableNames()[0]); + assertEquals("update_table_2", connectionFactory.getTableNames()[1]); + + // Second UpdateTableConnectionFactory + connectionFactory = reader.read(UpdateTableConnectionFactory.class); + assertNotNull(connectionFactory); + assertEquals("org.h2.Driver", connectionFactory.getDriverClassName()); + assertEquals("jdbc:h2:~/test", connectionFactory.getJdbcUrl()); + assertEquals("sa", connectionFactory.getUsername()); + assertEquals("pass", connectionFactory.getPassword()); + + assertEquals(55, connectionFactory.getWeight()); + assertEquals("UPDATE_TABLE_LOCK_BATCH", connectionFactory.getStoredProcedureName()); + assertEquals("My Stored Proc", connectionFactory.getStoredProcedureDefinition()); + + assertNotNull(connectionFactory.getTableNames()); + assertEquals(1, connectionFactory.getTableNames().length); + assertEquals("update_table_1", connectionFactory.getTableNames()[0]); + } + catch (FileNotFoundException e) { + fail("Error processing config file updatetablesconnectionfactories.yml: " + e.getMessage()); + } catch (YamlException e) { + fail("YAML Error processing config file updatetablesconnectionfactories.yml: " + e.getMessage()); + } + } +} diff --git a/src/test/resources/baseconnectionfactories.yml b/src/test/resources/baseconnectionfactories.yml new file mode 100644 index 0000000..ea4edfa --- /dev/null +++ b/src/test/resources/baseconnectionfactories.yml @@ -0,0 +1,11 @@ +# --- +name: TEST +preferredConnectionPool: hikari +driverClassName: org.h2.Driver +jdbcUrl: jdbc:h2:~/test +username: sa +password: pass +testQuery: SELECT 1 FROM dual +maxIdleTime: 1000 +minPoolSize: 5 +maxPoolSize: 25 \ No newline at end of file diff --git a/src/test/resources/updatetablesconnectionfactories.yml b/src/test/resources/updatetablesconnectionfactories.yml new file mode 100644 index 0000000..6a5ec88 --- /dev/null +++ b/src/test/resources/updatetablesconnectionfactories.yml @@ -0,0 +1,27 @@ +# --- +name: TEST +preferredConnectionPool: hikari +driverClassName: org.h2.Driver +jdbcUrl: jdbc:h2:~/test +username: sa +password: pass +testQuery: SELECT 1 FROM dual +maxIdleTime: 1000 +minPoolSize: 5 +maxPoolSize: 25 +tableNames: + - update_table_1 + - update_table_2 +weight: 50 +storedProcedureName: UPDATE_TABLE_LOCK_BATCH +storedProcedureDefinition: My Stored Proc +--- +driverClassName: org.h2.Driver +jdbcUrl: jdbc:h2:~/test +username: sa +password: pass +tableNames: + - update_table_1 +weight: 55 +storedProcedureName: UPDATE_TABLE_LOCK_BATCH +storedProcedureDefinition: My Stored Proc