package com.processpuzzle.litest.template; import static org.junit.Assert.fail; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.ParameterizedType; import java.sql.ResultSet; import java.util.ArrayList; import java.util.List; import org.apache.commons.configuration.HierarchicalConfiguration; import org.apache.commons.configuration.tree.xpath.XPathExpressionEngine; import org.hibernate.StaleStateException; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.Table; import com.processpuzzle.application.configuration.domain.PersistenceContext; import com.processpuzzle.application.configuration.domain.PersistentDataInitializationStrategies; import com.processpuzzle.application.configuration.domain.ProcessPuzzleContext; import com.processpuzzle.application.configuration.domain.PropertyKeys; import com.processpuzzle.application.domain.ConfigurableApplicationFixture; import com.processpuzzle.application.security.domain.User; import com.processpuzzle.application.security.domain.UserFactory; import com.processpuzzle.application.security.domain.UserRepository; import com.processpuzzle.commons.persistence.AggregateRoot; import com.processpuzzle.commons.persistence.PersistenceProvider; import com.processpuzzle.commons.persistence.PersistenceStrategy; import com.processpuzzle.commons.persistence.Repository; import com.processpuzzle.commons.persistence.RepositoryEventHandler; import com.processpuzzle.commons.persistence.UnitOfWork; import com.processpuzzle.commons.rdbms.DatabaseSpy; import com.processpuzzle.litest.fixture.TransientFreshFixture; import com.processpuzzle.persistence.domain.DefaultPersistenceStrategy; import com.processpuzzle.persistence.domain.DefaultUnitOfWork; import com.processpuzzle.persistence.domain.HibernatePersistenceProvider; import com.processpuzzle.persistence.domain.PersistentClassList; import com.processpuzzle.user_session.domain.UserRequestManager; public abstract class RepositoryTestFixture<R extends Repository<A>, A extends AggregateRoot> extends GenericTemplatedFixture<R> implements TransientFreshFixture<R> { private static final String PERSISTENCE_STRATEGY_NAME = "TEST_STRATEGY"; private static final String PERSISTENCE_PROVICDER_NAME = "TEST_PROVIDER"; private static final String DATABASE_DRIVER = "org.hsqldb.jdbcDriver"; private static final String DATABASE_URL = "jdbc:hsqldb:mem:DOMAINTIER_TEST_DB"; private static final String DATABASE_USER = "sa"; private ConfigurableApplicationFixture applicationFixture; private List<Class<?>> persistentClasses = new ArrayList<Class<?>>(); private PersistentClassList persistentClassLists; private DefaultPersistenceStrategy strategy = null; protected ProcessPuzzleContext applicationContext; protected DatabaseSpy databaseSpy; protected R repository; protected Class<R> repositoryClass; protected A root = null; protected Class<A> rootClass; protected ResultSet rootRecord; protected Table rootTable; protected UnitOfWork setUpWork = null; protected UnitOfWork testWork = null; protected UserFactory userFactory; //Constructors protected RepositoryTestFixture( RepositoryTestEnvironment<R, RepositoryTestFixture<R,A>> testEnvironment ) { super( testEnvironment ); this.applicationFixture = testEnvironment.getApplicationFixture(); } //Properties public ProcessPuzzleContext getApplicationContext(){ return applicationContext; } public DatabaseSpy getDatabaseSpy() { return databaseSpy; } public R getRepository() { return repository; } public A getRoot() { return root; } public ResultSet getRootRecord(){ return rootRecord; } public String getRootTableName(){ return rootTable.getName(); } // Protected abstract and helper methods protected abstract void afterAggregateCreation(); protected abstract void afterAggregateDeletion(); protected abstract void beforeAggregateCreation(); @Override protected void configureBeforeSutInstantiation() { applicationContext = applicationFixture.getApplicationContext(); saveCurrentUser(); instantiateDatabaseSpy(); determineAggregateRootClass(); determineRepositoryClass(); determineAggregateRootTable(); determineRepository(); determineUserFactory(); beforeAggregateCreation(); } @Override protected void configureAfterSutInstantiation() { try{ instantiateAggregate(); }catch( Exception e ){ e.printStackTrace(); } afterAggregateCreation(); determineAggregateRootRecord(); instantiateTestWork(); } protected abstract A createNewAggregate() throws Exception; @SuppressWarnings( "unchecked" ) protected Repository<A> instantiateGivenRepository( Class<A> repositoryClass ) { Repository<A> repository = null; Class<?>[] parameterTypes = new Class[] { DefaultPersistenceStrategy.class }; Constructor<?> repositoryConstructor; try{ repositoryConstructor = repositoryClass.getConstructor( parameterTypes ); Object[] constructorArguments = { strategy }; repository = (Repository<A>) repositoryConstructor.newInstance( constructorArguments ); }catch( SecurityException e ){ e.printStackTrace(); }catch( NoSuchMethodException e ){ e.printStackTrace(); }catch( IllegalArgumentException e ){ e.printStackTrace(); }catch( InstantiationException e ){ e.printStackTrace(); }catch( IllegalAccessException e ){ e.printStackTrace(); }catch( InvocationTargetException e ){ e.printStackTrace(); } return repository; } @Override protected R instantiateSUT() { return applicationContext.getRepository( repositoryClass ); } @Override protected void releaseResources() { tearDownTestWork(); deleteAggregate(); afterAggregateDeletion(); repository = null; } // Private helper methods @SuppressWarnings( "unused" ) private DefaultPersistenceStrategy configureStrategy() { DefaultPersistenceStrategy strategy = null; HierarchicalConfiguration configuration = defineConfiguration(); determinePersistentClasses(); HibernatePersistenceProvider hibernateProvider = new HibernatePersistenceProvider( PERSISTENCE_PROVICDER_NAME, configuration, persistentClasses, PersistentDataInitializationStrategies.dropAndCreate ); List<RepositoryEventHandler> eventHandlerList = new ArrayList<RepositoryEventHandler>(); eventHandlerList.add( hibernateProvider ); strategy = new DefaultPersistenceStrategy( "Repository test configuration", eventHandlerList ); strategy.configure(); return strategy; } private void deleteAggregate() { DefaultUnitOfWork tearDownWork = new DefaultUnitOfWork( true ); try{ if( (root != null) && (root.getId() != null) ) repository.delete( tearDownWork, root ); tearDownWork.finish(); }catch( StaleStateException e ){ e.printStackTrace(); }catch( Throwable e ){ e.printStackTrace(); } root = null; } private static HierarchicalConfiguration defineConfiguration() { HierarchicalConfiguration configuration = new HierarchicalConfiguration(); configuration.addProperty( "hibernate", "" ); configuration.setExpressionEngine( new XPathExpressionEngine() ); configuration.addProperty( "hibernate connection", "" ); configuration.addProperty( "hibernate/connection driver_class", DATABASE_DRIVER ); configuration.addProperty( "hibernate/connection url", DATABASE_URL ); configuration.addProperty( "hibernate/connection username", DATABASE_USER ); configuration.addProperty( "hibernate/connection password", "" ); configuration.addProperty( "hibernate dialect", "org.hibernate.dialect.HSQLDialect" ); configuration.addProperty( "hibernate current_session_context_class", "thread" ); configuration.addProperty( "hibernate show_sql", "true" ); configuration.addProperty( "hibernate hbm2ddl", "" ); configuration.addProperty( "hibernate/hbm2ddl auto", "create-drop" ); configuration.addProperty( "hibernate transaction", "" ); configuration.addProperty( "hibernate/transaction factory_class", "org.hibernate.transaction.JDBCTransactionFactory" ); configuration.addProperty( "hibernate cache", "" ); configuration.addProperty( "hibernate/cache provider_class", "org.hibernate.cache.HashtableCacheProvider" ); configuration.addProperty( "hibernate/cache use_second_level_cache", "true" ); configuration.addProperty( "hibernate/cache use_query_cache", "true" ); configuration.addProperty( "hibernate c3p0", "" ); configuration.addProperty( "hibernate/c3p0 max_size", "5" ); configuration.addProperty( "hibernate/c3p0 min_size", "2" ); configuration.addProperty( "hibernate/c3p0 timeout", "1000" ); configuration.addProperty( "hibernate/c3p0 max_statements", "100" ); configuration.addProperty( "hibernate/c3p0 idle_test_period", "3000" ); configuration.addProperty( "hibernate/c3p0 acquire_increment", "2" ); configuration.addProperty( "hibernate/c3p0 validate", "false" ); return configuration; } @SuppressWarnings("unchecked") private void determineAggregateRootClass() { rootClass = (Class<A>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[1]; } private void determineAggregateRootRecord(){ rootRecord = databaseSpy.retrieveRecord( rootTable.getName(), root.getId() ); } private void determineAggregateRootTable() { // ProcessPuzzleContext applicationContext = UserRequestManager.getInstance().getApplicationContext(); PersistenceContext persistenceContext = applicationContext.getPersistenceContext(); String strategyName = applicationContext.getProperty( PropertyKeys.PERSISTENCE_STRATEGY_NAME.getDefaultKey() ); PersistenceStrategy strategy = persistenceContext.getStrategy( strategyName ); for( RepositoryEventHandler eventHandler : strategy.getEventHandlers() ){ if( eventHandler instanceof HibernatePersistenceProvider ){ org.hibernate.cfg.Configuration hibernateConfiguration = ((HibernatePersistenceProvider) eventHandler).getHibernateConfiguration(); PersistentClass classMapping = hibernateConfiguration.getClassMapping( rootClass.getName() ); if( classMapping != null ){ rootTable = classMapping.getTable(); break; } } } } private void determinePersistentClasses() { persistentClasses.addAll( persistentClassLists.getAggregateRoots() ); persistentClasses.addAll( persistentClassLists.getEntities() ); persistentClasses.addAll( persistentClassLists.getValueObjects() ); } private void determineRepository() { repository = applicationContext.getRepository( repositoryClass ); if( repository == null ) fail( "Repository: " + repositoryClass.getName() + " is undefined in ProcessPuzzleContext" ); } @SuppressWarnings("unchecked") private void determineRepositoryClass() { repositoryClass = (Class<R>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; } private void determineUserFactory() { userFactory = applicationContext.getEntityFactory( UserFactory.class ); } private void instantiateAggregate() throws Exception { setUpWork = new DefaultUnitOfWork( true ); root = createNewAggregate(); repository.add( setUpWork, root ); setUpWork.finish(); } private void instantiateDatabaseSpy() { PersistenceStrategy domainComponentsTestStrategy = applicationContext.getPersistenceContext().getStrategy( PERSISTENCE_STRATEGY_NAME ); PersistenceProvider persistenceProvider = (PersistenceProvider) domainComponentsTestStrategy.getEventHandler( PERSISTENCE_PROVICDER_NAME ); databaseSpy = new DatabaseSpy( persistenceProvider ); databaseSpy.connect(); } @SuppressWarnings( { "unchecked", "unused" } ) private R instantiateRepository( DefaultPersistenceStrategy strategy ) { R repository = null; Class<R> repositoryClass = (Class<R>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[1]; Class<?>[] parameterTypes = new Class[] { DefaultPersistenceStrategy.class }; Constructor<R> repositoryConstructor; try{ repositoryConstructor = repositoryClass.getConstructor( parameterTypes ); Object[] constructorArguments = { strategy }; repository = repositoryConstructor.newInstance( constructorArguments ); }catch( SecurityException e ){ e.printStackTrace(); }catch( NoSuchMethodException e ){ e.printStackTrace(); }catch( IllegalArgumentException e ){ e.printStackTrace(); }catch( InstantiationException e ){ e.printStackTrace(); }catch( IllegalAccessException e ){ e.printStackTrace(); }catch( InvocationTargetException e ){ e.printStackTrace(); } return repository; } private void instantiateTestWork() { testWork = new DefaultUnitOfWork( true ); } private void saveCurrentUser() { User currentUser = UserRequestManager.getInstance().currentUser(); UserRepository userRepository = applicationContext.getRepository( UserRepository.class ); userRepository.add( currentUser ); } private void tearDownTestWork() { if(( testWork != null ) && ( testWork.isStarted() )) testWork.finish(); } }