(no commit message)
authorErik Brakkee <erik@brakkee.org>
Tue, 13 May 2008 20:59:45 +0000 (20:59 +0000)
committerErik Brakkee <erik@brakkee.org>
Tue, 13 May 2008 20:59:45 +0000 (20:59 +0000)
system/spring/pom.xml
system/spring/src/test/java/org/wamblee/system/spring/DatabaseTesterComponent.java [new file with mode: 0644]

index 29f796f5aba05e4c7e368eb38935331e8f1e37f7..a0ea02bc2959bb9f60a660d6a57a2cbaf38d1761 100644 (file)
             <artifactId>wamblee-system-general</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.wamblee</groupId>
+            <artifactId>wamblee-support-spring</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.wamblee</groupId>
+            <artifactId>wamblee-support-spring</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+        </dependency>
         <dependency>
             <groupId>commons-logging</groupId>
             <artifactId>commons-logging</artifactId>
             <groupId>org.springframework</groupId>
             <artifactId>spring-context</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-hibernate3</artifactId>
+        </dependency>
 
     </dependencies>
 
diff --git a/system/spring/src/test/java/org/wamblee/system/spring/DatabaseTesterComponent.java b/system/spring/src/test/java/org/wamblee/system/spring/DatabaseTesterComponent.java
new file mode 100644 (file)
index 0000000..cfc26cd
--- /dev/null
@@ -0,0 +1,531 @@
+/*
+ * Copyright 2005 the original author or authors.
+ * 
+ * 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.wamblee.system.spring;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import javax.sql.DataSource;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.dbunit.DatabaseUnitException;
+import org.dbunit.database.DatabaseConnection;
+import org.dbunit.database.DatabaseSequenceFilter;
+import org.dbunit.database.IDatabaseConnection;
+import org.dbunit.dataset.FilteredDataSet;
+import org.dbunit.dataset.IDataSet;
+import org.dbunit.dataset.filter.ITableFilter;
+import org.dbunit.operation.DatabaseOperation;
+import org.hibernate.SessionFactory;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+import org.springframework.context.support.GenericApplicationContext;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.datasource.DataSourceUtils;
+import org.springframework.jdbc.datasource.DriverManagerDataSource;
+import org.springframework.orm.hibernate3.HibernateTemplate;
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.TransactionDefinition;
+import org.springframework.transaction.TransactionStatus;
+import org.springframework.transaction.support.DefaultTransactionDefinition;
+import org.springframework.transaction.support.TransactionCallback;
+import org.springframework.transaction.support.TransactionCallbackWithoutResult;
+import org.springframework.transaction.support.TransactionTemplate;
+import org.wamblee.general.BeanKernel;
+import org.wamblee.persistence.hibernate.HibernateMappingFiles;
+import org.wamblee.test.spring.SpringConfigFiles;
+import org.wamblee.test.spring.TestSpringBeanFactory;
+import org.wamblee.test.spring.TestTransactionCallback;
+import org.wamblee.test.spring.TestTransactionCallbackWithoutResult;
+
+/**
+ * Test support class for database testing. Currently, this still requires the
+ * spring platform transaction manager and hibernate template.
+ */
+public class DatabaseTesterComponent {
+
+    private static final Log LOG = LogFactory
+            .getLog(DatabaseTesterComponent.class);
+
+    /**
+     * Schema pattern.
+     */
+    private static final String SCHEMA_PATTERN = "%";
+
+    /**
+     * Cached spring application context.
+     */
+    private ApplicationContext _context;
+
+    private HibernateTemplate _hibernateTemplate;
+
+    private PlatformTransactionManager _transactionManager;
+
+    private DataSource _dataSource;
+
+    public DatabaseTesterComponent(HibernateTemplate aHibernateTemplate,
+            PlatformTransactionManager aTransactionManager,
+            DataSource aDataSource) {
+        _hibernateTemplate = aHibernateTemplate;
+        _transactionManager = aTransactionManager;
+        _dataSource = aDataSource;
+    }
+
+    /**
+     * @return Hibernate session factory.
+     */
+    protected SessionFactory getSessionFactory() {
+        return _hibernateTemplate.getSessionFactory();
+    }
+
+    /**
+     * Performs common initialization for test cases:
+     * <ul>
+     * <li>Cleaning the database. </li>
+     * </ul>
+     * 
+     * @throws Exception
+     */
+    public void setUp() throws Exception {
+        LOG.info("Performing setUp()");
+
+        cleanDatabase();
+    }
+
+    /**
+     * Performs common tear down after execution of a test case. Currenlty this
+     * method does nothing.
+     * 
+     * @throws Exception
+     */
+    protected void tearDown() throws Exception {
+        // Empty
+    }
+
+    /**
+     * @return Transaction manager
+     */
+    protected PlatformTransactionManager getTransactionManager() {
+        return _transactionManager;
+    }
+
+    /**
+     * @return Starts a new transaction.
+     */
+    protected TransactionStatus getTransaction() {
+        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
+        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
+
+        return getTransactionManager().getTransaction(def);
+    }
+
+    /**
+     * Returns the hibernate template for executing hibernate-specific
+     * functionality.
+     * 
+     * @return Hibernate template.
+     */
+    public HibernateTemplate getTemplate() {
+        return _hibernateTemplate;
+    }
+
+    /**
+     * Flushes the session. Should be called after some Hibernate work and
+     * before JDBC is used to check results.
+     * 
+     */
+    public void flush() {
+        getTemplate().flush();
+    }
+
+    /**
+     * Flushes the session first and then removes all objects from the Session
+     * cache. Should be called after some Hibernate work and before JDBC is used
+     * to check results.
+     * 
+     */
+    public void clear() {
+        flush();
+        getTemplate().clear();
+    }
+
+    /**
+     * Evicts the object from the session. This is essential for the
+     * implementation of unit tests where first an object is saved and is
+     * retrieved later. By removing the object from the session, Hibernate must
+     * retrieve the object again from the database.
+     * 
+     * @param aObject
+     */
+    protected void evict(Object aObject) {
+        getTemplate().evict(aObject);
+    }
+
+    /**
+     * Gets the connection.
+     * 
+     * @return Connection.
+     */
+    public Connection getConnection() {
+        return DataSourceUtils.getConnection(getDataSource());
+    }
+
+    public void cleanDatabase() throws SQLException {
+
+        if (!isDatabaseConfigured()) {
+            return;
+        }
+
+        String[] tables = getTableNames();
+
+        try {
+            IDatabaseConnection connection = new DatabaseConnection(
+                    getConnection());
+            ITableFilter filter = new DatabaseSequenceFilter(connection, tables);
+            IDataSet dataset = new FilteredDataSet(filter, connection
+                    .createDataSet(tables));
+
+            DatabaseOperation.DELETE_ALL.execute(connection, dataset);
+        } catch (DatabaseUnitException e) {
+            SQLException exc = new SQLException(e.getMessage());
+            exc.initCause(e);
+            throw exc;
+        }
+    }
+
+    /**
+     * @throws SQLException
+     */
+    public String[] getTableNames() throws SQLException {
+
+        List<String> result = new ArrayList<String>();
+        LOG.debug("Getting database table names to clean (schema: '"
+                + SCHEMA_PATTERN + "'");
+
+        ResultSet tables = getConnection().getMetaData().getTables(null,
+                SCHEMA_PATTERN, "%", new String[] { "TABLE" });
+        while (tables.next()) {
+            String table = tables.getString("TABLE_NAME");
+            // Make sure we do not touch hibernate's specific
+            // infrastructure tables.
+            if (!table.toLowerCase().startsWith("hibernate")) {
+                result.add(table);
+                LOG.debug("Adding " + table
+                        + " to list of tables to be cleaned.");
+            }
+        }
+        return (String[]) result.toArray(new String[0]);
+    }
+
+    /**
+     * @return
+     * @throws SQLException
+     */
+    public void emptyTables(List aTableList) throws SQLException {
+        Iterator liTable = aTableList.iterator();
+        while (liTable.hasNext()) {
+            emptyTable((String) liTable.next());
+        }
+    }
+
+    /**
+     * @return
+     * @throws SQLException
+     */
+    public void emptyTable(String aTable) throws SQLException {
+        executeSql("delete from " + aTable);
+    }
+
+    /**
+     * @return
+     * @throws SQLException
+     */
+    public void dropTable(String aTable) throws SQLException {
+        executeQuery("drop table " + aTable);
+    }
+
+    /**
+     * Executes an SQL statement within a transaction.
+     * 
+     * @param aSql
+     *            SQL statement.
+     * @return Return code of the corresponding JDBC call.
+     */
+    public int executeSql(final String aSql) {
+        return executeSql(aSql, new Object[0]);
+    }
+
+    /**
+     * Executes an SQL statement within a transaction. See
+     * {@link #setPreparedParam(int, PreparedStatement, Object)}for details on
+     * supported argument types.
+     * 
+     * @param aSql
+     *            SQL statement.
+     * @param aArg
+     *            Argument of the sql statement.
+     * @return Return code of the corresponding JDBC call.
+     */
+    public int executeSql(final String aSql, final Object aArg) {
+        return executeSql(aSql, new Object[] { aArg });
+    }
+
+    /**
+     * Executes an sql statement. See
+     * {@link #setPreparedParam(int, PreparedStatement, Object)}for details on
+     * supported argument types.
+     * 
+     * @param aSql
+     *            SQL query to execute.
+     * @param aArgs
+     *            Arguments.
+     * @return Number of rows updated.
+     */
+    public int executeSql(final String aSql, final Object[] aArgs) {
+        Map results = executeTransaction(new TestTransactionCallback() {
+            public Map execute() throws Exception {
+                JdbcTemplate template = new JdbcTemplate(getDataSource());
+                int result = template.update(aSql, aArgs);
+
+                Map<String, Integer> map = new TreeMap<String, Integer>();
+                map.put("result", new Integer(result));
+
+                return map;
+            }
+        });
+
+        return ((Integer) results.get("result")).intValue();
+    }
+
+    /**
+     * Executes a transaction with a result.
+     * 
+     * @param aCallback
+     *            Callback to do your transactional work.
+     * @return Result.
+     */
+    public Object executeTransaction(TransactionCallback aCallback) {
+        TransactionTemplate lTemplate = new TransactionTemplate(
+                getTransactionManager());
+        return lTemplate.execute(aCallback);
+    }
+
+    /**
+     * Executes a transaction without a result.
+     * 
+     * @param aCallback
+     *            Callback to do your transactional work. .
+     */
+    public void executeTransaction(TransactionCallbackWithoutResult aCallback) {
+        TransactionTemplate template = new TransactionTemplate(
+                getTransactionManager());
+        template.execute(aCallback);
+    }
+
+    /**
+     * Executes a transaction with a result, causing the testcase to fail if any
+     * type of exception is thrown.
+     * 
+     * @param aCallback
+     *            Code to be executed within the transaction.
+     * @return Result.
+     */
+    public Map executeTransaction(final TestTransactionCallback aCallback) {
+        return (Map) executeTransaction(new TransactionCallback() {
+            public Object doInTransaction(TransactionStatus aArg) {
+                try {
+                    return aCallback.execute();
+                } catch (Exception e) {
+                    // test case must fail.
+                    e.printStackTrace();
+                    throw new RuntimeException(e);
+                }
+            }
+        });
+    }
+
+    /**
+     * Executes a transaction with a result, causing the testcase to fail if any
+     * type of exception is thrown.
+     * 
+     * @param aCallback
+     *            Code to be executed within the transaction.
+     */
+    public void executeTransaction(
+            final TestTransactionCallbackWithoutResult aCallback) {
+        executeTransaction(new TransactionCallbackWithoutResult() {
+            public void doInTransactionWithoutResult(TransactionStatus aArg) {
+                try {
+                    aCallback.execute();
+                } catch (Exception e) {
+                    // test case must fail.
+                    throw new RuntimeException(e.getMessage(), e);
+                }
+            }
+        });
+    }
+
+    /**
+     * Executes an SQL query.
+     * 
+     * @param aSql
+     *            Query to execute.
+     * @return Result set.
+     */
+    public ResultSet executeQuery(String aSql) {
+        return executeQuery(aSql, new Object[0]);
+    }
+
+    /**
+     * Executes a query with a single argument. See
+     * {@link #setPreparedParam(int, PreparedStatement, Object)}for details on
+     * supported argument types.
+     * 
+     * @param aSql
+     *            Query.
+     * @param aArg
+     *            Argument.
+     * @return Result set.
+     */
+    public ResultSet executeQuery(String aSql, Object aArg) {
+        return executeQuery(aSql, new Object[] { aArg });
+    }
+
+    /**
+     * Executes a query. See
+     * {@link #setPreparedParam(int, PreparedStatement, Object)}for details on
+     * supported argument types.
+     * 
+     * @param aSql
+     *            Sql query.
+     * @param aArgs
+     *            Arguments to the query.
+     * @return Result set.
+     */
+    public ResultSet executeQuery(final String aSql, final Object[] aArgs) {
+        try {
+            Connection connection = getConnection();
+
+            PreparedStatement statement = connection.prepareStatement(aSql);
+            setPreparedParams(aArgs, statement);
+
+            return statement.executeQuery();
+        } catch (SQLException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Sets the values of a prepared statement. See
+     * {@link #setPreparedParam(int, PreparedStatement, Object)}for details on
+     * supported argument types.
+     * 
+     * @param aArgs
+     *            Arguments to the prepared statement.
+     * @param aStatement
+     *            Prepared statement
+     * @throws SQLException
+     */
+    private void setPreparedParams(final Object[] aArgs,
+            PreparedStatement aStatement) throws SQLException {
+        for (int i = 1; i <= aArgs.length; i++) {
+            setPreparedParam(i, aStatement, aArgs[i - 1]);
+        }
+    }
+
+    /**
+     * Sets a prepared statement parameter.
+     * 
+     * @param aIndex
+     *            Index of the parameter.
+     * @param aStatement
+     *            Prepared statement.
+     * @param aObject
+     *            Value Must be of type Integer, Long, or String. TODO extend
+     *            with more types of values.
+     * @throws SQLException
+     */
+    private void setPreparedParam(int aIndex, PreparedStatement aStatement,
+            Object aObject) throws SQLException {
+        if (aObject instanceof Integer) {
+            aStatement.setInt(aIndex, ((Integer) aObject).intValue());
+        } else if (aObject instanceof Long) {
+            aStatement.setLong(aIndex, ((Integer) aObject).longValue());
+        } else if (aObject instanceof String) {
+            aStatement.setString(aIndex, (String) aObject);
+        } else {
+            TestCase.fail("Unsupported object type for prepared statement: "
+                    + aObject.getClass() + " value: " + aObject
+                    + " statement: " + aStatement);
+        }
+    }
+
+    private boolean isDatabaseConfigured() {
+        try {
+            getDataSource();
+        } catch (NoSuchBeanDefinitionException e) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * @return Returns the dataSource.
+     */
+    public DataSource getDataSource() {
+        return _dataSource;
+    }
+
+    /**
+     * @return
+     * @throws SQLException
+     */
+    public int getTableSize(final String aTable) throws SQLException {
+
+        ResultSet resultSet = executeQuery("select * from " + aTable);
+        int count = 0;
+
+        while (resultSet.next()) {
+            count++;
+        }
+        return count;
+    }
+
+    public int countResultSet(ResultSet aResultSet) throws SQLException {
+        int count = 0;
+
+        while (aResultSet.next()) {
+            count++;
+        }
+
+        return count;
+    }
+
+}