User: osh     
  Date: 01/01/23 21:59:57

  Modified:    src/main/org/jboss/tm TransactionImpl.java
                        TransactionManagerService.java TxCapsule.java
                        TxManager.java XidImpl.java
  Log:
  General cleanup.
  Preparations for 1:1 TransactionImpl:TxCapsule relation.
  Catching unexpected throwables from Synchronization ans XAResource.
  
  Revision  Changes    Path
  1.12      +2 -2      jboss/src/main/org/jboss/tm/TransactionImpl.java
  
  Index: TransactionImpl.java
  ===================================================================
  RCS file: /products/cvs/ejboss/jboss/src/main/org/jboss/tm/TransactionImpl.java,v
  retrieving revision 1.11
  retrieving revision 1.12
  diff -u -r1.11 -r1.12
  --- TransactionImpl.java      2000/12/07 15:45:17     1.11
  +++ TransactionImpl.java      2001/01/24 05:59:56     1.12
  @@ -35,7 +35,7 @@
    *  @author Rickard �berg ([EMAIL PROTECTED])
    *  @author <a href="mailto:[EMAIL PROTECTED]">Marc Fleury</a>
    *  @author <a href="mailto:[EMAIL PROTECTED]">Ole Husgaard</a>
  - *  @version $Revision: 1.11 $
  + *  @version $Revision: 1.12 $
    */
   public class TransactionImpl
      implements Transaction, Serializable
  @@ -188,7 +188,7 @@
   
      /**
       *  Setter for property done.
  -    *  No argument for this mutator; we can only set to false.
  +    *  No argument for this mutator; we can only set to true.
       *  This will also clear the txCapsule reference.
       */
      synchronized void setDone()
  
  
  
  1.6       +3 -3      jboss/src/main/org/jboss/tm/TransactionManagerService.java
  
  Index: TransactionManagerService.java
  ===================================================================
  RCS file: 
/products/cvs/ejboss/jboss/src/main/org/jboss/tm/TransactionManagerService.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- TransactionManagerService.java    2000/12/07 15:45:17     1.5
  +++ TransactionManagerService.java    2001/01/24 05:59:56     1.6
  @@ -36,7 +36,7 @@
    *      
    *   @see TxManager
    *   @author Rickard �berg ([EMAIL PROTECTED])
  - *   @version $Revision: 1.5 $
  + *   @version $Revision: 1.6 $
    */
   public class TransactionManagerService
      extends ServiceMBeanSupport
  @@ -69,8 +69,8 @@
      protected void startService()
         throws Exception
      {
  -       // Create a new TM
  -       tm = new TxManager();
  +       // Get a reference to the TxManager singleton.
  +       tm = TxManager.getInstance();
          
          // Set timeout
          tm.setTransactionTimeout(timeout);
  
  
  
  1.22      +332 -175  jboss/src/main/org/jboss/tm/TxCapsule.java
  
  Index: TxCapsule.java
  ===================================================================
  RCS file: /products/cvs/ejboss/jboss/src/main/org/jboss/tm/TxCapsule.java,v
  retrieving revision 1.21
  retrieving revision 1.22
  diff -u -r1.21 -r1.22
  --- TxCapsule.java    2000/12/07 15:45:17     1.21
  +++ TxCapsule.java    2001/01/24 05:59:56     1.22
  @@ -45,7 +45,7 @@
    *  @author <a href="mailto:[EMAIL PROTECTED]">Marc Fleury</a>
    *  @author <a href="mailto:[EMAIL PROTECTED]">Ole Husgaard</a>
    *
  - *  @version $Revision: 1.21 $
  + *  @version $Revision: 1.22 $
    */
   class TxCapsule implements TimeoutTarget
   {
  @@ -53,17 +53,39 @@
   
      // Trace enabled flag
      static private final boolean trace = false;
  -   // Constructor for Xid instances
  -   private static Constructor xidConstructor = null;
   
      // Code meaning "no heuristics seen", must not be XAException.XA_HEURxxx
  -   static private final int HEUR_NONE     = XAException.XA_RETRY;
  +   static private final int HEUR_NONE = XAException.XA_RETRY;
   
      // Attributes ----------------------------------------------------
  -   private HashMap branchXids;
   
      // Static --------------------------------------------------------
   
  +   /**
  +    *  Constructor for Xid instances of specified class, or null.
  +    */
  +   private static Constructor xidConstructor = null;
  +
  +   /**
  +    *  Initialize the Xid constructor.
  +    */
  +   static {
  +      String name = System.getProperty("jboss.xa.xidclass",
  +                                       "org.jboss.tm.XidImpl");
  +
  +      if (!name.equals("org.jboss.tm.XidImpl")) {
  +         try {
  +            Class cls = Class.forName(name);
  +
  +            xidConstructor = cls.getConstructor(new Class[]{ Integer.TYPE,
  +                                                             byte[].class,
  +                                                             byte[].class });
  +         } catch (Exception e) {
  +            System.out.println("Unable to load Xid class '"+name+"': " + e);
  +         }
  +      }
  +   }
  +
      // Constructors --------------------------------------------------
   
      /**
  @@ -75,24 +97,21 @@
       */
      TxCapsule(TxManager tm, long timeout)
      {
  -      this(tm);
  +      xid = new XidImpl();
  +
  +      if (xidConstructor != null) {
  +         // Allocate constructor argument array.
  +         xidConstructorArgs = new Object[3];
  +         // First constructor argument is always the same: Pre-init it.
  +         xidConstructorArgs[0] = new Integer(xid.getFormatId());
  +      }
   
  +      this.tm = tm;
  +
         status = Status.STATUS_ACTIVE;
   
         start = System.currentTimeMillis();
         this.timeout = TimeoutFactory.createTimeout(start+timeout, this);
  -      branchXids = new HashMap();
  -   }
  -
  -   /**
  -    *  Create a new TxCapsule.
  -    *
  -    *  @param tm The transaction manager for this transaction.
  -    */
  -   private TxCapsule(TxManager tm)
  -   {
  -      xid = createXid();
  -      this.tm = tm;
   
         if (trace)
            Logger.debug("TxCapsule: Created new instance for tx=" + toString());
  @@ -120,7 +139,8 @@
         done = false;
         resourcesEnded = false;
   
  -      xid = createXid();
  +      xid = new XidImpl();
  +      lastBranchId = 0; // BQIDs start over again in scope of the new GID.
   
         status = Status.STATUS_ACTIVE;
         heuristicCode = HEUR_NONE;
  @@ -163,7 +183,7 @@
               // We are in the second commit phase, and have decided
               // to commit, but now we get a timeout and should rollback.
               // So we end up with a mixed decision.
  -            gotHeuristic(null, XAException.XA_HEURMIX);
  +            gotHeuristic(-1, XAException.XA_HEURMIX);
               status = Status.STATUS_MARKED_ROLLBACK;
               return; // commit will fail
   
  @@ -200,11 +220,6 @@
   
      // Package protected ---------------------------------------------
   
  -   Xid getXid()
  -   {
  -       return xid;
  -   }
  -
      /**
       *  Import a transaction encapsulated here.
       */
  @@ -486,14 +501,7 @@
            }
   
            try {
  -            endResource(xaRes, flag);
  -            if (flag == XAResource.TMSUSPEND)
  -               resourceState[idx] = RS_SUSPENDED;
  -            else {
  -               if (flag == XAResource.TMFAIL)
  -                  status = Status.STATUS_MARKED_ROLLBACK;
  -               resourceState[idx] = RS_ENDED;
  -            }
  +            endResource(idx, flag);
               return true;
            } catch(XAException e) {
               Logger.warning("XAException: tx=" + toString() + " errorCode=" +
  @@ -561,37 +569,30 @@
               int idx = findResource(xaRes);
   
               if (idx != -1) {
  -               if (resourceState[idx] == RS_SUSPENDED) {
  -                  startResource(xaRes, XAResource.TMRESUME, 
(Xid)branchXids.get(xaRes));
  -                  resourceState[idx] = RS_ENLISTED;
  -                  return true;
  -               } else if (resourceState[idx] == RS_ENDED) {
  -                  startResource(xaRes, XAResource.TMJOIN, 
(Xid)branchXids.get(xaRes));
  -                  resourceState[idx] = RS_ENLISTED;
  -                  return true;
  -               } else
  +               if (resourceState[idx] == RS_ENLISTED)
                     return false; // already enlisted
  +
  +               startResource(idx);
  +               return true;
               }
   /* This optimization hangs the Oracle XAResource
      when you perform xaCon.start(Xid, TMJOIN)
  +*/
  +if (xidConstructor == null) {
               for (int i = 0; i < resourceCount; ++i) {
  -               if (xaRes.isSameRM(resources[i])) {
  -                  Xid parentXid = (Xid)branchXids.get(resources[i]);
  -                  startResource(xaRes, XAResource.TMJOIN, parentXid);
  -                  addResource(xaRes);  // Do we still need to do this?
  +               if (resourceSameRM[i] == -1 && xaRes.isSameRM(resources[i])) {
  +                  // The xaRes is new. We register the xaRes with the Xid
  +                  // that the RM has previously seen from this transaction,
  +                  // and note that it has the same RM.
  +                  startResource(addResource(xaRes, resourceXids[i], i));
  +
                     return true;
                  }
  -            }
  -*/
  -            // New resource
  -            // According to the JTA spec we should create a new
  -            // transaction branch here.
  -            Xid useXid = xid;
  -            if(resourceCount > 0) {
  -                useXid = createXid((Xid)branchXids.get(resources[resourceCount-1]));
               }
  -            startResource(xaRes, XAResource.TMNOFLAGS, useXid);
  -            addResource(xaRes);
  +}
  +
  +            // New resource and new RM: Create a new transaction branch.
  +            startResource(addResource(xaRes, createXidBranch(), -1));
               return true;
            } catch(XAException e) {
               Logger.warning("XAException: tx=" + toString() + " errorCode=" +
  @@ -644,10 +645,17 @@
            case Status.STATUS_COMMITTED:
               throw new IllegalStateException("Already committed.");
            case Status.STATUS_MARKED_ROLLBACK:
  -            //throw new RollbackException("Already marked for rollback");
  +// OSH: EntitySynchronizationInterceptor bug is fixed long ago,
  +// and since nobody seems to get the warning anymore it should
  +// be safe to be JTA-conformant.
  +// In case of trouble, try changing "true" below to "false".
  +if (true)
  +            throw new RollbackException("Already marked for rollback");
  +else {
               // Workaround for EntitySynchronizationInterceptor bug.
               Logger.warning("TxCapsule: Violating JTA by adding synchronization to a 
transaction marked for rollback.");
               break;
  +}
            case Status.STATUS_ROLLING_BACK:
               throw new RollbackException("Already started rolling back.");
            case Status.STATUS_ROLLEDBACK:
  @@ -706,7 +714,7 @@
      private int syncAllocSize = 1;
   
      /**
  -    *  Count of ynchronizations for this transaction.
  +    *  Count of synchronizations for this transaction.
       */
      private int syncCount = 0;
   
  @@ -721,6 +729,7 @@
       */
      private int[] resourceState = new int[1];
   
  +   private final static int RS_NEW           = 0; // not yet enlisted
      private final static int RS_ENLISTED      = 1; // enlisted
      private final static int RS_SUSPENDED     = 2; // suspended
      private final static int RS_ENDED         = 3; // not associated
  @@ -728,6 +737,18 @@
      private final static int RS_VOTE_OK       = 5; // voted ok
   
      /**
  +    *  Index of the first XAResource representing the same resource manager,
  +    *  or <code>-1</code> if this XAResource is the first XAResource in this
  +    *  transaction that represents its resource manager.
  +    */
  +   private int[] resourceSameRM = new int[1];
  +
  +   /**
  +    *  A list of the XARessources that have participated in this transaction.
  +    */
  +   private Xid[] resourceXids = new Xid[1];
  +
  +   /**
       *  Size of allocated resource arrays.
       */
      private int resourceAllocSize = 1;
  @@ -743,10 +764,23 @@
       */
      private boolean resourcesEnded = false;
   
  +   /**
  +    *  The ID of this transaction. This Xid corresponds to the root branch
  +    *  of the transaction branch tree, and is never passed outside this
  +    *  package.
  +    */
  +   private XidImpl xid;
  +
  +   /**
  +    *  Xid constructor arguments. Never used unless <code>xidConstructor</code>
  +    *  is <code>null</code>.
  +    */
  +   private Object[] xidConstructorArgs = null;
  +
      /**
  -    *  The ID of this transaction.
  +    *  Last branch id used.
       */
  -   private Xid xid; // Transaction id
  +   private int lastBranchId = 0;
   
      /**
       *  Status of this transaction.
  @@ -900,15 +934,12 @@
   
            while (locked) {
               try {
  +               // Wakeup happens when:
  +               // - notify() is called from unlock()
  +               // - notifyAll is called from instanceDone()
                  wait();
               } catch (InterruptedException ex) {}
   
  -            // MF FIXME: don't we need a notify() in this case?
  -            // we need to release all the thread waiting on this lock
  -
  -            // OSH: notifyAll() is done in instanceDone()
  -            // and notify() is done in unlock().
  -
               if (done || myIncarnation != incarnationCount)
                 throw new IllegalStateException("Transaction has now terminated");
            }
  @@ -957,18 +988,31 @@
       *  Return index of XAResource, or <code>-1</code> if not found.
       */
       private int findResource(XAResource xaRes) {
  -        int pos = -1;
  -        for (int i = 0; i < resourceCount; ++i)
  -            if(xaRes == resources[i])
  -                pos = i;
  +       // A linear search may seem slow, but please note that
  +       // the number of XA resources registered with a transaction
  +       // are usually low.
  +       for (int idx = 0; idx < resourceCount; ++idx)
  +          if (xaRes == resources[idx])
  +             return idx;
   
  -        return pos;
  +       return -1;
       }
   
      /**
       *  Add a resource, expanding tables if needed.
  +    *
  +    *  @param xaRes The new XA resource to add. It is assumed that the
  +    *         resource is not already in the table of XA resources.
  +    *  @param branchXid The Xid for the transaction branch that is to
  +    *         be used for associating with this resource.
  +    *  @param idxSameRM The index in our XA resource tables of the first
  +    *         XA resource having the same resource manager as
  +    *         <code>xaRes</code>, or <code>-1</code> if <code>xaRes</code>
  +    *         is the first resource seen with this resource manager.
  +    *
  +    *  @return The index of the new resource in our internal tables.
       */
  -   private void addResource(XAResource xaRes)
  +   private int addResource(XAResource xaRes, Xid branchXid, int idxSameRM)
      {
         if (resourceCount == resourceAllocSize) {
            // expand tables
  @@ -981,10 +1025,21 @@
            int[] stat = new int[resourceAllocSize];
            System.arraycopy(resourceState, 0, stat, 0, resourceCount);
            resourceState = stat;
  +
  +         Xid[] xids = new Xid[resourceAllocSize];
  +         System.arraycopy(resourceXids, 0, xid, 0, resourceCount);
  +         resourceXids = xids;
  +
  +         int[] sameRM = new int[resourceAllocSize];
  +         System.arraycopy(resourceSameRM, 0, sameRM, 0, resourceCount);
  +         resourceSameRM = sameRM;
         }
         resources[resourceCount] = xaRes;
  -      resourceState[resourceCount] = RS_ENLISTED;
  -      ++resourceCount;
  +      resourceState[resourceCount] = RS_NEW;
  +      resourceXids[resourceCount] = branchXid;
  +      resourceSameRM[resourceCount] = idxSameRM;
  +
  +      return resourceCount++;
      }
   
      /**
  @@ -1004,51 +1059,108 @@
      }
   
      /**
  -    *  Call <code>start()</code> on the XAResource.
  +    *  Call <code>start()</code> on a XAResource and update
  +    *  internal state information.
       *  This will release the lock while calling out.
  +    *
  +    *  @param idx The index of the resource in our internal tables.
       */
  -   private void startResource(XAResource xaRes, int flags, Xid currentBranchXid)
  +   private void startResource(int idx)
         throws XAException
      {
  -        if(trace)
  -            Logger.debug("TxCapsule.startResource(" + 
XidImpl.toString(currentBranchXid) +
  -                         ") entered: " + xaRes.toString() +
  -                         " flags=" + flags);
  +      int flags = XAResource.TMJOIN;
  +
  +      if (resourceSameRM[idx] == -1) {
  +         switch (resourceState[idx]) {
  +            case RS_NEW:
  +               flags = XAResource.TMNOFLAGS;
  +               break;
  +            case RS_SUSPENDED:
  +               flags = XAResource.TMRESUME;
  +               break;
  +         }
  +      }
  +
  +      if (trace)
  +         Logger.debug("TxCapsule.startResource(" +
  +                      XidImpl.toString(resourceXids[idx]) +
  +                      ") entered: " + resources[idx].toString() +
  +                      " flags=" + flags);
  +
         unlock();
         // OSH FIXME: resourceState could be incorrect during this callout.
         try {
  -         xaRes.start(currentBranchXid, flags);
  -         branchXids.put(xaRes, currentBranchXid);
  +         try {
  +            resources[idx].start(resourceXids[idx], flags);
  +         } catch(XAException e) {
  +            throw e;
  +         } catch (Throwable t) {
  +            if (trace)
  +               Logger.exception(t);
  +            status = Status.STATUS_MARKED_ROLLBACK;
  +            return;
  +         }
  +
  +         // Now the XA resource is associated with a transaction.
  +         resourceState[idx] = RS_ENLISTED;
         } finally {
            lock();
  -         if(trace)
  -            Logger.debug("TxCapsule.startResource(" + 
XidImpl.toString(currentBranchXid) +
  -                         ") leaving: " + xaRes.toString() +
  +         if (trace)
  +            Logger.debug("TxCapsule.startResource(" +
  +                         XidImpl.toString(resourceXids[idx]) +
  +                         ") leaving: " + resources[idx].toString() +
                            " flags=" + flags);
         }
      }
   
      /**
  -    *  Call <code>end()</code> on the XAResource.
  +    *  Call <code>end()</code> on the XAResource and update
  +    *  internal state information.
       *  This will release the lock while calling out.
  +    *
  +    *  @param idx The index of the resource in our internal tables.
  +    *  @param flag The flag argument for the end() call.
       */
  -   private void endResource(XAResource xaRes, int flag)
  +   private void endResource(int idx, int flag)
         throws XAException
      {
  -      Xid useXid = (Xid)branchXids.get(xaRes);
  -      if(trace)
  -          Logger.debug("TxCapsule.endResource(" + XidImpl.toString(useXid) +
  -                       ") entered: " + xaRes.toString() +
  +      if (trace)
  +          Logger.debug("TxCapsule.endResource(" +
  +                       XidImpl.toString(resourceXids[idx]) +
  +                       ") entered: " + resources[idx].toString() +
                          " flag=" + flag);
         unlock();
         // OSH FIXME: resourceState could be incorrect during this callout.
         try {
  -         xaRes.end(useXid, flag);
  +         try {
  +            resources[idx].end(resourceXids[idx], flag);
  +         } catch(XAException e) {
  +            throw e;
  +         } catch (Throwable t) {
  +            if (trace)
  +               Logger.exception(t);
  +            status = Status.STATUS_MARKED_ROLLBACK;
  +            // Resource may or may not be ended after illegal exception.
  +            // We just assume it ended.
  +            resourceState[idx] = RS_ENDED;
  +            return;
  +         }
  +
  +
  +         // Update our internal state information
  +         if (flag == XAResource.TMSUSPEND)
  +            resourceState[idx] = RS_SUSPENDED;
  +         else {
  +            if (flag == XAResource.TMFAIL)
  +               status = Status.STATUS_MARKED_ROLLBACK;
  +            resourceState[idx] = RS_ENDED;
  +         }
         } finally {
            lock();
  -         if(trace)
  -             Logger.debug("TxCapsule.endResource(" + XidImpl.toString(useXid) +
  -                          ") leaving: " + xaRes.toString() +
  +         if (trace)
  +             Logger.debug("TxCapsule.endResource(" +
  +                          XidImpl.toString(resourceXids[idx]) +
  +                          ") leaving: " + resources[idx].toString() +
                             " flag=" + flag);
         }
      }
  @@ -1058,9 +1170,9 @@
       */
      private void endResources()
      {
  -      for (int i = 0; i < resourceCount; i++) {
  +      for (int idx = 0; idx < resourceCount; idx++) {
            try {
  -            if (resourceState[i] == RS_SUSPENDED) {
  +            if (resourceState[idx] == RS_SUSPENDED) {
                  // This is mad, but JTA 1.0.1 spec says on page 41:
                  // "If TMSUSPEND is specified in flags, the transaction
                  // branch is temporarily suspended in incomplete state.
  @@ -1073,13 +1185,13 @@
                  // a suspended state.
                  // But the Minerva XA pool does not like that we call end()
                  // two times in a row, so we resume before ending.
  -               startResource(resources[i], XAResource.TMRESUME, 
(Xid)branchXids.get(resources[i]));
  -               resourceState[i] = RS_ENLISTED;
  +               startResource(idx);
               }
  -            if (resourceState[i] == RS_ENLISTED) {
  -              Logger.debug("endresources("+i+"): state="+resourceState[i]);
  -              endResource(resources[i], XAResource.TMSUCCESS);
  -              resourceState[i] = RS_ENDED;
  +            if (resourceState[idx] == RS_ENLISTED) {
  +              if (trace)
  +                  Logger.debug("endresources(" + idx + "): state=" +
  +                               resourceState[idx]);
  +               endResource(idx, XAResource.TMSUCCESS);
               }
            } catch(XAException e) {
               Logger.warning("XAException: tx=" + toString() + " errorCode=" +
  @@ -1100,8 +1212,16 @@
      {
         unlock();
         try {
  -         for (int i = 0; i < syncCount; i++)
  -            sync[i].beforeCompletion();
  +         for (int i = 0; i < syncCount; i++) {
  +            try {
  +               sync[i].beforeCompletion();
  +            } catch (Throwable t) {
  +               if (trace)
  +                  Logger.exception(t);
  +               status = Status.STATUS_MARKED_ROLLBACK;
  +               break;
  +            }
  +         }
         } finally {
            lock();
         }
  @@ -1116,8 +1236,14 @@
         // Assert: Status indicates: Too late to add new synchronizations.
         unlock();
         try {
  -         for (int i = 0; i < syncCount; i++)
  -            sync[i].afterCompletion(status);
  +         for (int i = 0; i < syncCount; i++) {
  +            try {
  +               sync[i].afterCompletion(status);
  +            } catch (Throwable t) {
  +               if (trace)
  +                  Logger.exception(t);
  +            }
  +         }
         } finally {
            lock();
         }
  @@ -1125,11 +1251,18 @@
   
      /**
       *  We got another heuristic.
  +    *
       *  Promote <code>heuristicCode</code> if needed and tell
       *  the resource to forget the heuristic.
       *  This will release the lock while calling out.
  +    *
  +    *  @param resIdx The index of the XA resource that got a
  +    *         heurictic in our internal tables, or <code>-1</code>
  +    *         if the heuristic came from here.
  +    *  @param code The heuristic code, one of
  +    *         <code>XAException.XA_HEURxxx</code>.
       */
  -   private void gotHeuristic(XAResource resource, int code)
  +   private void gotHeuristic(int resIdx, int code)
      {
         switch (code) {
         case XAException.XA_HEURMIX:
  @@ -1160,10 +1293,10 @@
            throw new IllegalArgumentException();
         }
   
  -      if (resource != null) {
  +      if (resIdx != -1) {
            try {
               unlock();
  -            resource.forget((Xid)branchXids.get(resource));
  +            resources[resIdx].forget(resourceXids[resIdx]);
            } catch (XAException e) {
               Logger.warning("XAException at forget(): errorCode=" +
                              getStringXAErrorCode(e.errorCode));
  @@ -1230,12 +1363,26 @@
         }
   
         // Clear content of collections.
  +      for (int i = 0; i < syncCount; ++i)
  +         sync[i] = null; // release for GC
         syncCount = 0;
  +
  +      for (int i = 0; i < transactionCount; ++i)
  +         transactions[i] = null; // release for GC
         transactionCount = 0;
  +
  +      for (int i = 0; i < resourceCount; ++i) {
  +         resources[i] = null; // release for GC
  +         resourceXids[i] = null; // release for GC
  +      }
         resourceCount = 0;
   
  +      // If using a special class, second constructor argument is now useless.
  +      if (xidConstructor != null)
  +        xidConstructorArgs[1] = null; // This now needs initializing
  +
         // This instance is now ready for reuse (when we release the lock).
  -      tm.releaseTxCapsule(this);
  +      tm.releaseTxCapsule(this, xid);
      }
   
      /**
  @@ -1260,6 +1407,9 @@
            if (status != Status.STATUS_PREPARING)
               return false;
   
  +         if (resourceSameRM[i] != -1)
  +           continue; // This RM already prepared.
  +
            XAResource resource = resources[i];
   
            try {
  @@ -1267,7 +1417,7 @@
   
               unlock();
               try {
  -               vote = resources[i].prepare((Xid)branchXids.get(resources[i]));
  +               vote = resources[i].prepare(resourceXids[i]);
               } finally {
                  lock();
               }
  @@ -1289,12 +1439,12 @@
               case XAException.XA_HEURCOM:
                  // Heuristic commit is not that bad when preparing.
                  // But it means trouble if we have to rollback.
  -               gotHeuristic(resources[i], e.errorCode);
  +               gotHeuristic(i, e.errorCode);
                  break;
               case XAException.XA_HEURRB:
               case XAException.XA_HEURMIX:
               case XAException.XA_HEURHAZ:
  -               gotHeuristic(resources[i], e.errorCode);
  +               gotHeuristic(i, e.errorCode);
                  if (status == Status.STATUS_PREPARING)
                     status = Status.STATUS_MARKED_ROLLBACK;
                  break;
  @@ -1306,6 +1456,11 @@
                     status = Status.STATUS_MARKED_ROLLBACK;
                  break;
               }
  +         } catch (Throwable t) {
  +            if (trace)
  +               Logger.exception(t);
  +            if (status == Status.STATUS_PREPARING)
  +               status = Status.STATUS_MARKED_ROLLBACK;
            }
         }
   
  @@ -1324,11 +1479,15 @@
         status = Status.STATUS_COMMITTING;
   
         for (int i = 0; i < resourceCount; i++) {
  -         if(trace) {
  -            Logger.debug("TxCapsule.commitResources(): 
resourceStates["+i+"]="+resourceState[i]);
  -         }
  +         if (trace)
  +            Logger.debug("TxCapsule.commitResources(): " +
  +                         "resourceStates["+i+"]="+resourceState[i]);
  +
            if (!onePhase && resourceState[i] != RS_VOTE_OK)
  -           continue;
  +           continue; // Voted read-only at prepare phase.
  +
  +         if (resourceSameRM[i] != -1)
  +           continue; // This RM already committed.
   
            // Abort commit on state change.
            if (status != Status.STATUS_COMMITTING)
  @@ -1337,7 +1496,7 @@
            try {
               unlock();
               try {
  -               resources[i].commit((Xid)branchXids.get(resources[i]), onePhase);
  +               resources[i].commit(resourceXids[i], onePhase);
               } finally {
                  lock();
               }
  @@ -1347,7 +1506,7 @@
               case XAException.XA_HEURCOM:
               case XAException.XA_HEURMIX:
               case XAException.XA_HEURHAZ:
  -               gotHeuristic(resources[i], e.errorCode);
  +               gotHeuristic(i, e.errorCode);
                  break;
               default:
                  Logger.warning("XAException: tx=" + toString() + " errorCode=" +
  @@ -1356,10 +1515,15 @@
                  break;
               }
               try {
  -               resources[i].forget((Xid)branchXids.get(resources[i]));
  -            } catch(XAException forgetEx) {}
  -         } finally {
  -            branchXids.remove(resources[i]);
  +               // OSH: Why this?
  +               // Heuristics only exist if we go one of the heuristic
  +               // error codes, and for these forget() is called from
  +               // gotHeuristic(). 
  +               resources[i].forget(resourceXids[i]);
  +            } catch (XAException forgetEx) {}
  +         } catch (Throwable t) {
  +            if (trace)
  +               Logger.exception(t);
            }
         }
   
  @@ -1382,7 +1546,7 @@
            try {
               unlock();
               try {
  -               resources[i].rollback((Xid)branchXids.get(resources[i]));
  +               resources[i].rollback(resourceXids[i]);
               } finally {
                  lock();
               }
  @@ -1390,12 +1554,12 @@
               switch (e.errorCode) {
               case XAException.XA_HEURRB:
                  // Heuristic rollback is not that bad when rolling back.
  -               gotHeuristic(resources[i], e.errorCode);
  +               gotHeuristic(i, e.errorCode);
                  break;
               case XAException.XA_HEURCOM:
               case XAException.XA_HEURMIX:
               case XAException.XA_HEURHAZ:
  -               gotHeuristic(resources[i], e.errorCode);
  +               gotHeuristic(i, e.errorCode);
                  break;
               default:
                  Logger.warning("XAException: tx=" + toString() + " errorCode=" +
  @@ -1404,61 +1568,54 @@
                  break;
               }
               try {
  -                resources[i].forget((Xid)branchXids.get(resources[i]));
  -            } catch(XAException forgetEx) {}
  -         } finally {
  -            branchXids.remove(resources[i]);
  +               // OSH: Why this?
  +               // Heuristics only exist if we go one of the heuristic
  +               // error codes, and for these forget() is called from
  +               // gotHeuristic(). 
  +               resources[i].forget(resourceXids[i]);
  +            } catch (XAException forgetEx) {}
  +         } catch (Throwable t) {
  +            if (trace)
  +               Logger.exception(t);
            }
         }
   
         status = Status.STATUS_ROLLEDBACK;
      }
   
  -    private Xid createXid() {
  -       if (xidConstructor == null) {
  -          String name = System.getProperty("jboss.xa.xidclass", 
"org.jboss.tm.XidImpl");
  -
  -          try {
  -             Class cls = Class.forName(name);
  -             xidConstructor = cls.getConstructor(new Class[]{Integer.TYPE, 
byte[].class, byte[].class});
  -          } catch (Exception e) {
  -             System.out.println("Unable to load Xid class '"+name+"'");
  -          }
  -       }
  -
  -       try {
  -          Object xid = xidConstructor.newInstance(new Object[]{new 
Integer(XidImpl.getJbossFormatId()),
  -                                                               
XidImpl.getGlobalIdString(XidImpl.getNextId()),
  -                                                               
XidImpl.noBranchQualifier});
  -          return (Xid)xid;
  -       } catch (Exception e) {
  -          System.out.println("Unable to create an Xid (reverting to default impl): 
"+e);
  -          return new XidImpl(XidImpl.getJbossFormatId(), 
XidImpl.getGlobalIdString(XidImpl.getNextId()), XidImpl.noBranchQualifier);
  -       }
  -    }
  +   /**
  +    *  Create an Xid representing a new branch of this transaction.
  +    */
  +   private Xid createXidBranch() {
  +      int branchId = ++lastBranchId;
   
  -    private Xid createXid(Xid lastBranch) {
  -       if (xidConstructor == null) {
  -          String name = System.getProperty("jboss.xa.xidclass", 
"org.jboss.tm.XidImpl");
  -
  -          try {
  -             Class cls = Class.forName(name);
  -             xidConstructor = cls.getConstructor(new Class[]{Integer.TYPE, 
byte[].class, byte[].class});
  -          } catch (Exception e) {
  -             System.out.println("Unable to load Xid class '"+name+"'");
  -          }
  -       }
  -
  -       try {
  -          Object xid = xidConstructor.newInstance(new Object[]{new 
Integer(XidImpl.getJbossFormatId()),
  -                                                               
XidImpl.getGlobalIdString(XidImpl.getNextId()),
  -                                                               
XidImpl.getNextBranchQualifier(lastBranch.getBranchQualifier())});
  -          return (Xid)xid;
  -       } catch (Exception e) {
  -          System.out.println("Unable to create an Xid (reverting to default impl): 
"+e);
  -          return new XidImpl(XidImpl.getJbossFormatId(), 
XidImpl.getGlobalIdString(XidImpl.getNextId()), XidImpl.noBranchQualifier);
  -       }
  -    }
  +      if (xidConstructor == null)
  +         return new XidImpl(xid, branchId);
  +      else {
  +         try {
  +            if (xidConstructorArgs[1] == null) {
  +              // First branching of this global transaction id.
  +              // Oracle XA driver needs the full length GID.
  +              byte[] gidShort = xid.getGlobalTransactionId();
  +              byte[] gid = new byte[Xid.MAXGTRIDSIZE];
  +              System.arraycopy(gidShort, 0, gid, 0, gidShort.length);
  +              xidConstructorArgs[1] = gid;
  +            }
  +
  +            // Oracle XA driver needs the full length BQID.
  +            byte[] bqidShort = Integer.toString(branchId).getBytes();
  +            byte[] bqid = new byte[Xid.MAXBQUALSIZE];
  +            System.arraycopy(bqidShort, 0, bqid, 0, bqidShort.length);
  +            xidConstructorArgs[2] = bqid;
  +
  +            return (Xid)xidConstructor.newInstance(xidConstructorArgs);
  +         } catch (Exception e) {
  +            System.out.println("Unable to create an Xid " +
  +                               "(reverting to default impl): " + e);
  +            return new XidImpl(xid, branchId);
  +         }
  +      }
  +   }
   
      // Inner classes -------------------------------------------------
   }
  
  
  
  1.26      +94 -28    jboss/src/main/org/jboss/tm/TxManager.java
  
  Index: TxManager.java
  ===================================================================
  RCS file: /products/cvs/ejboss/jboss/src/main/org/jboss/tm/TxManager.java,v
  retrieving revision 1.25
  retrieving revision 1.26
  diff -u -r1.25 -r1.26
  --- TxManager.java    2000/12/07 15:45:17     1.25
  +++ TxManager.java    2001/01/24 05:59:56     1.26
  @@ -38,7 +38,7 @@
    *  @author Rickard �berg ([EMAIL PROTECTED])
    *  @author <a href="mailto:[EMAIL PROTECTED]">Marc Fleury</a>
    *  @author <a href="mailto:[EMAIL PROTECTED]">Ole Husgaard</a>
  - *  @version $Revision: 1.25 $
  + *  @version $Revision: 1.26 $
    */
   public class TxManager
   implements TransactionManager
  @@ -54,9 +54,30 @@
      long timeOut = 5*60*1000; 
       
      // Static --------------------------------------------------------
  -    
  + 
  +   /**
  +    *  The singleton instance.
  +    */
  +   private static TxManager singleton = new TxManager();
  +
  +   /**
  +    *  Get a reference to the singleton instance.
  +    */
  +   static TxManager getInstance()
  +   {
  +      return singleton;
  +   }
  +
      // Constructors --------------------------------------------------
  -    
  + 
  +   /**
  +    *  Private constructor for singleton. Use getInstance() to obtain
  +    *  a reference to the singleton.
  +    */
  +   private TxManager()
  +   {
  +   }
  +
      // Public --------------------------------------------------------
   
      public void begin()
  @@ -225,10 +246,54 @@
       
      public void associateThread(Transaction transaction)
      {
  +      //
         // If the transaction has travelled, we have to import it.
  +      //
  +      // This implicit import will go away at some point in the
  +      // future and be replaced by an explicit import by calling
  +      // importTPC().
  +      // That will make it possible to only propagate XidImpl over the
  +      // wire so that we no longer have to handle multible Transaction
  +      // frontends for each transaction.
  +      //
         if (transaction != null && transaction instanceof TransactionImpl) {
            TransactionImpl tx = (TransactionImpl)transaction;
   
  +         if (tx.importNeeded())
  +            transaction = importTPC(transaction);
  +      }
  +
  +      // Associate with the thread
  +      threadTx.set(transaction);
  +   }
  +
  +   /**
  +    *  Import a transaction propagation context into this TM.
  +    *  The TPC is loosely typed, as we may (at a later time) want to
  +    *  import TPCs that come from other transaction monitors without
  +    *  offloading the conversion to the client.
  +    *
  +    *  @param tpc The transaction propagation context that we want to
  +    *             import into this TM. Currently this is an instance
  +    *             of TransactionImpl. Later this will be changed to an
  +    *             instance of XidImpl. And at some later time this may
  +    *             even be an instance of a transaction propagation context
  +    *             from another kind of transaction monitor like 
  +    *             org.omg.CosTransactions.PropagationContext. 
  +    *
  +    *  @return A transaction representing this transaction propagation
  +    *          context, or null if this TPC cannot be imported.
  +    */
  +   public Transaction importTPC(Object tpc)
  +   {
  +      if (tpc instanceof TransactionImpl) {
  +         // TODO: Change to just return the transaction without importing.
  +         // A raw TransactionImpl will only be used for optimized local calls,
  +         // where the import will not be needed.
  +         // But that will have to wait until remote calls use XidImpl instead,
  +         // otherwise we cannot distinguish cases.
  +         TransactionImpl tx = (TransactionImpl)tpc;
  +
            if (tx.importNeeded()) {
               synchronized(tx) {
                  // Recheck with synchronization.
  @@ -242,38 +307,23 @@
                  }
               }
            }
  -      }
  -
  -      // Associate with the thread
  -      threadTx.set(transaction);
  +         return tx;
  +      } else if (tpc instanceof XidImpl)
  +         Logger.warning("XidImpl import not yet implemented.");
  +      else
  +         Logger.warning("Cannot import transaction propagation context: " +
  +                        tpc.toString());
  +      return null;
      }
  -    
  -    
  +
      // Package protected ---------------------------------------------
       
  -   // There has got to be something better :)
  -   static TxManager getTransactionManager()
  -   {
  -      try {
  -         javax.naming.InitialContext context = new javax.naming.InitialContext();
  -            
  -         //One tx in naming
  -         Logger.log("Calling get manager from JNDI");
  -         TxManager manager = (TxManager) context.lookup("java:/TransactionManager");
  -         Logger.log("Returning TM " + manager.hashCode());
  -            
  -         return manager;
  -      } catch (Exception e ) {
  -         return null;
  -      }
  -   }
  -    
      /**
       *  Release the given txCapsule for reuse.
       */
  -   void releaseTxCapsule(TxCapsule txCapsule)
  +   void releaseTxCapsule(TxCapsule txCapsule, XidImpl xid)
      {
  -      activeCapsules.remove(txCapsule.getXid());
  +      activeCapsules.remove(xid);
   
         SoftReference ref = new SoftReference(txCapsule);
         synchronized (inactiveCapsules) {
  @@ -281,6 +331,22 @@
         }
      }
   
  +   /**
  +    *  Return an Xid that identifies the transaction associated
  +    *  with the invoking thread, or <code>null</code> if the invoking
  +    *  thread is not associated with a transaction.
  +    *  This is used for JRMP transaction context propagation.
  +    */
  +   Xid getXid()
  +   {
  +      Transaction current = (Transaction)threadTx.get();
  +
  +      // If no transaction or unknown transaction class, return null.
  +      if (current == null || !(current instanceof TransactionImpl))
  +         return null;
  +
  +      return ((TransactionImpl)current).xid;
  +   }
   
      // Protected -----------------------------------------------------
   
  
  
  
  1.9       +75 -95    jboss/src/main/org/jboss/tm/XidImpl.java
  
  Index: XidImpl.java
  ===================================================================
  RCS file: /products/cvs/ejboss/jboss/src/main/org/jboss/tm/XidImpl.java,v
  retrieving revision 1.8
  retrieving revision 1.9
  diff -u -r1.8 -r1.9
  --- XidImpl.java      2001/01/13 18:24:15     1.8
  +++ XidImpl.java      2001/01/24 05:59:56     1.9
  @@ -14,61 +14,51 @@
   
   /**
    *  This object encapsulates the ID of a transaction.
  + *  This implementation is immutable and always serializable at runtime.
    *
    *  @see TransactionImpl
    *  @author Rickard �berg ([EMAIL PROTECTED])
    *  @author <a href="mailto:[EMAIL PROTECTED]">Ole Husgaard</a>
  - *  @version $Revision: 1.8 $
  + *  @version $Revision: 1.9 $
    */
   class XidImpl
      implements Xid, java.io.Serializable
   {
      // Constants -----------------------------------------------------
   
  +   public static final int JBOSS_FORMAT_ID = 0x0101;
  +
      // Attributes ----------------------------------------------------
   
      /**
       *  Hash code of this instance. This is really a sequence number.
       */
  -   int hash;
  +   private int hash;
   
      /**
       *  Global transaction id of this instance.
  +    *  The coding of this class depends on the fact that this variable is
  +    *  initialized in the constructor and never modified. References to
  +    *  this array are never given away, instead a clone is delivered.
       */
  -   byte[] globalId;
  +   private byte[] globalId;
   
      /**
       *  Branch qualifier of this instance.
       *  This identifies the branch of a transaction.
       */
  -   byte[] branchId;
  +   private byte[] branchId;
   
      // Static --------------------------------------------------------
   
  -   /**
  -    *  A cache for the <code>getHostName()</code> function.
  -    */
  -   private static String hostName;
  -
      /**
  -    *  Return the host name of this host, followed by a slash.
  +    *  The host name of this host, followed by a slash.
       *
       *  This is used for building globally unique transaction identifiers.
       *  It would be safer to use the IP address, but a host name is better
       *  for humans to read and will do for now.
       */
  -   static private String getHostName()
  -   {
  -      if (hostName == null) {
  -         try {
  -            hostName = InetAddress.getLocalHost().getHostName() + "/";
  -         } catch (UnknownHostException e) {
  -            hostName = "localhost/";
  -         }
  -      }
  -
  -      return hostName;
  -   }
  +   private static String hostName;
   
      /**
       *  The next transaction id to use on this host.
  @@ -78,40 +68,71 @@
      /**
       *  Return a new unique transaction id to use on this host.
       */
  -   static synchronized int getNextId()
  +   static private synchronized int getNextId()
      {
         return nextId++;
      }
   
      /**
       *  Singleton for no branch qualifier.
  +    */
  +   static private byte[] noBranchQualifier = new byte[0];
  +
  +   /**
  +    *  Initialize the <code>hostName</code> class variable.
  +    */
  +   static {
  +      try {
  +         hostName = InetAddress.getLocalHost().getHostName() + "/";
  +         // Ensure room for 14 digits of serial no.
  +         if (hostName.length() > MAXGTRIDSIZE - 15)
  +            hostName = hostName.substring(0, MAXGTRIDSIZE - 15);
  +         hostName = hostName + "/";
  +      } catch (UnknownHostException e) {
  +         hostName = "localhost/";
  +      }
  +   }
  +
  +   /**
  +    *  Return a string that describes any Xid instance.
       */
  -    static byte[] noBranchQualifier = new byte[MAXBQUALSIZE];
  -    static {
  -        for(int i=0; i<noBranchQualifier.length; i++)
  -            noBranchQualifier[i] = 0;
  -    }
  +   static String toString(Xid id) {
  +      if (id == null)
  +         return "[NULL Xid]";
  +
  +      String s = id.getClass().getName();
  +      s = s.substring(s.lastIndexOf('.') + 1);
  +      s = s + " [FormatId=" + id.getFormatId() +
  +              ", GlobalId=" + new String(id.getGlobalTransactionId()).trim() +
  +              ", BranchQual=" + new String(id.getBranchQualifier()).trim()+"]";
   
  +      return s;
  +   }
  +
      // Constructors --------------------------------------------------
   
      /**
  -    *  Create a new unique branch qualifier.
  +    *  Create a new instance.
       */
  -   public XidImpl(int formatId, byte[] globalId, byte[] branchId)
  +   public XidImpl()
      {
  -      this.hash = getNextId();
  -      this.globalId = globalId;
  -      this.branchId = branchId;
  +      hash = getNextId();
  +      globalId = (hostName + Integer.toString(hash)).getBytes();
  +      branchId = noBranchQualifier;
      }
   
      /**
  -    *  Create a new branch of an existing global transaction id.
  +    *  Create a new branch of an existing global transaction ID.
  +    *
  +    *  @param xid The transaction ID to create a new branch of.
  +    *  @param branchId The ID of the new branch.
  +    *
       */
  -   public XidImpl(XidImpl xid, byte[] branchId)
  +   public XidImpl(XidImpl xid, int branchId)
      {
  -      hash = xid.hash;
  -      this.globalId = xid.globalId;
  -      this.branchId = branchId;
  +      this.hash = xid.hash;
  +      this.globalId = xid.globalId; // reuse array instance, we never modify.
  +      this.branchId = Integer.toString(branchId).getBytes();
      }
   
      // Public --------------------------------------------------------
  @@ -144,11 +165,6 @@
       *  how the global id and branch qualifier should be interpreted.
       */
      public int getFormatId() {
  -       return getJbossFormatId();
  -   }
  -
  -   public static int getJbossFormatId()
  -   {
         // The id we return here should be different from all other transaction
         // implementations.
         // Known IDs are:
  @@ -156,31 +172,38 @@
         // 0:      OSI TP (javadoc states OSI CCR, but that is a bit misleading
         //         as OSI CCR doesn't even have ACID properties. But OSI CCR and
         //         OSI TP do have the same id format.)
  -      // 0xBB14: Used by JONAS
  -      // 0xBB20: Used by JONAS
  -      return 0x0101;
  +      // 1:      Was used by early betas of jBoss.
  +      // 0x0101: The JBOSS_FORMAT_ID we use here.
  +      // 0xBB14: Used by JONAS.
  +      // 0xBB20: Used by JONAS.
  +
  +      return JBOSS_FORMAT_ID;
      }
   
      /**
       *  Compare for equality.
       *
  -    *  This checks the format id and the global transaction ID, but
  -    *  ignores the branch qualifier.
  +    *  Instances are considered equal if they are both instances of XidImpl,
  +    *  and if they have the same global transaction id and transaction
  +    *  branch qualifier.
       */
      public boolean equals(Object obj)
      {
  -      // OSH: Should we also compare the branch ID ?
  -
  -      if (obj != null && obj instanceof XidImpl) {
  +      if (obj instanceof XidImpl) {
            XidImpl other = (XidImpl)obj;
   
  -         if (globalId.length != other.globalId.length)
  +         if (globalId.length != other.globalId.length ||
  +             branchId.length != other.branchId.length)
               return false;
   
            for (int i = 0; i < globalId.length; ++i)
               if (globalId[i] != other.globalId[i])
                  return false;
   
  +         for (int i = 0; i < branchId.length; ++i)
  +            if (branchId[i] != other.branchId[i])
  +               return false;
  +
            return true;
         }
         return false;
  @@ -201,49 +224,6 @@
      // Protected -----------------------------------------------------
   
      // Private -------------------------------------------------------
  -
  -   static byte[] getGlobalIdString(int hash)
  -   {
  -      String value;
  -      String s = getHostName();
  -      String h = Integer.toString(hash);
  -      if(s.length() + h.length() > 64) {
  -          value = s.substring(0, 64-h.length())+h;
  -      } else value = s+h;
  -      byte b[] = new byte[MAXGTRIDSIZE];
  -      System.arraycopy(value.getBytes(), 0, b, 0, value.length());
  -      return b;
  -   }
  -
  -   static byte[] getNextBranchQualifier() {
  -       int id = getNextId();
  -       String ids = Integer.toString(id);
  -       byte[] source = ids.getBytes();
  -       byte[] result = new byte[MAXBQUALSIZE];
  -       System.arraycopy(source, 0, result, 0, source.length);
  -       return result;
  -   }
  -
  -   static byte[] getNextBranchQualifier(byte[] previous) {
  -       String ids = new String(previous).trim();
  -       if(ids.length() == 0)
  -          ids = "1";
  -       else
  -          ids = Integer.toString(Integer.parseInt(ids)+1);
  -       byte[] source = ids.getBytes();
  -       byte[] result = new byte[MAXBQUALSIZE];
  -       System.arraycopy(source, 0, result, 0, source.length);
  -       return result;
  -   }
  -
  -   static String toString(Xid id) {
  -       if(id == null)
  -          return "[NULL XID!!]";
  -       String s = id.getClass().getName();
  -       s = s.substring(s.lastIndexOf('.')+1);
  -       s = s+" [ID="+id.getFormatId()+", Global="+new 
String(id.getGlobalTransactionId()).trim()+", Branch="+new 
String(id.getBranchQualifier()).trim()+"]";
  -       return s;
  -    }
   
      // Inner classes -------------------------------------------------
   }
  
  
  

Reply via email to