package org.jboss.cache.transaction;

import org.jboss.logging.Logger;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.transaction.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * @author bela
 * @version $Revision: 1.10.2.3 $
 *          Date: May 15, 2003
 *          Time: 4:11:37 PM
 */
public class DummyTransactionManager implements TransactionManager, java.io.Serializable {
   static DummyTransactionManager instance=null;
   static Logger log=Logger.getLogger(DummyTransactionManager.class);

   static ThreadLocal thread_local=new ThreadLocal() {
      protected synchronized Object initialValue() {
         Map map=new HashMap();
         return java.util.Collections.synchronizedMap(map);
      }
   };

   public DummyTransactionManager() {
      ;
   }

   public static DummyTransactionManager getInstance() {
      if(instance == null) {
         instance=new DummyTransactionManager();
         try {
            Properties p=new Properties();
            p.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.cache.transaction.DummyContextFactory");
            Context ctx=new InitialContext(p);
            ctx.bind("java:/TransactionManager", instance);
            ctx.bind("UserTransaction", new DummyUserTransaction(instance));
         }
         catch(NamingException e) {
            log.error("binding of DummyTransactionManager failed", e);
         }
      }
      return instance;
   }

   public static void destroy() {
      if(instance == null) return;
      try {
         Properties p=new Properties();
         p.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.cache.transaction.DummyContextFactory");
         Context ctx=new InitialContext(p);
         ctx.unbind("java:/TransactionManager");
         ctx.unbind("UserTransaction");
      }
      catch(NamingException e) {
         log.error("unbinding of DummyTransactionManager failed", e);
      }

      instance.setTransaction(null);
      instance=null;
   }

   /**
    * Starts a new transaction, and associate it with the calling thread.
    *
    * @throws NotSupportedException If the calling thread is already
    *                               associated with a transaction, and nested transactions are
    *                               not supported.
    * @throws SystemException       If the transaction service fails in an
    *                               unexpected way.
    */
   public void begin() throws NotSupportedException, SystemException {
      if(getTransaction() != null)
         throw new NotSupportedException("thread " + Thread.currentThread() +
                 " is already associated with a transaction");
      DummyTransaction tx=new DummyTransaction(this);
      setTransaction(tx);
   }

   /**
    * Commit the transaction associated with the calling thread.
    *
    * @throws RollbackException          If the transaction was marked for rollback
    *                                    only, the transaction is rolled back and this exception is
    *                                    thrown.
    * @throws IllegalStateException      If the calling thread is not associated
    *                                    with a transaction.
    * @throws SystemException            If the transaction service fails in an
    *                                    unexpected way.
    * @throws HeuristicMixedException    If a heuristic decision was made and
    *                                    some some parts of the transaction have been committed while
    *                                    other parts have been rolled back.
    * @throws HeuristicRollbackException If a heuristic decision to roll
    *                                    back the transaction was made.
    * @throws SecurityException          If the caller is not allowed to commit this
    *                                    transaction.
    */
   public void commit() throws RollbackException, HeuristicMixedException,
           HeuristicRollbackException, SecurityException,
           IllegalStateException, SystemException {
      int status;
      Transaction tx=getTransaction();
      if(tx == null)
         throw new IllegalStateException("thread not associated with transaction");
      status=tx.getStatus();
      if(status == Status.STATUS_MARKED_ROLLBACK)
         throw new RollbackException();
      tx.commit();

      // Disassociate tx from thread.
      setTransaction(null);
   }

   /**
    * Rolls back the transaction associated with the calling thread.
    *
    * @throws IllegalStateException If the transaction is in a state
    *                               where it cannot be rolled back. This could be because the
    *                               calling thread is not associated with a transaction, or
    *                               because it is in the
    *                               {@link Status#STATUS_PREPARED prepared state}.
    * @throws SecurityException     If the caller is not allowed to roll back
    *                               this transaction.
    * @throws SystemException       If the transaction service fails in an
    *                               unexpected way.
    */
   public void rollback() throws IllegalStateException, SecurityException,
           SystemException {
      Transaction tx=getTransaction();
      if(tx == null)
         throw new IllegalStateException("no transaction associated with thread");
      tx.rollback();

      // Disassociate tx from thread.
      setTransaction(null);
   }

   /**
    * Mark the transaction associated with the calling thread for rollback
    * only.
    *
    * @throws IllegalStateException If the transaction is in a state
    *                               where it cannot be rolled back. This could be because the
    *                               calling thread is not associated with a transaction, or
    *                               because it is in the
    *                               {@link Status#STATUS_PREPARED prepared state}.
    * @throws SystemException       If the transaction service fails in an
    *                               unexpected way.
    */
   public void setRollbackOnly() throws IllegalStateException, SystemException {
      Transaction tx=getTransaction();
      if(tx == null)
         throw new IllegalStateException("no transaction associated with calling thread");
      tx.setRollbackOnly();
   }

   /**
    * Get the status of the transaction associated with the calling thread.
    *
    * @return The status of the transaction. This is one of the
    *         {@link Status} constants. If no transaction is associated
    *         with the calling thread,
    *         {@link Status#STATUS_NO_TRANSACTION} is returned.
    * @throws SystemException If the transaction service fails in an
    *                         unexpected way.
    */
   public int getStatus() throws SystemException {
      Transaction tx=getTransaction();
      return tx != null ? tx.getStatus() : Status.STATUS_NO_TRANSACTION;
   }

   /**
    * Get the transaction associated with the calling thread.
    *
    * @return The transaction associated with the calling thread, or
    *         <code>null</code> if the calling thread is not associated
    *         with a transaction.
    * @throws SystemException If the transaction service fails in an
    *                         unexpected way.
    */
   public Transaction getTransaction() throws SystemException {
      Map map=(Map)thread_local.get();
      String thread=Thread.currentThread().toString();
      return (Transaction)map.get(thread);
   }

   /**
    * Change the transaction timeout for transactions started by the calling
    * thread with the {@link #begin()} method.
    *
    * @param seconds The new timeout value, in seconds. If this parameter
    *                is <code>0</code>, the timeout value is reset to the default
    *                value.
    * @throws SystemException If the transaction service fails in an
    *                         unexpected way.
    */
   public void setTransactionTimeout(int seconds) throws SystemException {
      throw new SystemException("not supported");
   }

   /**
    * Suspend the association the calling thread has to a transaction,
    * and return the suspended transaction.
    * When returning from this method, the calling thread is no longer
    * associated with a transaction.
    *
    * @return The transaction that the calling thread was associated with,
    *         or <code>null</code> if the calling thread was not associated
    *         with a transaction.
    * @throws SystemException If the transaction service fails in an
    *                         unexpected way.
    */
   public Transaction suspend() throws SystemException {
      throw new SystemException("not supported");
   }

   /**
    * Resume the association of the calling thread with the given
    * transaction.
    *
    * @param tobj The transaction to be associated with the calling thread.
    * @throws InvalidTransactionException If the argument does not represent
    *                                     a valid transaction.
    * @throws IllegalStateException       If the calling thread is already
    *                                     associated with a transaction.
    * @throws SystemException             If the transaction service fails in an
    *                                     unexpected way.
    */
   public void resume(Transaction tobj) throws InvalidTransactionException,
           IllegalStateException, SystemException {
      throw new SystemException("not supported");
   }

   void setTransaction(Transaction tx) {
      Map map=(Map)thread_local.get();
      String thread=Thread.currentThread().toString();
//        map.put(this, tx);
      if(tx == null) {
         map.remove(thread);
      }
      else {
         map.put(thread, tx);
      }
   }

}
