/* * Copyright 2010-2017 Norwegian Agency for Public Management and eGovernment (Difi) * * Licensed under the EUPL, Version 1.1 or – as soon they * will be approved by the European Commission - subsequent * versions of the EUPL (the "Licence"); * * You may not use this work except in compliance with the Licence. * * You may obtain a copy of the Licence at: * * https://joinup.ec.europa.eu/community/eupl/og_page/eupl * * Unless required by applicable law or agreed to in * writing, software distributed under the Licence is * distributed on an "AS IS" basis, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. * See the Licence for the specific language governing * permissions and limitations under the Licence. */ package no.difi.oxalis.persistence.datasource; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.name.Named; import no.difi.oxalis.api.settings.Settings; import no.difi.oxalis.commons.filesystem.ClassLoaderUtils; import no.difi.oxalis.persistence.testng.PersistenceModuleFactory; import no.difi.oxalis.persistence.util.PersistenceConf; import org.apache.commons.dbcp2.*; import org.apache.commons.pool2.impl.GenericObjectPool; import org.testng.annotations.BeforeClass; import org.testng.annotations.Guice; import org.testng.annotations.Test; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import javax.sql.DataSource; import java.net.MalformedURLException; import java.net.URI; import java.nio.file.Path; import java.sql.*; import java.util.Properties; import static org.testng.Assert.*; /** * TODO Enable testing. * * @author steinar * Date: 18.04.13 * Time: 14:08 */ @Guice(moduleFactory = PersistenceModuleFactory.class) public class DbcpDataSourceProviderTest { @Inject private Provider<DataSource> dataSourceProvider; @Inject private Provider<DataSource> dataSourceProvider2; @Inject private Settings<PersistenceConf> settings; @Inject @Named("home") private Path homeFolder; @BeforeClass public void setUp() { assertNotNull(settings); } @Test public void oxalisDataSourceFactoryIsSingleton() throws Exception { // Attempts to load the first instance of DataSourceProvider assertNotNull(dataSourceProvider); // Second invocation should return same instance assertEquals(dataSourceProvider, dataSourceProvider2, "Seems the Singleton pattern in DataSourceProviderFactory is not working"); // The datasource should also be the same instance DataSource dataSource1 = dataSourceProvider.get(); assertNotNull(dataSource1); DataSource dataSource2 = dataSourceProvider.get(); assertEquals(dataSource1, dataSource2, dataSourceProvider.getClass().getSimpleName() + " is not returning a singleton instance of DataSource"); } /** * Verifies that we can create a pooled jdbc data source using the * JDBC .jar-file supplied in the global configuration file. */ @Test public void testLoadJdbcDriverUsingCustomClassLoader() throws Exception { ConnectionFactory driverConnectionFactory = createConnectionFactory(false); ObjectName poolName = new ObjectName("no.difi.oxalis", "connectionPool", "TestPool"); PoolableConnectionFactory factory = new PoolableConnectionFactory(driverConnectionFactory, poolName); GenericObjectPool<PoolableConnection> pool = new GenericObjectPool<>(factory); factory.setPool(pool); pool.setMaxTotal(10); pool.setMaxWaitMillis(100); assertEquals(pool.getFactory(), factory); PoolableConnectionFactory pcf = (PoolableConnectionFactory) ((GenericObjectPool<?>) pool).getFactory(); //ObjectPool<PoolableConnection> pool1 = pcf.getPool(); PoolingDataSource<PoolableConnection> poolingDataSource = new PoolingDataSource<>(pool); Connection connection = poolingDataSource.getConnection(); assertNotNull(connection); Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery("select current_date()"); assertTrue(resultSet.next()); } @Test public void testFailWithStaleConnection() throws Exception { ConnectionFactory driverConnectionFactory = createConnectionFactory(false); PoolingDataSource poolingDataSource = createPoolingDataSource(driverConnectionFactory); try { runTwoSqlStatementsWithTwoConnections(poolingDataSource); } catch (Exception e) { assertTrue(e.getClass().getName().contains("CommunicationsException")); } } @Test(enabled = false) public void testHandleStaleConnections() throws Exception { ConnectionFactory driverConnectionFactory = createConnectionFactory(true); PoolingDataSource poolingDataSource = createPoolingDataSource(driverConnectionFactory); runTwoSqlStatementsWithTwoConnections(poolingDataSource); } @Test public void testBasicDataSource() throws Exception { Path jdbcDriverClassPath = settings.getPath(PersistenceConf.DRIVER_PATH, homeFolder); ClassLoader classLoader = ClassLoaderUtils.initiate(jdbcDriverClassPath); BasicDataSource basicDataSource = new BasicDataSource(); basicDataSource.setDriverClassName(settings.getString(PersistenceConf.DRIVER_CLASS)); basicDataSource.setUrl(settings.getString(PersistenceConf.JDBC_CONNECTION_URI)); basicDataSource.setUsername(settings.getString(PersistenceConf.JDBC_USERNAME)); basicDataSource.setPassword(settings.getString(PersistenceConf.JDBC_PASSWORD)); // Does not work in 1.4, fixed in 1.4.1 basicDataSource.setDriverClassLoader(classLoader); try { Connection connection = basicDataSource.getConnection(); assertNotNull(connection); } catch (SQLException e) { // As expected when using DBCP 1.4 } } private void runTwoSqlStatementsWithTwoConnections(PoolingDataSource poolingDataSource) throws SQLException, InterruptedException { Connection connection = poolingDataSource.getConnection(); if (connection.getMetaData().getDatabaseProductName().toLowerCase().contains("mysql")) { assertNotNull(connection); Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery("select current_date()"); statement = connection.createStatement(); statement.execute("set session wait_timeout=1"); assertTrue(resultSet.next()); connection.close(); // return to pool // Wait for 2 seconds System.err.print("Sleeping for 2 seconds...."); Thread.sleep(2 * 1000L); System.err.println("Running again now"); connection = poolingDataSource.getConnection(); statement = connection.createStatement(); statement.executeQuery("select current_time()"); } } private ConnectionFactory createConnectionFactory(boolean profileSql) throws MalformedURLException, ClassNotFoundException, InstantiationException, IllegalAccessException, SQLException { Path jdbcDriverClassPath = settings.getPath(PersistenceConf.DRIVER_PATH, homeFolder); ClassLoader classLoader = ClassLoaderUtils.initiate(jdbcDriverClassPath); String jdbcDriverClassName = settings.getString(PersistenceConf.DRIVER_CLASS); URI connectURI = URI.create(settings.getString(PersistenceConf.JDBC_CONNECTION_URI)); // + "?initialTimeout=2"; String userName = settings.getString(PersistenceConf.JDBC_USERNAME); String password = settings.getString(PersistenceConf.JDBC_PASSWORD); Class<?> aClass; try { aClass = Class.forName(jdbcDriverClassName, true, classLoader); } catch (ClassNotFoundException e) { throw new IllegalStateException(String.format( "Unable to locate class '%s' in class path '%s'", jdbcDriverClassName, jdbcDriverClassPath)); } Driver driver = (Driver) aClass.newInstance(); assertTrue(driver.acceptsURL(connectURI.toString())); Properties properties = new Properties(); properties.put("user", userName); properties.put("password", password); if (profileSql) { properties.put("profileSQL", "true"); // MySQL debug option } return new DriverConnectionFactory(driver, connectURI.toString(), properties); } @SuppressWarnings("unchecked") private PoolingDataSource createPoolingDataSource(ConnectionFactory driverConnectionFactory) { PoolableConnectionFactory poolableConnectionFactory; try { poolableConnectionFactory = new PoolableConnectionFactory(driverConnectionFactory, new ObjectName("no.difi.oxalis", "connectionPool", "TestPool")); GenericObjectPool<PoolableConnection> pool = new GenericObjectPool<>(poolableConnectionFactory); poolableConnectionFactory.setPool(pool); poolableConnectionFactory.setValidationQuery("select 1"); return new PoolingDataSource(pool); } catch (MalformedObjectNameException e) { throw new IllegalStateException("Unable to create poolable conneciton factory: " + e.getMessage(), e); } } }