package com.softwaremill.common.dbtest; import com.google.common.io.Resources; import org.apache.log4j.BasicConfigurator; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.hibernate.cfg.Environment; import org.hibernate.ejb.AvailableSettings; import org.hibernate.ejb.Ejb3Configuration; import org.hibernate.testing.tm.ConnectionProviderImpl; import org.hibernate.testing.tm.SimpleJtaTransactionManagerImpl; import org.hibernate.testing.tm.TransactionManagerLookupImpl; import org.testng.annotations.AfterClass; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeSuite; import com.softwaremill.common.arquillian.BetterArquillian; import com.softwaremill.common.cdi.persistence.EntityManagerFactoryProducer; import com.softwaremill.common.dbtest.util.DbMode; import com.softwaremill.common.dbtest.util.SqlFileResolver; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.transaction.HeuristicMixedException; import javax.transaction.HeuristicRollbackException; import javax.transaction.NotSupportedException; import javax.transaction.RollbackException; import javax.transaction.Synchronization; import javax.transaction.SystemException; import java.io.IOException; import java.net.URL; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import static org.apache.commons.lang.StringUtils.*; /** * Extend this class to create tests which have a private database. The persistence classes from cdi-ext are available. * A template for a test is: * <pre> * public class MyTest extends AbstractDBTest { * @Override * public void configureEntities(Ejb3Configuration cfg) { * cfg.addAnnotatedClass(MyEntity.class); * } * <p/> * @Deployment * public static JavaArchive createTestArchive() { * return new ArchiveConfigurator() { * @Override * protected JavaArchive configureBeans(JavaArchive ar) { * return ar.addPackage(MyBean.class.getPackage()); * }.createTestArchive(); * } * <p/> * @Test * public void mytest() { } * } * </pre> * * @author Adam Warski (adam at warski dot org) */ public abstract class AbstractDBTest extends BetterArquillian { private EntityManagerFactory emf; private static final Logger log = Logger.getLogger(AbstractDBTest.class); private DbMode compatibilityMode = null; /** * Additional Hibernate configuration. * E.g. add entities using {@code cfg.addAnnotatedClass(MyEntity.class)} */ protected abstract void configureEntities(Ejb3Configuration cfg); /** * Loads test data. By default reads the content of an sql file that is named the same as the test class. * Override if you want to add additional test data, or suppress default test data loading. * <p/> * You can use {@link #loadURLContent(javax.persistence.EntityManager, java.net.URL)} to load the specified * file. * * @param em Entity manager which can be used to load data. */ protected void loadTestData(EntityManager em) throws IOException { final String sqlFilePath = new SqlFileResolver(this.getClass()).getSqlFilePath(); try { loadURLContent(em, Resources.getResource(sqlFilePath)); } catch (IllegalArgumentException e) { log.info("File for initializing database (" + sqlFilePath + ") not found."); } } /** * Executes the SQL statements contained in the content of the given URL. */ protected void loadURLContent(EntityManager em, URL url) throws IOException { String queries = Resources.toString(url, Charset.defaultCharset()); for (String query : divideQueries(queries)) { try { em.createNativeQuery(query).executeUpdate(); } catch (Exception e) { log.error("Problem running query:\n" + query + "\n", e); } } } private List<String> divideQueries(String queries) { List<String> queryList = new ArrayList<String>(); for (String q : queries.split(";")) { if (! isBlank(q)) { queryList.add(q + ";"); } } return queryList; } @BeforeSuite public void setupLog4J() { BasicConfigurator.configure(); Logger.getRootLogger().setLevel(Level.INFO); } @BeforeClass public void initDatabase() throws IOException, SystemException, RollbackException, HeuristicRollbackException, HeuristicMixedException, NotSupportedException { initEntityManagerFactory(); // Loading the test data for this test SimpleJtaTransactionManagerImpl.getInstance().begin(); EntityManager em = emf.createEntityManager(); em.joinTransaction(); loadTestData(em); SimpleJtaTransactionManagerImpl.getInstance().commit(); em.close(); } protected void initEntityManagerFactory() { Ejb3Configuration cfg = new Ejb3Configuration(); cfg.configure(getHibernateConfigurationFile()); configureDatabase(cfg); configureTransactions(cfg); configureEntities(cfg); emf = cfg.buildEntityManagerFactory(); // Setting the EMF so that it's produced correctly EntityManagerFactoryProducer.setStaticEntityManagerFactory(emf); } protected void configureDatabase(Ejb3Configuration cfg){ cfg.setProperty("hibernate.connection.url", "jdbc:h2:mem:" + this.getClass().getName() + addCompatibilityMode()); cfg.setProperty("connection.provider_class", ConnectionProviderImpl.class.getName()); } protected void configureTransactions(Ejb3Configuration cfg){ cfg.setProperty(Environment.TRANSACTION_MANAGER_STRATEGY, TransactionManagerLookupImpl.class.getName()); cfg.setProperty(AvailableSettings.TRANSACTION_TYPE, "JTA"); } /** * Can be overwritten in subclass and can return null * * @return name of Hibernate XML Configuration file */ protected String getHibernateConfigurationFile() { return "hibernate.test.cfg.xml"; } private String addCompatibilityMode() { if (compatibilityMode != null) { return ";MODE=" + compatibilityMode.getParameterValue(); } return ""; } @AfterClass public void cleanupDatabase() { emf.close(); } @BeforeMethod public void beginTransaction() throws SystemException, NotSupportedException, RollbackException { SimpleJtaTransactionManagerImpl.getInstance().begin(); // There must be at least one sync, otherwise an exception is thrown. SimpleJtaTransactionManagerImpl.getInstance().getTransaction().registerSynchronization(new Synchronization() { @Override public void beforeCompletion() { } @Override public void afterCompletion(int status) { } }); } @AfterMethod public void commitTransaction() throws SystemException, RollbackException, HeuristicRollbackException, HeuristicMixedException { SimpleJtaTransactionManagerImpl.getInstance().commit(); } public void setCompatibilityMode(DbMode compatibilityMode) { this.compatibilityMode = compatibilityMode; } public DbMode getCompatibilityMode() { return compatibilityMode; } }