Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   /*
    * 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.
   */
  /*
   * Copyright (C) 2002,
   *
   * Hewlett-Packard Arjuna Labs,
   * Newcastle upon Tyne,
   * Tyne and Wear,
   * UK.
   *
   * $Id: TransactionImple.java 2342 2006-03-30 13:06:17Z  $
   */
  
  package com.arjuna.ats.internal.jta.transaction.arjunacore;
  
  import java.util.HashMap;
  import java.util.Map;
  
  import  javax.transaction.RollbackException;
  import  javax.transaction.Status;
  
  
  /*
   * Is given an AtomicAction, but uses the TwoPhaseCoordinator aspects of it to
   * ensure that the thread association continues.
   */
  public class TransactionImple implements javax.transaction.Transaction,
  {
  
  	/*
  	 * Only works with AtomicAction and TwoPhaseCoordinator.
  	 */

Create a new transaction with the specified timeout.
  
  
  	public TransactionImple(int timeout)
  	{
  
  		.begin(timeout);
  
  		 = new Hashtable();
  		 = 0;
  
           = Collections.synchronizedMap(new HashMap());
     }

Overloads Object.equals()
 
 
 	public boolean equals(Object obj)
 	{
 		if (..isTraceEnabled()) {
             ..trace("TransactionImple.equals");
         }
 
 		if (obj == null)
 			return false;
 
 		if (obj == this)
 			return true;
 
 		if (obj instanceof TransactionImple)
 		{
 			/*
 			 * If we can't get either coordinator to compare, then assume
 			 * transactions are different.
 			 */
 
 			try
 			{
 
 			}
 			catch (Exception e)
 			{
 			}
 		}
 
 		return false;
 	}

Return -1 if we fail.
 
 
 	public int hashCode()
 	{
 		if ( == null)
 			return -1;
 		else
 	}

The JTA specification is vague on whether the calling thread can have any transaction associated with it. It does say that it need not have the same transaction as this one. We could call suspend prior to making these calls, but for now we do nothing.
 

We should never throw a HeuristicRollbackException because if we get a HeuristicRollback from a resource, and can successfully rollback the other resources, this is then the same as having simply been forced to rollback the transaction during phase 1.
 
 
 	public void commit() throws javax.transaction.RollbackException,
 			javax.transaction.HeuristicMixedException,
 			javax.transaction.HeuristicRollbackException,
 			java.lang.SecurityException, javax.transaction.SystemException,
 	{
 		if (..isTraceEnabled()) {
             ..trace("TransactionImple.commit");
         }
 
 		if ( != null)
 		{
 			switch (.status())
 			{
 				break;
 			default:
 			}
 
 			/*
 			 * Call end on any suspended resources. If this fails, then the
 			 * transaction will be rolled back.
 			 */
 
 			if (!endSuspendedRMs())
 
 			// use end of TwoPhaseCoordinator to avoid thread changes.
 
 			int status = .end(true);
 
 			TransactionImple.removeTransaction(this);
 
 			switch (status)
 			{
 			case .// in case of async commit
 				break;
 				throw new javax.transaction.HeuristicMixedException();
 				throw new javax.transaction.HeuristicMixedException();
 				RollbackException rollbackException = new RollbackException(
                         ..get_transaction_arjunacore_commitwhenaborted() );
 				{
 					rollbackException.initCause(
 				}
 				throw rollbackException;
 			default:
 			}
 		}
 		else
 	}
 
 	public void rollback() throws java.lang.IllegalStateException,
 			java.lang.SecurityException, javax.transaction.SystemException
 	{
 		if (..isTraceEnabled()) {
             ..trace("TransactionImple.rollback");
         }
 
 		if ( != null)
 		{
 			switch (.status())
 			{
 				break;
 			default:
 			}
 
 			/*
 			 * Call end on any suspended resources. If this fails, then there's
 			 * not a lot else we can do because the transaction is about to roll
 			 * back anyway!
 			 */
 
 			boolean endSuspendedFailed = !endSuspendedRMs();
 
 			if (endSuspendedFailed)
 			{
 			}
 
 			int outcome = .cancel(); // use cancel of
 			// TwoPhaseCoordinator to
 			// avoid thread changes.
 
 			TransactionImple.removeTransaction(this);
 
 			switch (outcome)
 			{
 			case .// in case of async rollback
 				break;
 			default:
 								+ ActionStatus.stringForm(outcome));
 			}
 
 			if (endSuspendedFailed)
 		}
 		else
 	}
 
 	public void setRollbackOnly() throws java.lang.IllegalStateException,
 			javax.transaction.SystemException
 	{
 	    if (..isTraceEnabled()) {
             ..trace("TransactionImple.setRollbackOnly");
         }
 
 	    if ( != null)
 	    {
 	        /*
 	         * Try to mark the transaction as rollback-only. If that fails figure out why.
 	         */
 	        
 	        if (!.preventCommit())
 	        {
 	            switch (getStatus())
                     {
                     case Status.STATUS_ROLLEDBACK:
                     case Status.STATUS_ROLLING_BACK:
                         break;
                     case Status.STATUS_PREPARING:
                     case Status.STATUS_PREPARED:
                         throw new InvalidTerminationStateException..get_transaction_arjunacore_invalidstate() );
                     default:
                         throw new InactiveTransactionException..get_transaction_arjunacore_inactive(.get_uid()) );
                     }
 	        }
 	        else
 	        {
 	            // keep a record of why we are rolling back i.e. who called us first, it's a useful debug aid.
 	            if( == null)
 	            {
 	                 = new Throwable("setRollbackOnly called from:");
 	            }
 	        }
 	    }
 	    else
 	}
 
 	public int getStatus() throws javax.transaction.SystemException
 	{
 		if (..isTraceEnabled()) {
             ..trace("TransactionImple.getStatus");
         }
 
 		int status = javax.transaction.Status.STATUS_NO_TRANSACTION;
 		
 		if ( != null)
 		{
 			return StatusConverter.convert(.status());
 		}
 
 		return status;
 	}
 
 	public void registerSynchronization(javax.transaction.Synchronization sync)
 			throws javax.transaction.RollbackException,
 			java.lang.IllegalStateException, javax.transaction.SystemException
 	{
 		if (..isTraceEnabled()) {
             ..trace("TransactionImple.registerSynchronization");
         }
 
 		if (sync == null)
 		{
 			throw new javax.transaction.SystemException(
 					"TransactionImple.registerSynchronization - " + ..get_transaction_arjunacore_nullparam() );
 		}
 
 	}
 
 	// package-private method also for use by
 	// TransactionSynchronizationRegistryImple
 			throws javax.transaction.RollbackException,
 			java.lang.IllegalStateException, javax.transaction.SystemException
 	{
 		if ( != null)
 		{
 			if (.addSynchronization(synchronizationImple) != .)
 			{
 				int status = .status();
 
 				switch (status)
 				{
 					throw new javax.transaction.RollbackException( ..get_transaction_arjunacore_syncwhenaborted() );
 				default:
 					throw new IllegalStateException(
                             ..get_transaction_arjunacore_syncsnotallowed() + ActionStatus.stringForm(status));
 				}
 			}
 		}
 		else
 	}

This is the JTA compliant version of the method. However, you had better know that your XAResource and family are truly compliant implementations. If they aren't then we may fail gracefully (e.g., some versions of Oracle don't work with arbitrary Xid implementations!) If the family isn't compliant, then you should use the other method and pass through a relevant XAModifier, which should address the issues we have already come across.
 
 
 	public boolean enlistResource(XAResource xaResthrows RollbackException,
 			IllegalStateException, javax.transaction.SystemException
 	{
 		return enlistResource(xaResnull);
 	}
 
 	public boolean enlistResource(XAResource xaResObject[] params)
 			throws RollbackException, IllegalStateException,
 			javax.transaction.SystemException
 	{
 		if (..isTraceEnabled()) {
             ..trace("TransactionImple.enlistResource ( " + xaRes + " )");
         }
 
 		if (xaRes == null)
 			throw new javax.transaction.SystemException(
 					"TransactionImple.enlistResource - " + ..get_transaction_arjunacore_nullres() );
 
 		int status = getStatus();
 
 		switch (status)
 		{
 		case javax.transaction.Status.STATUS_MARKED_ROLLBACK:
 			throw new RollbackException(
 					"TransactionImple.enlistResource - " + ..get_transaction_arjunacore_invalidstate() );
 		case javax.transaction.Status.STATUS_ACTIVE:
 			break;
 		default:
 		}
 
 		XAModifier theModifier = null;
 
 		if (params != null)
 		{
 			if (params.length >=  + 1)
 			{
 				if (params[instanceof XAModifier)
 				{
 					theModifier = (XAModifierparams[];
 				}
 			}
 		}
 
 		try
 		{
 			/*
 			 * For each transaction we maintain a list of resources registered
 			 * with it. Each element on this list also contains a list of
 			 * threads which have registered this resource, and what their XID
 			 * was for that registration.
 			 */
 
 			TxInfo info = null;
 
 			/*
 			 * Have we seen this specific resource instance before? Do this
 			 * trawl first before checking the RM instance later. Saves time.
 			 */
 
 			try
 			{
 				synchronized (this)
 				{
 					info = (TxInfo.get(xaRes);
 
 					if (info == null)
 					{
 						/*
 						 * Null info means it's not in the main resources list,
 						 * but may be in the duplicates.
 						 */
 
 						info = (TxInfo.get(xaRes);
 					}
 				}
 
 				if (info != null)
 				{
 					switch (info.getState())
 					{
 					{
 						/*
 						 * Have seen resource before, so do a resume. The
 						 * Resource instance will still be registered with the
 						 * transaction though.
 						 */
 
 						int xaStartResume = ((theModifier == null) ? .
 								: theModifier
 
 						xaRes.start(info.xid(), xaStartResume);
 
 
 						synchronized (this)
 						{
 						}
 
 						return true// already registered resource with this
 						// transaction!
 					}
 					{
 						/*
 						 * Already active on this transaction.
 						 */
 
 						return true;
 					}
 					{
 						/*
 						 * Resource was associated, but was presumably delisted.
 						 */
 
 						int xaStartJoin = ((theModifier == null) ? .
 								: theModifier
 
 						xaRes.start(info.xid(), xaStartJoin);
 
 
 						return true;
 					}
 					default:
 					{
 						// Note: this exception will be caught by our catch
 						// block
 
 						throw new IllegalStateException(
 								"TransactionImple.enlistResource - " + ..get_transaction_arjunacore_illresstate()
 										+ ":" + info.getState());
 					}
 					}
 				}
 			}
 			catch (IllegalStateException ex)
 			{
 				throw ex// we threw it in the first place
 			}
 			catch (XAException exp)
 			{
 				if (info != null)
 
                 ..warn_transaction_arjunacore_enlisterror("TransactionImple.enlistResource",
                         XAHelper.printXAErrorCode(exp));
 
 				return false;
 			}
 
 			// if (threadIsActive(xaRes))
 			// return true; // this thread has already registered a resource for
 			// this db
 
 			/*
 			 * We definitely haven't seen this specific resource instance
 			 * before, but that doesn't mean that we haven't seen the RM it is
 			 * connected to.
 			 */
 
 			Xid xid = null;
 			TxInfo existingRM = isNewRM(xaRes);
 
 			if (existingRM == null)
 			{
 				/*
 				 * New RM, so create xid with new branch.
 				 */
 
 				boolean branchRequired = true;
 
 				synchronized (this)
 				{
 					if (.size() == 0)// first ever, so no need for
 					// branch
 					{
 						// branchRequired = false;
 						branchRequired = true;
 					}
 				}
 
 				xid = createXid(branchRequiredtheModifierxaRes);
 
 				boolean associatedWork = false;
 				int retry = 20;
 
 				/*
 				 * If another process has (or is about to) create the same
 				 * transaction association then we will probably get a failure
 				 * during start with XAER_DUPID. We know this must be due to
 				 * another server, since we keep track of our own registrations.
 				 * So, if this happens we create a new transaction branch and
 				 * try again.
 				 *
 				 * To save time we could always just create branches by default.
 				 *
 				 * Is there a benefit to a zero branch?
 				 */
 
 				while (!associatedWork)
 				{
 					try
 					{
 						{
 							int timeout = .getTimeout();
 
 							if (timeout > 0)
 							{
 								try
 								{
 									xaRes.setTransactionTimeout(timeout);
 								}
 								catch (XAException te)
 								{
                                     ..warn_transaction_arjunacore_timeouterror("TransactionImple.enlistResource",XAHelper.xidToString(xid),  XAHelper.printXAErrorCode(te), te);
 								}
 							}
 						}
 
 						int xaStartNormal = ((theModifier == null) ? .
 								: theModifier
 
 
                         // Pay attention now, this bit is hairy. We need to add a new AbstractRecord (XAResourceRecord)
                         // to the BasicAction, which will thereafter drive its completion. However, the transaction
                         // core is not directly XA aware, so it's our job to start the XAResource. Problem is, if
                         // adding the record fails, BasicAction will never end the resource via the XAResourceRecord,
                         // so we must do so directly.  start may fail due to dupl xid or other reason, and transactions
                         // may rollback async, for which reasons we can't call add before start.
                         // The xid will change on each pass of the loop, so we need to create a new record on each pass.
                         // The add will fail in the case of multiple last resources being disallowed
                         // see JBTM-362 and JBTM-363
                         AbstractRecord abstractRecord = createRecord(xaResparamsxid);
                         if(abstractRecord != null) {
                             xaRes.start(xidxaStartNormal);
                             if(.add(abstractRecord) == .) {
                                 .put(xaResnew TxInfo(xid));
                                 return true// dive out, no need to set associatedWork = true;
                             } else {
                                 // we called start on the resource, but _theTransaction did not accept it.
                                 // we therefore have a mess which we must now clean up by ensuring the start is undone:
                                 abstractRecord.topLevelAbort();
                             }
                         }
 
                         // if we get to here, something other than a failure of xaRes.start probably went wrong.
                         // so we don't loop and retry, we just give up.
                         markRollbackOnly();
                         return false;
 
 					}
 					catch (XAException e)
 					{
 						// transaction already created by another server
 
 						if ((e.errorCode == .)
 								|| (e.errorCode == .))
 						{
 							if (retry > 0)
 								xid = createXid(truetheModifierxaRes);
 
 							retry--;
 						}
 						else
 						{
 							/*
 							 * Can't do start, so set transaction to rollback
 							 * only.
 							 */
 
                             ..warn_transaction_arjunacore_enliststarterror("TransactionImple.enlistResource",
                                     XAHelper.xidToString(xid), XAHelper.printXAErrorCode(e), e);
 
 
 							throw e;
 						}
 
 						if (retry < 0)
 						{
                             ..warn_transaction_arjunacore_enliststarterror("TransactionImple.enlistResource",
                                     XAHelper.xidToString(xid), XAHelper.printXAErrorCode(e), e);
 
 
 							throw new javax.transaction.SystemException(
 									"TransactionImple.enlistResource - XAResource.start "
 											+ ": " + xid);
 						}
 					}
 				}
 			}
 			else
 			{
 				/*
 				 * Have seen this RM before, so ignore this instance. The first
 				 * registered RM instance will be used to drive the transaction
 				 * completion. We add it to the duplicateResource list so we can
 				 * delist it correctly later though.
 				 */
 
 				/*
 				 * Re-create xid.
 				 */
 
 				xid = existingRM.xid();
 
 				try
 				{
 					int xaStartJoin = ((theModifier == null) ? .
 
 					xaRes.start(xidxaStartJoin);
 				}
 				catch (XAException ex)
 				{
                     ..warn_transaction_arjunacore_xastart("TransactionImple.enlistResource - xa_start ",
                             XAHelper.xidToString(xid), XAHelper.printXAErrorCode(ex), ex);
 
 
 					throw ex;
 				}
 
 				/*
 				 * Add to duplicate resources list so we can keep track of it
 				 * (particularly if we later have to delist).
 				 */
 
 				.put(xaResnew TxInfo(xid));
 
 				return true;
 			}
 
             return false;
         }
 		catch (Exception e)
 		{
 
 			/*
 			 * Some exceptional condition arose and we probably could not enlist
 			 * the resouce. So, for safety mark the transaction as rollback
 			 * only.
 			 */
 
 
 			return false;
 		}
 	}

    
Attempt to create an AbstractRecord wrapping the given XAResource. Return null if this fails, or is diallowed by the current configuration of multiple last resource behaviour.

Parameters:
xaRes
params
xid
Returns:
 
     private AbstractRecord createRecord(XAResource xaResObject[] paramsXid xid)
     {
         final AbstractRecord record;
         if ((xaRes instanceof LastResourceCommitOptimisation)
                 || (( != null) && 
                 .isInstance(xaRes)))
         {
             record = new LastResourceRecord(new XAOnePhaseResource(xaResxidparams));
         }
         else
         {
             record = new XAResourceRecord(thisxaResxidparams);
         }
 
         return record;
     }
 
     /*
       * Do we have to unregister resources? Assume not as it would not make much
       * sense otherwise!
       */
 
 	public boolean delistResource(XAResource xaResint flags)
 			throws IllegalStateException, javax.transaction.SystemException
 	{
 		if (..isTraceEnabled()) {
             ..trace("TransactionImple.delistResource ( " + xaRes + " )");
         }
 
 		if (xaRes == null)
 			throw new javax.transaction.SystemException(
 					"TransactionImple.delistResource - " + ..get_transaction_arjunacore_nullres() );
 
 		int status = getStatus();
 
 		switch (status)
 		{
 		case javax.transaction.Status.STATUS_ACTIVE:
 			break;
 		case javax.transaction.Status.STATUS_MARKED_ROLLBACK:
 			break;
 		default:
 		}
 
 		TxInfo info = null;
 
 		try
 		{
 			synchronized (this)
 			{
 				info = (TxInfo.get(xaRes);
 
 				if (info == null)
 					info = (TxInfo.get(xaRes);
 			}
 
 			if (info == null)
 			{
                 ..warn_transaction_arjunacore_unknownresource("TransactionImple.delistResource");
 
 				return false;
 			}
 			else
 			{
 				boolean optimizedRollback = false;
 
 				try
 				{
 					/*
 					 * If we know the transaction is going to rollback, then we
 					 * can try to rollback the RM now. Just an optimisation.
 					 */
 
 					if (status == javax.transaction.Status.STATUS_MARKED_ROLLBACK)
 					{
 						if (XAUtils.canOptimizeDelist(xaRes))
 						{
 							xaRes.end(info.xid(), .);
 							xaRes.rollback(info.xid());
 
 
 							optimizedRollback = true;
 						}
 					}
 				}
 				catch (Exception e)
 				{
 					// failed, so try again when transaction does rollback
 				}
 
 				switch (info.getState())
 				{
 				{
 					if ((flags & .) != 0)
 					{
 						xaRes.end(info.xid(), .);
 					}
 					else
 					{
 						if ((flags & .) != 0)
 						{
 							xaRes.end(info.xid(), .);
 
 							synchronized (this)
 							{
 							}
 						}
 						else
 						{
 							xaRes.end(info.xid(), .);
 						}
 					}
 				}
 					break;
 				{
 					if ((flags & .) != 0)
 					{
 						// Oracle barfs if we don't send resume first, despite
 						// what XA says!
 
 						if (XAUtils.mustEndSuspendedRMs(xaRes))
 							xaRes.start(info.xid(), .);
 
 						xaRes.end(info.xid(), .);
 
 						synchronized (this)
 						{
 						}
 					}
 					else
 					{
 						if ((flags & .) != 0)
 						{
 							// Note: this exception will be caught by our catch
 							// block
 
 							throw new IllegalStateException(
 									"TransactionImple.delistResource - " + ..get_transaction_arjunacore_ressuspended() );
 						}
 						else
 						{
 							xaRes.end(info.xid(), .);
 
 							synchronized (this)
 							{
 							}
 						}
 					}
 				}
 					break;
 				default:
 				{
 					if (!optimizedRollback)
 						throw new IllegalStateException(
 								"TransactionImple.delistResource - " +
                                         ..get_transaction_arjunacore_illresstate()
 										+ ":" + info.getState());
 				}
 				}
 
 				info = null;
 
 				return true;
 			}
 		}
 		catch (IllegalStateException ex)
 		{
 			throw ex;
 		}
 		catch (XAException exp)
 		{
 			if (info != null)
 
 			/*
 			 * For safety mark the transaction as rollback only.
 			 */
 
 
             ..warn_transaction_arjunacore_delistresource("TransactionImple.delistResource", XAHelper.printXAErrorCode(exp), exp);
 
 			return false;
 		}
 		catch (Exception e)
 		{
             ..warn_transaction_arjunacore_delistgeneral("TransactionImple.delistResource"e);
 
 			/*
 			 * Some exception occurred and we probably could not delist the
 			 * resource. So, for safety mark the transaction as rollback only.
 			 */
 
 
 			return false;
 		}
 	}
 
 	public final Uid get_uid()
 	{
 	}
 
 	public final Xid getTxId ()
 	{
 	    Xid res = baseXid();
 	    
 	    if (res == null)
 	        res = new XidImple();
 	    
 	    return res;
 	}
 	
 	public String toString()
 	{
 		if ( == null)
 			return "TransactionImple < ac, NoTransaction >";
 		else
 		{
 			return "TransactionImple < ac, " +  + " >";
 		}
 	}
 
	public int getXAResourceState(XAResource xaRes)
		int state = .;
		if (xaRes != null)
			TxInfo info = (TxInfo.get(xaRes);
			if (info == null)
				info = (TxInfo.get(xaRes);
			if (info != null)
				state = info.getState();
		return state;
	public static final TransactionImple getTransaction()
		TransactionImple tx = null;
		final BasicAction current = BasicAction.Current();
		if (current != null)
			final Uid txid = current.get_uid();
			if (tx == null)
				tx = new TransactionImple(current);
		return tx;
	public static final TransactionImple getTransaction(Uid id)
		try
			if (id != null)
			else
				return null;
		catch (Exception e)
			return new TransactionImple(null);
	// get a key-value pair from a transaction specific Map
		return .get(key);
	// store a key-value pair in the scope of the transaction.
	public void putTxLocalResource(Object keyObject value)
		.put(keyvalue);
    /*
     * For JBossAS integration TransactionLocal implementation, we need to know if a tx has been
     * resolved yet or not. We could use getStatus() and a case stmt, but since an instance is
     * removed from _transactions on completion this is just as effective.
     * @param tx
     * @return
     */
    public boolean isAlive() {
        try {
            if( != null) {
                return .containsKey(this.get_uid());
            } else {
                return false;
            }
        } catch(NullPointerException e) {
            return false// there is no tx/action, therefore it's not alive.
        }
    }
    protected TransactionImple()
		this(null);
	}

Create a new TransactionImple representation of a specified transaction.
		try
			if (curr == null)
			else
		if ( != null)
             = Collections.synchronizedMap(new HashMap());
		else
			 = null;
	}

Does the same as commit, but also changes the thread-to-tx association.
	protected void commitAndDisassociate()
			throws javax.transaction.RollbackException,
			javax.transaction.HeuristicMixedException,
			javax.transaction.HeuristicRollbackException,
			java.lang.SecurityException, javax.transaction.SystemException,
		if (..isTraceEnabled()) {
            ..trace("TransactionImple.commitAndDisassociate");
        }
		try
			if ( != null)
						.abort(); // assure thread disassociation
						throw new javax.transaction.RollbackException( ..get_transaction_arjunacore_inactive(.get_uid()) );
					case .// in case of async commit
						.commit(true); // assure thread disassociation
						return;
                final Throwable preexistingRollbackOnlyCallerStacktrace = ;
				switch (.commit(true))
					case .// in case of async commit
						break;
						throw new javax.transaction.HeuristicMixedException();
						throw new javax.transaction.HeuristicMixedException();
                        RollbackException rollbackException = new RollbackException( ..get_transaction_arjunacore_commitwhenaborted() );
                        // Don't mess with the following flow until you've read JBTM-575 in its entirety.
                        if(preexistingRollbackOnlyCallerStacktrace != null) {
                            // we rolled back because the user (note: NOT a beforeCompletion) explicitly told us not to commit.
                            // beforeCompletions should not be called in such case anyhow, so getDeferredThrowable is irrelevant.
                            // Attach the trace of who did that for debug:
                            rollbackException.initCause(preexistingRollbackOnlyCallerStacktrace);
                        } else if(.getDeferredThrowable() != null) {
                            // problems occurring during beforeCompletion (the only place deferredThrowable is set) take priority
                            // over 'immediately prior' (i.e. after the commit call - likely from within beforeCompletion) calls to setRollbackOnly
                            // despite the small chance that the causal relationship is not infact valid
                            rollbackException.initCause(.getDeferredThrowable());
else if( != null) {
                            // we tried to commit but it went wrong, resulting in a call to setRollbackOnly from within a
                            // beforeCompletion. The beforeCompletion did not then throw an exception as that would be handled above
							rollbackException.initCause();
                        }
						throw rollbackException;
					default:
			else
		finally
			TransactionImple.removeTransaction(this);
	}

If this is an imported transaction (via JCA) then this will be the Xid we are pretending to be. Otherwise, it will be null.

Returns:
null if we are a local transaction, a valid Xid if we have been imported.
	protected Xid baseXid()
		return null;
	}

Does the same as rollback, but also changes the thread-to-tx association.
	protected void rollbackAndDisassociate()
			java.lang.SecurityException, javax.transaction.SystemException
		if (..isTraceEnabled()) {
            ..trace("TransactionImple.rollbackAndDisassociate");
        }
		try
			boolean statusIsValid = false;
			if ( != null)