/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.transaction;
import javax.persistence.EntityManager;
import javax.transaction.RollbackException;
import javax.transaction.Status;
import javax.transaction.SystemException;
import java.util.Map;
import org.hibernate.HibernateException;
import org.hibernate.jpa.AvailableSettings;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.junit.Ignore;
import org.junit.Test;
import org.hibernate.testing.jta.TestingJtaBootstrap;
import org.hibernate.testing.jta.TestingJtaPlatformImpl;
import static org.junit.Assert.fail;
/**
* Recreate test failure that occurs when three threads share the same entity manager and
* one of them calls set rollback only on its transaction.
*
* @author Scott Marlow
*/
public class TransactionRolledBackInDifferentThreadTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected void addConfigOptions(Map options) {
super.addConfigOptions( options );
TestingJtaBootstrap.prepare( options );
options.put( AvailableSettings.TRANSACTION_TYPE, "JTA" );
}
@Test
public void testTransactionRolledBackInDifferentThreadFailure() throws Exception {
/**
* The three test threads share the same entity manager.
* The main test thread creates an EntityManager, joins it to the transaction and ends the transaction.
* Test thread 1 joins the EntityManager to its transaction, sets rollbackonly and ends the transaction.
* Test thread 2 attempts to join the EntityManager to its transaction but will fail with a
* HibernateException("Transaction was rolled back in a different thread!")
*/
// main test thread
TestingJtaPlatformImpl.INSTANCE.getTransactionManager().begin();
final EntityManager em = entityManagerFactory().createEntityManager();
em.joinTransaction();
TestingJtaPlatformImpl.INSTANCE.getTransactionManager().commit();
// will be set to the failing exception
final HibernateException[] transactionRolledBackInDifferentThreadException = new HibernateException[2];
transactionRolledBackInDifferentThreadException[0] = transactionRolledBackInDifferentThreadException[1] = null;
// background test thread 1
final Runnable run1 = new Runnable() {
@Override
public void run() {
try {
TestingJtaPlatformImpl.INSTANCE.getTransactionManager().begin();
em.joinTransaction();
TestingJtaPlatformImpl.INSTANCE.getTransactionManager().setRollbackOnly();
TestingJtaPlatformImpl.INSTANCE.getTransactionManager().commit();
}
catch (javax.persistence.PersistenceException e) {
if ( e.getCause() instanceof HibernateException &&
e.getCause().getMessage().equals( "Transaction was rolled back in a different thread!" ) ) {
/**
* Save the exception for the main test thread to fail
*/
e.printStackTrace(); // show the error first
transactionRolledBackInDifferentThreadException[0] = (HibernateException) e.getCause();
}
}
catch (RollbackException ignored) {
// expected to see RollbackException: ARJUNA016053: Could not commit transaction.
}
catch (Throwable throwable) {
throwable.printStackTrace();
}
finally {
try {
if ( TestingJtaPlatformImpl.INSTANCE.getTransactionManager()
.getStatus() != Status.STATUS_NO_TRANSACTION ) {
TestingJtaPlatformImpl.INSTANCE.getTransactionManager().rollback();
}
}
catch (SystemException ignore) {
}
}
}
};
// test thread 2
final Runnable run2 = new Runnable() {
@Override
public void run() {
try {
TestingJtaPlatformImpl.INSTANCE.getTransactionManager().begin();
/**
* the following call to em.joinTransaction() will throw:
* org.hibernate.HibernateException: Transaction was rolled back in a different thread!
*/
em.joinTransaction();
TestingJtaPlatformImpl.INSTANCE.getTransactionManager().commit();
}
catch (javax.persistence.PersistenceException e) {
if ( e.getCause() instanceof HibernateException &&
e.getCause().getMessage().equals( "Transaction was rolled back in a different thread!" ) ) {
/**
* Save the exception for the main test thread to fail
*/
e.printStackTrace(); // show the error first
transactionRolledBackInDifferentThreadException[1] = (HibernateException) e.getCause();
}
}
catch (Throwable throwable) {
throwable.printStackTrace();
}
finally {
try {
if ( TestingJtaPlatformImpl.INSTANCE.getTransactionManager()
.getStatus() != Status.STATUS_NO_TRANSACTION ) {
TestingJtaPlatformImpl.INSTANCE.getTransactionManager().rollback();
}
}
catch (SystemException ignore) {
}
}
}
};
Thread thread = new Thread( run1, "test thread1" );
thread.start();
thread.join();
Thread thread2 = new Thread( run2, "test thread2" );
thread2.start();
thread2.join();
// show failure for exception caught in run2.run()
if ( transactionRolledBackInDifferentThreadException[0] != null
|| transactionRolledBackInDifferentThreadException[1] != null )
{
fail(
"failure in test thread 1 = " +
( transactionRolledBackInDifferentThreadException[0] != null ?
transactionRolledBackInDifferentThreadException[0].getMessage() :
"(none)" )
+ ", failure in test thread 2 = " +
( transactionRolledBackInDifferentThreadException[1] != null ?
transactionRolledBackInDifferentThreadException[1].getMessage() :
"(none)" )
);
}
em.close();
}
@Override
public Class[] getAnnotatedClasses() {
return new Class[] {
};
}
}