User: fleury  
  Date: 00/09/07 19:25:01

  Modified:    src/main/org/jboss/tm TxManager.java TxCapsule.java
                        TransactionImpl.java
  Log:
  Ole husgaard updates
  
  Revision  Changes    Path
  1.13      +111 -108  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.12
  retrieving revision 1.13
  diff -u -r1.12 -r1.13
  --- TxManager.java    2000/08/28 17:51:37     1.12
  +++ TxManager.java    2000/09/08 02:25:00     1.13
  @@ -31,7 +31,7 @@
    *   @see <related>
    *   @author Rickard �berg ([EMAIL PROTECTED])
    *  @author <a href="mailto:[EMAIL PROTECTED]">Marc Fleury</a>
  - *   @version $Revision: 1.12 $
  + *   @version $Revision: 1.13 $
    */
   public class TxManager
      implements TransactionManager
  @@ -52,101 +52,105 @@
   
      // Public --------------------------------------------------------
      public void begin()
  -      throws NotSupportedException,SystemException
  +      throws NotSupportedException,
  +             SystemException
      {
  +try {
   //      Logger.log("begin tx");
   
  -             // create tx capsule
  -             TxCapsule txCap = new TxCapsule(this, timeOut);
  +      // create tx capsule
  +      TxCapsule txCap = new TxCapsule(this, timeOut);
   
  -             // Store it
  -             txCapsules.put(txCap.getTransaction(), txCap);
  +      // Store it
  +      txCapsules.put(txCap.getTransaction(), txCap);
   
  -             // Associate it with the Thread
  -             threadTx.set(txCap.getTransaction());
  +      // 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
  +      throws RollbackException,
  +             HeuristicMixedException,
  +             HeuristicRollbackException,
  +             java.lang.SecurityException,
  +             java.lang.IllegalStateException,
  +             SystemException
      {
   //      Logger.log("commit tx");
   
  -             
  -
  -                     getTransaction().commit();
  +      getTransaction().commit();
      }
   
      public int getStatus()
  -              throws SystemException
  +      throws SystemException
      {
  -        // Get the txCapsule running now with the thread
  +      // Get the txCapsule running now with the thread
         Object current = threadTx.get();
  -      if(current != null) {
  -       TxCapsule txCap = (TxCapsule) txCapsules.get(current);
  +      if (current != null) {
  +      TxCapsule txCap = (TxCapsule) txCapsules.get(current);
   
  -       if (txCap == null)
  -             return Status.STATUS_NO_TRANSACTION;
  -          else
  -             return txCap.getStatus();
  +      if (txCap == null)
  +            return Status.STATUS_NO_TRANSACTION;
  +         else
  +            return txCap.getStatus();
         } else {
            return Status.STATUS_NO_TRANSACTION;
         }
      }
   
      public Transaction getTransaction()
  -                           throws SystemException
  +     throws SystemException
      {
         return (Transaction)threadTx.get();
  -
      }
   
      public void resume(Transaction tobj)
  -            throws InvalidTransactionException,
  -                   java.lang.IllegalStateException,
  -                   SystemException
  +      throws InvalidTransactionException,
  +             java.lang.IllegalStateException,
  +             SystemException
      {
  -        //Useless
  +      //Useless
   
  -        //throw new Exception("txMan.resume() NYI");
  +      //throw new Exception("txMan.resume() NYI");
      }
   
   
      public Transaction suspend()
  -                    throws SystemException
  +      throws SystemException
      {
   //      Logger.log("suspend tx");
   
  -        // Useless
  +      // Useless
   
  -        return null;
  -        //throw new Exception("txMan.suspend() NYI");
  +      return null;
  +      //throw new Exception("txMan.suspend() NYI");
      }
   
   
      public void rollback()
  -              throws java.lang.IllegalStateException,
  -                     java.lang.SecurityException,
  -                     SystemException
  +      throws java.lang.IllegalStateException,
  +             java.lang.SecurityException,
  +             SystemException
      {
   //      Logger.log("rollback tx");
         getTransaction().rollback();
      }
   
      public void setRollbackOnly()
  -                     throws java.lang.IllegalStateException,
  -                            SystemException
  +      throws java.lang.IllegalStateException,
  +             SystemException
      {
   //      Logger.log("set rollback only tx");
         getTransaction().setRollbackOnly();
      }
   
      public void setTransactionTimeout(int seconds)
  -                           throws SystemException
  +      throws SystemException
      {
         timeOut = seconds;
      }
  @@ -155,21 +159,19 @@
      * The following 2 methods are here to provide association and disassociation of 
the thread
      */
      public Transaction disassociateThread() {
  +      Transaction current = (Transaction) threadTx.get();
   
  -             Transaction current = (Transaction) threadTx.get();
  +      threadTx.set(null);
   
  -             threadTx.set(null);
  -
  -             return current;
  +      return current;
      }
   
      public void associateThread(Transaction transaction) {
  -     
  -        // If the tx has traveled it needs the TxManager
  -        ((TransactionImpl) transaction).setTxManager(this);
  +      // If the tx has traveled it needs the TxManager
  +      ((TransactionImpl) transaction).setTxManager(this);
                
  -             // Associate with the thread
  -        threadTx.set(transaction);
  +      // Associate with the thread
  +      threadTx.set(transaction);
      }
   
   
  @@ -177,64 +179,65 @@
   
      // There has got to be something better :)
      static TxManager getTransactionManager() {
  +      try {
   
  -             try {
  +         javax.naming.InitialContext context = new javax.naming.InitialContext();
   
  -                     javax.naming.InitialContext context = new 
javax.naming.InitialContext();
  +         //One tx in naming
  +         Logger.log("Calling get manager from JNDI");
  +         TxManager manager = (TxManager) context.lookup("TransactionManager");
  +         Logger.log("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;
   
  -                     return manager;
  +      } catch (Exception e ) { return null;}
  +   }
   
  -             } catch (Exception e ) { return null;}
  -     }
      int getTransactionTimeout()
      {
         return timeOut;
      }
   
  -
   
  +   // Public --------------------------------------------------------
   
  -     // Public --------------------------------------------------------
  -     public void commit(Transaction tx)
  -     throws RollbackException,
  -     HeuristicMixedException,
  -     HeuristicRollbackException,
  -     java.lang.SecurityException,
  -     java.lang.IllegalStateException,
  -     SystemException
  -     {
  -             try {
  -                     
  -                     // Look up the txCapsule and delegate
  -                     ((TxCapsule) txCapsules.get(tx)).commit();
  -             }
  -             finally {
  -                     
  -                     // Disassociation
  -                     threadTx.set(null);
  -             }
  -     }
  +   public void commit(Transaction tx)
  +      throws RollbackException,
  +             HeuristicMixedException,
  +             HeuristicRollbackException,
  +             java.lang.SecurityException,
  +             java.lang.IllegalStateException,
  +             SystemException
  +   {
  +      try {
  +         // Look up the txCapsule and delegate
  +         ((TxCapsule) txCapsules.get(tx)).commit();
  +      }
  +      finally {
  +         // Disassociation
  +         threadTx.set(null);
  +      }
  +   }
        
      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);
  +      // Look up the txCapsule and delegate
  +      return ((TxCapsule) txCapsules.get(tx)).delistResource(xaRes, flag);
      }
   
      public boolean enlistResource(Transaction tx, XAResource xaRes)
  -      throws RollbackException
  +      throws RollbackException,
  +             java.lang.IllegalStateException,
  +             SystemException
      {
  -             // Look up the txCapsule and delegate
  -        return ((TxCapsule) txCapsules.get(tx)).enlistResource(xaRes);
  +      // Look up the txCapsule and delegate
  +      return ((TxCapsule) txCapsules.get(tx)).enlistResource(xaRes);
      }
   
      public int getStatus(Transaction tx)
  -              throws SystemException
  +      throws SystemException
      {
         // Look up the txCapsule and delegate
         TxCapsule txCap = ((TxCapsule) txCapsules.get(tx));
  @@ -242,35 +245,35 @@
      }
   
      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);
  +      ((TxCapsule) txCapsules.get(tx)).registerSynchronization(s);
      }
   
      public void rollback(Transaction tx)
  -              throws java.lang.IllegalStateException,
  -                     java.lang.SecurityException,
  -                     SystemException
  -     {
  -             try {
  -                     
  -                     // Look up the txCapsule and delegate
  -                     ((TxCapsule) txCapsules.get(tx)).rollback();
  -             }
  -             finally {
  -                     
  -                     // Disassociation
  -                     threadTx.set(null);
  -             }
  -     }
  +      throws java.lang.IllegalStateException,
  +             java.lang.SecurityException,
  +             SystemException
  +   {
  +      try {
  +         // Look up the txCapsule and delegate
  +         ((TxCapsule) txCapsules.get(tx)).rollback();
  +      }
  +      finally {
  +         // Disassociation
  +         threadTx.set(null);
  +      }
  +   }
        
      public void setRollbackOnly(Transaction tx)
  -                     throws java.lang.IllegalStateException,
  -                            SystemException
  +      throws java.lang.IllegalStateException,
  +             SystemException
      {
  -             // Look up the txCapsule and delegate
  -             ((TxCapsule) txCapsules.get(tx)).setRollbackOnly();
  -
  +      // Look up the txCapsule and delegate
  +      ((TxCapsule) txCapsules.get(tx)).setRollbackOnly();
      }
   
   
  
  
  
  1.3       +845 -190  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.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- TxCapsule.java    2000/08/18 03:21:12     1.2
  +++ TxCapsule.java    2000/09/08 02:25:01     1.3
  @@ -9,7 +9,11 @@
   import java.io.Serializable;
   import java.net.InetAddress;
   import java.net.UnknownHostException;
  -import java.util.Vector;
  +import java.util.ArrayList;
  +import java.util.Set;
  +import java.util.HashSet;
  +import java.util.Iterator;
  +import java.util.ConcurrentModificationException;
   
   import javax.transaction.Transaction;
   import javax.transaction.Status;
  @@ -23,254 +27,532 @@
   import javax.transaction.xa.Xid;
   import javax.transaction.xa.XAResource;
   import javax.transaction.xa.XAException;
  +
   import org.jboss.logging.Logger;
  +import org.jboss.util.timeout.Timeout;
  +import org.jboss.util.timeout.TimeoutTarget;
  +import org.jboss.util.timeout.TimeoutFactory;
   
   /**
  - *  TxCapsule
  + *  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
  + *  @see TransactionImpl
    *
  - *  TxCapsule holds all the information relevant to a transaction. Callbacks and 
synchronizations are held here
  + *  @author <a href="mailto:[EMAIL PROTECTED]">Marc Fleury</a>
  + *  @author <a href="mailto:[EMAIL PROTECTED]">Ole Husgaard</a>
    *
  - *   @see <related>
  - *   @author <a href="mailto:[EMAIL PROTECTED]">Marc Fleury</a>
  - *  @version $Revision: 1.2 $
  + *  @version $Revision: 1.3 $
    */
  -public class TxCapsule
  +class TxCapsule implements TimeoutTarget
   {
      // Constants -----------------------------------------------------
   
  -   // Attributes ----------------------------------------------------
  -   // A list of synchronizations to call back on commit (before and after)
  -   Vector sync;
  -   
  -   // A list of the XARessources to 2phi commit (prepare and commit)
  -   Vector resources;
  +   // Code meaning "no heuristics seen", must not be XAException.XA_HEURxxx
  +   static private final int HEUR_NONE     = XAException.XA_RETRY;
   
  -   Xid xid; // XA legacy 
  -   int hashCode;
  -   int status;
  -   long timeout;
  -   long start;
  -   
  -   // The public face of the capsule a JTA implementation
  -   Transaction transaction;
  +   // Attributes ----------------------------------------------------
   
      // Static --------------------------------------------------------
  -   static int nextId = 0;
  -   public int getNextId() { return nextId++; }
  -   public TxManager tm;
   
  -   static String hostName;
  +   static private int nextId = 0;
  +   static private synchronized int getNextId() { return nextId++; }
   
  +   private static String hostName;
  +
      // Constructors --------------------------------------------------
  -   public TxCapsule(TxManager tm, int timeout)
  +
  +   /**
  +    *  Create a new TxCapsule.
  +    *
  +    *  @param tm The transaction manager for this transaction.
  +    *  @param timeout The timeout for this transaction in milliseconds
  +    *                 (timeouts are not yet implemented).
  +    */
  +   TxCapsule(TxManager tm, int timeout)
      {
  -             this(tm);
  +      this(tm);
   
  -      resources = new Vector();
  -      sync = new Vector();
         status = Status.STATUS_ACTIVE;
   
  -      this.timeout = (long) timeout;
         start = System.currentTimeMillis();
  -     }
  +      this.timeout = TimeoutFactory.createTimeout(start+timeout, this);
  +   }
   
  -   public TxCapsule(TxManager tm)
  +   /**
  +    *  Create a new TxCapsule.
  +    *
  +    *  @param tm The transaction manager for this transaction.
  +    */
  +   private TxCapsule(TxManager tm)
      {
  -      status = Status.STATUS_NO_TRANSACTION;
  -       hashCode = getNextId();
  +      int hashCode = getNextId();
         xid = new XidImpl((getHostName()+"/"+hashCode).getBytes(), null);
         transaction = new TransactionImpl(tm, hashCode, xid);
         this.tm = tm;
      }
    
      // Public --------------------------------------------------------
  -   public void commit()
  -            throws RollbackException,
  -                   HeuristicMixedException,
  -                   HeuristicRollbackException,
  -                   java.lang.SecurityException,
  -                   java.lang.IllegalStateException,
  -                   SystemException
  -   {
  -      if (status == Status.STATUS_NO_TRANSACTION)
  -         throw new IllegalStateException("No transaction started");
  -      // Call Synchronization
  -      for (int i = 0; i < sync.size(); i++)
  -      {
  -         // Check rollback
  -         if (status == Status.STATUS_MARKED_ROLLBACK)
  -         {
  +
  +   /**
  +    *  Called when our timeout expires.
  +    */
  +   public void timedOut(Timeout timeout)
  +   {
  +      try {
  +         lock();
  +
  +         Logger.warning("Transaction " + toString() + " timed out.");
  +
  +         if (this.timeout == null)
  +            return; // Don't race with timeout cancellation.
  +         this.timeout = null;
  +
  +         switch (status) {
  +         case Status.STATUS_ROLLEDBACK:
  +         case Status.STATUS_COMMITTED:
  +         case Status.STATUS_NO_TRANSACTION:
  +            return; // Transaction done.
  +
  +         case Status.STATUS_ROLLING_BACK:
  +            return; // Will be done shortly.
  +
  +         case Status.STATUS_COMMITTING:
  +            // This is _very_ bad:
  +            // 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.
  +            suspendedResourcesDone();
  +            rollbackResources();
  +            doAfterCompletion();
  +            gotHeuristic(null, XAException.XA_HEURMIX);
  +            return;
  +
  +         case Status.STATUS_PREPARED:
  +            // This is bad:
  +            // We are done with the first phase, and are persistifying
  +            // our decision. Fortunately this case is currently never
  +            // hit, as we do not release the lock between the two phases.
  +         case Status.STATUS_ACTIVE:
  +            status = Status.STATUS_MARKED_ROLLBACK;
  +            // fall through..
  +         case Status.STATUS_MARKED_ROLLBACK:
  +            suspendedResourcesDone();
  +            rollbackResources();
  +            doAfterCompletion();
  +            gotHeuristic(null, XAException.XA_HEURRB);
  +            return;
  +
  +         case Status.STATUS_PREPARING:
  +            status = Status.STATUS_MARKED_ROLLBACK;
  +            return; // commit will fail
  +
  +         default:
  +            Logger.warning("TxCapsule: Unknown status at timeout.");
  +            return;
  +         }
  +      } finally {
  +        unlock();
  +      }
  +   }
  +
  +   public String toString()
  +   {
  +      return xid.toString();
  +   }
  +
  +   // Package protected ---------------------------------------------
  +
  +   /**
  +    *  Return the transaction encapsulated here.
  +    */    
  +   Transaction getTransaction() {
  +      return transaction;
  +   }
  +
  +   /**
  +    *  Commit the transaction encapsulated here.
  +    *  Should not be called directly, use <code>TxManager.commit()</code>
  +    *  instead.
  +    */
  +   void commit()
  +      throws RollbackException,
  +             HeuristicMixedException,
  +             HeuristicRollbackException,
  +             java.lang.SecurityException,
  +             java.lang.IllegalStateException,
  +             SystemException
  +   {
  +      try {
  +         lock();
  +
  +         switch (status) {
  +         case Status.STATUS_PREPARING:
  +            throw new IllegalStateException("Already started preparing.");
  +         case Status.STATUS_PREPARED:
  +            throw new IllegalStateException("Already prepared.");
  +         case Status.STATUS_ROLLING_BACK:
  +            throw new IllegalStateException("Already started rolling back.");
  +         case Status.STATUS_ROLLEDBACK:
  +            checkHeuristics();
  +            throw new IllegalStateException("Already rolled back.");
  +         case Status.STATUS_COMMITTING:
  +            throw new IllegalStateException("Already started committing.");
  +         case Status.STATUS_COMMITTED:
  +            checkHeuristics();
  +            throw new IllegalStateException("Already committed.");
  +         case Status.STATUS_NO_TRANSACTION:
  +            throw new IllegalStateException("No transaction.");
  +         case Status.STATUS_UNKNOWN:
  +            throw new IllegalStateException("Unknown state");
  +         case Status.STATUS_MARKED_ROLLBACK:
  +            suspendedResourcesDone();
  +            rollbackResources();
  +            doAfterCompletion();
  +            throw new RollbackException("Already marked for rollback");
  +         case Status.STATUS_ACTIVE:
               break;
  +         default:
  +            throw new IllegalStateException("Illegal status: " + status);
            }
   
  -         ((Synchronization)sync.elementAt(i)).beforeCompletion();
  -      }
  +         suspendedResourcesDone();
   
  -      // Check rollback
  -      if (status != Status.STATUS_MARKED_ROLLBACK)
  -      {
  -         // Prepare XAResources
  -         status = Status.STATUS_PREPARING;
  -         for (int i = 0; i < resources.size(); i++)
  -         {
  -            // Check rollback
  -            if (status == Status.STATUS_MARKED_ROLLBACK)
  -            {
  -               break;
  -            }
  +         doBeforeCompletion();
   
  -            try
  -            {
  -               ((XAResource)resources.elementAt(i)).prepare(xid);
  -            } catch (XAException e)
  -            {
  -               if(e.errorCode != XAException.XA_HEURCOM)
  -               {
  -                   Logger.exception(e);
  -                  // Rollback
  -                  setRollbackOnly();
  -                  break;
  -               }
  +         if (status == Status.STATUS_ACTIVE) {
  +            if (resources.size() == 0) {
  +               // Zero phase commit is really fast ;-)
  +               status = Status.STATUS_COMMITTED;
  +            } else if (resources.size() == 1) {
  +               // One phase commit
  +               commitResources(true);
  +            } else {
  +               // Two phase commit
  +
  +               if (!prepareResources()) {
  +                  boolean commitDecision = 
  +                          status == Status.STATUS_PREPARED &&
  +                          (heuristicCode == HEUR_NONE ||
  +                           heuristicCode == XAException.XA_HEURCOM);
  +
  +                  // TODO: Save decision to stable storage for recovery
  +                  // after system crash.
  +
  +                  if (commitDecision)
  +                     commitResources(false);
  +               } else
  +                 status = Status.STATUS_COMMITTED; // all was read-only
               }
            }
   
  -         // Check rollback
  -         if (status != Status.STATUS_MARKED_ROLLBACK)
  -         {
  -            status = Status.STATUS_PREPARED; // TODO: necessary to set?
  -
  -            // Commit XAResources
  -            status = Status.STATUS_COMMITTING;
  -            for (int i = 0; i < resources.size(); i++)
  -            {
  -
  -               try
  -               {
  -                  ((XAResource)resources.elementAt(i)).commit(xid, false);
  -               } catch (XAException e)
  -               {
  -                  try {
  -                     ((XAResource)resources.elementAt(i)).forget(xid);
  -                  } catch(XAException another) {}
  -                  Logger.exception(e);
  -                  // TODO: what to do here?
  -               }
  -            }
  -            status = Status.STATUS_COMMITTED;
  +         if (status != Status.STATUS_COMMITTED) {
  +            rollbackResources();
  +            doAfterCompletion();
  +            cancelTimeout();
  +            throw new RollbackException("Unable to commit.");
            }
  +
  +         cancelTimeout();
  +
  +         doAfterCompletion();
  +
  +         checkHeuristics();
  +      } finally {
  +        unlock();
         }
  +   }
   
  -      // Check rollback
  -      if (status == Status.STATUS_MARKED_ROLLBACK)
  -      {
  -         // Rollback XAResources
  -         status = Status.STATUS_ROLLING_BACK;
  -         for (int i = 0; i < resources.size(); i++)
  -         {
  -
  -            try
  -            {
  -               ((XAResource)resources.elementAt(i)).rollback(xid);
  -            } catch (XAException e)
  -            {
  -               try {
  -                  ((XAResource)resources.elementAt(i)).forget(xid);
  -               } catch(XAException another) {}
  -               // TODO: what to do here?
  -            }
  +   /**
  +    *  Rollback the transaction encapsulated here.
  +    *  Should not be called directly, use <code>TxManager.rollback()</code>
  +    *  instead.
  +    */
  +   void rollback()
  +      throws java.lang.IllegalStateException,
  +             java.lang.SecurityException,
  +             SystemException
  +   {
  +      try {
  +         lock();
  +
  +         switch (status) {
  +         case Status.STATUS_ACTIVE:
  +         case Status.STATUS_MARKED_ROLLBACK:
  +            suspendedResourcesDone();
  +            rollbackResources();
  +            cancelTimeout();
  +            doAfterCompletion();
  +            // Cannot throw heuristic exception, so we just have to
  +            // clear the heuristics without reporting.
  +            heuristicCode = HEUR_NONE;
  +            return;
  +         case Status.STATUS_PREPARING:
  +            // Set status to avoid race with prepareResources().
  +            status = Status.STATUS_MARKED_ROLLBACK;
  +            return; // commit() will do rollback.
  +         default:
  +            throw new IllegalStateException("Cannot rollback()");
            }
  -         status = Status.STATUS_ROLLEDBACK;
  +      } finally {
  +         unlock();
         }
  +   }
   
  -      // Call Synchronization
  -      for (int i = 0; i < sync.size(); i++)
  -      {
  -         ((Synchronization)sync.elementAt(i)).afterCompletion(status);
  +   /**
  +    *  Mark the transaction encapsulated here so that the only possible
  +    *  outcome is a rollback.
  +    *  Should not be called directly, use <code>TxManager.rollback()</code>
  +    *  instead.
  +    */
  +   void setRollbackOnly()
  +      throws java.lang.IllegalStateException,
  +             SystemException
  +   {
  +      try {
  +         lock();
  +
  +         switch (status) {
  +         case Status.STATUS_ACTIVE:
  +         case Status.STATUS_PREPARING:
  +         case Status.STATUS_PREPARED:
  +            status = Status.STATUS_MARKED_ROLLBACK;
  +            // fall through..
  +         case Status.STATUS_MARKED_ROLLBACK:
  +         case Status.STATUS_ROLLING_BACK:
  +            return;
  +         case Status.STATUS_COMMITTING:
  +            throw new IllegalStateException("Already started committing.");
  +         case Status.STATUS_COMMITTED:
  +            throw new IllegalStateException("Already committed.");
  +         case Status.STATUS_ROLLEDBACK:
  +            throw new IllegalStateException("Already rolled back.");
  +         case Status.STATUS_NO_TRANSACTION:
  +            throw new IllegalStateException("No transaction.");
  +         case Status.STATUS_UNKNOWN:
  +            throw new IllegalStateException("Unknown state");
  +         default:
  +            throw new IllegalStateException("Illegal status: " + status);
  +         }
  +      } finally {
  +         unlock();
         }
  -
      }
   
  -   public boolean delistResource(XAResource xaRes, int flag)
  -   {
  -        try {
  -            xaRes.end(xid, Status.STATUS_ACTIVE);
  -//            resources.removeElement(xaRes);
  +   /**
  +    *  Delist a resource from the transaction encapsulated here.
  +    *
  +    *  @param xaRes The resource to delist.
  +    *  @param flag One of <code>XAResource.TMSUCCESS</code>,
  +    *             <code>XAResource.TMSUSPEND</code>
  +    *             or <code>XAResource.TMFAIL</code>.
  +    *
  +    *  @returns True iff the resource was successfully delisted.
  +    */
  +   boolean delistResource(XAResource xaRes, int flag)
  +      throws java.lang.IllegalStateException,
  +             SystemException
  +   {
  +      if (xaRes == null)
  +         throw new IllegalArgumentException("null xaRes");
  +      if (flag != XAResource.TMSUCCESS &&
  +          flag != XAResource.TMSUSPEND &&
  +          flag != XAResource.TMFAIL)
  +         throw new IllegalArgumentException("Bad flag: " + flag);
  +
  +      try {
  +         lock();
  +
  +         if (!resources.contains(xaRes))
  +           throw new IllegalArgumentException("xaRes not enlisted");
  +
  +         switch (status) {
  +         case Status.STATUS_ACTIVE:
  +         case Status.STATUS_MARKED_ROLLBACK:
  +            break;
  +         case Status.STATUS_PREPARING:
  +            throw new IllegalStateException("Already started preparing.");
  +         case Status.STATUS_ROLLING_BACK:
  +            throw new IllegalStateException("Already started rolling back.");
  +         case Status.STATUS_PREPARED:
  +            throw new IllegalStateException("Already prepared.");
  +         case Status.STATUS_COMMITTING:
  +            throw new IllegalStateException("Already started committing.");
  +         case Status.STATUS_COMMITTED:
  +            throw new IllegalStateException("Already committed.");
  +         case Status.STATUS_ROLLEDBACK:
  +            throw new IllegalStateException("Already rolled back.");
  +         case Status.STATUS_NO_TRANSACTION:
  +            throw new IllegalStateException("No transaction.");
  +         case Status.STATUS_UNKNOWN:
  +            throw new IllegalStateException("Unknown state");
  +         default:
  +            throw new IllegalStateException("Illegal status: " + status);
  +         }
  +
  +         try {
  +            endResource(xaRes, flag);
  +            if (flag == XAResource.TMSUSPEND)
  +               suspendedResources.add(xaRes);
  +            else if (flag == XAResource.TMFAIL)
  +               status = Status.STATUS_MARKED_ROLLBACK;
               return true;
  -        } catch(XAException e) {
  +         } catch(XAException e) {
               Logger.exception(e);
  +            status = Status.STATUS_MARKED_ROLLBACK;
               return false;
  -        }
  +         }
  +      } finally {
  +         unlock();
  +      }
      }
   
  -   public boolean enlistResource(XAResource xaRes)
  -      throws RollbackException
  -   {
  -      // Check rollback only
  -      if (status == Status.STATUS_MARKED_ROLLBACK)
  -      {
  -         throw new RollbackException();
  -      }
  +   /**
  +    *  Enlist a resource with the transaction encapsulated here.
  +    *
  +    *  @param xaRes The resource to enlist.
  +    *
  +    *  @returns True iff the resource was successfully enlisted.
  +    */
  +   boolean enlistResource(XAResource xaRes)
  +      throws RollbackException,
  +             java.lang.IllegalStateException,
  +             SystemException
  +   {
  +      if (xaRes == null)
  +         throw new IllegalArgumentException("null xaRes");
  +
  +      try {
  +         lock();
  +
  +         switch (status) {
  +         case Status.STATUS_ACTIVE:
  +         case Status.STATUS_PREPARING:
  +            break;
  +         case Status.STATUS_PREPARED:
  +            throw new IllegalStateException("Already prepared.");
  +         case Status.STATUS_COMMITTING:
  +            throw new IllegalStateException("Already started committing.");
  +         case Status.STATUS_COMMITTED:
  +            throw new IllegalStateException("Already committed.");
  +         case Status.STATUS_MARKED_ROLLBACK:
  +            throw new RollbackException("Already marked for rollback");
  +         case Status.STATUS_ROLLING_BACK:
  +            throw new RollbackException("Already started rolling back.");
  +         case Status.STATUS_ROLLEDBACK:
  +            throw new RollbackException("Already rolled back.");
  +         case Status.STATUS_NO_TRANSACTION:
  +            throw new IllegalStateException("No transaction.");
  +         case Status.STATUS_UNKNOWN:
  +            throw new IllegalStateException("Unknown state");
  +         default:
  +            throw new IllegalStateException("Illegal status: " + status);
  +         }
   
  -      // Add resource
  -        try {
  -            xaRes.start(xid, Status.STATUS_ACTIVE);
  -            resources.addElement(xaRes);
  +         // Add resource
  +         try {
  +            if (suspendedResources.contains(xaRes)) {
  +               startResource(xaRes, XAResource.TMRESUME);
  +               suspendedResources.remove(xaRes);
  +               return true;
  +            }
  +            for (int i = 0; i < resources.size(); ++i) {
  +               if (xaRes.isSameRM((XAResource)resources.get(i))) {
  +                  startResource(xaRes, XAResource.TMJOIN);
  +                  resources.add(xaRes);
  +                  return true;
  +               }
  +            }
  +            // New resource
  +            // According to the JTA spec we should create a new
  +            // transaction branch here.
  +            startResource(xaRes, XAResource.TMNOFLAGS);
  +            resources.add(xaRes);
               return true;
  -        } catch(XAException e) {
  +         } catch(XAException e) {
               Logger.exception(e);
               return false;
  -        }
  +         }
  +      } finally {
  +         unlock();
  +      }
      }
   
  -   public int getStatus()
  -              throws SystemException
  +   /**
  +    *  Return the status of the transaction encapsulated here.
  +    */
  +   int getStatus()
  +      throws SystemException
      {
         return status;
      }
  -
  -   public void registerSynchronization(Synchronization s)
  -   {
  -      sync.addElement(s);
  -   }
  -
  -   public void rollback()
  -              throws java.lang.IllegalStateException,
  -                     java.lang.SecurityException,
  -                     SystemException
  -   {
  -      if (status == Status.STATUS_NO_TRANSACTION)
  -         throw new IllegalStateException("No transaction started");
  -              
  -              //MF FIXME I don't get it what is the use of this call if the 
"rollback is done in the commit
  -   }
   
  -   public void setRollbackOnly()
  -                     throws java.lang.IllegalStateException,
  -                            SystemException
  -   {
  -      if (status == Status.STATUS_NO_TRANSACTION)
  -         throw new IllegalStateException("No transaction started");
  +   /**
  +    *  Register a new transaction synchronization for the transaction
  +    *  encapsulated here.
  +    */
  +   void registerSynchronization(Synchronization s)
  +      throws RollbackException,
  +             java.lang.IllegalStateException,
  +             SystemException
  +   {
  +      if (s == null)
  +         throw new IllegalArgumentException("Null synchronization");
  +
  +      try {
  +         lock();
  +
  +         switch (status) {
  +         case Status.STATUS_ACTIVE:
  +         case Status.STATUS_PREPARING:
  +            break;
  +         case Status.STATUS_PREPARED:
  +            throw new IllegalStateException("Already prepared.");
  +         case Status.STATUS_COMMITTING:
  +            throw new IllegalStateException("Already started committing.");
  +         case Status.STATUS_COMMITTED:
  +            throw new IllegalStateException("Already committed.");
  +         case Status.STATUS_MARKED_ROLLBACK:
  +            //throw new RollbackException("Already marked for rollback");
  +            // 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:
  +            throw new RollbackException("Already rolled back.");
  +         case Status.STATUS_NO_TRANSACTION:
  +            throw new IllegalStateException("No transaction.");
  +         case Status.STATUS_UNKNOWN:
  +            throw new IllegalStateException("Unknown state");
  +         default:
  +            throw new IllegalStateException("Illegal status: " + status);
  +         }
   
  -      status = Status.STATUS_MARKED_ROLLBACK;
  +         sync.add(s);
  +      } finally {
  +         unlock();
  +      }
      }
   
  -   // Package protected ---------------------------------------------
  -
  -     Transaction getTransaction() {
  -             
  -             return transaction;
  -     }
  -     
      // Protected -----------------------------------------------------
  +
  +   /**
  +    *  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.
  +    */
      protected String getHostName()
      {
  -      if (hostName == null)
  -      {
  -         try
  -         {
  +      if (hostName == null) {
  +         try {
               hostName = InetAddress.getLocalHost().getHostName();
  -         } catch (UnknownHostException e)
  -         {
  +         } catch (UnknownHostException e) {
               hostName = "localhost";
            }
         }
  @@ -278,16 +560,389 @@
         return hostName;
      }
   
  -    public String toString() {
  -        return xid.toString();
  -    }
  -
      // Private -------------------------------------------------------
  -/*   private Object writeReplace(java.io.ObjectOutputStream out)
  -      throws IOException
  +
  +   // 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();
  +
  +   // Suspended XAResources are in this set
  +   private Set suspendedResources = new HashSet();
  +
  +   private Xid xid; // XA legacy 
  +   private int status; // status code
  +   private int heuristicCode = HEUR_NONE; // heuristics status
  +   private long start;
  +   private Timeout timeout;
  +   
  +   // The public face of the capsule a JTA implementation
  +   private Transaction transaction;
  +
  +   // My manager
  +   private TxManager tm;
  +
  +   // Mutex for thread-safety
  +   private boolean locked = false;
  +
  +   /**
  +    *  Lock this instance.
  +    */
  +   private synchronized void lock()
  +   {
  +      if (locked) {
  +         Logger.warning("TxCapsule: Lock contention."); // Good for debugging.
  +         Thread.currentThread().dumpStack();
  +      }
  +
  +      while (locked) {
  +         try {
  +            wait();
  +         } catch (InterruptedException ex) {}
  +      }
  +
  +      locked = true;
  +   }
  +
  +   /**
  +    *  Unlock this instance.
  +    */
  +   private synchronized void unlock()
      {
  -      return new TransactionProxy(this);
  +      if (!locked)
  +         Logger.warning("TxCapsule: Unlocking, but not locked.");
  +
  +      locked = false;
  +
  +      notify();
  +   }
  +
  +   /**
  +    *  Cancel the timeout.
  +    *  This will release the lock while calling out.
  +    */
  +   private void cancelTimeout()
  +   {
  +      if (timeout != null) {
  +         unlock();
  +         try {
  +            timeout.cancel();
  +         } finally {
  +            lock();
  +         }
  +         timeout = null;
  +      }
  +   }
  +
  +   /**
  +    *  Call <code>start()</code> on the XAResource.
  +    *  This will release the lock while calling out.
  +    */
  +   private void startResource(XAResource xaRes, int flags)
  +      throws XAException
  +   {
  +      unlock();
  +      try {
  +         xaRes.start(xid, flags);
  +      } finally {
  +         lock();
  +      }
  +   }
  +
  +   /**
  +    *  Call <code>end()</code> on the XAResource.
  +    *  This will release the lock while calling out.
  +    */
  +   private void endResource(XAResource xaRes, int flag)
  +      throws XAException
  +   {
  +      unlock();
  +      try {
  +         xaRes.end(xid, flag);
  +      } finally {
  +         lock();
  +      }
  +   }
  +
  +   /**
  +    *  End Tx association for all suspended resources.
  +    */
  +   private void suspendedResourcesDone()
  +   {
  +      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) { }
  +         }
  +      } catch(XAException e) {
  +         Logger.exception(e);
  +         status = Status.STATUS_MARKED_ROLLBACK;
  +      }
  +   }
  +
  +   /**
  +    *  Call synchronization <code>beforeCompletion()</code>.
  +    *  This will release the lock while calling out.
  +    */
  +   private void doBeforeCompletion()
  +   {
  +      unlock();
  +      try {
  +         for (int i = 0; i < sync.size(); i++)
  +            ((Synchronization)sync.get(i)).beforeCompletion();
  +      } finally {
  +         lock();
  +      }
  +   }
  +
  +   /**
  +    *  Call synchronization <code>afterCompletion()</code>.
  +    *  This will release the lock while calling out.
  +    */
  +   private void doAfterCompletion()
  +   {
  +      // 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);
  +      } finally {
  +         lock();
  +      }
  +   }
  +
  +   /**
  +    *  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.
  +    */
  +   private void gotHeuristic(XAResource resource, int code)
  +   {
  +      switch (code) {
  +      case XAException.XA_HEURMIX:
  +         heuristicCode = XAException.XA_HEURMIX;
  +         break;
  +      case XAException.XA_HEURRB:
  +         if (heuristicCode == HEUR_NONE)
  +            heuristicCode = XAException.XA_HEURRB;
  +         else if (heuristicCode == XAException.XA_HEURCOM ||
  +                  heuristicCode == XAException.XA_HEURHAZ)
  +            heuristicCode = XAException.XA_HEURMIX;
  +         break;
  +      case XAException.XA_HEURCOM:
  +         if (heuristicCode == HEUR_NONE)
  +            heuristicCode = XAException.XA_HEURCOM;
  +         else if (heuristicCode == XAException.XA_HEURRB ||
  +                  heuristicCode == XAException.XA_HEURHAZ)
  +            heuristicCode = XAException.XA_HEURMIX;
  +         break;
  +      case XAException.XA_HEURHAZ:
  +         if (heuristicCode == HEUR_NONE)
  +            heuristicCode = XAException.XA_HEURHAZ;
  +         else if (heuristicCode == XAException.XA_HEURCOM ||
  +                  heuristicCode == XAException.XA_HEURRB)
  +            heuristicCode = XAException.XA_HEURMIX;
  +         break;
  +      default:
  +         throw new IllegalArgumentException();
  +      }
  +
  +      if (resource != null) {
  +         try {
  +            unlock();
  +            resource.forget(xid);
  +         } catch (XAException e) {
  +            Logger.exception(e);
  +         } finally {
  +            lock();
  +         }
  +      }
  +   }
  +
  +   /**
  +    *  Check for heuristics, clear and throw exception if any found.
  +    */
  +   private void checkHeuristics()
  +     throws HeuristicMixedException,
  +            HeuristicRollbackException
  +   {
  +      switch (heuristicCode) {
  +      case XAException.XA_HEURHAZ:
  +      case XAException.XA_HEURMIX:
  +         heuristicCode = HEUR_NONE;
  +         throw new HeuristicMixedException();
  +      case XAException.XA_HEURRB:
  +         heuristicCode = HEUR_NONE;
  +         throw new HeuristicRollbackException();
  +      case XAException.XA_HEURCOM:
  +         heuristicCode = HEUR_NONE;
  +         // Why isn't HeuristicCommitException used in JTA ?
  +         // And why define something that is not used ?
  +         // For now we just have to ignore this failure, even if it happened
  +         // on rollback.
  +         return;
  +      }
      }
  -*/
  +
  +   /**
  +    *  Prepare all enlisted resources.
  +    *  If the first phase of the commit process results in a decision
  +    *  to commit the <code>status</code> will be
  +    *  <code>Status.STATUS_PREPARED</code> on return.
  +    *  Otherwise the <code>status</code> will be
  +    *  <code>Status.STATUS_MARKED_ROLLBACK</code> on return.
  +    *  This will release the lock while calling out.
  +    *
  +    *  @returns True iff all resources voted read-only.
  +    */
  +   private boolean prepareResources()
  +   {
  +      boolean readOnly = true;
  +
  +      status = Status.STATUS_PREPARING;
  +
  +      for (int i = 0; i < resources.size(); i++) {
  +         // Abort prepare on state change.
  +         if (status != Status.STATUS_PREPARING)
  +            return false;
  +   
  +         XAResource resource = (XAResource)resources.get(i);
  +
  +         try {
  +            int vote;
  +
  +            unlock();
  +            try {
  +               vote = resource.prepare(xid);
  +            } finally {
  +               lock();
  +            }
  +
  +            if (vote != XAResource.XA_RDONLY)
  +               readOnly = false;
  +            else {
  +               // Resource voted read-only: Can forget about resource.
  +               resources.remove(i);
  +               --i; // undo future increment
  +            }
  +         } catch (XAException e) {
  +            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);
  +               break;
  +            case XAException.XA_HEURRB:
  +            case XAException.XA_HEURMIX:
  +            case XAException.XA_HEURHAZ:
  +               gotHeuristic(resource, e.errorCode);
  +               if (status == Status.STATUS_PREPARING)
  +                  status = Status.STATUS_MARKED_ROLLBACK;
  +               break;
  +            default:
  +               Logger.exception(e);
  +               if (status == Status.STATUS_PREPARING)
  +                  status = Status.STATUS_MARKED_ROLLBACK;
  +               break;
  +            }
  +         }
  +      }
  +
  +      if (status == Status.STATUS_PREPARING)
  +         status = Status.STATUS_PREPARED;
  +
  +      return readOnly;
  +   }
  +
  +   /**
  +    *  Commit all enlisted resources.
  +    *  This will release the lock while calling out.
  +    */
  +   private void commitResources(boolean onePhase)
  +   {
  +      status = Status.STATUS_COMMITTING;
  +
  +      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);
  +            } finally {
  +               lock();
  +            }
  +         } catch (XAException e) {
  +            switch (e.errorCode) {
  +            case XAException.XA_HEURRB:
  +            case XAException.XA_HEURCOM:
  +            case XAException.XA_HEURMIX:
  +            case XAException.XA_HEURHAZ:
  +               gotHeuristic(resource, e.errorCode);
  +               break;
  +            default:
  +               Logger.exception(e);
  +               break;
  +            }
  +         }
  +      }
  +
  +      if (status != Status.STATUS_COMMITTING)
  +         status = Status.STATUS_COMMITTED;
  +   }
  +
  +   /**
  +    *  Rollback all enlisted resources.
  +    *  This will release the lock while calling out.
  +    */
  +   private void rollbackResources()
  +   {
  +      status = Status.STATUS_ROLLING_BACK;
  +
  +      for (int i = 0; i < resources.size(); i++) {
  +         XAResource resource = (XAResource)resources.get(i);
  +
  +         try {
  +            unlock();
  +            try {
  +               resource.rollback(xid);
  +            } finally {
  +               lock();
  +            }
  +         } catch (XAException e) {
  +            switch (e.errorCode) {
  +            case XAException.XA_HEURRB:
  +               // Heuristic rollback is not that bad when rolling back.
  +               gotHeuristic(resource, e.errorCode);
  +               break;
  +            case XAException.XA_HEURCOM:
  +            case XAException.XA_HEURMIX:
  +            case XAException.XA_HEURHAZ:
  +               gotHeuristic(resource, e.errorCode);
  +               break;
  +            default:
  +               Logger.exception(e);
  +               break;
  +            }
  +         }
  +      }
  +
  +      status = Status.STATUS_ROLLEDBACK;
  +   }
  +
      // Inner classes -------------------------------------------------
   }
  
  
  
  1.7       +19 -12    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.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- TransactionImpl.java      2000/08/11 03:00:54     1.6
  +++ TransactionImpl.java      2000/09/08 02:25:01     1.7
  @@ -33,7 +33,7 @@
    *   @see <related>
    *   @author Rickard �berg ([EMAIL PROTECTED])
    *  @author <a href="mailto:[EMAIL PROTECTED]">Marc Fleury</a>
  - *   @version $Revision: 1.6 $
  + *   @version $Revision: 1.7 $
    */
   public class TransactionImpl
      implements Transaction, Serializable
  @@ -54,8 +54,8 @@
      public TransactionImpl(TxManager tm,int hash, Xid xid)
      {
         this.tm = tm;
  -       this.hash = hash;
  -       this.xid = xid; 
  +      this.hash = hash;
  +      this.xid = xid; 
      }
      
      /*
  @@ -64,8 +64,8 @@
      * used for propagated Tx
      */
      public void setTxManager(TxManager tm) {
  -        
  -        this.tm= tm;
  +       
  +       this.tm= tm;
      }
      
      
  @@ -84,14 +84,18 @@
      }
   
      public boolean delistResource(XAResource xaRes, int flag)
  +      throws java.lang.IllegalStateException,
  +             SystemException
      {
          return tm.delistResource(this, xaRes, flag);
      }
   
      public boolean enlistResource(XAResource xaRes)
  -      throws RollbackException
  +      throws RollbackException,
  +             java.lang.IllegalStateException,
  +             SystemException
      {
  -        return tm.enlistResource(this, xaRes);
  +       return tm.enlistResource(this, xaRes);
      }
   
      public int getStatus()
  @@ -101,8 +105,11 @@
      }
   
      public void registerSynchronization(Synchronization s)
  +    throws RollbackException,
  +           java.lang.IllegalStateException,
  +           SystemException
      {
  -        tm.registerSynchronization(this, s);
  +       tm.registerSynchronization(this, s);
      }
   
      public void rollback()
  @@ -110,7 +117,7 @@
                        java.lang.SecurityException,
                        SystemException
      {
  -        tm.rollback(this);
  +       tm.rollback(this);
      }
   
      public void setRollbackOnly()
  @@ -118,12 +125,12 @@
                               SystemException
      {
        
  -             tm.setRollbackOnly(this); 
  -     }
  +       tm.setRollbackOnly(this); 
  +    }
   
      public boolean equals(Object obj)
      {
  -       return ((TransactionImpl)obj).hash == hash;
  +      return ((TransactionImpl)obj).hash == hash;
      }
   
      public int hashCode()
  
  
  

Reply via email to