com.arjuna.ats.jbossatx.BaseTransactionManagerDelegate Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jbossjts-integration
Show all versions of jbossjts-integration
JBossTS: ArjunaJTS integration (atx)
/*
* JBoss, Home of Professional Open Source
* Copyright 2006, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
* 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, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* 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,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
* (C) 2005-2006,
* @author JBoss Inc.
*/
package com.arjuna.ats.jbossatx;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.InvalidTransactionException;
import javax.transaction.NotSupportedException;
import javax.transaction.RollbackException;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import org.jboss.tm.TransactionLocal;
import org.jboss.tm.TransactionLocalDelegate;
import org.jboss.tm.TransactionTimeoutConfiguration;
import java.util.Map;
import java.util.HashMap;
/**
* Delegate for JBoss TransactionManager/TransactionLocalDelegate.
* @author kevin
*/
public abstract class BaseTransactionManagerDelegate implements TransactionManager, TransactionLocalDelegate, TransactionTimeoutConfiguration
{
/**
* Delegate transaction manager.
*/
private final TransactionManager transactionManager ;
/**
* Construct the delegate using the specified transaction manager.
* @param transactionManager The delegate transaction manager.
*/
protected BaseTransactionManagerDelegate(final TransactionManager transactionManager)
{
this.transactionManager = transactionManager ;
}
/**
* Begin a transaction and associate it with the current thread.
*/
public void begin()
throws NotSupportedException, SystemException
{
transactionManager.begin() ;
}
/**
* Commit the current transaction and disassociate from the thread.
*/
public void commit()
throws RollbackException, HeuristicMixedException, HeuristicRollbackException,
SecurityException, IllegalStateException, SystemException
{
transactionManager.commit() ;
}
/**
* Get the transaction status.
* @return the transaction status.
*/
public int getStatus()
throws SystemException
{
return transactionManager.getStatus() ;
}
/**
* Get the transaction associated with the thread.
* @return the transaction or null if none associated.
*/
public Transaction getTransaction()
throws SystemException
{
return transactionManager.getTransaction() ;
}
/**
* Resume the specified transaction.
* @param transaction The transaction to resume.
*/
public void resume(final Transaction transaction)
throws InvalidTransactionException, IllegalStateException, SystemException
{
transactionManager.resume(transaction) ;
}
/**
* Rollback the current transaction and disassociate from the thread.
*/
public void rollback()
throws IllegalStateException, SecurityException, SystemException
{
transactionManager.rollback() ;
}
/**
* Set rollback only on the current transaction.
*/
public void setRollbackOnly()
throws IllegalStateException, SystemException
{
transactionManager.setRollbackOnly() ;
}
/**
* Set the transaction timeout on the current thread.
* @param timeout The transaction timeout.
*/
public void setTransactionTimeout(final int timeout)
throws SystemException
{
transactionManager.setTransactionTimeout(timeout) ;
}
/**
* Suspend the current transaction.
* @return The suspended transaction.
*/
public Transaction suspend()
throws SystemException
{
return transactionManager.suspend() ;
}
/////////////////////////
// TransactionLocalDelegate implementation methods. This part is basically
// stateless, we just delegate down to the object storage on the TransactionImple
// Note this has some interesting effects around Transaction termination. The TransactionImple instance
// lifetime is up to tx termination only. After that getTransaction() on the tm returns a new instance
// representing the same tx. Hence TransactionLocal state goes away magically at tx termination time
// since it's part of the TransactionImple instance. That's what we want and saves writing cleanup code.
// On the down side, since locks use the same storage they go away too. This causes us to have to
// jump through some hoops to deal with locks vanishing and maybe never getting unlocked, see below.
/**
* Does the specified transaction contain a value for the transaction local.
*
* @param transactionLocal The associated transaction local.
* @param transaction The associated transaction.
* @return true if a value exists within the specified transaction, false otherwise.
*/
public boolean containsValue(final TransactionLocal transactionLocal, final Transaction transaction) {
com.arjuna.ats.jta.transaction.Transaction transactionImple = (com.arjuna.ats.jta.transaction.Transaction) transaction;
if(transactionImple.isAlive()) {
return (transactionImple.getTxLocalResource(transactionLocal) != null ? true : false);
} else {
return false;
}
}
/**
* Get value of the transaction local in the specified transaction.
*
* @param transactionLocal The associated transaction local.
* @param transaction The associated transaction.
* @return The value of the transaction local.
*/
public Object getValue(final TransactionLocal transactionLocal, final Transaction transaction) {
com.arjuna.ats.jta.transaction.Transaction transactionImple = (com.arjuna.ats.jta.transaction.Transaction) transaction;
if(transactionImple.isAlive()) {
return transactionImple.getTxLocalResource(transactionLocal);
} else {
return null;
}
}
/**
* Store the value of the transaction local in the specified transaction.
*
* @param transactionLocal The associated transaction local.
* @param transaction The associated transaction.
* @param value The value of the transaction local.
*/
public void storeValue(final TransactionLocal transactionLocal, final Transaction transaction,
final Object value) {
com.arjuna.ats.jta.transaction.Transaction transactionImple = (com.arjuna.ats.jta.transaction.Transaction) transaction;
if(transactionImple.isAlive()) {
transactionImple.putTxLocalResource(transactionLocal, value);
} else {
throw new IllegalStateException("Can't store value in a TransactionLocal after the Transaction has ended");
}
}
/**
* Lock the transaction local in the context of this transaction.
*
* @throws IllegalStateException if the transaction is not active
* @throws InterruptedException if the thread is interrupted
*/
public void lock(final TransactionLocal local, final Transaction transaction)
throws InterruptedException {
com.arjuna.ats.jta.transaction.Transaction transactionImple = (com.arjuna.ats.jta.transaction.Transaction) transaction;
if(transactionImple.isAlive()) {
// There is a race here, as the transaction may terminate between the check
// and the lock attempt. See lock() implementation below.
// It's still possible to lock a dead transaction, but that does no real harm.
TransactionLocalLock lock = findLock(local, transaction);
if(lock.lock(transactionImple)) {
return;
}
}
throw new IllegalStateException("Can't lock a TransactionLocal after the Transaction has ended");
}
/**
* Unlock the transaction local in the context of this transaction
*/
public void unlock(final TransactionLocal local, final Transaction transaction) {
TransactionLocalLock lock = findLock(local, transaction);
lock.unlock();
}
// Lock implementation: This used to use a Synchronization for lock storage, but
// we need to be able to lock things in some states where registration of
// Synchronizations is not permitted. Besides, the JTA 1.1 work gives us a nice
// general object storage mechanism on a TransactionImple, so we use that.
// This is the key under which the map of TransactionLocals to locks is stored.
// Bad things will probably happen if users ever use this key themselves
private final String LOCKS_MAP = "__LOCKS_MAP";
// locate and return the lock for a given TransactionLocal+Transaction tuple.
// create it if it does not exist.
private TransactionLocalLock findLock(final TransactionLocal local, final Transaction transaction) {
com.arjuna.ats.jta.transaction.Transaction transactionImple = (com.arjuna.ats.jta.transaction.Transaction) transaction;
Map locks; //
// ideally for performance we should sync on the tx instance itself but that may have nasty
// side effects so we use something else as the lock object for the sync block
locks = (Map) transactionImple.getTxLocalResource(LOCKS_MAP);
// this is not a double-check locking anti-pattern, because locks
// is a local variable and thus can not leak.
if (locks == null) {
synchronized (LOCKS_MAP) {
// ensure there is a holder for lock storage on the given tx instance.
locks = (Map) transactionImple.getTxLocalResource(LOCKS_MAP);
if (locks == null) {
locks = new HashMap(); //
transactionImple.putTxLocalResource(LOCKS_MAP, locks);
}
}
}
TransactionLocalLock transactionLocalLock = (TransactionLocalLock) locks.get(local);
if (transactionLocalLock == null) {
synchronized (locks) {
// ensure there is a lock for the specified local+tx tuple
transactionLocalLock = (TransactionLocalLock)locks.get(local);
if (transactionLocalLock == null) {
transactionLocalLock = new TransactionLocalLock();
locks.put(local, transactionLocalLock);
}
}
}
return transactionLocalLock;
}
// A class for the storage of individual lock state:
private class TransactionLocalLock
{
/**
* The locking thread.
*/
private Thread lockingThread ;
/**
* The lock count.
*/
private int lockCount ;
/**
* The lock.
*/
private byte[] lock = new byte[0] ;
/**
* Lock the transaction local within the current thread context.
* true on lock acquired, false otherwise
*/
public boolean lock(com.arjuna.ats.jta.transaction.Transaction tx)
{
// The current code in the app server locks the transaction for all, we follow that practice
synchronized(lock)
{
final Thread currentThread = Thread.currentThread() ;
if (currentThread == lockingThread)
{
lockCount++ ;
return true;
}
while (lockingThread != null)
{
try
{
// lock object instances get thrown away at Transaction termination. That makes it impossible
// to call unlock() on them. Searching through them and unlocking them from the transaction
// termination code is a pain and finalizers suck.
// Hence we need to make sure we don't wait forever for a notify
// that will never come. Probably the only thing that will terminate a Transaction in another
// Thread is the transaction reaper, so we wait not longer than the tx timeout plus a fudge factor.
long timeout = 0;
try {
timeout = getTransactionTimeout();
} catch(SystemException e) {}
lock.wait(timeout+1000);
if(!tx.isAlive()) {
// transaction is dead, can't be locked, cleanup
lockingThread = null;
lockCount = 0;
return false;
}
}
catch (final InterruptedException ie) {}
}
lockingThread = currentThread ;
lockCount ++ ;
return true;
}
}
/**
* Unlock the transaction local within the current thread context.
*/
public void unlock()
{
synchronized(lock)
{
if(lockCount == 0 && lockingThread == null) {
// the lock was probably reset by a transaction termination.
// we fail silent to save the caller having to deal with race condition.
return;
}
final Thread currentThread = Thread.currentThread() ;
if (currentThread != lockingThread)
{
throw new IllegalStateException("Unlock called from wrong thread. Locking thread: " + lockingThread +
", current thread: " + currentThread) ;
}
if (--lockCount == 0)
{
lockingThread = null ;
lock.notify() ;
}
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy