Save This Page
Home » jboss-5.0.0.CR1-src » org » jboss » tm » [javadoc | source]
    1   /*
    2    * JBoss, the OpenSource J2EE webOS
    3    *
    4    * Distributable under LGPL license.
    5    * See terms of license at gnu.org.
    6    */
    7   package org.jboss.tm;
    8   
    9   import org.jboss.logging.Logger;
   10   import org.jboss.util.timeout.Timeout;
   11   import org.jboss.util.timeout.TimeoutFactory;
   12   import org.jboss.util.timeout.TimeoutTarget;
   13   
   14   import javax.transaction.HeuristicMixedException;
   15   import javax.transaction.HeuristicRollbackException;
   16   import javax.transaction.RollbackException;
   17   import javax.transaction.Status;
   18   import javax.transaction.Synchronization;
   19   import javax.transaction.SystemException;
   20   import javax.transaction.Transaction;
   21   import javax.transaction.xa.XAException;
   22   import javax.transaction.xa.XAResource;
   23   import javax.transaction.xa.Xid;
   24   import java.util.HashMap;
   25   import java.util.HashSet;
   26   import java.util.Iterator;
   27   import java.util.ArrayList;
   28   
   29   
   30   /**
   31    *  Our <code>Transaction</code> implementation.
   32    *
   33    *  @see TxManager
   34    *
   35    *  @author <a href="mailto:rickard.oberg@telkel.com">Rickard Öberg</a>
   36    *  @author <a href="mailto:marc.fleury@telkel.com">Marc Fleury</a>
   37    *  @author <a href="mailto:osh@sparre.dk">Ole Husgaard</a>
   38    *  @author <a href="mailto:toby.allsopp@peace.com">Toby Allsopp</a>
   39    *  @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
   40    *  @author <a href="mailto:d_jencks@users.sourceforge.net">David Jencks</a>
   41    *  @author <a href="mailto:bill@jboss.org">Bill Burke</a>
   42    *  @version $Revision: 1.5.2.16 $
   43    */
   44   class TransactionImpl
   45      implements Transaction, TimeoutTarget
   46   {
   47      // Constants -----------------------------------------------------
   48   
   49      /**
   50       * Code meaning "no heuristics seen",
   51       * must not be XAException.XA_HEURxxx
   52       */
   53      private static final int HEUR_NONE = XAException.XA_RETRY;
   54   
   55      // Resource states
   56      private final static int RS_NEW           = 0; // not yet enlisted
   57      private final static int RS_ENLISTED      = 1; // enlisted
   58      private final static int RS_SUSPENDED     = 2; // suspended
   59      private final static int RS_ENDED         = 3; // not associated
   60      private final static int RS_VOTE_READONLY = 4; // voted read-only
   61      private final static int RS_VOTE_OK       = 5; // voted ok
   62      private final static int RS_FORGOT        = 6; // RM has forgotten     
   63   
   64   
   65      // Attributes ----------------------------------------------------
   66   
   67      /** Class logger, we don't want a new logger with every transaction. */
   68      private static Logger log = Logger.getLogger(TransactionImpl.class);
   69   
   70      /** True if trace messages should be logged. */
   71      private boolean trace = log.isTraceEnabled();
   72   
   73      /** The ID of this transaction. */
   74      private Xid xid;
   75   
   76      private ArrayList threads = new ArrayList(1);
   77   
   78      private HashMap transactionLocalMap = new HashMap();
   79   
   80      private Throwable cause;
   81   
   82      /**
   83       *  The global ID of this transaction.
   84       *  This is used as a transaction propagation context, and in the
   85       *  TxManager for mapping transaction IDs to transactions.
   86       */
   87      private GlobalId globalId;
   88   
   89      /**
   90       *  The synchronizations to call back.
   91       */
   92      private Synchronization[] sync = new Synchronization[3];
   93   
   94      /**
   95       *  Size of allocated synchronization array.
   96       */
   97      private int syncAllocSize = 3;
   98   
   99      /**
  100       *  Count of synchronizations for this transaction.
  101       */
  102      private int syncCount = 0;
  103   
  104      /**
  105       *  A list of the XARessources that have participated in this transaction.
  106       */
  107      private XAResource[] resources = new XAResource[3];
  108   
  109      /**
  110       *  The state of the resources.
  111       */
  112      private int[] resourceState = new int[3];
  113   
  114      /**
  115       *  Index of the first XAResource representing the same resource manager,
  116       *  or <code>-1</code> if this XAResource is the first XAResource in this
  117       *  transaction that represents its resource manager.
  118       */
  119      private int[] resourceSameRM = new int[3];
  120   
  121      /**
  122       *  A list of the XARessources that have participated in this transaction.
  123       */
  124      private Xid[] resourceXids = new Xid[3];
  125   
  126      /**
  127       *  Size of allocated resource arrays.
  128       */
  129      private int resourceAllocSize = 3;
  130   
  131      /**
  132       *  Count of resources that have participated in this transaction.
  133       *  This contains a count of all XAResources, not a count of distinct
  134       *  resource managers.
  135       *  It is the length of resources and other such arrays.
  136       */
  137      private int resourceCount = 0;
  138   
  139      /**
  140       *  Flags that it is too late to enlist new resources.
  141       */
  142      private boolean resourcesEnded = false;
  143   
  144      /**
  145       *  Last branch id used.
  146       */
  147      private long lastBranchId = 0;
  148   
  149      /**
  150       *  Status of this transaction.
  151       */
  152      private int status;
  153   
  154      /**
  155       *  The heuristics status of this transaction.
  156       */
  157      private int heuristicCode = HEUR_NONE;
  158   
  159      /**
  160       *  The time when this transaction was started.
  161       */
  162      private long start;
  163   
  164      /**
  165       *  The timeout handle for this transaction.
  166       */
  167      private Timeout timeout;
  168   
  169      /**
  170       * Timeout in millisecs
  171       */
  172      private long timeoutPeriod;
  173      /**
  174       *  Mutex for thread-safety. This should only be changed in the
  175       *  <code>lock()</code> and <code>unlock()</code> methods.
  176       */
  177      private boolean locked = false;
  178   
  179      /**
  180       *  Flags that we are done with this transaction and that it can be reused.
  181       */
  182      private boolean done = false;
  183   
  184      // Static --------------------------------------------------------
  185   
  186      /**
  187       *  Factory for Xid instances of specified class.
  188       *  This is set from the <code>TransactionManagerService</code>
  189       *  MBean.
  190       */
  191      static XidFactoryMBean xidFactory;
  192   
  193      static TransactionManagerService txManagerService;
  194   
  195      /**
  196       * This static code is only present for testing purposes so a
  197       * tm can be usable without a lot of setup.
  198       *
  199       */
  200      static void defaultXidFactory()
  201      {
  202         if (xidFactory == null)
  203         {
  204            xidFactory = new XidFactory();
  205         } // end of if ()
  206      }
  207   
  208   
  209      // Constructors --------------------------------------------------
  210   
  211      TransactionImpl(long timeout)
  212      {
  213         xid = xidFactory.newXid();
  214         globalId = new GlobalId(xid);
  215   
  216         status = Status.STATUS_ACTIVE;
  217   
  218         start = System.currentTimeMillis();
  219         this.timeout = TimeoutFactory.createTimeout(start+timeout, this);
  220         this.timeoutPeriod = timeout;
  221         if (trace)
  222            log.trace("Created new instance for tx=" + toString());
  223   
  224      }
  225   
  226      // Implements TimeoutTarget --------------------------------------
  227   
  228      /**
  229       *  Called when our timeout expires.
  230       */
  231      public void timedOut(Timeout timeout)
  232      {
  233         try
  234         {
  235            lock();
  236   
  237            log.warn("Transaction " + toString() + " timed out." +
  238                     " status=" + getStringStatus(status));
  239   
  240            if (this.timeout == null)
  241               return; // Don't race with timeout cancellation.
  242            this.timeout = null;
  243   
  244            switch (status)
  245            {
  246               case Status.STATUS_ROLLEDBACK:
  247               case Status.STATUS_COMMITTED:
  248               case Status.STATUS_NO_TRANSACTION:
  249                  return; // Transaction done.
  250   
  251               case Status.STATUS_ROLLING_BACK:
  252                  return; // Will be done shortly.
  253   
  254               case Status.STATUS_COMMITTING:
  255                  // This is _very_ bad:
  256                  // We are in the second commit phase, and have decided
  257                  // to commit, but now we get a timeout and should rollback.
  258                  // So we end up with a mixed decision.
  259                  gotHeuristic(-1, XAException.XA_HEURMIX);
  260                  status = Status.STATUS_MARKED_ROLLBACK;
  261                  return; // commit will fail
  262   
  263               case Status.STATUS_PREPARED:
  264                  // This is bad:
  265                  // We are done with the first phase, and are persistifying
  266                  // our decision. Fortunately this case is currently never
  267                  // hit, as we do not release the lock between the two phases.
  268               case Status.STATUS_ACTIVE:
  269                  status = Status.STATUS_MARKED_ROLLBACK;
  270                  // fall through..
  271               case Status.STATUS_MARKED_ROLLBACK:
  272                  // don't rollback for now, this messes up with the TxInterceptor.
  273                  interruptThreads();
  274                  return;
  275   
  276               case Status.STATUS_PREPARING:
  277                  status = Status.STATUS_MARKED_ROLLBACK;
  278                  return; // commit will fail
  279   
  280               default:
  281                  log.warn("Unknown status at timeout, tx=" + toString());
  282                  return;
  283            }
  284         } finally
  285         {
  286            unlock();
  287         }
  288      }
  289   
  290      // Implements Transaction ----------------------------------------
  291   
  292      public void commit()
  293         throws RollbackException,
  294                HeuristicMixedException,
  295                HeuristicRollbackException,
  296                java.lang.SecurityException,
  297                java.lang.IllegalStateException,
  298                SystemException
  299      {
  300         try
  301         {
  302            lock();
  303   
  304            if (trace)
  305            {
  306               log.trace("Committing, tx=" + this +
  307                         ", status=" + getStringStatus(status));
  308            }
  309   
  310            switch (status)
  311            {
  312               case Status.STATUS_PREPARING:
  313                  throw new IllegalStateException("Already started preparing.");
  314               case Status.STATUS_PREPARED:
  315                  throw new IllegalStateException("Already prepared.");
  316               case Status.STATUS_ROLLING_BACK:
  317                  throw new IllegalStateException("Already started rolling back.");
  318               case Status.STATUS_ROLLEDBACK:
  319                  instanceDone();
  320                  checkHeuristics();
  321                  throw new IllegalStateException("Already rolled back.");
  322               case Status.STATUS_COMMITTING:
  323                  throw new IllegalStateException("Already started committing.");
  324               case Status.STATUS_COMMITTED:
  325                  instanceDone();
  326                  checkHeuristics();
  327                  throw new IllegalStateException("Already committed.");
  328               case Status.STATUS_NO_TRANSACTION:
  329                  throw new IllegalStateException("No transaction.");
  330               case Status.STATUS_UNKNOWN:
  331                  throw new IllegalStateException("Unknown state");
  332               case Status.STATUS_MARKED_ROLLBACK:
  333                  doBeforeCompletion();
  334                  endResources();
  335                  rollbackResources();
  336                  doAfterCompletion();
  337                  cancelTimeout();
  338                  instanceDone();
  339                  checkHeuristics();
  340                  throw new RollbackException("Already marked for rollback");
  341               case Status.STATUS_ACTIVE:
  342                  break;
  343               default:
  344                  throw new IllegalStateException("Illegal status: " + status);
  345            }
  346   
  347            doBeforeCompletion();
  348   
  349            if (trace)
  350            {
  351               log.trace("Before completion done, tx=" + this +
  352               ", status=" + getStringStatus(status));
  353            }
  354   
  355            endResources();
  356   
  357            if (status == Status.STATUS_ACTIVE)
  358            {
  359               if (resourceCount == 0)
  360               {
  361                  // Zero phase commit is really fast ;-)
  362                  if (trace)
  363                  {
  364                     log.trace("Zero phase commit: No resources.");
  365                  }
  366                  status = Status.STATUS_COMMITTED;
  367               }
  368               else if (isOneResource())
  369               {
  370                  // One phase commit
  371                  if (trace)
  372                  {
  373                     log.trace("One phase commit: One resource.");
  374                  }
  375                  commitResources(true);
  376               } else
  377               {
  378                  // Two phase commit
  379                  if (trace)
  380                  {
  381                     log.trace("Two phase commit: Many resources.");
  382                  }
  383   
  384                  if (!prepareResources())
  385                  {
  386                     boolean commitDecision =
  387                        status == Status.STATUS_PREPARED &&
  388                        (heuristicCode == HEUR_NONE ||
  389                         heuristicCode == XAException.XA_HEURCOM);
  390   
  391                     // TODO: Save decision to stable storage for recovery
  392                     //       after system crash.
  393   
  394                     if (commitDecision)
  395                        commitResources(false);
  396                  } else
  397                     status = Status.STATUS_COMMITTED; // all was read-only
  398               }
  399            }
  400   
  401            if (status != Status.STATUS_COMMITTED)
  402            {
  403               rollbackResources();
  404               doAfterCompletion();
  405               cancelTimeout();
  406   
  407               // save off the cause throwable as Instance done resets it to null
  408               Throwable causedByThrowable = cause;
  409   
  410               instanceDone();
  411   
  412               // throw jboss rollback exception with the saved off cause
  413               throw new JBossRollbackException("Unable to commit, tx=" +
  414                     toString() + " status=" + getStringStatus(status),
  415                     causedByThrowable);
  416            }
  417   
  418            cancelTimeout();
  419            doAfterCompletion();
  420            instanceDone();
  421            checkHeuristics();
  422   
  423            if (trace)
  424            {
  425               log.trace("Committed OK, tx=" + this);
  426            }
  427   
  428         } finally {
  429            transactionLocalMap.clear();
  430            threads.clear();
  431            unlock();
  432         }
  433      }
  434   
  435      public void rollback()
  436         throws java.lang.IllegalStateException,
  437                java.lang.SecurityException,
  438                SystemException
  439      {
  440         try
  441         {
  442            lock();
  443   
  444            if (trace)
  445            {
  446               log.trace("rollback(): Entered, tx=" + toString() +
  447               " status=" + getStringStatus(status));
  448            }
  449   
  450            switch (status)
  451            {
  452               case Status.STATUS_ACTIVE:
  453                  status = Status.STATUS_MARKED_ROLLBACK;
  454                  // fall through..
  455               case Status.STATUS_MARKED_ROLLBACK:
  456                  doBeforeCompletion();
  457                  endResources();
  458                  rollbackResources();
  459                  cancelTimeout();
  460                  doAfterCompletion();
  461                  instanceDone();
  462                  // Cannot throw heuristic exception, so we just have to
  463                  // clear the heuristics without reporting.
  464                  heuristicCode = HEUR_NONE;
  465                  return;
  466               case Status.STATUS_PREPARING:
  467                  // Set status to avoid race with prepareResources().
  468                  status = Status.STATUS_MARKED_ROLLBACK;
  469                  return; // commit() will do rollback.
  470               default:
  471                  throw new IllegalStateException("Cannot rollback(), " +
  472                  "tx=" + toString() +
  473                  " status=" +
  474                  getStringStatus(status));
  475            }
  476         } finally {
  477            transactionLocalMap.clear();
  478            threads.clear();
  479            Thread.interrupted();// clear timeout that did an interrupt
  480            unlock();
  481         }
  482      }
  483   
  484      public boolean delistResource(XAResource xaRes, int flag)
  485         throws java.lang.IllegalStateException,
  486                SystemException
  487      {
  488         if (xaRes == null)
  489            throw new IllegalArgumentException("null xaRes");
  490         if (flag != XAResource.TMSUCCESS &&
  491             flag != XAResource.TMSUSPEND &&
  492             flag != XAResource.TMFAIL)
  493            throw new IllegalArgumentException("Bad flag: " + flag);
  494   
  495         try
  496         {
  497            lock();
  498   
  499            if (trace)
  500            {
  501               log.trace("delistResource(): Entered, tx=" +
  502               toString() + " status=" + getStringStatus(status));
  503            }
  504   
  505            int idx = findResource(xaRes);
  506   
  507            if (idx == -1)
  508               throw new IllegalArgumentException("xaRes not enlisted");
  509   
  510            switch (status)
  511            {
  512               case Status.STATUS_ACTIVE:
  513               case Status.STATUS_MARKED_ROLLBACK:
  514                  break;
  515               case Status.STATUS_PREPARING:
  516                  throw new IllegalStateException("Already started preparing.");
  517               case Status.STATUS_ROLLING_BACK:
  518                  throw new IllegalStateException("Already started rolling back.");
  519               case Status.STATUS_PREPARED:
  520                  throw new IllegalStateException("Already prepared.");
  521               case Status.STATUS_COMMITTING:
  522                  throw new IllegalStateException("Already started committing.");
  523               case Status.STATUS_COMMITTED:
  524                  throw new IllegalStateException("Already committed.");
  525               case Status.STATUS_ROLLEDBACK:
  526                  throw new IllegalStateException("Already rolled back.");
  527               case Status.STATUS_NO_TRANSACTION:
  528                  throw new IllegalStateException("No transaction.");
  529               case Status.STATUS_UNKNOWN:
  530                  throw new IllegalStateException("Unknown state");
  531               default:
  532                  throw new IllegalStateException("Illegal status: " + status);
  533            }
  534   
  535            try
  536            {
  537               if (resourceState[idx] == RS_ENDED && !resources[idx].isSameRM(xaRes)) {
  538                  // This RM always returns false on isSameRM.  Further,
  539                  // the last resource has already been delisted.
  540                  log.warn("Resource already delisted.  tx=" + toString());
  541                  return false;
  542               }
  543               endResource(idx, flag);
  544               return true;
  545            } catch (XAException xae)
  546            {
  547               logXAException(xae);
  548               status = Status.STATUS_MARKED_ROLLBACK;
  549               cause = xae;
  550               return false;
  551            }
  552         } finally
  553         {
  554            unlock();
  555         }
  556      }
  557   
  558      public boolean enlistResource(XAResource xaRes)
  559         throws RollbackException,
  560                java.lang.IllegalStateException,
  561                SystemException
  562      {
  563         if (xaRes == null)
  564            throw new IllegalArgumentException("null xaRes");
  565   
  566         try
  567         {
  568            lock();
  569   
  570            if (trace)
  571            {
  572               log.trace("enlistResource(): Entered, tx=" +
  573               toString() + " status=" + getStringStatus(status));
  574            }
  575   
  576            switch (status)
  577            {
  578               case Status.STATUS_ACTIVE:
  579               case Status.STATUS_PREPARING:
  580                  break;
  581               case Status.STATUS_PREPARED:
  582                  throw new IllegalStateException("Already prepared.");
  583               case Status.STATUS_COMMITTING:
  584                  throw new IllegalStateException("Already started committing.");
  585               case Status.STATUS_COMMITTED:
  586                  throw new IllegalStateException("Already committed.");
  587               case Status.STATUS_MARKED_ROLLBACK:
  588                  throw new RollbackException("Already marked for rollback");
  589               case Status.STATUS_ROLLING_BACK:
  590                  throw new RollbackException("Already started rolling back.");
  591               case Status.STATUS_ROLLEDBACK:
  592                  throw new RollbackException("Already rolled back.");
  593               case Status.STATUS_NO_TRANSACTION:
  594                  throw new IllegalStateException("No transaction.");
  595               case Status.STATUS_UNKNOWN:
  596                  throw new IllegalStateException("Unknown state");
  597               default:
  598                  throw new IllegalStateException("Illegal status: " + status);
  599            }
  600   
  601            if (resourcesEnded)
  602               throw new IllegalStateException("Too late to enlist resources");
  603   
  604            // Add resource
  605            try
  606            {
  607               int idx = findResource(xaRes);
  608   
  609               if (idx != -1)
  610               {
  611                  if (resourceState[idx] == RS_ENLISTED)
  612                     return false; // already enlisted
  613                  if (resourceState[idx] == RS_ENDED && !resources[idx].isSameRM(xaRes)) {
  614                     // this is a resource that returns false on all calls to
  615                     // isSameRM.  Further, the last resource enlisted has
  616                     // already been delisted, so it is time to enlist it again.
  617                     idx = -1;
  618                  } else {
  619                     startResource(idx);
  620                     return true;
  621                  }
  622               }
  623   
  624               for (int i = 0; i < resourceCount; ++i) {
  625                  if (resourceSameRM[i] == -1 && xaRes.isSameRM(resources[i])) {
  626                     // The xaRes is new. We register the xaRes with the Xid
  627                     // that the RM has previously seen from this transaction,
  628                     // and note that it has the same RM.
  629                     startResource(addResource(xaRes, resourceXids[i], i));
  630   
  631                     return true;
  632                  }
  633               }
  634   
  635               // New resource and new RM: Create a new transaction branch.
  636               startResource(addResource(xaRes, createXidBranch(), -1));
  637               return true;
  638            } catch (XAException xae)
  639            {
  640               logXAException(xae);
  641               cause = xae;
  642               return false;
  643            }
  644         } finally
  645         {
  646            unlock();
  647         }
  648   
  649      }
  650   
  651      public int getStatus()
  652         throws SystemException
  653      {
  654         if (done)
  655            return Status.STATUS_NO_TRANSACTION;
  656         return status;
  657      }
  658   
  659      public void registerSynchronization(Synchronization s)
  660         throws RollbackException,
  661                java.lang.IllegalStateException,
  662                SystemException
  663      {
  664         if (s == null)
  665            throw new IllegalArgumentException("Null synchronization");
  666   
  667         try
  668         {
  669            lock();
  670   
  671            if (trace)
  672            {
  673               log.trace("registerSynchronization(): Entered, " +
  674               "tx=" + toString() +
  675               " status=" + getStringStatus(status));
  676            }
  677   
  678            switch (status) {
  679            case Status.STATUS_ACTIVE:
  680            case Status.STATUS_PREPARING:
  681               break;
  682            case Status.STATUS_PREPARED:
  683               throw new IllegalStateException("Already prepared.");
  684            case Status.STATUS_COMMITTING:
  685               throw new IllegalStateException("Already started committing.");
  686            case Status.STATUS_COMMITTED:
  687               throw new IllegalStateException("Already committed.");
  688            case Status.STATUS_MARKED_ROLLBACK:
  689               throw new RollbackException("Already marked for rollback");
  690            case Status.STATUS_ROLLING_BACK:
  691               throw new RollbackException("Already started rolling back.");
  692            case Status.STATUS_ROLLEDBACK:
  693               throw new RollbackException("Already rolled back.");
  694            case Status.STATUS_NO_TRANSACTION:
  695               throw new IllegalStateException("No transaction.");
  696            case Status.STATUS_UNKNOWN:
  697               throw new IllegalStateException("Unknown state");
  698            default:
  699               throw new IllegalStateException("Illegal status: " + status);
  700            }
  701   
  702            if (syncCount == syncAllocSize)
  703            {
  704               // expand table
  705               syncAllocSize = 2 * syncAllocSize;
  706   
  707               Synchronization[] sy = new Synchronization[syncAllocSize];
  708               System.arraycopy(sync, 0, sy, 0, syncCount);
  709               sync = sy;
  710            }
  711            sync[syncCount++] = s;
  712         } finally
  713         {
  714            unlock();
  715         }
  716      }
  717   
  718      public void setRollbackOnly()
  719         throws java.lang.IllegalStateException,
  720                SystemException
  721      {
  722         try {
  723            lock();
  724   
  725            if (trace)
  726               log.trace("setRollbackOnly(): Entered, tx=" +
  727                         toString() + " status=" + getStringStatus(status));
  728   
  729            switch (status) {
  730               case Status.STATUS_ACTIVE:
  731               case Status.STATUS_PREPARING:
  732               case Status.STATUS_PREPARED:
  733                  status = Status.STATUS_MARKED_ROLLBACK;
  734                  // fall through..
  735               case Status.STATUS_MARKED_ROLLBACK:
  736               case Status.STATUS_ROLLING_BACK:
  737                  return;
  738               case Status.STATUS_COMMITTING:
  739                  throw new IllegalStateException("Already started committing.");
  740               case Status.STATUS_COMMITTED:
  741                  throw new IllegalStateException("Already committed.");
  742               case Status.STATUS_ROLLEDBACK:
  743                  throw new IllegalStateException("Already rolled back.");
  744               case Status.STATUS_NO_TRANSACTION:
  745                  throw new IllegalStateException("No transaction.");
  746               case Status.STATUS_UNKNOWN:
  747                  throw new IllegalStateException("Unknown state");
  748               default:
  749                  throw new IllegalStateException("Illegal status: " + status);
  750            }
  751         } finally {
  752            unlock();
  753         }
  754      }
  755   
  756      // Public --------------------------------------------------------
  757   
  758      public void associateCurrentThread()
  759      {
  760         threads.add(Thread.currentThread());
  761      }
  762   
  763      public void disassociateCurrentThread()
  764      {
  765         threads.remove(Thread.currentThread());
  766         Thread.interrupted();
  767      }
  768      public void clearThreads()
  769      {
  770         for (int i = 0; i < threads.size(); i++)
  771         {
  772            Thread t = (Thread)threads.get(i);
  773         }
  774      }
  775      public int hashCode()
  776      {
  777         return globalId.hashCode();
  778      }
  779   
  780      public String toString()
  781      {
  782         return "TransactionImpl:" + xidFactory.toString(xid);
  783      }
  784   
  785      public boolean equals(Object obj)
  786      {
  787         if (obj != null && obj instanceof TransactionImpl)
  788            return globalId.equals(((TransactionImpl)obj).globalId);
  789         return false;
  790      }
  791   
  792   
  793      // Package protected ---------------------------------------------
  794   
  795      /**
  796       *  Getter for property done.
  797       */
  798      boolean isDone()
  799      {
  800         return done;
  801      }
  802   
  803      /**
  804       *  Return the global id of this transaction.
  805       */
  806      GlobalId getGlobalId()
  807      {
  808         return globalId;
  809      }
  810   
  811   
  812      // Private -------------------------------------------------------
  813   
  814      /**
  815       * Interrupt all threads involved with transaction
  816       * This is called on timeout
  817       */
  818      private void interruptThreads()
  819      {
  820         Iterator it = threads.iterator();
  821         while (it.hasNext())
  822         {
  823            Thread thread = (Thread)it.next();
  824            try
  825            {
  826               thread.interrupt();
  827            }
  828            catch (Exception ignored) {}
  829         }
  830         threads.clear();
  831      }
  832   
  833      /**
  834       *  Return a string representation of the given status code.
  835       */
  836      private String getStringStatus(int status)
  837      {
  838         switch (status) {
  839            case Status.STATUS_PREPARING:
  840               return "STATUS_PREPARING";
  841            case Status.STATUS_PREPARED:
  842               return "STATUS_PREPARED";
  843            case Status.STATUS_ROLLING_BACK:
  844               return "STATUS_ROLLING_BACK";
  845            case Status.STATUS_ROLLEDBACK:
  846               return "STATUS_ROLLEDBACK";
  847            case Status.STATUS_COMMITTING:
  848               return "STATUS_COMMITING";
  849            case Status.STATUS_COMMITTED:
  850               return "STATUS_COMMITED";
  851            case Status.STATUS_NO_TRANSACTION:
  852               return "STATUS_NO_TRANSACTION";
  853            case Status.STATUS_UNKNOWN:
  854               return "STATUS_UNKNOWN";
  855            case Status.STATUS_MARKED_ROLLBACK:
  856               return "STATUS_MARKED_ROLLBACK";
  857            case Status.STATUS_ACTIVE:
  858               return "STATUS_ACTIVE";
  859   
  860            default:
  861               return "STATUS_UNKNOWN(" + status + ")";
  862         }
  863      }
  864   
  865      /**
  866       *  Return a string representation of the given XA error code.
  867       */
  868      private String getStringXAErrorCode(int errorCode)
  869      {
  870         switch (errorCode) {
  871            case XAException.XA_HEURCOM:
  872               return "XA_HEURCOM";
  873            case XAException.XA_HEURHAZ:
  874               return "XA_HEURHAZ";
  875            case XAException.XA_HEURMIX:
  876               return "XA_HEURMIX";
  877            case XAException.XA_HEURRB:
  878               return "XA_HEURRB";
  879   
  880            case XAException.XA_NOMIGRATE:
  881               return "XA_NOMIGRATE";
  882   
  883            case XAException.XA_RBCOMMFAIL:
  884               return "XA_RBCOMMFAIL";
  885            case XAException.XA_RBDEADLOCK:
  886               return "XA_RBDEADLOCK";
  887            case XAException.XA_RBINTEGRITY:
  888               return "XA_RBINTEGRITY";
  889            case XAException.XA_RBOTHER:
  890               return "XA_RBOTHER";
  891            case XAException.XA_RBPROTO:
  892               return "XA_RBPROTO";
  893            case XAException.XA_RBROLLBACK:
  894               return "XA_RBROLLBACK";
  895            case XAException.XA_RBTIMEOUT:
  896               return "XA_RBTIMEOUT";
  897            case XAException.XA_RBTRANSIENT:
  898               return "XA_RBTRANSIENT";
  899   
  900            case XAException.XA_RDONLY:
  901               return "XA_RDONLY";
  902            case XAException.XA_RETRY:
  903               return "XA_RETRY";
  904   
  905            case XAException.XAER_ASYNC:
  906               return "XAER_ASYNC";
  907            case XAException.XAER_DUPID:
  908               return "XAER_DUPID";
  909            case XAException.XAER_INVAL:
  910               return "XAER_INVAL";
  911            case XAException.XAER_NOTA:
  912               return "XAER_NOTA";
  913            case XAException.XAER_OUTSIDE:
  914               return "XAER_OUTSIDE";
  915            case XAException.XAER_PROTO:
  916               return "XAER_PROTO";
  917            case XAException.XAER_RMERR:
  918               return "XAER_RMERR";
  919            case XAException.XAER_RMFAIL:
  920               return "XAER_RMFAIL";
  921   
  922            default:
  923               return "XA_UNKNOWN(" + errorCode + ")";
  924         }
  925      }
  926   
  927      private void logXAException(XAException xae)
  928      {
  929         log.warn("XAException: tx=" + toString() + " errorCode=" +
  930                  getStringXAErrorCode(xae.errorCode), xae);
  931         if (txManagerService != null)
  932         {
  933            txManagerService.formatXAException(xae, log);
  934         } // end of if ()
  935      }
  936   
  937      /**
  938       *  Lock this instance.
  939       */
  940      private synchronized void lock()
  941      {
  942         if (done)
  943            throw new IllegalStateException("Transaction has terminated");
  944   
  945         if (locked) {
  946            log.warn("Lock contention, tx=" + toString());
  947            //DEBUG Thread.currentThread().dumpStack();
  948   
  949            while (locked) {
  950               try {
  951                  // Wakeup happens when:
  952                  // - notify() is called from unlock()
  953                  // - notifyAll is called from instanceDone()
  954                  wait();
  955               } catch (InterruptedException ex) {
  956                  // ignore
  957               }
  958   
  959               if (done)
  960                  throw new IllegalStateException("Transaction has now terminated");
  961            }
  962         }
  963   
  964         locked = true;
  965      }
  966   
  967      /**
  968       *  Unlock this instance.
  969       */
  970      private synchronized void unlock()
  971      {
  972         if (!locked)
  973         {
  974            log.warn("Unlocking, but not locked, tx=" + toString(),
  975            new Throwable("[Stack trace]"));
  976         }
  977   
  978         locked = false;
  979   
  980         notify();
  981      }
  982   
  983      /**
  984       *  Mark this transaction as non-existing.
  985       */
  986      private synchronized void instanceDone()
  987      {
  988         TxManager manager = TxManager.getInstance();
  989   
  990         if (status == Status.STATUS_COMMITTED)
  991            manager.incCommitCount();
  992         else
  993            manager.incRollbackCount();
  994   
  995         // Garbage collection
  996         manager.releaseTransactionImpl(this);
  997   
  998         // Set the status
  999         status = Status.STATUS_NO_TRANSACTION;
 1000   
 1001         // Clear tables refering to external objects.
 1002         // Even if a client holds on to this instance forever, the objects
 1003         // that we have referenced may be garbage collected.
 1004         sync = null;
 1005         resources = null;
 1006   
 1007         // Notify all threads waiting for the lock.
 1008         notifyAll();
 1009   
 1010         // set the done flag
 1011         done = true;
 1012      }
 1013   
 1014      /**
 1015       *  Cancel the timeout.
 1016       *  This will release the lock while calling out.
 1017       */
 1018      private void cancelTimeout()
 1019      {
 1020         if (timeout != null) {
 1021            unlock();
 1022            try
 1023            {
 1024               timeout.cancel();
 1025            } catch (Exception e)
 1026            {
 1027               if (trace)
 1028                  log.trace("failed to cancel timeout", e);
 1029            } finally
 1030            {
 1031               lock();
 1032            }
 1033            timeout = null;
 1034         }
 1035      }
 1036   
 1037      /**
 1038       *  Return index of XAResource, or <code>-1</code> if not found.
 1039       */
 1040      private int findResource(XAResource xaRes)
 1041      {
 1042         // A linear search may seem slow, but please note that
 1043         // the number of XA resources registered with a transaction
 1044         // are usually low.
 1045         // Note: This searches backwards intentionally!  It ensures that
 1046         // if this resource was enlisted multiple times, then the last one
 1047         // will be returned.  All others should be in the state RS_ENDED.
 1048         // This allows ResourceManagers that always return false from isSameRM
 1049         // to be enlisted and delisted multiple times.
 1050         for (int idx = resourceCount - 1; idx >= 0; --idx)
 1051            if (xaRes == resources[idx])
 1052               return idx;
 1053   
 1054         return -1;
 1055      }
 1056   
 1057      /**
 1058       *  Add a resource, expanding tables if needed.
 1059       *
 1060       *  @param xaRes The new XA resource to add. It is assumed that the
 1061       *         resource is not already in the table of XA resources.
 1062       *  @param branchXid The Xid for the transaction branch that is to
 1063       *         be used for associating with this resource.
 1064       *  @param idxSameRM The index in our XA resource tables of the first
 1065       *         XA resource having the same resource manager as
 1066       *         <code>xaRes</code>, or <code>-1</code> if <code>xaRes</code>
 1067       *         is the first resource seen with this resource manager.
 1068       *
 1069       *  @return The index of the new resource in our internal tables.
 1070       */
 1071      private int addResource(XAResource xaRes, Xid branchXid, int idxSameRM)
 1072      {
 1073         if (resourceCount == resourceAllocSize)
 1074         {
 1075            // expand tables
 1076            resourceAllocSize = 2 * resourceAllocSize;
 1077   
 1078            XAResource[] res = new XAResource[resourceAllocSize];
 1079            System.arraycopy(resources, 0, res, 0, resourceCount);
 1080            resources = res;
 1081   
 1082            int[] stat = new int[resourceAllocSize];
 1083            System.arraycopy(resourceState, 0, stat, 0, resourceCount);
 1084            resourceState = stat;
 1085   
 1086            Xid[] xids = new Xid[resourceAllocSize];
 1087            System.arraycopy(resourceXids, 0, xids, 0, resourceCount);
 1088            resourceXids = xids;
 1089   
 1090            int[] sameRM = new int[resourceAllocSize];
 1091            System.arraycopy(resourceSameRM, 0, sameRM, 0, resourceCount);
 1092            resourceSameRM = sameRM;
 1093         }
 1094         resources[resourceCount] = xaRes;
 1095         resourceState[resourceCount] = RS_NEW;
 1096         resourceXids[resourceCount] = branchXid;
 1097         resourceSameRM[resourceCount] = idxSameRM;
 1098   
 1099         return resourceCount++;
 1100      }
 1101   
 1102      /**
 1103       *  Call <code>start()</code> on a XAResource and update
 1104       *  internal state information.
 1105       *  This will release the lock while calling out.
 1106       *
 1107       *  @param idx The index of the resource in our internal tables.
 1108       */
 1109      private void startResource(int idx)
 1110         throws XAException
 1111      {
 1112         int flags = XAResource.TMJOIN;
 1113   
 1114         if (resourceSameRM[idx] == -1)
 1115         {
 1116            switch (resourceState[idx])
 1117            {
 1118               case RS_NEW:
 1119                  flags = XAResource.TMNOFLAGS;
 1120                  break;
 1121               case RS_SUSPENDED:
 1122                  flags = XAResource.TMRESUME;
 1123                  break;
 1124   
 1125             default:
 1126                if (trace)
 1127                {
 1128                   log.trace("Unhandled resource state: " + resourceState[idx] +
 1129                             " (not RS_NEW or RS_SUSPENDED, using TMJOIN flags)");
 1130                }
 1131            }
 1132         }
 1133   
 1134         if (trace)
 1135         {
 1136            log.trace("startResource(" +
 1137                      xidFactory.toString(resourceXids[idx]) +
 1138                      ") entered: " + resources[idx].toString() +
 1139                      " flags=" + flags);
 1140         }
 1141   
 1142         unlock();
 1143         // OSH FIXME: resourceState could be incorrect during this callout.
 1144         try
 1145         {
 1146            try
 1147            {
 1148               resources[idx].start(resourceXids[idx], flags);
 1149            }
 1150            catch(XAException e)
 1151            {
 1152               throw e;
 1153            }
 1154            catch (Throwable t)
 1155            {
 1156               if (trace)
 1157               {
 1158                  log.trace("unhandled throwable error in startResource", t);
 1159               }
 1160               status = Status.STATUS_MARKED_ROLLBACK;
 1161               return;
 1162            }
 1163   
 1164            // Now the XA resource is associated with a transaction.
 1165            resourceState[idx] = RS_ENLISTED;
 1166         }
 1167         finally
 1168         {
 1169            lock();
 1170            if (trace)
 1171            {
 1172               log.trace("startResource(" +
 1173                         xidFactory.toString(resourceXids[idx]) +
 1174                         ") leaving: " + resources[idx].toString() +
 1175                         " flags=" + flags);
 1176            }
 1177         }
 1178      }
 1179   
 1180      /**
 1181       *  Call <code>end()</code> on the XAResource and update
 1182       *  internal state information.
 1183       *  This will release the lock while calling out.
 1184       *
 1185       *  @param idx The index of the resource in our internal tables.
 1186       *  @param flag The flag argument for the end() call.
 1187       */
 1188      private void endResource(int idx, int flag)
 1189         throws XAException
 1190      {
 1191         if (trace)
 1192         {
 1193            log.trace("endResource(" +
 1194                      xidFactory.toString(resourceXids[idx]) +
 1195            ") entered: " + resources[idx].toString() +
 1196            " flag=" + flag);
 1197         }
 1198   
 1199         unlock();
 1200         // OSH FIXME: resourceState could be incorrect during this callout.
 1201         try
 1202         {
 1203            try
 1204            {
 1205               resources[idx].end(resourceXids[idx], flag);
 1206            } catch(XAException e)
 1207            {
 1208               throw e;
 1209            } catch (Throwable t)
 1210            {
 1211               if (trace)
 1212               {
 1213                  log.trace("unhandled throwable error in endResource", t);
 1214               }
 1215               status = Status.STATUS_MARKED_ROLLBACK;
 1216               // Resource may or may not be ended after illegal exception.
 1217               // We just assume it ended.
 1218               resourceState[idx] = RS_ENDED;
 1219               return;
 1220            }
 1221   
 1222   
 1223            // Update our internal state information
 1224            if (flag == XAResource.TMSUSPEND)
 1225               resourceState[idx] = RS_SUSPENDED;
 1226            else
 1227            {
 1228               if (flag == XAResource.TMFAIL)
 1229               {
 1230   
 1231                  status = Status.STATUS_MARKED_ROLLBACK;
 1232               }
 1233               resourceState[idx] = RS_ENDED;
 1234            }
 1235         } finally
 1236         {
 1237            lock();
 1238            if (trace)
 1239            {
 1240               log.trace("endResource(" +
 1241                         xidFactory.toString(resourceXids[idx]) +
 1242                         ") leaving: " + resources[idx].toString() +
 1243                         " flag=" + flag);
 1244            }
 1245         }
 1246      }
 1247   
 1248      /**
 1249       *  End Tx association for all resources.
 1250       */
 1251      private void endResources()
 1252      {
 1253         for (int idx = 0; idx < resourceCount; idx++) {
 1254            try {
 1255               /*We don't have minerva crap any more!  If your adapter doesn't
 1256                 like this, use the matchConnectionWithTx flag to prevent
 1257                 your resources from getting suspended.
 1258               if (resourceState[idx] == RS_SUSPENDED) {
 1259                  // This is mad, but JTA 1.0.1 spec says on page 41:
 1260                  // "If TMSUSPEND is specified in flags, the transaction
 1261                  // branch is temporarily suspended in incomplete state.
 1262                  // The transaction context is in suspened state and must
 1263                  // be resumed via start with TMRESUME specified."
 1264                  // Note the _must_ above: It does not say _may_.
 1265                  // The above citation also seem to contradict the XA resource
 1266                  // state table on pages 17-18 where it is legal to do both
 1267                  // end(TMSUCCESS) and end(TMFAIL) when the resource is in
 1268                  // a suspended state.
 1269                  // But the Minerva XA pool does not like that we call end()
 1270                  // two times in a row, so we resume before ending.
 1271                  startResource(idx);
 1272               }*/
 1273               if (resourceState[idx] == RS_ENLISTED || resourceState[idx] == RS_SUSPENDED)
 1274               {
 1275                  if (trace)
 1276                     log.trace("endresources(" + idx + "): state=" +
 1277                               resourceState[idx]);
 1278                  endResource(idx, XAResource.TMSUCCESS);
 1279               }
 1280            } catch(XAException xae)
 1281            {
 1282               logXAException(xae);
 1283               status = Status.STATUS_MARKED_ROLLBACK;
 1284               cause = xae;
 1285            }
 1286         }
 1287         resourcesEnded = true; // Too late to enlist new resources.
 1288      }
 1289   
 1290   
 1291      /**
 1292       *  Call synchronization <code>beforeCompletion()</code>.
 1293       *  This will release the lock while calling out.
 1294       */
 1295      private void doBeforeCompletion()
 1296      {
 1297         unlock();
 1298         try
 1299         {
 1300            for (int i = 0; i < syncCount; i++)
 1301            {
 1302               try
 1303               {
 1304                  if (trace)
 1305                  {
 1306                     log.trace("calling sync " + i + ", " + sync[i]);
 1307                  } // end of if ()
 1308                  sync[i].beforeCompletion();
 1309               } catch (Throwable t)
 1310               {
 1311                  if (trace)
 1312                  {
 1313                     log.trace("failed before completion", t);
 1314                  }
 1315                  status = Status.STATUS_MARKED_ROLLBACK;
 1316   
 1317                  // save the cause off so the user can inspect it
 1318                  cause = t;
 1319                  break;
 1320               }
 1321            }
 1322         } finally
 1323         {
 1324            lock();
 1325         }
 1326      }
 1327   
 1328      /**
 1329       *  Call synchronization <code>afterCompletion()</code>.
 1330       *  This will release the lock while calling out.
 1331       */
 1332      private void doAfterCompletion()
 1333      {
 1334         // Assert: Status indicates: Too late to add new synchronizations.
 1335         unlock();
 1336         try
 1337         {
 1338            for (int i = 0; i < syncCount; i++)
 1339            {
 1340               try
 1341               {
 1342                  sync[i].afterCompletion(status);
 1343               } catch (Throwable t)
 1344               {
 1345                  if (trace)
 1346                  {
 1347                     log.trace("failed after completion", t);
 1348                  }
 1349               }
 1350            }
 1351         } finally
 1352         {
 1353            lock();
 1354         }
 1355      }
 1356   
 1357      /**
 1358       *  We got another heuristic.
 1359       *
 1360       *  Promote <code>heuristicCode</code> if needed and tell
 1361       *  the resource to forget the heuristic.
 1362       *  This will release the lock while calling out.
 1363       *
 1364       *  @param resIdx The index of the XA resource that got a
 1365       *         heurictic in our internal tables, or <code>-1</code>
 1366       *         if the heuristic came from here.
 1367       *  @param code The heuristic code, one of
 1368       *         <code>XAException.XA_HEURxxx</code>.
 1369       */
 1370      private void gotHeuristic(int resIdx, int code)
 1371      {
 1372         switch (code)
 1373         {
 1374            case XAException.XA_HEURMIX:
 1375               heuristicCode = XAException.XA_HEURMIX;
 1376               break;
 1377            case XAException.XA_HEURRB:
 1378               if (heuristicCode == HEUR_NONE)
 1379                  heuristicCode = XAException.XA_HEURRB;
 1380               else if (heuristicCode == XAException.XA_HEURCOM ||
 1381               heuristicCode == XAException.XA_HEURHAZ)
 1382                  heuristicCode = XAException.XA_HEURMIX;
 1383               break;
 1384            case XAException.XA_HEURCOM:
 1385               if (heuristicCode == HEUR_NONE)
 1386                  heuristicCode = XAException.XA_HEURCOM;
 1387               else if (heuristicCode == XAException.XA_HEURRB ||
 1388               heuristicCode == XAException.XA_HEURHAZ)
 1389                  heuristicCode = XAException.XA_HEURMIX;
 1390               break;
 1391            case XAException.XA_HEURHAZ:
 1392               if (heuristicCode == HEUR_NONE)
 1393                  heuristicCode = XAException.XA_HEURHAZ;
 1394               else if (heuristicCode == XAException.XA_HEURCOM ||
 1395               heuristicCode == XAException.XA_HEURRB)
 1396                  heuristicCode = XAException.XA_HEURMIX;
 1397               break;
 1398            default:
 1399               throw new IllegalArgumentException();
 1400         }
 1401   
 1402         if (resIdx != -1)
 1403         {
 1404            try
 1405            {
 1406               unlock();
 1407               resources[resIdx].forget(resourceXids[resIdx]);
 1408            } catch (XAException xae)
 1409            {
 1410               logXAException(xae);
 1411               cause = xae;
 1412            } finally
 1413            {
 1414               lock();
 1415            }
 1416            resourceState[resIdx] = RS_FORGOT;
 1417         }
 1418      }
 1419   
 1420      /**
 1421       *  Check for heuristics, clear and throw exception if any found.
 1422       */
 1423      private void checkHeuristics()
 1424         throws HeuristicMixedException, HeuristicRollbackException
 1425      {
 1426         switch (heuristicCode)
 1427         {
 1428            case XAException.XA_HEURHAZ:
 1429            case XAException.XA_HEURMIX:
 1430               heuristicCode = HEUR_NONE;
 1431               if (trace)
 1432               {
 1433                  log.trace("Throwing HeuristicMixedException, " +
 1434                  "status=" + getStringStatus(status));
 1435               }
 1436               throw new HeuristicMixedException();
 1437            case XAException.XA_HEURRB:
 1438               heuristicCode = HEUR_NONE;
 1439               if (trace)
 1440               {
 1441                  log.trace("Throwing HeuristicRollbackException, " +
 1442                  "status=" + getStringStatus(status));
 1443               }
 1444               throw new HeuristicRollbackException();
 1445            case XAException.XA_HEURCOM:
 1446               heuristicCode = HEUR_NONE;
 1447               // Why isn't HeuristicCommitException used in JTA ?
 1448               // And why define something that is not used ?
 1449               // For now we just have to ignore this failure, even if it happened
 1450               // on rollback.
 1451               if (trace)
 1452               {
 1453                  log.trace("NOT Throwing HeuristicCommitException, " +
 1454                  "status=" + getStringStatus(status));
 1455               }
 1456               return;
 1457         }
 1458      }
 1459   
 1460   
 1461      /**
 1462       *  Prepare all enlisted resources.
 1463       *  If the first phase of the commit process results in a decision
 1464       *  to commit the <code>status</code> will be
 1465       *  <code>Status.STATUS_PREPARED</code> on return.
 1466       *  Otherwise the <code>status</code> will be
 1467       *  <code>Status.STATUS_MARKED_ROLLBACK</code> on return.
 1468       *  This will release the lock while calling out.
 1469       *
 1470       *  @returns True iff all resources voted read-only.
 1471       */
 1472      private boolean prepareResources()
 1473      {
 1474         boolean readOnly = true;
 1475   
 1476         status = Status.STATUS_PREPARING;
 1477   
 1478         for (int i = 0; i < resourceCount; i++)
 1479         {
 1480            // Abort prepare on state change.
 1481            if (status != Status.STATUS_PREPARING)
 1482               return false;
 1483   
 1484            if (resourceSameRM[i] != -1)
 1485               continue; // This RM already prepared.
 1486   
 1487            XAResource resource = resources[i];
 1488   
 1489            try
 1490            {
 1491               int vote;
 1492   
 1493               unlock();
 1494               try
 1495               {
 1496                  vote = resources[i].prepare(resourceXids[i]);
 1497               } finally
 1498               {
 1499                  lock();
 1500               }
 1501   
 1502               if (vote == XAResource.XA_OK)
 1503               {
 1504                  readOnly = false;
 1505                  resourceState[i] = RS_VOTE_OK;
 1506               } else if (vote == XAResource.XA_RDONLY)
 1507                  resourceState[i] = RS_VOTE_READONLY;
 1508               else
 1509               {
 1510                  // Illegal vote: rollback.
 1511                  if (trace)
 1512                  {
 1513                     log.trace("illegal vote in prepare resources", new Exception());
 1514                  } // end of if ()
 1515                  status = Status.STATUS_MARKED_ROLLBACK;
 1516                  return false;
 1517               }
 1518            } catch (XAException e)
 1519            {
 1520               readOnly = false;
 1521   
 1522               logXAException(e);
 1523   
 1524               switch (e.errorCode)
 1525               {
 1526                  case XAException.XA_HEURCOM:
 1527                     // Heuristic commit is not that bad when preparing.
 1528                     // But it means trouble if we have to rollback.
 1529                     gotHeuristic(i, e.errorCode);
 1530                     break;
 1531                  case XAException.XA_HEURRB:
 1532                  case XAException.XA_HEURMIX:
 1533                  case XAException.XA_HEURHAZ:
 1534                     gotHeuristic(i, e.errorCode);
 1535                     if (status == Status.STATUS_PREPARING)
 1536                        status = Status.STATUS_MARKED_ROLLBACK;
 1537                     break;
 1538                  default:
 1539                     cause = e;
 1540                     if (status == Status.STATUS_PREPARING)
 1541                        status = Status.STATUS_MARKED_ROLLBACK;
 1542                     break;
 1543               }
 1544            } catch (Throwable t)
 1545            {
 1546               if (trace)
 1547               {
 1548                  log.trace("unhandled throwable in prepareResources", t);
 1549               }
 1550               if (status == Status.STATUS_PREPARING)
 1551                  status = Status.STATUS_MARKED_ROLLBACK;
 1552               cause = t;
 1553            }
 1554         }
 1555   
 1556         if (status == Status.STATUS_PREPARING)
 1557            status = Status.STATUS_PREPARED;
 1558   
 1559         return readOnly;
 1560      }
 1561   
 1562      /**
 1563       *  Commit all enlisted resources.
 1564       *  This will release the lock while calling out.
 1565       */
 1566      private void commitResources(boolean onePhase)
 1567      {
 1568         status = Status.STATUS_COMMITTING;
 1569   
 1570         for (int i = 0; i < resourceCount; i++)
 1571         {
 1572            if (trace)
 1573            {
 1574               log.trace("Committing resources, resourceStates["+i+"]=" +
 1575                         resourceState[i]);
 1576            }
 1577   
 1578            if (!onePhase && resourceState[i] != RS_VOTE_OK)
 1579               continue; // Voted read-only at prepare phase.
 1580   
 1581            if (resourceSameRM[i] != -1)
 1582               continue; // This RM already committed.
 1583   
 1584            // Abort commit on state change.
 1585            if (status != Status.STATUS_COMMITTING)
 1586               return;
 1587   
 1588            try
 1589            {
 1590               unlock();
 1591               try
 1592               {
 1593                  resources[i].commit(resourceXids[i], onePhase);
 1594               } finally
 1595               {
 1596                  lock();
 1597               }
 1598            } catch (XAException e) {
 1599               logXAException(e);
 1600               switch (e.errorCode) {
 1601                  case XAException.XA_HEURRB:
 1602                  case XAException.XA_HEURCOM:
 1603                  case XAException.XA_HEURMIX:
 1604                  case XAException.XA_HEURHAZ:
 1605                     //usually throws an exception, but not for a couple of cases.
 1606                     gotHeuristic(i, e.errorCode);
 1607                     //May not be correct for HEURCOM
 1608                     //Two phase commit is committed after prepare is logged.
 1609                     if (onePhase)
 1610                     {
 1611                        status = Status.STATUS_MARKED_ROLLBACK;
 1612                     } // end of if ()
 1613   
 1614                     break;
 1615                  default:
 1616                     cause = e;
 1617                     if (onePhase)
 1618                     {
 1619                        status = Status.STATUS_MARKED_ROLLBACK;
 1620                        break;
 1621                     } // end of if ()
 1622                     //Not much we can do if there is an RMERR in the
 1623                     //commit phase of 2pc. I guess we try the other rms.
 1624               }
 1625            } catch (Throwable t)
 1626            {
 1627               if (trace)
 1628               {
 1629                  log.trace("unhandled throwable in commitResources", t);
 1630               }
 1631            }
 1632         }
 1633   
 1634         if (status == Status.STATUS_COMMITTING)
 1635            status = Status.STATUS_COMMITTED;
 1636      }
 1637   
 1638      /**
 1639       *  Rollback all enlisted resources.
 1640       *  This will release the lock while calling out.
 1641       */
 1642      private void rollbackResources()
 1643      {
 1644         status = Status.STATUS_ROLLING_BACK;
 1645   
 1646         for (int i = 0; i < resourceCount; i++)
 1647         {
 1648            if (resourceState[i] == RS_VOTE_READONLY)
 1649            {
 1650               continue;
 1651            }
 1652            // Already forgotten
 1653            if (resourceState[i] == RS_FORGOT)
 1654               continue;
 1655            if (resourceSameRM[i] != -1)
 1656            {
 1657               continue; // This RM already rolled back.
 1658            }
 1659            try
 1660            {
 1661               unlock();
 1662               try
 1663               {
 1664                  resources[i].rollback(resourceXids[i]);
 1665               } finally
 1666               {
 1667                  lock();
 1668               }
 1669            } catch (XAException e)
 1670            {
 1671               logXAException(e);
 1672               switch (e.errorCode)
 1673               {
 1674                  case XAException.XA_HEURRB:
 1675                     // Heuristic rollback is not that bad when rolling back.
 1676                     gotHeuristic(i, e.errorCode);
 1677                     continue;
 1678                  case XAException.XA_HEURCOM:
 1679                  case XAException.XA_HEURMIX:
 1680                  case XAException.XA_HEURHAZ:
 1681                     gotHeuristic(i, e.errorCode);
 1682                     continue;
 1683                  default:
 1684                     cause = e;
 1685                     break;
 1686               }
 1687            } catch (Throwable t) {
 1688               if (trace)
 1689                  log.trace("unhandled throwable in rollbackResources", t);
 1690            }
 1691         }
 1692   
 1693         status = Status.STATUS_ROLLEDBACK;
 1694      }
 1695   
 1696      /**
 1697       *  Create an Xid representing a new branch of this transaction.
 1698       */
 1699      private Xid createXidBranch()
 1700      {
 1701         long branchId = ++lastBranchId;
 1702   
 1703         return xidFactory.newBranch(xid, branchId);
 1704      }
 1705   
 1706      /**
 1707       *  Check if we can do one-phase optimization.
 1708       *  We can do that only if no more than a single resource manager
 1709       *  is involved in this transaction.
 1710       */
 1711      private boolean isOneResource()
 1712      {
 1713         if (resourceCount == 1)
 1714            return true;
 1715   
 1716         // first XAResource surely has -1, it's the first!
 1717         for (int i = 1; i < resourceCount; i++) {
 1718            if (resourceSameRM[i] == -1) {
 1719               // this one is not the same rm as previous ones,
 1720               // there must be at least 2
 1721               return false;
 1722            }
 1723   
 1724         }
 1725         // all rms are the same one, one phase commit is ok.
 1726         return true;
 1727      }
 1728   
 1729   
 1730      public long getTimeLeftBeforeTimeout()
 1731      {
 1732         return (start + timeoutPeriod) - System.currentTimeMillis();
 1733      }
 1734   
 1735      Object getTransactionLocalValue(TransactionLocal tlocal)
 1736      {
 1737         return transactionLocalMap.get(tlocal);
 1738      }
 1739   
 1740      void putTransactionLocalValue(TransactionLocal tlocal, Object value)
 1741      {
 1742         transactionLocalMap.put(tlocal, value);
 1743      }
 1744   
 1745      boolean containsTransactionLocal(TransactionLocal tlocal)
 1746      {
 1747         return transactionLocalMap.containsKey(tlocal);
 1748      }
 1749   
 1750      // Inner classes -------------------------------------------------
 1751   }

Save This Page
Home » jboss-5.0.0.CR1-src » org » jboss » tm » [javadoc | source]