/*
* JBoss, the OpenSource J2EE webOS
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.mq.pm;

import java.io.Externalizable;
import java.util.ArrayList;

import javax.jms.JMSException;

/**
 * A transaction
 *
 * @author <a href="mailto:adrian@jboss.org">Adrian Brock</a>
 * @version $Revision: 1.4.2.2 $
 */
public class Tx implements Comparable, Externalizable
{
   // Constants -----------------------------------------------------

   /** The serialVersionUID */
   static final long serialVersionUID = -5428592493990463362L;  

   /** Transaction open */
   private static final int OPEN = 0;
   /** Transaction ended */
   private static final int ENDED = 2;
   
   // Attributes ----------------------------------------------------

   /** The transaction value */
   long value = 0;
   
   /** Whether the transaction has been persisted */
   boolean persisted = false;

   /** List of Runnable tasks */
   transient ArrayList postCommitTasks;
   /** List of Runnable tasks */
   transient ArrayList postRollbackTasks;

   /** The status */
   transient int status = OPEN;

   /** The lock */
   transient Object lock = new Object();
   
   // Static --------------------------------------------------------
   
   // Constructors --------------------------------------------------
   
   /**
    * Create a new Tx for externailzation
    */
   public Tx()
   {
   }
   
   /**
    * Create a new Tx
    * 
    * @param value the value
    */
   public Tx(long value)
   {
      this.value = value;
   }
   
   // Public --------------------------------------------------------
   
   /**
    * Set the value
    *
    * @param tx the new value
    */
   public void setValue(long tx)
   {
      value = tx;
   }

   /**
    * Get the long value
    *
    * @return the long value
    */
   public long longValue()
   {
      return value;
   }

   /**
    * Get whether the transaction has been persisted
    *
    * @return true when persisted
    */
   public synchronized boolean checkPersisted()
   {
      boolean result = persisted;
      persisted = true;
      return result;
   }

   /**
    * Get whether the transaction has been persisted
    *
    * @return true when persisted
    */
   public synchronized boolean wasPersisted()
   {
      return persisted;
   }

   /**
    * Compare
    *
    * @param anotherLong the other value
    * @return -1, 0, 1 if less than, equal or greater than respectively
    */
   public int compareTo(Tx anotherLong)
   {
      long thisVal = this.value;
      long anotherVal = anotherLong.value;
      return (thisVal < anotherVal ? -1 : (thisVal == anotherVal ? 0 : 1));
   }
   
   // Comparable implementation -------------------------------------
   
   public int compareTo(Object o)
   {
      return compareTo((Tx) o);
   }
   
   // Externalizable implementation ---------------------------------
   
   public void readExternal(java.io.ObjectInput in) throws java.io.IOException
   {
      value = in.readLong();
   }

   public void writeExternal(java.io.ObjectOutput out) throws java.io.IOException
   {
      out.writeLong(value);
   }
   
   // Object overrides ----------------------------------------------
   
   public String toString()
   {
      return String.valueOf(value);
   }
   
   public int hashCode()
   {
      return (int) (value ^ (value >> 32));
   }
   
   // Package protected ---------------------------------------------

   /**
    * Commit the transaction
    * 
    * @param pm the persistence manager
    * @throws JMSExecption for any error
    */
   void commit(PersistenceManager pm) throws JMSException
   {
      synchronized (lock)
      {
         if (status == ENDED)
            throw new JMSException("Transaction is not active for commit");
         status = ENDED;
         postRollbackTasks = null;
      }
      
      pm.commitPersistentTx(this);
      
      synchronized (lock)
      {
         if (postCommitTasks == null)
            return;
         
         for (int i = 0; i < postCommitTasks.size(); ++i)
         {
            Runnable task = (Runnable) postCommitTasks.get(i);
            task.run();
         }
         
         postCommitTasks = null;
      }
   }

   /**
    * Add post commit task
    * 
    * @param task the task
    * @throws JMSExecption for any error
    */
   void addPostCommitTask(Runnable task) throws JMSException
   {
      synchronized (lock)
      {
         if (status == ENDED)
            throw new JMSException("Transacation is not active.");
         if (postCommitTasks == null)
            postCommitTasks = new ArrayList();
         postCommitTasks.add(task);
      }
   }

   /**
    * Commit the transaction
    * 
    * @param pm the persistence manager
    * @throws JMSExecption for any error
    */
   public void rollback(PersistenceManager pm) throws JMSException
   {
      synchronized (lock)
      {
         if (status == ENDED)
            throw new JMSException("Transaction is not active for rollback");
         status = ENDED;
         postCommitTasks = null;
      }
      
      pm.rollbackPersistentTx(this);
      
      synchronized (lock)
      {
         if (postRollbackTasks == null)
            return;

         for (int i = 0; i < postRollbackTasks.size(); ++i)
         {
            Runnable task = (Runnable) postRollbackTasks.get(i);
            task.run();
         }
         
         postRollbackTasks = null;
      }
   }

   /**
    * Add post rollback task
    * 
    * @param task the task
    * @throws JMSExecption for any error
    */
   public void addPostRollbackTask(Runnable task) throws JMSException
   {
      synchronized (lock)
      {
         if (status == ENDED)
            throw new JMSException("Transacation is not active.");
         if (postRollbackTasks == null)
               postRollbackTasks = new ArrayList();
         postRollbackTasks.add(task);
      }
   }
   
   // Protected -----------------------------------------------------
   
   // Private -------------------------------------------------------
   
   // Inner classes -------------------------------------------------
}