Package com.arjuna.ats.internal.jta.transaction.jts

Source Code of com.arjuna.ats.internal.jta.transaction.jts.TransactionImple

/*
* 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) 2001, 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.jts;

import com.arjuna.ats.internal.jta.utils.*;
import com.arjuna.ats.internal.jta.utils.jts.StatusConverter;
import com.arjuna.ats.internal.jta.resources.jts.CleanupSynchronization;
import com.arjuna.ats.internal.jta.resources.jts.LocalCleanupSynchronization;
import com.arjuna.ats.internal.jta.resources.jts.orbspecific.LastResourceRecord;
import com.arjuna.ats.internal.jta.resources.jts.orbspecific.SynchronizationImple;
import com.arjuna.ats.internal.jta.resources.jts.orbspecific.XAResourceRecord;
import com.arjuna.ats.internal.jta.resources.jts.orbspecific.ManagedSynchronizationImple;

import com.arjuna.ats.jta.resources.LastResourceCommitOptimisation;
import com.arjuna.ats.jta.utils.XAHelper;

import org.omg.CosTransactions.*;

import com.arjuna.ats.arjuna.coordinator.ActionStatus;
import com.arjuna.ats.arjuna.coordinator.BasicAction;
import com.arjuna.ats.arjuna.coordinator.TwoPhaseCoordinator;
import com.arjuna.ats.arjuna.coordinator.TransactionReaper;

import com.arjuna.ats.jta.xa.*;
import com.arjuna.ats.jta.common.jtaPropertyManager;
import com.arjuna.ats.jta.exceptions.InactiveTransactionException;
import com.arjuna.ats.jta.exceptions.InvalidTerminationStateException;
import com.arjuna.ats.jta.logging.*;

import com.arjuna.ats.internal.jta.xa.TxInfo;

import com.arjuna.ats.internal.jts.OTSImpleManager;
import com.arjuna.ats.internal.jts.ControlWrapper;

import com.arjuna.common.util.logging.*;

import javax.transaction.xa.*;

import com.arjuna.ats.arjuna.common.*;

import java.util.concurrent.ConcurrentHashMap;

import java.util.*;

import javax.transaction.RollbackException;
import javax.transaction.HeuristicMixedException;
import javax.transaction.Status;

import java.lang.SecurityException;
import java.lang.IllegalStateException;
import org.omg.CosTransactions.SubtransactionsUnavailable;
import org.omg.CosTransactions.NoTransaction;
import org.omg.CORBA.INVALID_TRANSACTION;
import org.omg.CORBA.TRANSACTION_ROLLEDBACK;
import org.omg.CORBA.UNKNOWN;

/**
* An implementation of javax.transaction.Transaction.
*
* @author Mark Little (mark_little@hp.com)
* @version $Id: TransactionImple.java 2342 2006-03-30 13:06:17Z  $
* @since JTS 1.2.4.
*
* @message com.arjuna.ats.internal.jta.transaction.jts.xaerror
*          [com.arjuna.ats.internal.jta.transaction.jts.xaerror] {0} caught XA
*          exception: {1}
* @message com.arjuna.ats.internal.jta.transaction.jts.inactivetx
*          [com.arjuna.ats.internal.jta.transaction.jts.inactivetx] Transaction
*          is not active.
* @message com.arjuna.ats.internal.jta.transaction.jts.invalidtx2
*          [com.arjuna.ats.internal.jta.transaction.jts.invalidtx2] Invalid
*          transaction.
* @message com.arjuna.ats.internal.jta.transaction.jts.nox
*          [com.arjuna.ats.internal.jta.transaction.jts.notx] No such
*          transaction.
* @message com.arjuna.ats.internal.jta.transaction.jts.wrongstatetx
*          [com.arjuna.ats.internal.jta.transaction.jts.wrongstatetx] The
*          current transaction does not match this transaction!
* @message com.arjuna.ats.internal.jta.transaction.jts.nullparam
*          [com.arjuna.ats.internal.jta.transaction.jts.nullparam] paramater is
*          null!
* @message com.arjuna.ats.internal.jta.transaction.jts.illegalstate
*          [com.arjuna.ats.internal.jta.transaction.jts.illegalstate] - illegal
*          resource state:
* @message com.arjuna.ats.internal.jta.transaction.jts.syncproblem
*          [com.arjuna.ats.internal.jta.transaction.jts.syncproble] - cleanup
*          synchronization failed to register:
* @message com.arjuna.ats.internal.jta.transaction.jts.lastResourceOptimisationInterface
*       [com.arjuna.ats.internal.jta.transaction.jts.lastResourceOptimisationInterface] - failed
*          to load Last Resource Optimisation Interface
* @message com.arjuna.ats.internal.jta.transaction.jts.setrollback
*                      [com.arjuna.ats.internal.jta.transaction.jts.setrollback] - setRollbackOnly called from:
*/

public class TransactionImple implements javax.transaction.Transaction,
    com.arjuna.ats.jta.transaction.Transaction
{

  public TransactionImple () throws SubtransactionsUnavailable
  {
    this(new AtomicTransaction());

    _theTransaction.begin();

    try
    {
      TwoPhaseCoordinator theTx = null;

      try
      {
        /*
         * If this is an imported transaction and we have just performed
         * interposition, then register a purely local Synchronization.
         * This gets us over a performance issue with JacORB.
         */

        theTx = (TwoPhaseCoordinator) BasicAction.Current();

        if (theTx != null// TM is local
          theTx.addSynchronization(new LocalCleanupSynchronization(this));
        else
          registerSynchronization(new CleanupSynchronization(this));
      }
      catch (ClassCastException ex)
      {
        /*
         * Not a local/interposed transaction.
         */

        registerSynchronization(new CleanupSynchronization(this));
      }
    }
    catch (Exception ex)
    {
      /*
       * Could set rollback only, but let's take the memory leak hit for
       * now.
       */

      jtaLogger.loggerI18N.warn("com.arjuna.ats.internal.jta.transaction.jts.syncproblem", ex);
    }
  }

  /**
   * Overloads Object.equals()
   */

  public boolean equals (Object obj)
  {
    if (jtaLogger.logger.isDebugEnabled())
    {
      jtaLogger.logger.debug(DebugLevel.OPERATORS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jta.logging.FacilityCode.FAC_JTA, "TransactionImple.equals");
    }

    if (obj == null)
      return false;

    if (obj == this)
      return true;

    if (obj instanceof TransactionImple)
    {
      if (_theTransaction != null)
      {
        return _theTransaction.equals(((TransactionImple) obj)._theTransaction);
      }
    }

    return false;
  }

  /**
   * Return -1 if we fail.
   */

  public int hashCode ()
  {
    if (_theTransaction != null)
      return _theTransaction.hashCode();
    else
      return -1;
  }

  public ControlWrapper getControlWrapper ()
  {
    if (_theTransaction != null)
      return _theTransaction.getControlWrapper();
    else
      return null;
  }

  /**
   * 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, and simply treat it like a Control.
   */

  /**
   * We will 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. The OTS interfaces do not allow
   * a differentiation.
   */

  public void commit () throws javax.transaction.RollbackException,
      javax.transaction.HeuristicMixedException,
      javax.transaction.HeuristicRollbackException,
      java.lang.SecurityException, javax.transaction.SystemException,
      java.lang.IllegalStateException
  {
    if (jtaLogger.logger.isDebugEnabled())
    {
      jtaLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jta.logging.FacilityCode.FAC_JTA, "TransactionImple.commit");
    }

    if (_theTransaction != null)
    {
      /*
       * Call end on any suspended resources. If this fails, then the
       * transaction will be rolled back.
       */

      try
      {
        if (!endSuspendedRMs())
          _theTransaction.rollbackOnly();

        _theTransaction.end(true);
      }
      catch (org.omg.CosTransactions.WrongTransaction wt)
      {
                InactiveTransactionException inactiveTransactionException = new InactiveTransactionException(
            jtaLogger.loggerI18N.getString("com.arjuna.ats.internal.jta.transaction.jts.wrongstatetx"));
                inactiveTransactionException.initCause(wt);
                throw inactiveTransactionException;
      }
      catch (org.omg.CosTransactions.NoTransaction e1)
      {
                IllegalStateException illegalStateException = new IllegalStateException(jtaLogger.loggerI18N.getString("com.arjuna.ats.internal.jta.transaction.jts.notx"));
                illegalStateException.initCause(e1);
                throw illegalStateException;
      }
      catch (org.omg.CosTransactions.HeuristicMixed e2)
      {
                HeuristicMixedException heuristicMixedException = new javax.transaction.HeuristicMixedException();
                heuristicMixedException.initCause(e2);
                throw heuristicMixedException;
      }
      catch (org.omg.CosTransactions.HeuristicHazard e3)
      {
                HeuristicMixedException heuristicMixedException = new javax.transaction.HeuristicMixedException();
                heuristicMixedException.initCause(e3);
                throw heuristicMixedException;
      }
      catch (TRANSACTION_ROLLEDBACK e4)
      {
                RollbackException rollbackException = new RollbackException(e4.toString());
                rollbackException.initCause(e4);
                throw rollbackException;
      }
      catch (org.omg.CORBA.NO_PERMISSION e5)
      {
        throw new SecurityException(e5);
      }
      catch (INVALID_TRANSACTION e6)
      {
        InactiveTransactionException inactiveTransactionException = new InactiveTransactionException(
            jtaLogger.loggerI18N.getString("com.arjuna.ats.internal.jta.transaction.jts.invalidtx2"));
                inactiveTransactionException.initCause(e6);
                throw inactiveTransactionException;
      }
      catch (org.omg.CORBA.SystemException e7)
      {
                javax.transaction.SystemException systemException = new javax.transaction.SystemException(e7.toString());
                systemException.initCause(e7);
            throw systemException;
            }
      finally
      {
        TransactionImple.removeTransaction(this);
      }
    }
    else
      throw new IllegalStateException(
          jtaLogger.loggerI18N.getString("com.arjuna.ats.internal.jta.transaction.jts.inactivetx"));
  }

  /**
   * @message com.arjuna.ats.internal.jta.transaction.jts.endsuspendfailed1
   *          [com.arjuna.ats.internal.jta.transaction.jts.endsuspendfailed1]
   *          Ending suspended RMs failed when rolling back the transaction!
   * @message com.arjuna.ats.internal.jta.transaction.jts.endsuspendfailed2
   *          [com.arjuna.ats.internal.jta.transaction.jts.endsuspendfailed2]
   *          Ending suspended RMs failed when rolling back the transaction,
   *          but transaction rolled back.
   */

  public void rollback () throws java.lang.IllegalStateException,
      java.lang.SecurityException, javax.transaction.SystemException
  {
    if (jtaLogger.logger.isDebugEnabled())
    {
      jtaLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jta.logging.FacilityCode.FAC_JTA, "TransactionImple.rollback");
    }

    if (_theTransaction != null)
    {
      /*
       * 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)
      {
        if (jtaLogger.loggerI18N.isWarnEnabled())
        {
          jtaLogger.loggerI18N.warn("com.arjuna.ats.internal.jta.transaction.jts.endsuspendfailed1");
        }
      }

      try
      {
        _theTransaction.abort();
      }
      catch (org.omg.CosTransactions.WrongTransaction e1)
      {
                InactiveTransactionException inactiveTransactionException =new InactiveTransactionException(
            jtaLogger.loggerI18N.getString("com.arjuna.ats.internal.jta.transaction.jts.wrongstatetx"));
                inactiveTransactionException.initCause(e1);
                throw inactiveTransactionException;
      }
      catch (org.omg.CORBA.NO_PERMISSION e2)
      {
        throw new SecurityException(e2);
      }
      catch (INVALID_TRANSACTION e3)
      {
                InactiveTransactionException inactiveTransactionException = new InactiveTransactionException(
            jtaLogger.loggerI18N.getString("com.arjuna.ats.internal.jta.transaction.jts.invalidtx2"));
                inactiveTransactionException.initCause(e3);
                throw inactiveTransactionException;
      }
      catch (NoTransaction e4)
      {
        throw new IllegalStateException(
            jtaLogger.loggerI18N.getString("com.arjuna.ats.internal.jta.transaction.jts.notx"), e4);
      }
      catch (org.omg.CORBA.SystemException e5)
      {
                javax.transaction.SystemException systemException = new javax.transaction.SystemException(e5.toString());
                systemException.initCause(e5);
            throw systemException;
      }
      finally
      {
        TransactionImple.removeTransaction(this);
      }

      if (endSuspendedFailed)
        throw new InvalidTerminationStateException (
            jtaLogger.loggerI18N.getString("com.arjuna.ats.internal.jta.transaction.jts.endsuspendfailed2"));
    }
    else
      throw new IllegalStateException(
          jtaLogger.loggerI18N.getString("com.arjuna.ats.internal.jta.transaction.jts.inactivetx"));
  }

  public void setRollbackOnly () throws java.lang.IllegalStateException,
      javax.transaction.SystemException
  {
      if (jtaLogger.logger.isDebugEnabled())
      {
          jtaLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jta.logging.FacilityCode.FAC_JTA, "TransactionImple.setRollbackOnly");
      }

      if (_theTransaction != null)
      {
          try
          {
              _theTransaction.rollbackOnly();
              // keep a record of why we are rolling back i.e. who called us first, it's a useful debug aid.
              if (_rollbackOnlyCallerStacktrace == null)
              {
                  _rollbackOnlyCallerStacktrace = new Throwable(jtaLogger.loggerI18N.getString("com.arjuna.ats.internal.jta.transaction.jts.notx"));
              }
          }
          catch (org.omg.CosTransactions.NoTransaction e3)
          {
              throw new IllegalStateException(
                      jtaLogger.loggerI18N.getString("com.arjuna.ats.internal.jta.transaction.jts.notx"), e3);
          }
          catch (final INVALID_TRANSACTION ex)
          {
              switch (getStatus())
                    {
              case Status.STATUS_ROLLEDBACK:
              case Status.STATUS_ROLLING_BACK:
                        break;
              case Status.STATUS_PREPARING:
              case Status.STATUS_PREPARED:
                        throw new InvalidTerminationStateException(
                                jtaLogger.logMesg
                                .getString("com.arjuna.ats.internal.jta.transaction.arjunacore.invalidstate"));
                    default:
                        throw new InactiveTransactionException(
                                jtaLogger.logMesg
                                .getString("com.arjuna.ats.internal.jta.transaction.arjunacore.inactive"));
                    }
          }
          catch (org.omg.CORBA.SystemException e4)
          {
              javax.transaction.SystemException systemException = new javax.transaction.SystemException(e4.toString());
              systemException.initCause(e4);
              throw systemException;
          }
      }
      else
          throw new IllegalStateException(
                  jtaLogger.loggerI18N.getString("com.arjuna.ats.internal.jta.transaction.jts.inactivetx"));
  }

  public int getStatus () throws javax.transaction.SystemException
  {
    if (jtaLogger.logger.isDebugEnabled())
    {
      jtaLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jta.logging.FacilityCode.FAC_JTA, "TransactionImple.getStatus");
    }

    int status = javax.transaction.Status.STATUS_NO_TRANSACTION;

    if (_theTransaction != null)
    {
      try
      {
        return StatusConverter.convert(_theTransaction.get_status());
      }
      catch (org.omg.CORBA.SystemException e2)
      {
                javax.transaction.SystemException systemException = new javax.transaction.SystemException(e2.toString());
                systemException.initCause(e2);
            throw systemException;
      }
    }

    return status;
  }

  /**
   * @message com.arjuna.ats.internal.jta.transaction.jts.syncerror
   *          [com.arjuna.ats.internal.jta.transaction.jts.syncerror]
   *          Synchronizations are not allowed!
   */
  public void registerSynchronization (javax.transaction.Synchronization sync)
      throws javax.transaction.RollbackException,
      java.lang.IllegalStateException, javax.transaction.SystemException
  {
        if (jtaLogger.logger.isDebugEnabled())
    {
      jtaLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jta.logging.FacilityCode.FAC_JTA, "TransactionImple.registerSynchronization");
    }

    if (sync == null)
      throw new javax.transaction.SystemException(
          "TransactionImple.registerSynchronization - "
              + jtaLogger.loggerI18N.getString("com.arjuna.ats.internal.jta.transaction.jts.nullparam"));

        registerSynchronizationImple(new ManagedSynchronizationImple(sync));
  }

  // package-private method also for use by
  // TransactionSynchronizationRegistryImple

  void registerSynchronizationImple(SynchronizationImple synchronizationImple)
      throws javax.transaction.RollbackException,
      java.lang.IllegalStateException, javax.transaction.SystemException
  {
    if (_theTransaction != null)
    {
      try
      {
                _theTransaction.registerSynchronization(synchronizationImple.getSynchronization());
      }
      catch (TRANSACTION_ROLLEDBACK e2)
      {
                RollbackException rollbackException = new javax.transaction.RollbackException(e2.toString());
                rollbackException.initCause(e2);
                throw rollbackException;
      }
      catch (org.omg.CosTransactions.Inactive e3)
      {
        throw new IllegalStateException(
            jtaLogger.loggerI18N.getString("com.arjuna.ats.internal.jta.transaction.jts.inactivetx"), e3);
      }
      catch (org.omg.CosTransactions.SynchronizationUnavailable e4)
      {
        throw new IllegalStateException(
            jtaLogger.loggerI18N.getString("com.arjuna.ats.internal.jta.transaction.jts.syncerror"), e4);
      }
      catch (INVALID_TRANSACTION e5)
      {
        throw new IllegalStateException(
            jtaLogger.loggerI18N.getString("com.arjuna.ats.internal.jta.transaction.jts.invalidtx2"), e5);
      }
      catch (org.omg.CORBA.SystemException e6)
      {
                javax.transaction.SystemException systemException = new javax.transaction.SystemException(e6.toString());
                systemException.initCause(e6);
            throw systemException;
      }
    }
    else
      throw new IllegalStateException(
          jtaLogger.loggerI18N.getString("com.arjuna.ats.internal.jta.transaction.jts.inactivetx"));
  }


    public boolean enlistResource (XAResource xaRes) throws RollbackException,
      IllegalStateException, javax.transaction.SystemException
  {
    return enlistResource(xaRes, null);
  }

  /**
   * @message com.arjuna.ats.internal.jta.transaction.jts.starterror
   *          [com.arjuna.ats.internal.jta.transaction.jts.starterror] {0}
   *          returned XA error {1} for transaction {2}
   * @message com.arjuna.ats.internal.jta.transaction.jts.regerror
   *          [com.arjuna.ats.internal.jta.transaction.jts.regerror] {0} could
   *          not register transaction: {1}
   * @message com.arjuna.ats.internal.jta.transaction.jts.markedrollback
   *       [com.arjuna.ats.internal.jta.transaction.jts.markedrollback] Could not
   *       enlist resource because the transaction is marked for rollback.
   */

  public boolean enlistResource (XAResource xaRes, Object[] params)
      throws RollbackException, IllegalStateException,
      javax.transaction.SystemException
  {
    if (jtaLogger.logger.isDebugEnabled())
    {
      jtaLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jta.logging.FacilityCode.FAC_JTA, "TransactionImple.enlistResource ( "
          + xaRes + " )");
    }

    if (xaRes == null)
      throw new javax.transaction.SystemException(
          "TransactionImple.enlistResource - "
              + jtaLogger.loggerI18N.getString("com.arjuna.ats.internal.jta.transaction.jts.nullparam"));

    int status = getStatus();

    switch (status)
    {
    case javax.transaction.Status.STATUS_MARKED_ROLLBACK:
      throw new RollbackException("TransactionImple.enlistResource - "
                    + jtaLogger.loggerI18N.getString("com.arjuna.ats.internal.jta.transaction.jts.markedrollback"));
    case javax.transaction.Status.STATUS_ACTIVE:
      break;
    default:
      throw new IllegalStateException(
          jtaLogger.loggerI18N.getString("com.arjuna.ats.internal.jta.transaction.jts.inactivetx"));
    }

    XAModifier theModifier = null;

    if (params != null)
    {
      if (params.length >= XAMODIFIER + 1)
      {
        if (params[XAMODIFIER] instanceof XAModifier)
        {
          theModifier = (XAModifier) params[XAMODIFIER];
        }
      }
    }

    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) _resources.get(xaRes);

          if (info == null)
          {
            /*
             * Null info means it's not in the main resources list,
             * but may be in the duplicates.
             */

            info = (TxInfo) _duplicateResources.get(xaRes);
          }
        }

        if (info != null)
        {
          switch (info.getState())
          {
          case TxInfo.ASSOCIATION_SUSPENDED:
          {
            /*
             * Have seen resource before, so do a resume. The
             * Resource instance will still be registered with the
             * transaction though.
             */

            xaRes.start(info.xid(), XAResource.TMRESUME);

            info.setState(TxInfo.ASSOCIATED);

            synchronized (this)
            {
              _suspendCount--;
            }

            return true; // already registered resource with this
            // transaction!
          }
          case TxInfo.ASSOCIATED:
          {
            /*
             * Already active on this transaction.
             */

            return true;
          }
          case TxInfo.NOT_ASSOCIATED:
          {
            /*
             * Resource was associated, but was presumably delisted.
             */
            xaRes.start(info.xid(), XAResource.TMJOIN);

            info.setState(TxInfo.ASSOCIATED);

            return true;
          }
          default:
          {
            // Note: this exception will be caught by our catch
            // block
            throw new IllegalStateException(
                "TransactionImple.enlistResource - "
                    + jtaLogger.loggerI18N.getString("com.arjuna.ats.internal.jta.transaction.jts.illegalstate")
                    + info.getState());
          }
          }
        }
      }
      catch (IllegalStateException ex)
      {
        throw ex; // we threw it in the first place
      }
      catch (XAException exp)
      {
        if (info != null)
          info.setState(TxInfo.FAILED);

        if (jtaLogger.loggerI18N.isWarnEnabled())
        {
          jtaLogger.loggerI18N.warn("com.arjuna.ats.internal.jta.transaction.jts.xaerror", new Object[]
          { "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 (_resources.size() == 0)// first ever, so no need for
          // branch
          {
            //branchRequired = false;
            branchRequired = true;
          }
        }

        xid = createXid(branchRequired, theModifier);

        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
          {
                        if (_xaTransactionTimeoutEnabled)
                        {
                            int timeout = _theTransaction.getTimeout();

                            if (timeout > 0)
                            {
                                try
                                {
                                    xaRes.setTransactionTimeout(timeout);
                                }
                                catch (XAException te)
                                {
                                    if (jtaLogger.loggerI18N.isWarnEnabled())
                                    {
                                        jtaLogger.loggerI18N.warn("com.arjuna.ats.internal.jta.transaction.jts.timeouterror", new Object[]
                                        { "TransactionImple.enlistResource", XAHelper.printXAErrorCode(te), xid });
                                    }
                                }
                            }
                        }

                        // Pay attention now, this bit is hairy. We need to add a new XAResourceRecord
                        // to the transaction, 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, the tx 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 registerResource will fail in the case of multiple last resources being disallowed.
                        // see JBTM-362 and JBTM-363
                        XAResourceRecord xaResourceRecord = createRecord(xaRes, params, xid);
                        if(xaResourceRecord != null) {
                            xaRes.start(xid, XAResource.TMNOFLAGS);
                            try {
                                RecoveryCoordinator recCoord = _theTransaction.registerResource(xaResourceRecord.getResource());
                                xaResourceRecord.setRecoveryCoordinator(recCoord);
                            } catch(Exception e) {
                                // 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:
                                xaResourceRecord.rollback();
                                markRollbackOnly();
                                return false;
                            }
                            _resources.put(xaRes, new TxInfo(xid));
                            return true; // dive out, no need to set associatedWork = true;
                        }

                        // 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

            /* We get this from Oracle instead of DUPID. */
            if (e.errorCode == XAException.XAER_RMERR)
            {

              if (retry > 0)
                xid = createXid(true, theModifier);

              retry--;
            }
            else
              if (e.errorCode == XAException.XAER_DUPID)
              {
                if (retry > 0)
                  xid = createXid(true, theModifier);

                retry--;
              }
              else
              {
                /*
                 * Can't do start, so set transaction to
                 * rollback only.
                 */

                if (jtaLogger.loggerI18N.isWarnEnabled())
                {
                  jtaLogger.loggerI18N.warn("com.arjuna.ats.internal.jta.transaction.jts.starterror", new Object[]
                  { "TransactionImple.enlistResource - XAResource.start", XAHelper.printXAErrorCode(e), xid });
                }

                markRollbackOnly();

                throw e;
              }

            if (retry < 0)
            {
              if (jtaLogger.loggerI18N.isWarnEnabled())
              {
                jtaLogger.loggerI18N.warn("com.arjuna.ats.internal.jta.transaction.jts.starterror", new Object[]
                { "TransactionImple.enlistResource - XAResource.start", XAHelper.printXAErrorCode(e), xid });
              }

              markRollbackOnly();

              throw new UNKNOWN();
            }
          }
        }
      }
      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
        {
          xaRes.start(xid, XAResource.TMJOIN);
        }
        catch (XAException ex)
        {
          if (jtaLogger.loggerI18N.isWarnEnabled())
          {
            jtaLogger.loggerI18N.warn("com.arjuna.ats.internal.jta.transaction.jts.xaerror", new Object[]
            { "TransactionImple.enlistResource - xa_start: ", XAHelper.printXAErrorCode(ex) });
          }

          markRollbackOnly();

          throw ex;
        }

        /*
         * Add to duplicate resources list so we can keep track of it
         * (particularly if we later have to delist).
         */

        _duplicateResources.put(xaRes, new 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.
       */

      markRollbackOnly();

      return false;
    }
  }

    /**
     * Attempt to create an XAResourceRecord wrapping the given XAResource. Return null if this fails, or
     * is diallowed by the current configuration of multiple last resource behaviour.
     *
     * @param xaRes
     * @param params
     * @param xid
     * @return
     */
    private XAResourceRecord createRecord(XAResource xaRes, Object[] params, Xid xid)
    {
        final XAResourceRecord record;
        if ((xaRes instanceof LastResourceCommitOptimisation)
                || ((LAST_RESOURCE_OPTIMISATION_INTERFACE != null) && LAST_RESOURCE_OPTIMISATION_INTERFACE
                .isInstance(xaRes)))
        {
                record = new LastResourceRecord(this, xaRes, xid, params);
        }
        else
        {
            record = new XAResourceRecord(this, xaRes, xid, params);
        }

        return record;
    }

    /*
   * Do we have to unregister resources? Assume not as it would not make much
   * sense otherwise!
   */

  /**
   * @message com.arjuna.ats.internal.jta.transaction.jts.unknownres
   *          [com.arjuna.ats.internal.jta.transaction.jts.unknownres] {0}
   *          attempt to delist unknown resource!
   * @message com.arjuna.ats.internal.jta.transaction.jts.delistfailed
   *          [com.arjuna.ats.internal.jta.transaction.jts.delistfailed]
   *          Delist of resource failed with: {0}
   * @message com.arjuna.ats.internal.jta.transaction.jts.ressusp resource
   *          [com.arjuna.ats.internal.jta.transaction.jts.ressusp resource]
   *          is already suspended!
   */

  public boolean delistResource (XAResource xaRes, int flags)
      throws IllegalStateException, javax.transaction.SystemException
  {
    if (jtaLogger.logger.isDebugEnabled())
    {
      jtaLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jta.logging.FacilityCode.FAC_JTA, "TransactionImple.delistResource ( "
          + xaRes + " )");
    }

    if (xaRes == null)
      throw new javax.transaction.SystemException(
          "TransactionImple.delistResource - "
              + jtaLogger.loggerI18N.getString("com.arjuna.ats.internal.jta.transaction.jts.nullparam"));

    int status = getStatus();

    switch (status)
    {
    case javax.transaction.Status.STATUS_ACTIVE:
      break;
    case javax.transaction.Status.STATUS_MARKED_ROLLBACK:
      break;
    default:
      throw new IllegalStateException(
          jtaLogger.loggerI18N.getString("com.arjuna.ats.internal.jta.transaction.jts.inactivetx"));
    }

    TxInfo info = null;

    try
    {
      synchronized (this)
      {
        info = (TxInfo) _resources.get(xaRes);

        if (info == null)
          info = (TxInfo) _duplicateResources.get(xaRes);
      }

      if (info == null)
      {
        if (jtaLogger.loggerI18N.isWarnEnabled())
        {
          jtaLogger.loggerI18N.warn("com.arjuna.ats.internal.jta.transaction.jts.unknownres", new Object[]
          { "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(), XAResource.TMFAIL);
              xaRes.rollback(info.xid());

              info.setState(TxInfo.OPTIMIZED_ROLLBACK);

              optimizedRollback = true;
            }
          }
        }
        catch (Exception e)
        {
          // failed, so try again when transaction does rollback
        }

        switch (info.getState())
        {
        case TxInfo.ASSOCIATED:
        {
          if ((flags & XAResource.TMSUCCESS) != 0)
          {
            xaRes.end(info.xid(), XAResource.TMSUCCESS);
            info.setState(TxInfo.NOT_ASSOCIATED);
          }
          else
          {
            if ((flags & XAResource.TMSUSPEND) != 0)
            {
              xaRes.end(info.xid(), XAResource.TMSUSPEND);
              info.setState(TxInfo.ASSOCIATION_SUSPENDED);

              synchronized (this)
              {
                _suspendCount++;
              }
            }
            else
            {
              xaRes.end(info.xid(), XAResource.TMFAIL);
              info.setState(TxInfo.FAILED);
            }
          }
        }
          break;
        case TxInfo.ASSOCIATION_SUSPENDED:
        {
          if ((flags & XAResource.TMSUCCESS) != 0)
          {
            // Oracle barfs if we don't resume first, despite what
            // XA says!

            if (XAUtils.mustEndSuspendedRMs(xaRes))
              xaRes.start(info.xid(), XAResource.TMRESUME);

            xaRes.end(info.xid(), XAResource.TMSUCCESS);
            info.setState(TxInfo.NOT_ASSOCIATED);

            synchronized (this)
            {
              _suspendCount--;
            }
          }
          else
          {
            if ((flags & XAResource.TMSUSPEND) != 0)
            {
              // Note: this exception will be caught by our catch
              // block

              throw new IllegalStateException(
                  "TransactionImple.delistResource - "
                      + jtaLogger.loggerI18N.getString("com.arjuna.ats.internal.jta.transaction.jts.ressusp"));
            }
            else
            {
              xaRes.end(info.xid(), XAResource.TMFAIL);
              info.setState(TxInfo.FAILED);

              synchronized (this)
              {
                _suspendCount--;
              }
            }
          }
        }
          break;
        default:
          if (!optimizedRollback)
            throw new IllegalStateException(
                "TransactionImple.delistResource - "
                    + jtaLogger.loggerI18N.getString("com.arjuna.ats.internal.jta.transaction.jts.illegalstate")
                    + info.getState());
        }

        info = null;

        return true;
      }
    }
    catch (IllegalStateException ex)
    {
      throw ex;
    }
    catch (XAException exp)
    {
      if (info != null)
        info.setState(TxInfo.FAILED);

      /*
       * For safety mark the transaction as rollback only.
       */

      markRollbackOnly();

      if (jtaLogger.loggerI18N.isWarnEnabled())
      {
        jtaLogger.loggerI18N.warn("com.arjuna.ats.internal.jta.transaction.jts.xaerror", new Object[]
        { "TransactionImple.delistResource", XAHelper.printXAErrorCode(exp) });
      }

      return false;
    }
    catch (Exception e)
    {
      if (jtaLogger.loggerI18N.isWarnEnabled())
      {
        jtaLogger.loggerI18N.warn("com.arjuna.ats.internal.jta.transaction.jts.delistfailed", new Object[]
        { e });
      }

      /*
       * Some exception occurred and we probably could not delist the
       * resource. So, for safety mark the transaction as rollback only.
       */

      markRollbackOnly();

      return false;
    }
  }

  public final Uid get_uid ()
  {
    return _theTransaction.get_uid();
  }
 
  public final Xid getTxId ()
  {
      Xid res = baseXid();
     
      if (res == null)
          return _theTransaction.get_xid(false);
     
      return res;
  }

  public String toString ()
  {
    if (_theTransaction == null)
      return "TransactionImple < jts, NoTransaction >";
    else
    {
      return "TransactionImple < jts, " + _theTransaction.get_uid()
          + " >";
    }
  }

  public int getXAResourceState (XAResource xaRes)
  {
    int state = TxInfo.UNKNOWN;

    if (xaRes != null)
    {
      TxInfo info = (TxInfo) _resources.get(xaRes);

      if (info == null)
      {
        info = (TxInfo) _duplicateResources.get(xaRes);
      }

      if (info != null)
        state = info.getState();
    }

    return state;
  }

  /**
   * Creates if does not exist and adds to our internal mapping table.
   *
   * @message com.arjuna.ats.internal.jta.transaction.jts.nottximple
   *          [com.arjuna.ats.internal.jta.transaction.jts.nottximple] Current
   *          transaction is not a TransactionImple
   */

  static final TransactionImple getTransaction ()
  {
    TransactionImple tx = null;

    ControlWrapper otx = OTSImpleManager.current().getControlWrapper();

    if (otx != null)
    {
            synchronized (TransactionImple._transactions)
            {
        try
        {
          tx = (TransactionImple) TransactionImple._transactions.get(otx.get_uid());

          if (tx == null)
          {
            /*
             * If it isn't active then don't add it to the
             * hashtable.
             */

            tx = new TransactionImple(new AtomicTransaction(otx));

            try
            {
              if (tx.getStatus() == javax.transaction.Status.STATUS_ACTIVE)
              {
                putTransaction(tx);
              }
            }
            catch (Exception ex)
            {
              // shouldn't happen!
            }
          }
        }
        catch (ClassCastException ex)
        {
          if (jtaLogger.loggerI18N.isWarnEnabled())
          {
            jtaLogger.loggerI18N.warn("com.arjuna.ats.internal.jta.transaction.jts.nottximple");
          }
        }
      }
    }

    return tx;
  }

  public final void shutdown ()
  {
    removeTransaction(this);
  }

    // get a key-value pair from a transaction specific Map
  public Object getTxLocalResource(Object key)
  {
    return _txLocalResources.get(key);
  }

  // store a key-value pair in the scope of the transaction.
  public void putTxLocalResource(Object key, Object value)
  {
    _txLocalResources.put(key, value);
  }


    /*
     * 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() {
        return _transactions.contains(this);
    }


    protected TransactionImple (AtomicTransaction tx)
  {
    _theTransaction = tx;

    if (tx != null)
    {
      _resources = new Hashtable();
      _duplicateResources = new Hashtable();
            _txLocalResources = Collections.synchronizedMap(new HashMap());
        }
    else
    {
      _resources = null;
      _duplicateResources = null;
    }

    _suspendCount = 0;

    try
    {
      if (getStatus() == javax.transaction.Status.STATUS_ACTIVE)
      {
        TwoPhaseCoordinator theTx = null;

        try
        {
          /*
           * If this is an imported transaction and we have just
           * performed interposition, then register a purely local
           * Synchronization. This gets us over a performance issue
           * with JacORB.
           */

          theTx = (TwoPhaseCoordinator) BasicAction.Current();

          if (theTx != null)
            theTx.addSynchronization(new LocalCleanupSynchronization(
                this));
          else
            registerSynchronization(new CleanupSynchronization(this));
        }
        catch (ClassCastException ex)
        {
          /*
           * Not a local/interposed transaction.
           */

          registerSynchronization(new CleanupSynchronization(this));
        }
      }
    }
    catch (Exception ex)
    {
      /*
       * Could set rollback only, but let's take the possible memory leak
       * hit for now.
       */

      jtaLogger.loggerI18N.warn("com.arjuna.ats.internal.jta.transaction.jts.syncproblem", ex);
    }
    _xaTransactionTimeoutEnabled = getXATransactionTimeoutEnabled() ;
    }

  protected void commitAndDisassociate ()
      throws javax.transaction.RollbackException,
      javax.transaction.HeuristicMixedException,
      javax.transaction.HeuristicRollbackException,
      java.lang.SecurityException, javax.transaction.SystemException,
      java.lang.IllegalStateException
  {
    if (jtaLogger.logger.isDebugEnabled())
    {
      jtaLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jta.logging.FacilityCode.FAC_JTA, "TransactionImple.commitAndDisassociate");
    }

    if (_theTransaction != null)
    {
      try
      {
        _theTransaction.commit(true);
      }
      catch (org.omg.CosTransactions.WrongTransaction wt)
      {
        throw new IllegalStateException(
            jtaLogger.loggerI18N.getString("com.arjuna.ats.internal.jta.transaction.jts.wrongstatetx"), wt);
      }
      catch (org.omg.CosTransactions.NoTransaction e1)
      {
        throw new IllegalStateException(
            jtaLogger.loggerI18N.getString("com.arjuna.ats.internal.jta.transaction.jts.notx"), e1);
      }
      catch (org.omg.CosTransactions.HeuristicMixed e2)
      {
                HeuristicMixedException heuristicMixedException = new javax.transaction.HeuristicMixedException();
                heuristicMixedException.initCause(e2);
                throw heuristicMixedException;
      }
      catch (org.omg.CosTransactions.HeuristicHazard e3)
      {
                HeuristicMixedException heuristicMixedException = new javax.transaction.HeuristicMixedException();
                heuristicMixedException.initCause(e3);
                throw heuristicMixedException;
      }
      catch (TRANSACTION_ROLLEDBACK e4)
      {
        RollbackException rollbackException = new RollbackException(e4.toString());
                if(_rollbackOnlyCallerStacktrace != null) {
                    // we rolled back beacuse the user explicitly told us not to commit. Attach the trace of who did that for debug:
                    rollbackException.initCause(_rollbackOnlyCallerStacktrace);
                }
                throw rollbackException;
      }
      catch (org.omg.CORBA.NO_PERMISSION e5)
      {
        throw new SecurityException(e5);
      }
      catch (INVALID_TRANSACTION e6)
      {
          /*
           * In JTS/OTS we can indicate that something was terminated by another thread.
           * JTA doesn't really prevent this, but ...
           */

        //throw new IllegalStateException(
        //    jtaLogger.loggerI18N.getString("com.arjuna.ats.internal.jta.transaction.jts.invalidtx2"));
      }
      catch (org.omg.CORBA.SystemException e7)
      {
                InvalidTerminationStateException invalidTerminationStateException = new InvalidTerminationStateException();
                invalidTerminationStateException.initCause(e7);
                throw invalidTerminationStateException;
      }
      finally
      {
        TransactionImple.removeTransaction(this);
      }
    }
    else
      throw new IllegalStateException(
          jtaLogger.loggerI18N.getString("com.arjuna.ats.internal.jta.transaction.jts.inactivetx"));
  }

  protected void rollbackAndDisassociate ()
      throws java.lang.IllegalStateException,
      java.lang.SecurityException, javax.transaction.SystemException
  {
    if (jtaLogger.logger.isDebugEnabled())
    {
      jtaLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC, com.arjuna.ats.jta.logging.FacilityCode.FAC_JTA, "TransactionImple.rollbackAndDisassociate");
    }

    if (_theTransaction != null)
    {
      try
      {
        _theTransaction.rollback();
      }
      catch (org.omg.CosTransactions.WrongTransaction e1)
      {
        throw new IllegalStateException(
            jtaLogger.loggerI18N.getString("com.arjuna.ats.internal.jta.transaction.jts.wrongstatetx"), e1);
      }
      catch (org.omg.CORBA.NO_PERMISSION e2)
      {
        throw new SecurityException(e2);
      }
      catch (INVALID_TRANSACTION e3)
      {
        throw new IllegalStateException(
            jtaLogger.loggerI18N.getString("com.arjuna.ats.internal.jta.transaction.jts.invalidtx2"), e3);
      }
      catch (NoTransaction e4)
      {
        throw new IllegalStateException(
            jtaLogger.loggerI18N.getString("com.arjuna.ats.internal.jta.transaction.jts.notx"), e4);
      }
      catch (org.omg.CORBA.SystemException e5)
      {
                javax.transaction.SystemException systemException = new javax.transaction.SystemException(e5.toString());
                systemException.initCause(e5);
            throw systemException;
      }
      finally
      {
        TransactionImple.removeTransaction(this);
      }
    }
    else
      throw new IllegalStateException(
          jtaLogger.loggerI18N.getString("com.arjuna.ats.internal.jta.transaction.jts.inactivetx"));
  }

  /**
   * If this is an imported JCA transaction, then this method will return the
   * Xid we should pretend to be. Otherwise it'll return null and we will generate
   * our own Xid.
   *
   * @return null for a pure ATS transaction, otherwise a valid JCA imported Xid.
   */

  protected Xid baseXid ()
  {
    return null;
  }

  /*
   * Add and remove transactions from list.
   */

  protected static final void putTransaction (TransactionImple tx)
  {
    TransactionImple._transactions.put(tx.get_uid(), tx);
  }

  protected static final void removeTransaction (TransactionImple tx)
  {
    TransactionImple._transactions.remove(tx.get_uid());
  }

  /**
   * If there are any suspended RMs then we should call end on them before the
   * transaction is terminated.
   *
   * @message com.arjuna.ats.internal.jta.transaction.jts.xaenderror
   *          [com.arjuna.ats.internal.jta.transaction.jts.xaenderror]
   *          Could not call end on a suspended resource!
   */

  protected boolean endSuspendedRMs ()
  {
    boolean result = true;

    if (_suspendCount > 0)
    {
      Enumeration el = _resources.keys();

      /*
       * Loop over all registered resources. Those that are in a suspended
       * state must have end called on them. If this fails, then we will
       * eventually roll back the transaction, but we will continue down
       * the list to try to end any other suspended resources.
       */

      if (el != null)
      {
        try
        {
          while (el.hasMoreElements())
          {
            /*
             * Get the XAResource in case we have to call end on it.
             */

            XAResource xaRes = (XAResource) el.nextElement();
            TxInfo info = (TxInfo) _resources.get(xaRes);

            if (info.getState() == TxInfo.ASSOCIATION_SUSPENDED)
            {
              if (XAUtils.mustEndSuspendedRMs(xaRes))
                xaRes.start(info.xid(), XAResource.TMRESUME);

              xaRes.end(info.xid(), XAResource.TMSUCCESS);
              info.setState(TxInfo.NOT_ASSOCIATED);
            }
          }
        }
        catch (XAException ex)
        {
          if (jtaLogger.loggerI18N.isWarnEnabled())
          {
            jtaLogger.loggerI18N.warn("com.arjuna.ats.internal.jta.transaction.jts.xaenderror");
          }

          result = false;
        }
      }

      /*
       * do the same again for duplicate resources
       */

      el = _duplicateResources.keys();

      if (el != null)
      {
        try
        {
          while (el.hasMoreElements())
          {
            /*
             * Get the XAResource in case we have to call end on it.
             */

            XAResource xaRes = (XAResource) el.nextElement();
            TxInfo info = (TxInfo) _duplicateResources.get(xaRes);

            if (info.getState() == TxInfo.ASSOCIATION_SUSPENDED)
            {
              if (XAUtils.mustEndSuspendedRMs(xaRes))
                xaRes.start(info.xid(), XAResource.TMRESUME);

              xaRes.end(info.xid(), XAResource.TMSUCCESS);
              info.setState(TxInfo.NOT_ASSOCIATED);
            }
          }
        }
        catch (XAException ex)
        {
          if (jtaLogger.loggerI18N.isWarnEnabled())
          {
            jtaLogger.loggerI18N.warn("com.arjuna.ats.internal.jta.transaction.jts.xaenderror");
          }

          result = false;
        }
      }

            _suspendCount = 0;
    }

    return result;
  }

  /**
   * If this thread has already registered a resource for the same db then
   * don't use this copy. For some databases it would actually be ok for us to
   * use the resource (at least to do an xa_start equivalent on it), but for
   * Oracle 8.1.6 it causes their JDBC driver to crash!
   *
   * @message com.arjuna.ats.internal.jta.transaction.jts.threaderror
   *          [com.arjuna.ats.internal.jta.transaction.jts.threaderror] Active
   *          thread error:
   */

  private final boolean threadIsActive (XAResource xaRes)
  {
    Thread t = Thread.currentThread();

    try
    {
      Enumeration el = _resources.keys();

      if (el != null)
      {
        while (el.hasMoreElements())
        {
          XAResource x = (XAResource) el.nextElement();

          if (x.isSameRM(xaRes))
          {
            TxInfo info = (TxInfo) _resources.get(x);

            if (info.thread() == t)
              return true;
          }
        }
      }

      el = _duplicateResources.keys();

      if (el != null)
      {
        while (el.hasMoreElements())
        {
          XAResource x = (XAResource) el.nextElement();

          if (x.isSameRM(xaRes))
          {
            TxInfo info = (TxInfo) _resources.get(x);

            if (info.thread() == t)
              return true;
          }
        }
      }
    }
    catch (Exception e)
    {
      if (jtaLogger.loggerI18N.isWarnEnabled())
      {
        jtaLogger.loggerI18N.warn("com.arjuna.ats.internal.jta.transaction.jts.threaderror", e);
      }

      throw new com.arjuna.ats.arjuna.exceptions.FatalError(e.toString(), e);
    }

    return false;
  }

  /**
   * isNewRM returns an existing TxInfo for the same RM, if present. Null
   * otherwise.
   *
   * @message com.arjuna.ats.internal.jta.transaction.jts.rmerror
   *          [com.arjuna.ats.internal.jta.transaction.jts.rmerror] An error
   *          occurred while checking if this is a new resource manager:
   */

  private final TxInfo isNewRM (XAResource xaRes)
  {
    try
    {
      synchronized (this)
      {
        Enumeration el = _resources.keys();

        if (el != null)
        {
          while (el.hasMoreElements())
          {
            XAResource x = (XAResource) el.nextElement();

            if (x.isSameRM(xaRes))
            {
              return (TxInfo) _resources.get(x);
            }
          }
        }

        el = _duplicateResources.keys();

        if (el != null)
        {
          while (el.hasMoreElements())
          {
            XAResource x = (XAResource) el.nextElement();

            if (x.isSameRM(xaRes))
            {
              return (TxInfo) _duplicateResources.get(x);
            }
          }
        }
      }
    }
    catch (XAException ex)
    {
      if (jtaLogger.loggerI18N.isWarnEnabled())
      {
        jtaLogger.loggerI18N.warn("com.arjuna.ats.internal.jta.transaction.jts.xaerror", new Object[]
        { "TransactionImple.isNewRM", XAHelper.printXAErrorCode(ex) });
      }

      throw new com.arjuna.ats.arjuna.exceptions.FatalError(ex.toString(), ex);
    }
    catch (Exception e)
    {
      if (jtaLogger.loggerI18N.isWarnEnabled())
      {
        jtaLogger.loggerI18N.warn("com.arjuna.ats.internal.jta.transaction.jts.rmerror", e);
      }

      throw new com.arjuna.ats.arjuna.exceptions.FatalError(e.toString(), e);
    }

    return null;
  }

  private final Xid createXid (boolean branch, XAModifier theModifier)
  {
    Xid jtaXid = baseXid();

    if (jtaXid != null)
      return jtaXid;

    try
    {
      jtaXid = _theTransaction.get_xid(branch);

      if (theModifier != null)
      {
        try
        {
          jtaXid = theModifier.createXid(jtaXid);
        }
        catch (Exception e)
        {
          e.printStackTrace();
        }
      }

      return jtaXid;
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }

    return null;
  }

  /**
   * This method calls setRollbackOnly and catches any exceptions it may throw
   * and issues a warning. We use this in places wherew we need to force the
   * outcome of the transaction but already have an exception to throw back to
   * the application, so a failure here will only be masked.
   *
   * @message com.arjuna.ats.internal.jta.transaction.jts.rollbackerror
   *          [com.arjuna.ats.internal.jta.transaction.jts.rollbackerror] {0}
   *          could not mark the transaction as rollback only: {1}
   */

  private final void markRollbackOnly ()
  {
    try
    {
      if (_theTransaction != null)
      {
        try
        {
          _theTransaction.rollbackOnly();
        }
        catch (org.omg.CosTransactions.NoTransaction e3)
        {
          // ok
        }
        catch (org.omg.CORBA.SystemException e3)
        {
                    javax.transaction.SystemException systemException = new javax.transaction.SystemException(e3.toString());
                    systemException.initCause(e3);
                throw systemException;
                }
      }
    }
    catch (Exception ex)
    {
      if (jtaLogger.loggerI18N.isWarnEnabled())
      {
        jtaLogger.loggerI18N.warn("com.arjuna.ats.internal.jta.transaction.jts.rollbackerror", new Object[]
        { "TransactionImple.markRollbackOnly -", ex }, ex);
      }
    }
  }

  private static boolean getXATransactionTimeoutEnabled()
  {
    return XA_TRANSACTION_TIMEOUT_ENABLED ;
  }

    public static Map<Uid, javax.transaction.Transaction> getTransactions()
    {
        return Collections.unmodifiableMap(_transactions);
    }

    public Map<XAResource, TxInfo> getResources()
    {
        return Collections.unmodifiableMap(_resources);
    }

    public int getTimeout()
    {
        return _theTransaction.getTimeout();
    }

    public long getRemainingTimeoutMills() {
        return TransactionReaper.transactionReaper().getRemainingTimeoutMills(_theTransaction.getControlWrapper());
    }

    public java.util.Map<Uid, String> getSynchronizations()
    {
        if (_theTransaction != null)
            return _theTransaction.getControlWrapper().getSynchronizations();

        return Collections.EMPTY_MAP;
    }

    protected AtomicTransaction _theTransaction;

  private Hashtable _resources;
  private Hashtable _duplicateResources;
  private int _suspendCount;
  private final boolean _xaTransactionTimeoutEnabled ;
    private Map _txLocalResources;

    private Throwable _rollbackOnlyCallerStacktrace;

  private static final boolean XA_TRANSACTION_TIMEOUT_ENABLED ;
  private static final Class LAST_RESOURCE_OPTIMISATION_INTERFACE ;

  static
  {
        XA_TRANSACTION_TIMEOUT_ENABLED = jtaPropertyManager.getJTAEnvironmentBean().isXaTransactionTimeoutEnabled();

        final String lastResourceOptimisationInterfaceName = jtaPropertyManager.getJTAEnvironmentBean().getLastResourceOptimisationInterface();
    Class lastResourceOptimisationInterface = null ;
    if (lastResourceOptimisationInterfaceName != null)
    {
      try
      {
        lastResourceOptimisationInterface = Thread.currentThread().getContextClassLoader().loadClass(lastResourceOptimisationInterfaceName) ;
      }
      catch (final Throwable th)
      {
        if (jtaLogger.loggerI18N.isWarnEnabled())
        {
          jtaLogger.loggerI18N.warn("com.arjuna.ats.internal.jta.transaction.jts.lastResourceOptimisationInterface",
            new Object[] {lastResourceOptimisationInterfaceName}, th);
        }
      }
    }
    LAST_RESOURCE_OPTIMISATION_INTERFACE = lastResourceOptimisationInterface ;
  }

    private static ConcurrentHashMap _transactions = new ConcurrentHashMap();

}
TOP

Related Classes of com.arjuna.ats.internal.jta.transaction.jts.TransactionImple

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.