From 46f7279d5e2cc3d086c5274cbb3f5ad14819c2cb Mon Sep 17 00:00:00 2001 From: erik Date: Tue, 13 May 2008 20:59:45 +0000 Subject: [PATCH] --- system/spring/pom.xml | 15 + .../spring/DatabaseTesterComponent.java | 531 ++++++++++++++++++ 2 files changed, 546 insertions(+) create mode 100644 system/spring/src/test/java/org/wamblee/system/spring/DatabaseTesterComponent.java diff --git a/system/spring/pom.xml b/system/spring/pom.xml index 29f796f5..a0ea02bc 100644 --- a/system/spring/pom.xml +++ b/system/spring/pom.xml @@ -26,6 +26,17 @@ wamblee-system-general ${project.version} + + org.wamblee + wamblee-support-spring + ${project.version} + + + org.wamblee + wamblee-support-spring + ${project.version} + test-jar + commons-logging commons-logging @@ -38,6 +49,10 @@ org.springframework spring-context + + org.springframework + spring-hibernate3 + 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 index 00000000..cfc26cd5 --- /dev/null +++ b/system/spring/src/test/java/org/wamblee/system/spring/DatabaseTesterComponent.java @@ -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: + * + * + * @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 result = new ArrayList(); + 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 map = new TreeMap(); + 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; + } + +} -- 2.31.1