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 :(
|
// magic numbers :(
|
||||||
private static final int NO_STATUS = -1;
|
private static final int NO_STATUS = -1;
|
||||||
private static final long NO_THREAD_ID = Long.MIN_VALUE;
|
|
||||||
|
|
||||||
private volatile long registrationThreadId;
|
private volatile long registrationThreadId;
|
||||||
private volatile int delayedCompletionHandlingStatus;
|
private volatile int delayedCompletionHandlingStatus;
|
||||||
|
@ -58,11 +57,8 @@ public class SynchronizationCallbackCoordinatorTrackingImpl extends Synchronizat
|
||||||
// 1) on initialization, and
|
// 1) on initialization, and
|
||||||
// 2) after "after completion" handling is finished.
|
// 2) after "after completion" handling is finished.
|
||||||
//
|
//
|
||||||
// Here we use that to "clear out" all 'delayed after-completion" state. The registrationThreadId will
|
// Here we use that to "clear out" all 'delayed after-completion" state.
|
||||||
// "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).
|
|
||||||
delayedCompletionHandlingStatus = NO_STATUS;
|
delayedCompletionHandlingStatus = NO_STATUS;
|
||||||
registrationThreadId = NO_THREAD_ID;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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