User: osh     
  Date: 00/09/28 14:10:12

  Modified:    src/main/org/jboss/tm TransactionImpl.java TxCapsule.java
                        TxManager.java XidImpl.java
  Log:
  Transaction manager performance enhancements
  
  Revision  Changes    Path
  1.9       +149 -80   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.8
  retrieving revision 1.9
  diff -u -r1.8 -r1.9
  --- TransactionImpl.java      2000/09/08 05:22:29     1.8
  +++ TransactionImpl.java      2000/09/28 21:10:11     1.9
  @@ -25,15 +25,17 @@
   import javax.transaction.xa.XAException;
   
   /**
  - *   A light weight transaction.
  + *  A light weight transaction.
    *
  - * It is the public face of the TxCapsule.  Many of these "transactions" can 
coexist representing the TxCap
  - * Access to the underlying txCap is done through the TransactionManager
  + *  It is the public face of the TxCapsule.
  + *  Many of these "transactions" can coexist representing the TxCap.
  + *  Access to the underlying txCap is done through the TransactionManager.
    *
  - *   @see <related>
  - *   @author Rickard �berg ([EMAIL PROTECTED])
  + *  @see TxCapsule
  + *  @author Rickard �berg ([EMAIL PROTECTED])
    *  @author <a href="mailto:[EMAIL PROTECTED]">Marc Fleury</a>
  - *   @version $Revision: 1.8 $
  + *  @author <a href="mailto:[EMAIL PROTECTED]">Ole Husgaard</a>
  + *  @version $Revision: 1.9 $
    */
   public class TransactionImpl
      implements Transaction, Serializable
  @@ -41,54 +43,63 @@
      // Constants -----------------------------------------------------
   
      // Attributes ----------------------------------------------------
  -   int hash;
  -   Xid xid; // XA legacy
  -   
  -   // Static --------------------------------------------------------
  -   public transient TxManager tm;
  -
  -   static String hostName;
   
  +   XidImpl xid; // Transaction ID.
  +   
      // Constructors --------------------------------------------------
      
  -   public TransactionImpl(TxManager tm,int hash, Xid xid)
  +   TransactionImpl(TxCapsule txCapsule, XidImpl xid)
      {
  -      this.tm = tm;
  -      this.hash = hash;
  +      this.txCapsule = txCapsule;
         this.xid = xid; 
  +      travelled = false;
      }
      
  -   /*
  -   * setTxManager()
  -   *
  -   * used for propagated Tx
  -   */
  -   public void setTxManager(TxManager tm) {
  -       
  -       this.tm= tm;
  -   }
  -   
  -   
  -        
  -
      // Public --------------------------------------------------------
  +
  +   // In the following methods we synchronize to avoid races with transaction
  +   // termination. The travelled flag is not checked, as we assume that the
  +   // transaction has already been imported.
  +
      public void commit()
  -            throws RollbackException,
  -                   HeuristicMixedException,
  -                   HeuristicRollbackException,
  -                   java.lang.SecurityException,
  -                   java.lang.IllegalStateException,
  -                   SystemException
  +      throws RollbackException,
  +             HeuristicMixedException,
  +             HeuristicRollbackException,
  +             java.lang.SecurityException,
  +             java.lang.IllegalStateException,
  +             SystemException
      {
  -       
  -      tm.commit(this);
  +      synchronized (this) {
  +        if (done)
  +           throw new IllegalStateException("No transaction.");
  +
  +        txCapsule.commit();
  +      }
      }
   
  +   public void rollback()
  +      throws java.lang.IllegalStateException,
  +             java.lang.SecurityException,
  +             SystemException
  +   {
  +      synchronized (this) {
  +         if (done)
  +            throw new IllegalStateException("No transaction.");
  +
  +         txCapsule.rollback();
  +      }
  +   }
  +
      public boolean delistResource(XAResource xaRes, int flag)
         throws java.lang.IllegalStateException,
                SystemException
      {
  -       return tm.delistResource(this, xaRes, flag);
  +      synchronized (this) {
  +         if (done)
  +            throw new IllegalStateException("No transaction.");
  +
  +         return txCapsule.delistResource(xaRes, flag);
  +      }
      }
   
      public boolean enlistResource(XAResource xaRes)
  @@ -96,73 +107,131 @@
                java.lang.IllegalStateException,
                SystemException
      {
  -       return tm.enlistResource(this, xaRes);
  +      synchronized (this) {
  +         if (done)
  +            throw new IllegalStateException("No transaction.");
  +
  +         return txCapsule.enlistResource(xaRes);
  +      }
      }
   
      public int getStatus()
  -              throws SystemException
  +      throws SystemException
      {
  -      return tm.getStatus(this);
  +      synchronized (this) {
  +         if (done)
  +            return Status.STATUS_NO_TRANSACTION;
  +
  +         return txCapsule.getStatus();
  +      }
      }
   
      public void registerSynchronization(Synchronization s)
  -    throws RollbackException,
  -           java.lang.IllegalStateException,
  -           SystemException
  +      throws RollbackException,
  +             java.lang.IllegalStateException,
  +             SystemException
      {
  -       tm.registerSynchronization(this, s);
  +      synchronized (this) {
  +         if (done)
  +            throw new IllegalStateException("No transaction.");
  +
  +         txCapsule.registerSynchronization(s);
  +      }
      }
   
  -   public void rollback()
  -              throws java.lang.IllegalStateException,
  -                     java.lang.SecurityException,
  -                     SystemException
  +   public void setRollbackOnly()
  +      throws java.lang.IllegalStateException,
  +             SystemException
      {
  -      
  -       tm.rollback(this);
  +      synchronized (this) {
  +         if (done)
  +            throw new IllegalStateException("No transaction.");
  +
  +         txCapsule.setRollbackOnly();
  +      }
      }
   
  -   public void setRollbackOnly()
  -                     throws java.lang.IllegalStateException,
  -                            SystemException
  +   public int hashCode()
      {
  -     
  -       tm.setRollbackOnly(this); 
  -    }
  +      return xid.hash;
  +   }
   
  -   public boolean equals(Object obj)
  +   public String toString()
      {
  -      return ((TransactionImpl)obj).hash == hash;
  +      return "TransactionImpl:" + xid.toString();
      }
   
  -   public int hashCode()
  +   public boolean equals(Object obj)
      {
  -      return hash;
  +      if (obj != null && obj instanceof TransactionImpl)
  +         return xid.equals(((TransactionImpl)obj).xid);
  +      return false;
      }
   
      // Package protected ---------------------------------------------
   
  -   // Protected -----------------------------------------------------
  -   protected String getHostName()
  +   /**
  +    *  Setter for property txCapsule.
  +    *
  +    *  This is needed when a propagated transaction is imported into the
  +    *  current transaction manager.
  +    */
  +   synchronized void setTxCapsule(TxCapsule txCapsule)
  +   {
  +      if (done)
  +         // Shouldn't happen.
  +         throw new IllegalStateException("Transaction " + toString() +
  +                                         " is done.");
  +      this.txCapsule = txCapsule;
  +      travelled = false;
  +   }
  +
  +   /**
  +    *  Setter for property done.
  +    *  No argument for this mutator; we can only set to false.
  +    *  This will also clear the txCapsule reference.
  +    */
  +   synchronized void setDone()
  +   {
  +      done = true;
  +      txCapsule = null;
  +   }
  +
  +   /**
  +    *  Getter for property done.
  +    */
  +   boolean isDone()
  +   {
  +      return done;
  +   }
  +
  +   /**
  +    *  Returns true iff this transaction needs to be imported into the
  +    *  local transaction manager.
  +    */
  +   boolean importNeeded()
      {
  -      if (hostName == null)
  -      {
  -         try
  -         {
  -            hostName = InetAddress.getLocalHost().getHostName();
  -         } catch (UnknownHostException e)
  -         {
  -            hostName = "localhost";
  -         }
  -      }
  -
  -      return hostName;
  -   }
  -
  -    public String toString() {
  -        return "tx:Xid:"+hash;
  -    }
  +      return !done && travelled;
  +   }
   
      // Private -------------------------------------------------------
  +
  +   private transient TxCapsule txCapsule; // The real implementation.
  +   private boolean done; // Flags that the transaction has terminated.
  +   transient boolean travelled; // Flags that the transaction has travelled.
  +
  +   private void writeObject(java.io.ObjectOutputStream stream)
  +      throws java.io.IOException
  +   {
  +      stream.defaultWriteObject();
  +   }
  +
  +   private void readObject(java.io.ObjectInputStream stream)
  +      throws java.io.IOException, ClassNotFoundException
  +   {
  +      stream.defaultReadObject();
  +      travelled = true;
  +   }
  +
      // Inner classes -------------------------------------------------
   }
  
  
  
  1.9       +453 -164  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.8
  retrieving revision 1.9
  diff -u -r1.8 -r1.9
  --- TxCapsule.java    2000/09/28 01:17:11     1.8
  +++ TxCapsule.java    2000/09/28 21:10:11     1.9
  @@ -7,8 +7,6 @@
   package org.jboss.tm;
   
   import java.io.Serializable;
  -import java.net.InetAddress;
  -import java.net.UnknownHostException;
   import java.util.ArrayList;
   import java.util.Set;
   import java.util.HashSet;
  @@ -37,7 +35,6 @@
    *  TxCapsule holds all the information relevant to a transaction.
    *  Callbacks and synchronizations are held here.
    *
  - *  TODO: Implement timeouts.
    *  TODO: Implement persistent storage and recovery.
    *
    *  @see TxManager
  @@ -46,23 +43,21 @@
    *  @author <a href="mailto:[EMAIL PROTECTED]">Marc Fleury</a>
    *  @author <a href="mailto:[EMAIL PROTECTED]">Ole Husgaard</a>
    *
  - *  @version $Revision: 1.8 $
  + *  @version $Revision: 1.9 $
    */
   class TxCapsule implements TimeoutTarget
   {
      // Constants -----------------------------------------------------
   
  +   // Trace enabled flag
  +   static private final boolean trace = true;
  +
      // Code meaning "no heuristics seen", must not be XAException.XA_HEURxxx
      static private final int HEUR_NONE     = XAException.XA_RETRY;
   
      // Attributes ----------------------------------------------------
  -   
  -   // Static --------------------------------------------------------
   
  -   static private int nextId = 0;
  -   static private synchronized int getNextId() { return nextId++; }
  -
  -   private static String hostName;
  +   // Static --------------------------------------------------------
   
      // Constructors --------------------------------------------------
   
  @@ -90,12 +85,41 @@
       */
      private TxCapsule(TxManager tm)
      {
  -      int hashCode = getNextId();
  -      xid = new XidImpl((getHostName()+"/"+hashCode).getBytes(), null);
  -      transaction = new TransactionImpl(tm, hashCode, xid);
  +      xid = new XidImpl();
         this.tm = tm;
      }
   
  +   /**
  +    *  Create a new front for this transaction.
  +    */
  +   TransactionImpl createTransactionImpl()
  +   {
  +      TransactionImpl tx = new TransactionImpl(this, xid);
  +      addTransaction(tx);
  +
  +      return tx;
  +   }
  +
  +   /**
  +    *  Prepare this instance for reuse.
  +    */
  +   void reUse(int timeout)
  +   {
  +      if (!done)
  +        throw new IllegalStateException();
  +
  +      done = false;
  +      resourcesEnded = false;
  +
  +      xid = new XidImpl();
  +
  +      status = Status.STATUS_ACTIVE;
  +      heuristicCode = HEUR_NONE;
  +
  +      start = System.currentTimeMillis();
  +      this.timeout = TimeoutFactory.createTimeout(start+timeout, this);
  +   }
  +
      // Public --------------------------------------------------------
   
      /**
  @@ -139,7 +163,7 @@
               status = Status.STATUS_MARKED_ROLLBACK;
               // fall through..
            case Status.STATUS_MARKED_ROLLBACK:
  -            suspendedResourcesDone();
  +            endResources();
               rollbackResources();
               doAfterCompletion();
               gotHeuristic(null, XAException.XA_HEURRB);
  @@ -166,10 +190,17 @@
      // Package protected ---------------------------------------------
   
      /**
  -    *  Return the transaction encapsulated here.
  +    *  Import a transaction encapsulated here.
       */
  -   Transaction getTransaction() {
  -      return transaction;
  +   void importTransaction(TransactionImpl tx) {
  +      try {
  +         lock();
  +
  +         tx.setTxCapsule(this);
  +         addTransaction(tx);
  +      } finally {
  +        unlock();
  +      }
      }
   
      /**
  @@ -186,10 +217,12 @@
                SystemException
      {
         try {
  -         //DEBUG Logger.debug("TxCapsule before lock");
  -              lock();
  -         //DEBUG Logger.debug("TxCapsule after lock status is 
"+getStringStatus(status));
  -              
  +         lock();
  +
  +         if (trace)
  +            Logger.debug("TxCapsule.commit(): Entered, status=" +
  +                         getStringStatus(status));
  +
            switch (status) {
            case Status.STATUS_PREPARING:
               throw new IllegalStateException("Already started preparing.");
  @@ -198,11 +231,13 @@
            case Status.STATUS_ROLLING_BACK:
               throw new IllegalStateException("Already started rolling back.");
            case Status.STATUS_ROLLEDBACK:
  +            instanceDone();
               checkHeuristics();
               throw new IllegalStateException("Already rolled back.");
            case Status.STATUS_COMMITTING:
               throw new IllegalStateException("Already started committing.");
            case Status.STATUS_COMMITTED:
  +            instanceDone();
               checkHeuristics();
               throw new IllegalStateException("Already committed.");
            case Status.STATUS_NO_TRANSACTION:
  @@ -210,37 +245,42 @@
            case Status.STATUS_UNKNOWN:
               throw new IllegalStateException("Unknown state");
            case Status.STATUS_MARKED_ROLLBACK:
  -            suspendedResourcesDone();
  +            endResources();
               rollbackResources();
               doAfterCompletion();
  +            cancelTimeout();
  +            instanceDone();
  +            checkHeuristics();
               throw new RollbackException("Already marked for rollback");
            case Status.STATUS_ACTIVE:
  -                      //DEBUG Logger.debug("Commiting tx with status Active");
               break;
            default:
               throw new IllegalStateException("Illegal status: " + status);
            }
   
  -         suspendedResourcesDone();
  -
            doBeforeCompletion();
  +
  +         if (trace)
  +            Logger.debug("TxCapsule.commit(): Before completion done, " +
  +                         "status=" + getStringStatus(status));
   
  -              Logger.debug("Before completion is done status is 
"+getStringStatus(status));
  -              
  +         endResources();
  +
            if (status == Status.STATUS_ACTIVE) {
  -            if (resources.size() == 0) {
  -                             //DEBUG Logger.debug("no resources 0 phi commit");
  +            if (resourceCount == 0) {
                  // Zero phase commit is really fast ;-)
  +               if (trace)
  +                  Logger.debug("TxCapsule.commit(): No resources.");
                  status = Status.STATUS_COMMITTED;
  -            } else if (resources.size() == 1) {
  -               // DEBUG Logger.debug("1 resource 1 phi commit");
  -                        // One phase commit
  -                        
  +            } else if (resourceCount == 1) {
  +               // One phase commit
  +               if (trace)
  +                  Logger.debug("TxCapsule.commit(): One resource.");
                  commitResources(true);
               } else {
  -                             
  -                             // DEBUG Logger.debug("many resources 2 phi commit");
                  // Two phase commit
  +               if (trace)
  +                  Logger.debug("TxCapsule.commit(): Many resources.");
   
                  if (!prepareResources()) {
                     boolean commitDecision =
  @@ -262,48 +302,24 @@
               rollbackResources();
               doAfterCompletion();
               cancelTimeout();
  -            throw new RollbackException("Unable to commit transaction has status. 
"+getStringStatus(status));
  +            instanceDone();
  +            throw new RollbackException("Unable to commit, status=" +
  +                                        getStringStatus(status));
            }
   
            cancelTimeout();
  -
            doAfterCompletion();
  -
  +         instanceDone();
            checkHeuristics();
  +
  +         if (trace)
  +            Logger.debug("TxCapsule.commit(): Committed OK.");
  +
         } finally {
           unlock();
         }
      }
   
  -     private String getStringStatus(int status) {
  -             
  -              switch (status) {
  -         case Status.STATUS_PREPARING:
  -            return "STATUS_PREPARING";
  -         case Status.STATUS_PREPARED:
  -            return "STATUS_PREPARED";
  -         case Status.STATUS_ROLLING_BACK:
  -            return "STATUS_ROLLING_BACK";
  -         case Status.STATUS_ROLLEDBACK:
  -            return "STATUS_ROLLEDBACK";
  -         case Status.STATUS_COMMITTING:
  -            return "STATUS_COMMITING";
  -         case Status.STATUS_COMMITTED:
  -            return "STATUS_COMMITED";
  -         case Status.STATUS_NO_TRANSACTION:
  -            return "STATUS_NO_TRANSACTION";
  -         case Status.STATUS_UNKNOWN:
  -            return "STATUS_UNKNOWN";
  -         case Status.STATUS_MARKED_ROLLBACK:
  -            return "STATUS_MARKED_ROLLBACK";
  -         case Status.STATUS_ACTIVE:
  -                     return "STATUS_ACTIVE"; 
  -          
  -         default:
  -                     return "STATUS_UNKNOWN";
  -             }
  -            
  -     }
      /**
       *  Rollback the transaction encapsulated here.
       *  Should not be called directly, use <code>TxManager.rollback()</code>
  @@ -317,13 +333,18 @@
         try {
            lock();
   
  +         if (trace)
  +            Logger.debug("TxCapsule.rollback(): Entered, status=" +
  +                         getStringStatus(status));
  +
            switch (status) {
            case Status.STATUS_ACTIVE:
            case Status.STATUS_MARKED_ROLLBACK:
  -            suspendedResourcesDone();
  +            endResources();
               rollbackResources();
               cancelTimeout();
               doAfterCompletion();
  +            instanceDone();
               // Cannot throw heuristic exception, so we just have to
               // clear the heuristics without reporting.
               heuristicCode = HEUR_NONE;
  @@ -333,7 +354,8 @@
               status = Status.STATUS_MARKED_ROLLBACK;
               return; // commit() will do rollback.
            default:
  -            throw new IllegalStateException("Cannot rollback()");
  +            throw new IllegalStateException("Cannot rollback(), status=" +
  +                                            getStringStatus(status));
            }
         } finally {
            unlock();
  @@ -353,6 +375,10 @@
         try {
            lock();
   
  +         if (trace)
  +            Logger.debug("TxCapsule.setRollbackOnly(): Entered, status=" +
  +                         getStringStatus(status));
  +
            switch (status) {
            case Status.STATUS_ACTIVE:
            case Status.STATUS_PREPARING:
  @@ -404,7 +430,13 @@
         try {
            lock();
   
  -         if (!resources.contains(xaRes))
  +         if (trace)
  +            Logger.debug("TxCapsule.delistResource(): Entered, status=" +
  +                         getStringStatus(status));
  +
  +         int idx = findResource(xaRes);
  +
  +         if (idx == -1)
              throw new IllegalArgumentException("xaRes not enlisted");
   
            switch (status) {
  @@ -434,9 +466,12 @@
            try {
               endResource(xaRes, flag);
               if (flag == XAResource.TMSUSPEND)
  -               suspendedResources.add(xaRes);
  -            else if (flag == XAResource.TMFAIL)
  -               status = Status.STATUS_MARKED_ROLLBACK;
  +               resourceState[idx] = RS_SUSPENDED;
  +            else {
  +               if (flag == XAResource.TMFAIL)
  +                  status = Status.STATUS_MARKED_ROLLBACK;
  +               resourceState[idx] = RS_ENDED;
  +            }
               return true;
            } catch(XAException e) {
               Logger.exception(e);
  @@ -466,6 +501,10 @@
         try {
            lock();
   
  +         if (trace)
  +            Logger.debug("TxCapsule.enlistResource(): Entered, status=" +
  +                         getStringStatus(status));
  +
            switch (status) {
            case Status.STATUS_ACTIVE:
            case Status.STATUS_PREPARING:
  @@ -490,17 +529,29 @@
               throw new IllegalStateException("Illegal status: " + status);
            }
   
  +         if (resourcesEnded)
  +            throw new IllegalStateException("Too late to enlist resources");
  +
            // Add resource
            try {
  -            if (suspendedResources.contains(xaRes)) {
  -               startResource(xaRes, XAResource.TMRESUME);
  -               suspendedResources.remove(xaRes);
  -               return true;
  +            int idx = findResource(xaRes);
  +
  +            if (idx != -1) {
  +               if (resourceState[idx] == RS_SUSPENDED) {
  +                  startResource(xaRes, XAResource.TMRESUME);
  +                  resourceState[idx] = RS_ENLISTED;
  +                  return true;
  +               } else if (resourceState[idx] == RS_ENDED) {
  +                  startResource(xaRes, XAResource.TMJOIN);
  +                  resourceState[idx] = RS_ENLISTED;
  +                  return true;
  +               } else
  +                  return false; // already enlisted
               }
  -            for (int i = 0; i < resources.size(); ++i) {
  -               if (xaRes.isSameRM((XAResource)resources.get(i))) {
  +            for (int i = 0; i < resourceCount; ++i) {
  +               if (xaRes.isSameRM(resources[i])) {
                     startResource(xaRes, XAResource.TMJOIN);
  -                  resources.add(xaRes);
  +                  addResource(xaRes);
                     return true;
                  }
               }
  @@ -508,7 +559,7 @@
               // According to the JTA spec we should create a new
               // transaction branch here.
               startResource(xaRes, XAResource.TMNOFLAGS);
  -            resources.add(xaRes);
  +            addResource(xaRes);
               return true;
            } catch(XAException e) {
               Logger.exception(e);
  @@ -543,6 +594,10 @@
         try {
            lock();
   
  +         if (trace)
  +            Logger.debug("TxCapsule.registerSynchronization(): Entered, " +
  +                         "status=" + getStringStatus(status));
  +
            switch (status) {
            case Status.STATUS_ACTIVE:
            case Status.STATUS_PREPARING:
  @@ -570,7 +625,15 @@
               throw new IllegalStateException("Illegal status: " + status);
            }
   
  -         sync.add(s);
  +         if (syncCount == syncAllocSize) {
  +            // expand table
  +            syncAllocSize = 2 * syncAllocSize;
  +
  +            Synchronization[] sy = new Synchronization[syncAllocSize];
  +            System.arraycopy(sync, 0, sy, 0, syncCount);
  +            sync = sy;
  +         }
  +         sync[syncCount++] = s;
         } finally {
            unlock();
         }
  @@ -578,65 +641,175 @@
   
      // Protected -----------------------------------------------------
   
  +
  +   // Private -------------------------------------------------------
  +
      /**
  -    *  Return the host name of this host.
  -    *  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.
  +    *  The public faces of this capsule are JTA Transaction implementations.
       */
  -   protected String getHostName()
  -   {
  -      if (hostName == null) {
  -         try {
  -            hostName = InetAddress.getLocalHost().getHostName();
  -         } catch (UnknownHostException e) {
  -            hostName = "localhost";
  -         }
  -      }
  +   private TransactionImpl[] transactions = new TransactionImpl[1];
   
  -      return hostName;
  -   }
  +   /**
  +    *  Size of allocated transaction frontend array.
  +    */
  +   private int transactionAllocSize = 1;
   
  -   // Private -------------------------------------------------------
  +   /**
  +    *  Count of transaction frontends for this transaction.
  +    */
  +   private int transactionCount = 0;
   
  -   // A list of synchronizations to call back on commit (before and after)
  -   private ArrayList sync = new ArrayList();
   
  -   // A list of the XARessources to 2phi commit (prepare and commit)
  -   private ArrayList resources = new ArrayList();
  +   /**
  +    *  The synchronizations to call back.
  +    */
  +   private Synchronization[] sync = new Synchronization[1];
   
  -   // Suspended XAResources are in this set
  -   private Set suspendedResources = new HashSet();
  +   /**
  +    *  Size of allocated synchronization array.
  +    */
  +   private int syncAllocSize = 1;
  +
  +   /**
  +    *  Count of ynchronizations for this transaction.
  +    */
  +   private int syncCount = 0;
   
  -   private Xid xid; // XA legacy
  -   private int status; // status code
  -   private int heuristicCode = HEUR_NONE; // heuristics status
  +
  +   /**
  +    *  A list of the XARessources that have participated in this transaction.
  +    */
  +   private XAResource[] resources = new XAResource[1];
  +
  +   /**
  +    *  The state of the resources.
  +    */
  +   private int[] resourceState = new int[1];
  +
  +   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
  +   private final static int RS_VOTE_READONLY = 4; // voted read-only
  +   private final static int RS_VOTE_OK       = 5; // voted ok
  +
  +   /**
  +    *  Size of allocated resource arrays.
  +    */
  +   private int resourceAllocSize = 1;
  +
  +   /**
  +    *  Count of resources that have participated in this transaction.
  +    */
  +   private int resourceCount = 0;
  +
  +
  +   /**
  +    *  Flags that it is too late to enlist new resources.
  +    */
  +   private boolean resourcesEnded = false;
  +
  +   /**
  +    *  The ID of this transaction.
  +    */
  +   private XidImpl xid; // Transaction id
  +
  +   /**
  +    *  Status of this transaction.
  +    */
  +   private int status;
  +
  +   /**
  +    *  The heuristics status of this transaction.
  +    */
  +   private int heuristicCode = HEUR_NONE;
  +
  +   /**
  +    *  The time when this transaction was started.
  +    */
      private long start;
  +
  +   /**
  +    *  The timeout handle for this transaction.
  +    */
      private Timeout timeout;
   
  -   // The public face of the capsule a JTA implementation
  -   private Transaction transaction;
  +   /**
  +    *  The incarnation count of this transaction. This is incremented
  +    *  whenever this instance is done so that nobody is waiting for the
  +    *  lock across incarnations.
  +    */
  +   private long incarnationCount = 1;
   
  -   // My manager
  +   /**
  +    *  The transaction manager for this transaction.
  +    */
      private TxManager tm;
   
  -   // Mutex for thread-safety
  +   /**
  +    *  Mutex for thread-safety. This should only be changed in the
  +    *  <code>lock()</code> and <code>unlock()</code> methods.
  +    */
      private boolean locked = false;
   
      /**
  +    *  Flags that we are done with this transaction and that it can be reused.
  +    */
  +   private boolean done = false;
  +
  +
  +   /**
  +    *  Return a string representation of the given status code.
  +    */
  +   private String getStringStatus(int status) {
  +      switch (status) {
  +         case Status.STATUS_PREPARING:
  +            return "STATUS_PREPARING";
  +         case Status.STATUS_PREPARED:
  +            return "STATUS_PREPARED";
  +         case Status.STATUS_ROLLING_BACK:
  +            return "STATUS_ROLLING_BACK";
  +         case Status.STATUS_ROLLEDBACK:
  +            return "STATUS_ROLLEDBACK";
  +         case Status.STATUS_COMMITTING:
  +            return "STATUS_COMMITING";
  +         case Status.STATUS_COMMITTED:
  +            return "STATUS_COMMITED";
  +         case Status.STATUS_NO_TRANSACTION:
  +            return "STATUS_NO_TRANSACTION";
  +         case Status.STATUS_UNKNOWN:
  +            return "STATUS_UNKNOWN";
  +         case Status.STATUS_MARKED_ROLLBACK:
  +            return "STATUS_MARKED_ROLLBACK";
  +         case Status.STATUS_ACTIVE:
  +            return "STATUS_ACTIVE";
  + 
  +         default:
  +            return "STATUS_UNKNOWN(" + status + ")";
  +      }
  +   }
  +
  +   /**
       *  Lock this instance.
       */
      private synchronized void lock()
      {
  +      if (done)
  +         throw new IllegalStateException("No transaction");
  +
         if (locked) {
            Logger.warning("TxCapsule: Lock contention."); // Good for debugging.
            Thread.currentThread().dumpStack();
  -      }
   
  -      while (locked) {
  -         try {
  -            wait();
  -         } catch (InterruptedException ex) {}
  +         long myIncarnation = incarnationCount;
  +
  +         while (locked) {
  +            try {
  +               wait();
  +            } catch (InterruptedException ex) {}
  +
  +            if (done || myIncarnation != incarnationCount)
  +              throw new IllegalStateException("No transaction");
  +         }
         }
   
         locked = true;
  @@ -673,17 +846,73 @@
      }
   
      /**
  +    *  Return index of XAResource, or <code>-1</code> if not found.
  +    */
  +   private int findResource(XAResource xaRes)
  +   {
  +      for (int i = 0; i < resourceCount; ++i)
  +         if (resources[i] == xaRes)
  +            return i;
  +
  +      return -1;
  +   }
  +
  +   /**
  +    *  Add a resource, expanding tables if needed.
  +    */
  +   private void addResource(XAResource xaRes)
  +   {
  +      if (resourceCount == resourceAllocSize) {
  +         // expand tables
  +         resourceAllocSize = 2 * resourceAllocSize;
  +
  +         XAResource[] res = new XAResource[resourceAllocSize];
  +         System.arraycopy(resources, 0, res, 0, resourceCount);
  +         resources = res;
  +
  +         int[] stat = new int[resourceAllocSize];
  +         System.arraycopy(resourceState, 0, stat, 0, resourceCount);
  +         resourceState = stat;
  +      }
  +      resources[resourceCount] = xaRes;
  +      resourceState[resourceCount] = RS_ENLISTED;
  +      ++resourceCount;
  +   }
  +
  +   /**
  +    *  Add a transaction frontend, expanding the table if needed.
  +    */
  +   private void addTransaction(TransactionImpl tx)
  +   {
  +      if (transactionCount == transactionAllocSize) {
  +         // expand table
  +         transactionAllocSize = 2 * transactionAllocSize;
  +
  +         TransactionImpl[] tr = new TransactionImpl[transactionAllocSize];
  +         System.arraycopy(transactions, 0, tr, 0, transactionCount);
  +         transactions = tr;
  +      }
  +      transactions[transactionCount++] = tx;
  +   }
  +
  +   /**
       *  Call <code>start()</code> on the XAResource.
       *  This will release the lock while calling out.
       */
      private void startResource(XAResource xaRes, int flags)
         throws XAException
      {
  +System.err.println("TxCapsule.startResource(" + xid.toString() +
  +                   ") entered: " + xaRes.toString() +
  +                   " flags=" + flags);
         unlock();
         try {
            xaRes.start(xid, flags);
         } finally {
            lock();
  +System.err.println("TxCapsule.startResource(" + xid.toString() +
  +                   ") leaving: " + xaRes.toString() +
  +                   " flags=" + flags);
         }
      }
   
  @@ -694,38 +923,59 @@
      private void endResource(XAResource xaRes, int flag)
         throws XAException
      {
  +System.err.println("TxCapsule.endResource(" + xid.toString() +
  +                   ") entered: " + xaRes.toString() +
  +                   " flag=" + flag);
         unlock();
         try {
            xaRes.end(xid, flag);
         } finally {
            lock();
  +System.err.println("TxCapsule.endResource(" + xid.toString() +
  +                   ") leaving: " + xaRes.toString() +
  +                   " flag=" + flag);
         }
      }
   
      /**
  -    *  End Tx association for all suspended resources.
  +    *  End Tx association for all resources.
       */
  -   private void suspendedResourcesDone()
  +   private void endResources()
      {
  -      try {
  -         while (!suspendedResources.isEmpty()) {
  -            Iterator iter = suspendedResources.iterator();
  -
  -            try {
  -               while (iter.hasNext()) {
  -                  XAResource xaRes = (XAResource)iter.next();
  -
  -                  iter.remove();
  -                  endResource(xaRes, XAResource.TMSUCCESS);
  -               }
  -            } catch (ConcurrentModificationException e) { }
  +      for (int i = 0; i < resourceCount; i++) {
  +         try {
  +            if (resourceState[i] == 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.
  +               // The transaction context is in suspened state and must
  +               // be resumed via start with TMRESUME specified."
  +               // Note the _must_ above: It does not say _may_.
  +               // The above citation also seem to contradict the XA resource
  +               // state table on pages 17-18 where it is legal to do both
  +               // end(TMSUCCESS) and end(TMFAIL) when the resource is in
  +               // 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);
  +               resourceState[i] = RS_ENLISTED;
  +            }
  +            if (resourceState[i] == RS_ENLISTED) {
  +System.err.println("endresources("+i+"): state="+resourceState[i]);
  +              endResource(resources[i], XAResource.TMSUCCESS);
  +              resourceState[i] = RS_ENDED;
  +            }
  +         } catch(XAException e) {
  +System.err.println("endresources: XAException: " + e);
  +System.err.println("endresources: XAException: errorCode=" + e.errorCode);
  +            Logger.exception(e);
  +            status = Status.STATUS_MARKED_ROLLBACK;
            }
  -      } catch(XAException e) {
  -         Logger.exception(e);
  -         status = Status.STATUS_MARKED_ROLLBACK;
         }
  +      resourcesEnded = true; // Too late to enlist new resources.
      }
   
  +
      /**
       *  Call synchronization <code>beforeCompletion()</code>.
       *  This will release the lock while calling out.
  @@ -734,12 +984,8 @@
      {
         unlock();
         try {
  -         for (int i = 0; i < sync.size(); i++) {
  -                     //DEBUG Logger.debug("calling beforeCompletion on synch status 
is "+getStringStatus(status));
  -            ((Synchronization)sync.get(i)).beforeCompletion();
  -                     //DEBUG Logger.debug("Done calling beforeCompletion on synch 
status is "+getStringStatus(status));
  -            
  -             }
  +         for (int i = 0; i < syncCount; i++)
  +            sync[i].beforeCompletion();
         } finally {
            lock();
         }
  @@ -754,8 +1000,8 @@
         // Assert: Status indicates: Too late to add new synchronizations.
         unlock();
         try {
  -         for (int i = 0; i < sync.size(); i++)
  -            ((Synchronization)sync.get(i)).afterCompletion(status);
  +         for (int i = 0; i < syncCount; i++)
  +            sync[i].afterCompletion(status);
         } finally {
            lock();
         }
  @@ -821,9 +1067,15 @@
         case XAException.XA_HEURHAZ:
         case XAException.XA_HEURMIX:
            heuristicCode = HEUR_NONE;
  +         if (trace)
  +            Logger.debug("TxCapsule: Throwing HeuristicMixedException, " +
  +                         "status=" + getStringStatus(status));
            throw new HeuristicMixedException();
         case XAException.XA_HEURRB:
            heuristicCode = HEUR_NONE;
  +         if (trace)
  +            Logger.debug("TxCapsule: Throwing HeuristicRollbackException, " +
  +                         "status=" + getStringStatus(status));
            throw new HeuristicRollbackException();
         case XAException.XA_HEURCOM:
            heuristicCode = HEUR_NONE;
  @@ -831,11 +1083,44 @@
            // And why define something that is not used ?
            // For now we just have to ignore this failure, even if it happened
            // on rollback.
  +         if (trace)
  +            Logger.debug("TxCapsule: NOT Throwing HeuristicCommitException, " +
  +                         "status=" + getStringStatus(status));
            return;
         }
      }
   
      /**
  +    *  Prepare this instance for reuse.
  +    */
  +   private void instanceDone()
  +   {
  +      synchronized (this) {
  +         // Done with this incarnation.
  +         ++incarnationCount;
  +
  +         // Set done flag so we get no more frontends waiting for
  +         // the lock.
  +         done = true;
  +
  +         // Wake up anybody waiting for the lock.
  +         notifyAll();
  +      }
  +
  +      // Notify transaction fronts that we are done.
  +      for (int i = 0; i < transactionCount; ++i)
  +        transactions[i].setDone();
  +
  +      // Clear content of collections.
  +      syncCount = 0;
  +      transactionCount = 0;
  +      resourceCount = 0;
  +
  +      // This instance is now ready for reuse (when we release the lock).
  +      tm.releaseTxCapsule(this);
  +   }
  +
  +   /**
       *  Prepare all enlisted resources.
       *  If the first phase of the commit process results in a decision
       *  to commit the <code>status</code> will be
  @@ -851,46 +1136,47 @@
         boolean readOnly = true;
   
         status = Status.STATUS_PREPARING;
  -      Logger.debug("Status Preparing: "+status);
   
  -      for (int i = 0; i < resources.size(); i++) {
  +      for (int i = 0; i < resourceCount; i++) {
            // Abort prepare on state change.
            if (status != Status.STATUS_PREPARING)
               return false;
   
  -         XAResource resource = (XAResource)resources.get(i);
  +         XAResource resource = resources[i];
   
            try {
               int vote;
   
               unlock();
               try {
  -                        vote = resource.prepare(xid);
  -                        
  -               Logger.debug("resource vote is "+vote);
  -                        
  +               vote = resources[i].prepare(xid);
               } finally {
                  lock();
               }
   
  -            if (vote != XAResource.XA_RDONLY)
  +            if (vote == XAResource.XA_OK) {
                  readOnly = false;
  +               resourceState[i] = RS_VOTE_OK;
  +            } else if (vote == XAResource.XA_RDONLY)
  +               resourceState[i] = RS_VOTE_READONLY;
               else {
  -               // Resource voted read-only: Can forget about resource.
  -               resources.remove(i);
  -               --i; // undo future increment
  +               // Illegal vote: rollback.
  +               status = Status.STATUS_MARKED_ROLLBACK;
  +               return false;
               }
            } catch (XAException e) {
  +            readOnly = false;
  +
               switch (e.errorCode) {
               case XAException.XA_HEURCOM:
                  // Heuristic commit is not that bad when preparing.
                  // But it means trouble if we have to rollback.
  -               gotHeuristic(resource, e.errorCode);
  +               gotHeuristic(resources[i], e.errorCode);
                  break;
               case XAException.XA_HEURRB:
               case XAException.XA_HEURMIX:
               case XAException.XA_HEURHAZ:
  -               gotHeuristic(resource, e.errorCode);
  +               gotHeuristic(resources[i], e.errorCode);
                  if (status == Status.STATUS_PREPARING)
                     status = Status.STATUS_MARKED_ROLLBACK;
                  break;
  @@ -916,18 +1202,20 @@
      private void commitResources(boolean onePhase)
      {
         status = Status.STATUS_COMMITTING;
  +
  +      for (int i = 0; i < resourceCount; i++) {
  +System.err.println("TxCapsule.commitResources(): 
resourceStates["+i+"]="+resourceState[i]);
  +         if (!onePhase && resourceState[i] != RS_VOTE_OK)
  +           continue;
   
  -      for (int i = 0; i < resources.size(); i++) {
            // Abort commit on state change.
            if (status != Status.STATUS_COMMITTING)
               return;
   
  -         XAResource resource = (XAResource)resources.get(i);
  -
            try {
               unlock();
               try {
  -               resource.commit(xid, onePhase);
  +               resources[i].commit(xid, onePhase);
               } finally {
                  lock();
               }
  @@ -937,7 +1225,7 @@
               case XAException.XA_HEURCOM:
               case XAException.XA_HEURMIX:
               case XAException.XA_HEURHAZ:
  -               gotHeuristic(resource, e.errorCode);
  +               gotHeuristic(resources[i], e.errorCode);
                  break;
               default:
                  Logger.exception(e);
  @@ -958,13 +1246,14 @@
      {
         status = Status.STATUS_ROLLING_BACK;
   
  -      for (int i = 0; i < resources.size(); i++) {
  -         XAResource resource = (XAResource)resources.get(i);
  +      for (int i = 0; i < resourceCount; i++) {
  +         if (resourceState[i] == RS_VOTE_READONLY)
  +           continue;
   
            try {
               unlock();
               try {
  -               resource.rollback(xid);
  +               resources[i].rollback(xid);
               } finally {
                  lock();
               }
  @@ -972,12 +1261,12 @@
               switch (e.errorCode) {
               case XAException.XA_HEURRB:
                  // Heuristic rollback is not that bad when rolling back.
  -               gotHeuristic(resource, e.errorCode);
  +               gotHeuristic(resources[i], e.errorCode);
                  break;
               case XAException.XA_HEURCOM:
               case XAException.XA_HEURMIX:
               case XAException.XA_HEURHAZ:
  -               gotHeuristic(resource, e.errorCode);
  +               gotHeuristic(resources[i], e.errorCode);
                  break;
               default:
                  Logger.exception(e);
  
  
  
  1.18      +242 -246  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.17
  retrieving revision 1.18
  diff -u -r1.17 -r1.18
  --- TxManager.java    2000/09/28 01:17:11     1.17
  +++ TxManager.java    2000/09/28 21:10:11     1.18
  @@ -6,7 +6,12 @@
   */
   package org.jboss.tm;
   
  +import java.lang.ref.SoftReference;
  +
   import java.util.Hashtable;
  +import java.util.LinkedList;
  +import java.util.Map;
  +import java.util.HashMap;
   
   import javax.transaction.Status;
   import javax.transaction.TransactionManager;
  @@ -26,274 +31,265 @@
   import org.jboss.logging.Logger;
   
   /**
  -*    <description>
  -*
  -*    @see <related>
  -*    @author Rickard �berg ([EMAIL PROTECTED])
  -*  @author <a href="mailto:[EMAIL PROTECTED]">Marc Fleury</a>
  -*    @version $Revision: 1.17 $
  -*/
  + *  Our TransactionManager implementation.
  + *
  + *  @see <related>
  + *  @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.18 $
  + */
   public class TxManager
   implements TransactionManager
   {
  -    // Constants -----------------------------------------------------
  -    
  -    // Attributes ----------------------------------------------------
  -    // threadTx keeps track of a thread local association of tx
  -    ThreadLocal threadTx = new ThreadLocal();
  -    // transactions maps
  -    Hashtable txCapsules = new Hashtable();
  -    
  -    int timeOut = 60*1000; // Timeout in milliseconds
  -    
  -    // Static --------------------------------------------------------
  -    
  -    // Constructors --------------------------------------------------
  +   // Constants -----------------------------------------------------
       
  -    // Public --------------------------------------------------------
  -    public void begin()
  -    throws NotSupportedException,
  -    SystemException
  -    {
  -        try {
  -            Logger.debug("begin tx");
  -            
  -            // create tx capsule
  -            TxCapsule txCap = new TxCapsule(this, timeOut);
  -            
  -            // Store it
  -            txCapsules.put(txCap.getTransaction(), txCap);
  -            
  -            // Associate it with the Thread
  -            threadTx.set(txCap.getTransaction());
  -        } catch (RuntimeException ex) {
  -            System.err.println("Exception: " + ex);
  -            ex.printStackTrace();
  -            throw ex;
  -        }
  -    }
  -    
  -    public void commit()
  -    throws RollbackException,
  -    HeuristicMixedException,
  -    HeuristicRollbackException,
  -    java.lang.SecurityException,
  -    java.lang.IllegalStateException,
  -    SystemException
  -    {   
  -        getTransaction().commit();
  -    }
  +   // Attributes ----------------------------------------------------
  +
  +   /**
  +    *  Default timeout in milliseconds.
  +    */
  +   int timeOut = 60*1000; // Default timeout in milliseconds
       
  -    public int getStatus()
  -    throws SystemException
  -    {
  -        // Get the txCapsule running now with the thread
  -        Object current = threadTx.get();
  -        if (current != null) {
  -            TxCapsule txCap = (TxCapsule) txCapsules.get(current);
  -            
  -            if (txCap == null)
  -                return Status.STATUS_NO_TRANSACTION;
  -            else
  -                return txCap.getStatus();
  -        } else {
  -            return Status.STATUS_NO_TRANSACTION;
  -        }
  -    }
  +   // Static --------------------------------------------------------
       
  -    public Transaction getTransaction()
  -    throws SystemException
  -    {
  -        return (Transaction)threadTx.get();
  -    }
  +   // Constructors --------------------------------------------------
       
  -    public void resume(Transaction tobj)
  -    throws InvalidTransactionException,
  -    java.lang.IllegalStateException,
  -    SystemException
  -    {
  +   // Public --------------------------------------------------------
  +
  +   public void begin()
  +      throws NotSupportedException,
  +             SystemException
  +   {
  +      Transaction current = (Transaction)threadTx.get();
  +
  +      if (current != null &&
  +          (!(current instanceof TransactionImpl) ||
  +           !((TransactionImpl)current).isDone()))
  +         throw new NotSupportedException("Transaction already active, " +
  +                                         "cannot nest transactions.");
  +
  +      TxCapsule txCapsule = null;
  +      while (inactiveCapsules.size() > 0) {
  +         SoftReference ref = (SoftReference)inactiveCapsules.removeFirst();
  +         txCapsule = (TxCapsule)ref.get();
  +         if (txCapsule != null) {
  +           txCapsule.reUse(timeOut);
  +           break;
  +         }
  +      }
  +      if (txCapsule == null)
  +         txCapsule = new TxCapsule(this, timeOut);
  +      TransactionImpl tx = txCapsule.createTransactionImpl();
  +      threadTx.set(tx);
  +      activeCapsules.put(tx.xid, txCapsule);
  +   }
  +
  +   /**
  +    *  Commit the transaction associated with the currently running thread.
  +    */
  +   public void commit()
  +      throws RollbackException,
  +             HeuristicMixedException,
  +             HeuristicRollbackException,
  +             java.lang.SecurityException,
  +             java.lang.IllegalStateException,
  +             SystemException
  +   {
  +      Transaction current = (Transaction)threadTx.get();
  +
  +      if (current != null) {
  +         current.commit();
  +         threadTx.set(null);
  +      } else
  +         throw new IllegalStateException("No transaction.");
  +   }
  + 
  +   /**
  +    *  Return the status of the transaction associated with the currently
  +    *  running thread, or <code>Status.STATUS_NO_TRANSACTION</code> if no
  +    *  active transaction is currently associated.
  +    */
  +   public int getStatus()
  +      throws SystemException
  +   {
  +      Transaction current = (Transaction)threadTx.get();
  +
  +      if (current != null)
  +         return current.getStatus();
  +      else
  +         return Status.STATUS_NO_TRANSACTION;
  +   }
  +
  +   /**
  +    *  Return the transaction currently associated with the invoking thread,
  +    *  or <code>null</code> if no active transaction is currently associated.
  +    */
  +   public Transaction getTransaction()
  +      throws SystemException
  +   {
  +      Transaction current = (Transaction)threadTx.get();
  +
  +      if (current != null && current instanceof TransactionImpl &&
  +          ((TransactionImpl)current).isDone()) {
  +         threadTx.set(null);
  +         return null;
  +      }
  +      return current;
  +   }
  +
  +   public void resume(Transaction tobj)
  +      throws InvalidTransactionException,
  +             java.lang.IllegalStateException,
  +             SystemException
  +   {
           //Useless
           
           //throw new Exception("txMan.resume() NYI");
  -    }
  -    
  -    
  -    public Transaction suspend()
  -    throws SystemException
  -    {
  -        //      Logger.debug("suspend tx");
  +   }
  +
  +   public Transaction suspend()
  +      throws SystemException
  +   {
  +        //      Logger.log("suspend tx");
           
           // Useless
           
           return null;
           //throw new Exception("txMan.suspend() NYI");
  -    }
  -    
  -    
  -    public void rollback()
  -    throws java.lang.IllegalStateException,
  -    java.lang.SecurityException,
  -    SystemException
  -    { 
  -    getTransaction().rollback();
  -    }
  -    
  -    public void setRollbackOnly()
  -    throws java.lang.IllegalStateException,
  -    SystemException
  -    {
  -        //      Logger.debug("set rollback only tx");
  -        getTransaction().setRollbackOnly();
  -    }
  -    
  -    public void setTransactionTimeout(int seconds)
  -    throws SystemException
  -    {
  -        timeOut = seconds;
  -    }
  -    
  -    /*
  -    * The following 2 methods are here to provide association and disassociation of 
the thread
  +   }
  +
  +   /**
  +    *  Roll back the transaction associated with the currently running thread.
       */
  -    public Transaction disassociateThread() {
  -        Transaction current = (Transaction) threadTx.get();
  -        
  -        threadTx.set(null);
  -        
  -             //DEBUG Logger.debug("DisassociateThread " + ((current==null) ? "null" 
: Integer.toString(current.hashCode())));
  -             Logger.debug("disassociateThread " + ((current==null) ? "null" : 
Integer.toString(current.hashCode())));
  -        
  -             return current;
  -    }
  -    
  -    public void associateThread(Transaction transaction) {
  -        // If the tx has traveled it needs the TxManager
  -        ((TransactionImpl) transaction).setTxManager(this);
  +   public void rollback()
  +      throws java.lang.IllegalStateException,
  +             java.lang.SecurityException,
  +             SystemException
  +   { 
  +      Transaction current = (Transaction)threadTx.get();
  +
  +      if (current != null) {
  +         current.rollback();
  +         threadTx.set(null);
  +      } else
  +         throw new IllegalStateException("No transaction.");
  +   }
  +
  +   /**
  +    *  Mark the transaction associated with the currently running thread
  +    *  so that the only possible outcome is a rollback.
  +    */
  +   public void setRollbackOnly()
  +      throws java.lang.IllegalStateException,
  +             SystemException
  +   {
  +      Transaction current = (Transaction)threadTx.get();
  +
  +      if (current != null)
  +         current.setRollbackOnly();
  +      else
  +         throw new IllegalStateException("No transaction.");
  +   }
  +
  +   /**
  +    *  Set the transaction timeout for new transactions started here.
  +    */
  +   public void setTransactionTimeout(int seconds)
  +      throws SystemException
  +   {
  +      timeOut = seconds;
  +   }
  +    
  +   /*
  +    *  The following 2 methods are here to provide association and
  +    *  disassociation of the thread.
  +    */
  +   public Transaction disassociateThread()
  +   {
  +      Transaction current = (Transaction)threadTx.get();
           
  -        // Associate with the thread
  -        threadTx.set(transaction);
  -             
  -             //DEBUG Logger.debug("DisassociateThread " + ((transaction==null) ? 
"null" : Integer.toString(transaction.hashCode())));
  -             Logger.debug("associateThread " + ((transaction==null) ? "null" : 
Integer.toString(transaction.hashCode())));
  +      threadTx.set(null);
           
  -    }
  +      return current;
  +   }
       
  +   public void associateThread(Transaction transaction)
  +   {
  +      // If the transaction has travelled, we have to import it.
  +      if (transaction != null && transaction instanceof TransactionImpl) {
  +         TransactionImpl tx = (TransactionImpl)transaction;
  +
  +         if (tx.importNeeded()) {
  +            synchronized(tx) {
  +               // Recheck with synchronization.
  +               if (tx.importNeeded()) {
  +                  TxCapsule txCapsule = (TxCapsule)activeCapsules.get(tx.xid);
  +                  if (txCapsule != null)
  +                     txCapsule.importTransaction(tx);
  +                  else
  +                     Logger.warning("Cannot import transaction: " +
  +                                    tx.toString());
  +               }
  +            }
  +         }
  +      }
  +
  +      // Associate with the thread
  +      threadTx.set(transaction);
  +   }
       
  -    // Package protected ---------------------------------------------
       
  -    // There has got to be something better :)
  -    static TxManager getTransactionManager() {
  -        try {
  -            
  -            javax.naming.InitialContext context = new javax.naming.InitialContext();
  +   // 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.debug("Calling get manager from JNDI");
  -            TxManager manager = (TxManager) context.lookup("TransactionManager");
  -            Logger.debug("Returning TM "+manager.hashCode());
  +         //One tx in naming
  +         Logger.log("Calling get manager from JNDI");
  +         TxManager manager = (TxManager) context.lookup("TransactionManager");
  +         Logger.log("Returning TM " + manager.hashCode());
               
  -            return manager;
  -        
  -        } catch (Exception e ) { return null;}
  -    }
  -    
  -    int getTransactionTimeout()
  -    {
  -        return timeOut;
  -    }
  -    
  -    
  -    // Public --------------------------------------------------------
  -    
  -    public void commit(Transaction tx)
  -    throws RollbackException,
  -    HeuristicMixedException,
  -    HeuristicRollbackException,
  -    java.lang.SecurityException,
  -    java.lang.IllegalStateException,
  -    SystemException
  -    {
  -         Logger.debug("txManager commit tx "+tx.hashCode());
  -        try {
  -            // Look up the txCapsule and delegate
  -            ((TxCapsule) txCapsules.get(tx)).commit();
  -        }
  -        finally {
  -            // Disassociation
  -            threadTx.set(null);
  -            
  -            //Remove from the internal maps, txCapsule should be GC'ed
  -            txCapsules.remove(tx);
  -        }
  -    }
  -    
  -    public boolean delistResource(Transaction tx, XAResource xaRes, int flag)
  -    throws java.lang.IllegalStateException,
  -    SystemException
  -    {
  -        // Look up the txCapsule and delegate
  -        return ((TxCapsule) txCapsules.get(tx)).delistResource(xaRes, flag);
  -    }
  -    
  -    public boolean enlistResource(Transaction tx, XAResource xaRes)
  -    throws RollbackException,
  -    java.lang.IllegalStateException,
  -    SystemException
  -    {
  -        // Look up the txCapsule and delegate
  -        return ((TxCapsule) txCapsules.get(tx)).enlistResource(xaRes);
  -    }
  -    
  -    public int getStatus(Transaction tx)
  -    throws SystemException
  -    {
  -        // Look up the txCapsule and delegate
  -        TxCapsule txCap = ((TxCapsule) txCapsules.get(tx));
  -        return txCap == null ? Status.STATUS_NO_TRANSACTION : txCap.getStatus();
  -    }
  +         return manager;
  +      } catch (Exception e ) {
  +         return null;
  +      }
  +   }
       
  -    public void registerSynchronization(Transaction tx, Synchronization s)
  -    throws RollbackException,
  -    java.lang.IllegalStateException,
  -    SystemException
  -    {
  -        // Look up the txCapsule and delegate
  -        ((TxCapsule) txCapsules.get(tx)).registerSynchronization(s);
  -    }
  -    
  -    public void rollback(Transaction tx)
  -    throws java.lang.IllegalStateException,
  -    java.lang.SecurityException,
  -    SystemException
  -    {
  -          Logger.debug("rollback tx "+tx.hashCode());
  -     
  -        try {
  -            // Look up the txCapsule and delegate
  -            ((TxCapsule) txCapsules.get(tx)).rollback();
  -        }
  -        finally {
  -            // Disassociation
  -            threadTx.set(null);
  -            
  -            //Remove from the internal maps, txCapsule should be GC'ed
  -            txCapsules.remove(tx);
  -        }
  -    }
  -    
  -    public void setRollbackOnly(Transaction tx)
  -    throws java.lang.IllegalStateException,
  -    SystemException
  -    {
  -        // Look up the txCapsule and delegate
  -        ((TxCapsule) txCapsules.get(tx)).setRollbackOnly();
  -    }
  -    
  -    
  -    
  -    // Protected -----------------------------------------------------
  -    
  -    // Private -------------------------------------------------------
  -    
  -    // Inner classes -------------------------------------------------
  +   /**
  +    *  Release the given txCapsule for reuse.
  +    */
  +   void releaseTxCapsule(TxCapsule txCapsule)
  +   {
  +      activeCapsules.remove(txCapsule);
  +      inactiveCapsules.add(new SoftReference(txCapsule));
  +   }
  +
  +
  +   // Protected -----------------------------------------------------
  +
  +   // Private -------------------------------------------------------
  + 
  +   /**
  +    *  This keeps track of the transaction association with threads.
  +    *  In some cases terminated transactions may not be cleared here.
  +    */
  +   private ThreadLocal threadTx = new ThreadLocal();
  +
  +   /**
  +    *  This map contains the active txCapsules as values.
  +    *  The keys are the <code>Xid</code> of the txCapsules.
  +    */
  +   private Map activeCapsules = new HashMap();
  +
  +   /**
  +    *  This collection contains the inactive txCapsules.
  +    *  We keep these for reuse.
  +    */
  +   private LinkedList inactiveCapsules = new LinkedList();
  +
  +   // Inner classes -------------------------------------------------
   }
  
  
  
  1.4       +156 -31   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.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- XidImpl.java      2000/07/27 22:24:52     1.3
  +++ XidImpl.java      2000/09/28 21:10:11     1.4
  @@ -6,73 +6,198 @@
    */
   package org.jboss.tm;
   
  +import java.net.InetAddress;
  +import java.net.UnknownHostException;
  +
   import javax.transaction.xa.Xid;
   
  +
   /**
  - *   <description> 
  - *      
  - *   @see <related>
  - *   @author Rickard �berg ([EMAIL PROTECTED])
  - *   @version $Revision: 1.3 $
  + *  This object encapsulates the ID of a transaction.
  + *
  + *  @see TransactionImpl
  + *  @author Rickard �berg ([EMAIL PROTECTED])
  + *  @author <a href="mailto:[EMAIL PROTECTED]">Ole Husgaard</a>
  + *  @version $Revision: 1.4 $
    */
  -public class XidImpl
  +class XidImpl
      implements Xid, java.io.Serializable
   {
      // Constants -----------------------------------------------------
       
      // Attributes ----------------------------------------------------
  +
  +   /**
  +    *  Hash code of this instance. This is really a sequence number.
  +    */
  +   int hash;
  +
  +   /**
  +    *  Global transaction id of this instance.
  +    */
      byte[] globalId;
  +
  +   /**
  +    *  Branch qualifier of this instance.
  +    *  This identifies the branch of a transaction.
  +    */
      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.
  +    *
  +    *  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;
  +   }
  +
  +   /**
  +    *  The next transaction id to use on this host.
  +    */
  +   static private int nextId = 0;
  +
  +   /**
  +    *  Return a new unique transaction id to use on this host.
  +    */
  +   static private synchronized int getNextId()
  +   {
  +      return nextId++;
  +   }
  +
  +   /**
  +    *  Singleton for no branch qualifier.
  +    */
  +   static private byte[] noBranchQualifier = new byte[0];
  +
      // Constructors --------------------------------------------------
  -   public XidImpl(byte[] globalId, byte[] branchId)
  +
  +   /**
  +    *  Create a new unique branch qualifier.
  +    */
  +   public XidImpl()
      {
  -      this.globalId = globalId;
  +      hash = getNextId();
  +      this.globalId = getGlobalIdString().getBytes();
  +      this.branchId = noBranchQualifier;
  +   }
  +
  +   /**
  +    *  Create a new branch of an existing global transaction id.
  +    */
  +   public XidImpl(XidImpl xid, byte[] branchId)
  +   {
  +      hash = xid.hash;
  +      this.globalId = xid.globalId;
         this.branchId = branchId;
      }
      
      // Public --------------------------------------------------------
      
      // Xid implementation --------------------------------------------
  +
  +   /**
  +    *  Return the global transaction id of this transaction.
  +    */
      public byte[] getGlobalTransactionId()
      {
  -      return globalId;
  +      return (byte[])globalId.clone();
      }
      
  +   /**
  +    *  Return the branch qualifier of this transaction.
  +    */
  +   public byte[] getBranchQualifier()
  +   {
  +      if (branchId.length == 0)
  +         return branchId; // Zero length arrays are immutable.
  +      else
  +         return (byte[])branchId.clone();
  +   }
  +
  +   /**
  +    *  Return the format identifier of this transaction.
  +    *
  +    *  The format identifier augments the global id and specifies
  +    *  how the global id and branch qualifier should be interpreted.
  +    */
      public int getFormatId()
      {
  -      return 1; // TODO: what should be here?
  +      // The id we return here should be different from all other transaction
  +      // implementations.
  +      // Known IDs are:
  +      // -1:     Sometimes used to denote a null transaction id.
  +      // 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 1;
      }
  -   
  -   public byte[] getBranchQualifier()
  +
  +   /**
  +    *  Compare for equality.
  +    *
  +    *  This checks the format id and the global transaction ID, but
  +    *  ignores the branch qualifier.
  +    */
  +   public boolean equals(Object obj)
      {
  -      return branchId;
  +      // OSH: Should we also compare the branch ID ?
  +
  +      if (obj != null && obj instanceof XidImpl) {
  +         XidImpl other = (XidImpl)obj;
  +
  +         if (globalId.length != other.globalId.length)
  +            return false;
  +
  +         for (int i = 0; i < globalId.length; ++i)
  +            if (globalId[i] != other.globalId[i])
  +               return false;
  +
  +         return true;
  +      }
  +      return false;
      }
  -   
  -   /*
  -   * equals works on all the bytes of the Xid
  -   */
  -   public boolean equals(Object obj) {
  -        
  -      byte[] otherGlobalId = ((XidImpl) obj).globalId;
  -     
  -      for (int i = 0 ; i<globalId.length ; i++) {
  -             
  -              if (otherGlobalId[i] != globalId[i]) {
  -                     
  -                       return false;
  -              }
  -      }      
  -     
  -      return true;
  +
  +   public int hashCode()
  +   {
  +      return hash;
  +   }
  +
  +   public String toString()
  +   {
  +      return "XidImpl:" + getGlobalIdString();
      }
  +
      // Package protected ---------------------------------------------
       
      // Protected -----------------------------------------------------
       
      // Private -------------------------------------------------------
  +
  +   private String getGlobalIdString()
  +   {
  +      return getHostName() + hash;
  +   }
   
      // Inner classes -------------------------------------------------
   }
  
  
  

Reply via email to