HHH-8739 - Tracking of JTA Synch registration thread
This commit is contained in:
parent
1b3829a95d
commit
9c96e23488
|
@ -41,7 +41,6 @@ public class SynchronizationCallbackCoordinatorTrackingImpl extends Synchronizat
|
|||
|
||||
// magic numbers :(
|
||||
private static final int NO_STATUS = -1;
|
||||
private static final long NO_THREAD_ID = Long.MIN_VALUE;
|
||||
|
||||
private volatile long registrationThreadId;
|
||||
private volatile int delayedCompletionHandlingStatus;
|
||||
|
@ -58,11 +57,8 @@ public class SynchronizationCallbackCoordinatorTrackingImpl extends Synchronizat
|
|||
// 1) on initialization, and
|
||||
// 2) after "after completion" handling is finished.
|
||||
//
|
||||
// Here we use that to "clear out" all 'delayed after-completion" state. The registrationThreadId will
|
||||
// "lazily" be re-populated on the next pulse call to allow for the potential of the next Session transaction
|
||||
// occurring on a different thread (though that transaction would need to completely operate on that thread).
|
||||
// Here we use that to "clear out" all 'delayed after-completion" state.
|
||||
delayedCompletionHandlingStatus = NO_STATUS;
|
||||
registrationThreadId = NO_THREAD_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,178 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
|
||||
* indicated by the @author tags or express copyright attribution
|
||||
* statements applied by the authors. All third-party contributions are
|
||||
* distributed under license by Red Hat Inc.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||
* Lesser General Public License, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this distribution; if not, write to:
|
||||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
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[]{
|
||||
|
||||
};
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue