/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.mq.pm;

import javax.jms.JMSException;

import org.jboss.mq.ConnectionToken;
import org.jboss.mq.SpyJMSException;

import EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap;

/**
 * This class allows provides the base for user supplied persistence packages.
 * 
 * @author Hiram Chirino (Cojonudo14@hotmail.com)
 * @author Paul Kendall (paul.kendall@orion.co.nz)
 * @author <a href="mailto:adrian@jboss.org">Adrian Brock</a>
 * @version $Revision$
 */
public class TxManager
{
   // Constants -----------------------------------------------------
   
   // Attributes ----------------------------------------------------

   /** The persistence manager */
   PersistenceManager persistenceManager;
   
   /** Maps Global transactions to local transactions */
   ConcurrentHashMap globalToLocal = new ConcurrentHashMap();
   
   /**
    * Create a new TxManager
    *
    * @param pm the persistence manager
    */
   // Static --------------------------------------------------------
   
   // Constructors --------------------------------------------------

   public TxManager(PersistenceManager pm)
   {
      persistenceManager = pm;
   }
   
   // Public --------------------------------------------------------

   /**
	 * Return the local transaction id for a distributed transaction id.
	 * 
	 * @param dc the connection
	 * @param xid the transaction id
	 * @return The Prepared transaction
	 * @exception javax.jms.JMSException Description of Exception
	 */
   public final Tx getPrepared(ConnectionToken dc, Object xid) throws JMSException
   {
      GlobalXID gxid = new GlobalXID(dc, xid);
      Tx txid = (Tx) globalToLocal.get(gxid);
      if (txid == null)
         throw new SpyJMSException("Transaction does not exist from: " + dc.getClientID() + " xid=" + xid);

      return txid;
   }

   /**
	 * Create and return a unique transaction id.
	 * 
	 * @return the transaction id
	 * @exception JMSException for any error
	 */
   public final Tx createTx() throws JMSException
   {
      Tx txId = persistenceManager.createPersistentTx();
      return txId;
   }

   /**
	 * Commit the transaction to the persistent store.
	 * 
	 * @param txId the transaction
	 * @exception JMSException for any error
	 */
   public final void commitTx(Tx txId) throws JMSException
   {
      txId.commit(persistenceManager);
   }

   /**
    * Add an operation for after a commit
    *
    * @param txId the transaction
    * @param task the task
    * @throws JMSException for any error
    */
   public void addPostCommitTask(Tx txId, Runnable task) throws JMSException
   {
      if (txId == null)
      {
         task.run();
         return;
      }

      txId.addPostCommitTask(task);
   }

   /**
	 * Rollback the transaction.
	 * 
	 * @param txId the transaction
	 * @exception JMSException for any error
	 */
   public void rollbackTx(Tx txId) throws JMSException
   {
      txId.rollback(persistenceManager);
   }

   /**
    * Add an operation for after a rollback
    *
    * @param txId the transaction
    * @param task the task
    * @throws JMSException for any error
    */
   public void addPostRollbackTask(Tx txId, Runnable task) throws JMSException
   {
      if (txId == null)
         return;

      txId.addPostRollbackTask(task);
   }

   /**
	 * Create and return a unique transaction id. Given a distributed connection
	 * and a transaction id object, allocate a unique local transaction id if
	 * the remote id is not already known.
	 * 
	 * @param dc the connection token
	 * @param xid the xid
	 * @return the transaction
	 * @exception JMSException for any error
	 */
   public Tx createTx(ConnectionToken dc, Object xid) throws JMSException
   {

      GlobalXID gxid = new GlobalXID(dc, xid);
      if (globalToLocal.containsKey(gxid))
         throw new SpyJMSException("Duplicate transaction from: " + dc.getClientID() + " xid=" + xid);

      Tx txId = createTx();
      globalToLocal.put(gxid, txId);

      //Tasks to remove the global to local mappings on commit/rollback
      txId.addPostCommitTask(gxid);
      txId.addPostRollbackTask(gxid);

      return txId;
   }
   
   // Package protected ---------------------------------------------
   
   // Protected -----------------------------------------------------
   
   // Private -------------------------------------------------------
   
   // Inner classes -------------------------------------------------
   
   /**
	 * A global transaction
	 */
   class GlobalXID implements Runnable
   {
      ConnectionToken dc;
      Object xid;

      GlobalXID(ConnectionToken dc, Object xid)
      {
         this.dc = dc;
         this.xid = xid;
      }

      public boolean equals(Object obj)
      {
         if (obj == null)
         {
            return false;
         }
         if (obj.getClass() != GlobalXID.class)
         {
            return false;
         }
         return ((GlobalXID) obj).xid.equals(xid) && ((GlobalXID) obj).dc.equals(dc);
      }

      public int hashCode()
      {
         return xid.hashCode();
      }

      public void run()
      {
         globalToLocal.remove(this);
      }
   }
}