/* Copyright 2008 Edward Yakop. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * * See the License for the specific language governing permissions and * limitations under the License. */ package org.qi4j.entitystore.qrm; import java.io.PrintWriter; import java.net.URL; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; import org.apache.derby.drda.NetworkServerControl; import org.jmock.Expectations; import org.jmock.Mockery; import org.qi4j.api.common.QualifiedName; import org.qi4j.api.property.Property; import org.qi4j.bootstrap.ModuleAssembly; import org.qi4j.entitystore.qrm.dbInitializer.DBInitializerConfiguration; import org.qi4j.entitystore.qrm.test.TestProperty; import static java.lang.System.*; import static java.lang.Thread.*; import static java.sql.DriverManager.*; import static org.junit.Assert.*; /** * @author edward.yakop@gmail.com * @since 0.1.0 */ public class DerbyDatabaseHandler { private static final String JDBC_URL = "jdbc:derby://localhost/testdb;create=true"; private static final String DERBY_DRIVER_CLASS_NAME = "org.apache.derby.jdbc.ClientDriver"; private static final String DERBY_USER = "sa"; private static final String DERBY_PASSWORD = "derbypass"; private static final String SCHEMA_FILE = "testDbSchema.sql"; private static final String DATA_FILE = "testDbData.sql"; private static final int PING_COUNT = 10; private static final int PING_SLEEP_MILLIS = 50; private final NetworkServerControl nsc; public DerbyDatabaseHandler() { try { System.setProperty( "derby.authentication.provider", "BUILTIN" ); System.setProperty( "derby.drda.securityMechanism", "CLEAR_TEXT_PASSWORD_SECURITY" ); nsc = startServer(); waitForStart(); initDriver(); initSchema(); } catch( Exception e ) { throw new RuntimeException( "Error initializing Derby", e ); } } private Properties createConnectionProperties() { final Properties dbProperties = new Properties(); dbProperties.setProperty( "username", DERBY_USER ); dbProperties.setProperty( "password", DERBY_PASSWORD ); return dbProperties; } public void initDbInitializerInfo( final ModuleAssembly module ) { initDbInitializerInfo( module, SCHEMA_FILE, DATA_FILE ); } public void initDbInitializerInfo( final ModuleAssembly module, final String schemaFile, final String dataFile ) { // final DBInitializerConfiguration configuration = module.on( DBInitializerConfiguration.class ).to(); // configuration.dbUrl().set( JDBC_URL ); // configuration.connectionProperties().set( createConnectionProperties() ); // if( schemaFile != null ) // { // configuration.schemaUrl().set( getUrlString( schemaFile ) ); // } // if( dataFile != null ) // { // configuration.dataUrl().set( getUrlString( dataFile ) ); // } } public String getUrlString( final String file ) { final URL url = getClass().getResource( file ); assertNotNull( "If run inside ide, make sure file " + file + " is part of project resources.", url ); return url.toString(); } private void initSchema() throws SQLException { final Connection connection = getJDBCConnection(); try { final Statement statement = connection.createStatement(); // executeIgnore( statement, "DROP SCHEMA SA" ); executeIgnore( statement, "CREATE SCHEMA SA" ); removeTables( statement ); } finally { if( connection != null ) { connection.close(); } } } private void removeTables( final Statement statement ) throws SQLException { // Ensure that the all test tables are removed. final DatabaseMetaData data = statement.getConnection().getMetaData(); final ResultSet tables = data.getTables( null, null, null, new String[]{ "TABLE" } ); while( tables.next() ) { executeIgnore( statement, "DROP TABLE " + tables.getString( "TABLE_NAME" ) ); } } private void executeIgnore( final Statement statement, final String sql ) { try { statement.execute( sql ); } catch( SQLException e ) { System.err.println( "Error executing statement: " + sql + " " + e.getMessage() ); } } private void initDriver() { // Initialize derby driver. try { Class.forName( DERBY_DRIVER_CLASS_NAME ); } catch( ClassNotFoundException e ) { fail( "Derby client artifact must be included to run this test." ); } } private NetworkServerControl startServer() throws Exception { final NetworkServerControl nsc = new NetworkServerControl(); final PrintWriter logOuput = new PrintWriter( out ); nsc.start( logOuput ); return nsc; } /** * Check data initialization. * * @throws java.sql.SQLException Thrown if closing connection failed. * @since 0.1.0 */ public final void checkDataInitialization() throws SQLException { // Validate that the db is initialized Connection connection = null; try { connection = getJDBCConnection(); final Statement statement = connection.createStatement(); final ResultSet resultSet = statement.executeQuery( "SELECT COUNT(*) FROM PERSON" ); assertTrue( resultSet.next() ); final int numberOfRows = resultSet.getInt( 1 ); assertEquals( "There must be 2 rows in persons table.", 2, numberOfRows ); } finally { if( connection != null ) { connection.close(); } } } /** * Returns the jdbc connection to test db. Must not return {@code null}. * * @return The jdbc connection to test db. * * @throws java.sql.SQLException Thrown if initializing connection failed. * @since 0.1.0 */ public final Connection getJDBCConnection() throws SQLException { return getConnection( JDBC_URL, DERBY_USER, DERBY_PASSWORD ); } /** * Wait until derby started. * * @throws InterruptedException Thrown if sleep fails. * @since 0.1.0 */ private void waitForStart() throws Exception { for( int count = 0; count < PING_COUNT; count++ ) { try { nsc.ping(); return; } catch( Exception e ) { sleep( PING_SLEEP_MILLIS ); } } shutdown(); fail( "DB is not started after waiting for [" + PING_COUNT * PING_SLEEP_MILLIS + "] ms" ); } public void shutdown() { if( nsc != null ) { try { nsc.shutdown(); } catch( Exception e ) { e.printStackTrace(); } } } public DBInitializerConfiguration createDbInitializerConfigMock() { final Mockery context = new Mockery(); final DBInitializerConfiguration info = context.mock( DBInitializerConfiguration.class ); context.checking( new Expectations() { { allowing( info ).connectionProperties(); will( returnValue( createProperty( "connectionProperties", createConnectionProperties() ) ) ); allowing( info ).schemaUrl(); will( returnValue( createProperty( "schemaUrl", getUrlString( SCHEMA_FILE ) ) ) ); allowing( info ).dataUrl(); will( returnValue( createProperty( "dataUrl", getUrlString( DATA_FILE ) ) ) ); allowing( info ).dbUrl(); will( returnValue( createProperty( "dbUrl", JDBC_URL ) ) ); } } ); return info; } private <T> Property<T> createProperty( final String name, final T value ) { return new TestProperty<T>( value, QualifiedName.fromClass( DBInitializerConfiguration.class, name ) ); } public int executeStatement( final String sql, final ResultSetCallback callback ) { Connection connection = null; Statement statement = null; ResultSet rs = null; try { connection = getJDBCConnection(); statement = connection.createStatement(); rs = statement.executeQuery( sql ); int count = 0; while( rs.next() ) { callback.row( rs ); count++; } return count; } catch( SQLException sqle ) { throw new RuntimeException( "Error executing statement: " + sql + " with callback " + callback, sqle ); } finally { closeIt( rs ); closeIt( statement ); closeIt( connection ); } } public int executeUpdate( final String sql ) { Connection connection = null; Statement statement = null; int result = -1; try { connection = getJDBCConnection(); statement = connection.createStatement(); result = statement.executeUpdate( sql ); return result; } catch( SQLException sqle ) { throw new RuntimeException( "Error executing update: " + sql, sqle ); } finally { closeIt( statement ); closeIt( connection ); } } private void closeIt( final Object jdbcHandle ) { if( jdbcHandle == null ) { return; } try { if( jdbcHandle instanceof ResultSet ) { ( (ResultSet) jdbcHandle ).close(); return; } if( jdbcHandle instanceof Statement ) { ( (Statement) jdbcHandle ).close(); return; } if( jdbcHandle instanceof Connection ) { ( (Connection) jdbcHandle ).close(); } } catch( SQLException e ) { System.err.print( "Error closing " + jdbcHandle.getClass() + ": " + e.getMessage() ); } } /** * @autor Michael Hunger * @since 19.05.2008 */ public static interface ResultSetCallback { void row( ResultSet rs ) throws SQLException; } }