package org.jpwh.env; import bitronix.tm.TransactionManagerServices; import bitronix.tm.resource.jdbc.PoolingDataSource; import javax.naming.Context; import javax.naming.InitialContext; import javax.sql.DataSource; import javax.transaction.Status; import javax.transaction.UserTransaction; import java.util.logging.Logger; /** * Provides a database connection pool with the Bitronix JTA transaction * manager (http://docs.codehaus.org/display/BTM/Home). *

* Hibernate will look up the datasource and UserTransaction through * JNDI, that's why you also need a jndi.properties file. A minimal * JNDI context is bundled with and started by Bitronix. *

*/ public class TransactionManagerSetup { public static final String DATASOURCE_NAME = "myDS"; private static final Logger logger = Logger.getLogger(TransactionManagerSetup.class.getName()); protected final Context context = new InitialContext(); protected final PoolingDataSource datasource; public final DatabaseProduct databaseProduct; public TransactionManagerSetup(DatabaseProduct databaseProduct) throws Exception { this(databaseProduct, null); } public TransactionManagerSetup(DatabaseProduct databaseProduct, String connectionURL) throws Exception { logger.fine("Starting database connection pool"); logger.fine("Setting stable unique identifier for transaction recovery"); TransactionManagerServices.getConfiguration().setServerId("myServer1234"); logger.fine("Disabling JMX binding of manager in unit tests"); TransactionManagerServices.getConfiguration().setDisableJmx(true); logger.fine("Disabling transaction logging for unit tests"); TransactionManagerServices.getConfiguration().setJournal("null"); logger.fine("Disabling warnings when the database isn't accessed in a transaction"); TransactionManagerServices.getConfiguration().setWarnAboutZeroResourceTransaction(false); logger.fine("Creating connection pool"); datasource = new PoolingDataSource(); datasource.setUniqueName(DATASOURCE_NAME); datasource.setMinPoolSize(1); datasource.setMaxPoolSize(5); datasource.setPreparedStatementCacheSize(10); // Our locking/versioning tests assume READ COMMITTED transaction // isolation. This is not the default on MySQL InnoDB, so we set // it here explicitly. datasource.setIsolationLevel("READ_COMMITTED"); // Hibernate's SQL schema generator calls connection.setAutoCommit(true) // and we use auto-commit mode when the EntityManager is in suspended // mode and not joined with a transaction. datasource.setAllowLocalTransactions(true); logger.info("Setting up database connection: " + databaseProduct); this.databaseProduct = databaseProduct; databaseProduct.configuration.configure(datasource, connectionURL); logger.fine("Initializing transaction and resource management"); datasource.init(); } public Context getNamingContext() { return context; } public UserTransaction getUserTransaction() { try { return (UserTransaction) getNamingContext() .lookup("java:comp/UserTransaction"); } catch (Exception ex) { throw new RuntimeException(ex); } } public DataSource getDataSource() { try { return (DataSource) getNamingContext().lookup(DATASOURCE_NAME); } catch (Exception ex) { throw new RuntimeException(ex); } } public void rollback() { UserTransaction tx = getUserTransaction(); try { if (tx.getStatus() == Status.STATUS_ACTIVE || tx.getStatus() == Status.STATUS_MARKED_ROLLBACK) tx.rollback(); } catch (Exception ex) { System.err.println("Rollback of transaction failed, trace follows!"); ex.printStackTrace(System.err); } } public void stop() throws Exception { logger.fine("Stopping database connection pool"); datasource.close(); TransactionManagerServices.getTransactionManager().shutdown(); } }