/*
* 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 java.util.Map;
import java.util.concurrent.CountDownLatch;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceException;
import javax.persistence.SynchronizationType;
import javax.persistence.TransactionRequiredException;
import javax.transaction.Status;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.engine.transaction.internal.jta.JtaStatusHelper;
import org.hibernate.internal.SessionImpl;
import org.hibernate.jpa.AvailableSettings;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorImpl;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.jta.TestingJtaBootstrap;
import org.hibernate.testing.jta.TestingJtaPlatformImpl;
import org.hibernate.testing.junit4.ExtraAssertions;
import org.junit.Test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* Largely a copy of {@link org.hibernate.test.jpa.txn.JtaTransactionJoiningTest}
*
* @author Steve Ebersole
*/
public class TransactionJoiningTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected void addConfigOptions(Map options) {
super.addConfigOptions( options );
TestingJtaBootstrap.prepare( options );
options.put( AvailableSettings.TRANSACTION_TYPE, "JTA" );
}
@Test
public void testExplicitJoining() throws Exception {
assertFalse( JtaStatusHelper.isActive( TestingJtaPlatformImpl.INSTANCE.getTransactionManager() ) );
EntityManager entityManager = entityManagerFactory().createEntityManager( SynchronizationType.UNSYNCHRONIZED );
TransactionJoinHandlingChecker.validateExplicitJoiningHandling( entityManager );
}
@Test
@SuppressWarnings("EmptyCatchBlock")
public void testExplicitJoiningTransactionRequiredException() throws Exception {
// explicitly calling EntityManager#joinTransaction outside of an active transaction should cause
// a TransactionRequiredException to be thrown
EntityManager entityManager = entityManagerFactory().createEntityManager();
assertFalse("setup problem", JtaStatusHelper.isActive(TestingJtaPlatformImpl.INSTANCE.getTransactionManager()));
try {
entityManager.joinTransaction();
fail( "Expected joinTransaction() to fail since there is no active JTA transaction" );
}
catch (TransactionRequiredException expected) {
}
}
@Test
public void testImplicitJoining() throws Exception {
// here the transaction is started before the EM is opened...
assertFalse( JtaStatusHelper.isActive( TestingJtaPlatformImpl.INSTANCE.getTransactionManager() ) );
TestingJtaPlatformImpl.INSTANCE.getTransactionManager().begin();
EntityManager entityManager = entityManagerFactory().createEntityManager();
SharedSessionContractImplementor session = entityManager.unwrap( SharedSessionContractImplementor.class );
ExtraAssertions.assertTyping( JtaTransactionCoordinatorImpl.class, session.getTransactionCoordinator() );
JtaTransactionCoordinatorImpl transactionCoordinator = (JtaTransactionCoordinatorImpl) session.getTransactionCoordinator();
assertTrue( transactionCoordinator.isSynchronizationRegistered() );
assertTrue( transactionCoordinator.isActive() );
assertTrue( transactionCoordinator.isJoined() );
assertTrue( entityManager.isOpen() );
assertTrue( session.isOpen() );
entityManager.close();
assertFalse( entityManager.isOpen() );
assertFalse( session.isOpen() );
TestingJtaPlatformImpl.INSTANCE.getTransactionManager().commit();
assertFalse( entityManager.isOpen() );
assertFalse( session.isOpen() );
}
@Test
@TestForIssue(jiraKey = "HHH-10807")
public void testIsJoinedAfterMarkedForRollbackImplict() throws Exception {
assertFalse( JtaStatusHelper.isActive( TestingJtaPlatformImpl.INSTANCE.getTransactionManager() ) );
TestingJtaPlatformImpl.INSTANCE.getTransactionManager().begin();
EntityManager entityManager = entityManagerFactory().createEntityManager();
SharedSessionContractImplementor session = entityManager.unwrap( SharedSessionContractImplementor.class );
ExtraAssertions.assertTyping( JtaTransactionCoordinatorImpl.class, session.getTransactionCoordinator() );
JtaTransactionCoordinatorImpl transactionCoordinator = (JtaTransactionCoordinatorImpl) session.getTransactionCoordinator();
assertTrue( transactionCoordinator.isSynchronizationRegistered() );
assertTrue( transactionCoordinator.isActive() );
assertTrue( transactionCoordinator.isJoined() );
assertTrue( entityManager.isOpen() );
assertTrue( session.isOpen() );
transactionCoordinator.getTransactionDriverControl().markRollbackOnly();
assertTrue( transactionCoordinator.isActive() );
assertTrue( transactionCoordinator.isJoined() );
assertTrue( entityManager.isJoinedToTransaction() );
TestingJtaPlatformImpl.INSTANCE.getTransactionManager().rollback();
entityManager.close();
assertFalse( entityManager.isOpen() );
assertFalse( session.isOpen() );
}
@Test
@TestForIssue(jiraKey = "HHH-10807")
public void testIsJoinedAfterMarkedForRollbackExplicit() throws Exception {
assertFalse( JtaStatusHelper.isActive( TestingJtaPlatformImpl.INSTANCE.getTransactionManager() ) );
EntityManager entityManager = entityManagerFactory().createEntityManager( SynchronizationType.UNSYNCHRONIZED );
SharedSessionContractImplementor session = entityManager.unwrap( SharedSessionContractImplementor.class );
assertTrue( entityManager.isOpen() );
assertTrue( session.isOpen() );
ExtraAssertions.assertTyping( JtaTransactionCoordinatorImpl.class, session.getTransactionCoordinator() );
JtaTransactionCoordinatorImpl transactionCoordinator = (JtaTransactionCoordinatorImpl) session.getTransactionCoordinator();
TestingJtaPlatformImpl.INSTANCE.getTransactionManager().begin();
entityManager.joinTransaction();
assertTrue( transactionCoordinator.isSynchronizationRegistered() );
assertTrue( transactionCoordinator.isActive() );
assertTrue( transactionCoordinator.isJoined() );
transactionCoordinator.getTransactionDriverControl().markRollbackOnly();
assertTrue( transactionCoordinator.isActive() );
assertTrue( transactionCoordinator.isJoined() );
assertTrue( entityManager.isJoinedToTransaction() );
TestingJtaPlatformImpl.INSTANCE.getTransactionManager().rollback();
entityManager.close();
assertFalse( entityManager.isOpen() );
assertFalse( session.isOpen() );
}
@Test
public void testCloseAfterCommit() throws Exception {
assertFalse( JtaStatusHelper.isActive( TestingJtaPlatformImpl.INSTANCE.getTransactionManager() ) );
TestingJtaPlatformImpl.INSTANCE.getTransactionManager().begin();
EntityManager entityManager = entityManagerFactory().createEntityManager();
SharedSessionContractImplementor session = entityManager.unwrap( SharedSessionContractImplementor.class );
ExtraAssertions.assertTyping( JtaTransactionCoordinatorImpl.class, session.getTransactionCoordinator() );
JtaTransactionCoordinatorImpl transactionCoordinator = (JtaTransactionCoordinatorImpl) session.getTransactionCoordinator();
assertTrue( transactionCoordinator.isSynchronizationRegistered() );
assertTrue( transactionCoordinator.isActive() );
assertTrue( transactionCoordinator.isJoined() );
assertTrue( entityManager.isOpen() );
assertTrue( session.isOpen() );
TestingJtaPlatformImpl.INSTANCE.getTransactionManager().commit();
assertTrue( entityManager.isOpen() );
assertTrue( session.isOpen() );
entityManager.close();
assertFalse( entityManager.isOpen() );
assertFalse( session.isOpen() );
}
@Test
public void testImplicitJoiningWithExtraSynchronization() throws Exception {
assertFalse( JtaStatusHelper.isActive( TestingJtaPlatformImpl.INSTANCE.getTransactionManager() ) );
TestingJtaPlatformImpl.INSTANCE.getTransactionManager().begin();
EntityManager entityManager = entityManagerFactory().createEntityManager();
SharedSessionContractImplementor session = entityManager.unwrap( SharedSessionContractImplementor.class );
ExtraAssertions.assertTyping( JtaTransactionCoordinatorImpl.class, session.getTransactionCoordinator() );
JtaTransactionCoordinatorImpl transactionCoordinator = (JtaTransactionCoordinatorImpl) session.getTransactionCoordinator();
assertTrue( transactionCoordinator.isSynchronizationRegistered() );
assertTrue( transactionCoordinator.isActive() );
assertTrue( transactionCoordinator.isJoined() );
entityManager.close();
TestingJtaPlatformImpl.INSTANCE.getTransactionManager().commit();
}
/**
* In certain JTA environments (JBossTM, etc.), a background thread (reaper)
* can rollback a transaction if it times out. These timeouts are rare and
* typically come from server failures. However, we need to handle the
* multi-threaded nature of the transaction afterCompletion action.
* Emulate a timeout with a simple afterCompletion call in a thread.
* See HHH-7910
*/
@Test
@TestForIssue(jiraKey = "HHH-7910")
public void testMultiThreadTransactionTimeout() throws Exception {
TestingJtaPlatformImpl.INSTANCE.getTransactionManager().begin();
EntityManager em = entityManagerFactory().createEntityManager();
final SessionImpl sImpl = em.unwrap( SessionImpl.class );
final CountDownLatch latch = new CountDownLatch( 1 );
Thread thread = new Thread() {
public void run() {
((JtaTransactionCoordinatorImpl)sImpl.getTransactionCoordinator()).getSynchronizationCallbackCoordinator()
.afterCompletion( Status.STATUS_ROLLEDBACK );
latch.countDown();
}
};
thread.start();
latch.await();
boolean caught = false;
try {
em.persist( new Book( "The Book of Foo", 1 ) );
}
catch ( PersistenceException e ) {
caught = e.getCause().getClass().equals( HibernateException.class );
}
assertTrue( caught );
// Ensure that the connection was closed by the background thread.
caught = false;
try {
em.createQuery( "from Book" ).getResultList();
}
catch ( PersistenceException e ) {
// HHH-9312
caught = true;
}catch (Exception e){
caught = true;
}
assertTrue( caught );
TestingJtaPlatformImpl.INSTANCE.getTransactionManager().rollback();
em.close();
}
@Override
public Class[] getAnnotatedClasses() {
return new Class[] {
Book.class
};
}
}