Save This Page
Home » jboss-5.0.0.CR1-src » org » jboss » tm » [javadoc | source]
    1   /*
    2    * JBoss, the OpenSource J2EE webOS
    3    *
    4    * Distributable under LGPL license.
    5    * See terms of license at gnu.org.
    6    */
    7   package org.jboss.tm;
    8   
    9   import java.util.Collections;
   10   import java.util.HashMap;
   11   import java.util.Map;
   12   
   13   import javax.transaction.Status;
   14   import javax.transaction.TransactionManager;
   15   import javax.transaction.Transaction;
   16   import javax.transaction.NotSupportedException;
   17   import javax.transaction.SystemException;
   18   import javax.transaction.RollbackException;
   19   import javax.transaction.HeuristicMixedException;
   20   import javax.transaction.HeuristicRollbackException;
   21   import javax.transaction.InvalidTransactionException;
   22   
   23   import org.jboss.logging.Logger;
   24   
   25   /**
   26    * Our TransactionManager implementation.
   27    *
   28    * @author <a href="mailto:rickard.oberg@telkel.com">Rickard Öberg</a>
   29    * @author <a href="mailto:marc.fleury@telkel.com">Marc Fleury</a>
   30    * @author <a href="mailto:osh@sparre.dk">Ole Husgaard</a>
   31    * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
   32    * @version $Revision: 1.3.2.9 $
   33    */
   34   public class TxManager
   35         implements TransactionManager,
   36         TransactionPropagationContextImporter,
   37         TransactionPropagationContextFactory,
   38         TransactionLocalDelegate
   39   {
   40      // Constants -----------------------------------------------------
   41   
   42      // Attributes ----------------------------------------------------
   43   
   44      /** Instance logger. */
   45      private Logger log = Logger.getLogger(this.getClass());
   46   
   47      /** True if trace messages should be logged. */
   48      private boolean trace = log.isTraceEnabled();
   49   
   50      /**
   51       *  Default timeout in milliseconds.
   52       *  Must be >= 1000!
   53       */
   54      private long timeOut = 5 * 60 * 1000;
   55   
   56      // The following two fields are ints (not longs) because
   57      // volatile 64Bit types are broken (i.e. access is not atomic) in most VMs, and we
   58      // don't want to lock just for a statistic. Additionaly,
   59      // it will take several years on a highly loaded system to
   60      // exceed the int range. Note that we might loose an
   61      // increment every now and then, since the ++ operation is
   62      // not atomic on volatile data types.
   63      /** A count of the transactions that have been committed */
   64      private volatile int commitCount;
   65      /** A count of the transactions that have been rolled back */
   66      private volatile int rollbackCount;
   67   
   68      // Static --------------------------------------------------------
   69   
   70      /**
   71       *  The singleton instance.
   72       */
   73      private static TxManager singleton = new TxManager();
   74   
   75      /**
   76       *  Get a reference to the singleton instance.
   77       */
   78      public static TxManager getInstance()
   79      {
   80         return singleton;
   81      }
   82   
   83      // Constructors --------------------------------------------------
   84   
   85      /**
   86       *  Private constructor for singleton. Use getInstance() to obtain
   87       *  a reference to the singleton.
   88       */
   89      private TxManager()
   90      {
   91         //make sure TxCapsule can be used
   92         TransactionImpl.defaultXidFactory();
   93      }
   94   
   95      // Public --------------------------------------------------------
   96   
   97      /**
   98       *  Begin a new transaction.
   99       *  The new transaction will be associated with the calling thread.
  100       */
  101      public void begin()
  102            throws NotSupportedException, SystemException
  103      {
  104         ThreadInfo ti = getThreadInfo();
  105         TransactionImpl current = ti.tx;
  106   
  107         if (current != null)
  108         {
  109            if (current.isDone())
  110               disassociateThread(ti);
  111            else
  112               throw new NotSupportedException
  113                     ("Transaction already active, cannot nest transactions.");
  114         }
  115   
  116         long timeout = (ti.timeout == 0) ? timeOut : ti.timeout;
  117         TransactionImpl tx = new TransactionImpl(timeout);
  118         associateThread(ti, tx);
  119         globalIdTx.put(tx.getGlobalId(), tx);
  120   
  121         if (trace)
  122            log.trace("began tx: " + tx);
  123      }
  124   
  125      /**
  126       *  Commit the transaction associated with the currently running thread.
  127       */
  128      public void commit()
  129            throws RollbackException,
  130            HeuristicMixedException,
  131            HeuristicRollbackException,
  132            SecurityException,
  133            IllegalStateException,
  134            SystemException
  135      {
  136         ThreadInfo ti = getThreadInfo();
  137         TransactionImpl current = ti.tx;
  138   
  139         if (current != null)
  140         {
  141            current.commit();
  142            disassociateThread(ti);
  143            if (trace)
  144               log.trace("commited tx: " + current);
  145         }
  146         else
  147            throw new IllegalStateException("No transaction.");
  148      }
  149   
  150      /**
  151       *  Return the status of the transaction associated with the currently
  152       *  running thread, or <code>Status.STATUS_NO_TRANSACTION</code> if no
  153       *  active transaction is currently associated.
  154       */
  155      public int getStatus() throws SystemException
  156      {
  157         ThreadInfo ti = getThreadInfo();
  158         TransactionImpl current = ti.tx;
  159   
  160         if (current != null)
  161         {
  162            if (current.isDone())
  163               disassociateThread(ti);
  164            else
  165               return current.getStatus();
  166         }
  167         return Status.STATUS_NO_TRANSACTION;
  168      }
  169   
  170      /**
  171       *  Return the transaction currently associated with the invoking thread,
  172       *  or <code>null</code> if no active transaction is currently associated.
  173       */
  174      public Transaction getTransaction() throws SystemException
  175      {
  176         ThreadInfo ti = getThreadInfo();
  177         TransactionImpl current = ti.tx;
  178   
  179         if (current != null && current.isDone())
  180         {
  181            current = null;
  182            disassociateThread(ti);
  183         }
  184   
  185         return current;
  186      }
  187   
  188      /**
  189       *  Resume a transaction.
  190       *
  191       *  Note: This will not enlist any resources involved in this
  192       *  transaction. According to JTA1.0.1 specification section 3.2.3,
  193       *  that is the responsibility of the application server.
  194       */
  195      public void resume(Transaction transaction)
  196            throws InvalidTransactionException,
  197            IllegalStateException,
  198            SystemException
  199      {
  200         if (transaction != null && !(transaction instanceof TransactionImpl))
  201            throw new RuntimeException("Not a TransactionImpl, but a " +
  202                  transaction.getClass().getName());
  203   
  204         ThreadInfo ti = getThreadInfo();
  205         TransactionImpl current = ti.tx;
  206   
  207         if (current != null)
  208         {
  209            if (current.isDone())
  210               current = ti.tx = null;
  211            else
  212               throw new IllegalStateException("Already associated with a tx");
  213         }
  214   
  215         if (current != transaction)
  216         {
  217            associateThread(ti, (TransactionImpl)transaction);
  218         }
  219   
  220         if (trace)
  221            log.trace("resumed tx: " + ti.tx);
  222      }
  223   
  224      /**
  225       *  Suspend the transaction currently associated with the current
  226       *  thread, and return it.
  227       *
  228       *  Note: This will not delist any resources involved in this
  229       *  transaction. According to JTA1.0.1 specification section 3.2.3,
  230       *  that is the responsibility of the application server.
  231       */
  232      public Transaction suspend() throws SystemException
  233      {
  234         ThreadInfo ti = getThreadInfo();
  235         TransactionImpl current = ti.tx;
  236   
  237         if (current != null)
  238         {
  239            ti.tx = null;
  240   
  241            if (trace)
  242               log.trace("suspended tx: " + current);
  243   
  244            if (current.isDone())
  245               current = null;
  246         }
  247   
  248         return current;
  249      }
  250   
  251      /**
  252       *  Roll back the transaction associated with the currently running thread.
  253       */
  254      public void rollback()
  255            throws IllegalStateException, SecurityException, SystemException
  256      {
  257         ThreadInfo ti = getThreadInfo();
  258         TransactionImpl current = ti.tx;
  259   
  260         if (current != null)
  261         {
  262            if (!current.isDone())
  263            {
  264               current.rollback();
  265   
  266               if (trace)
  267                  log.trace("rolled back tx: " + current);
  268               return;
  269            }
  270            disassociateThread(ti);
  271         }
  272         throw new IllegalStateException("No transaction.");
  273      }
  274   
  275      /**
  276       *  Mark the transaction associated with the currently running thread
  277       *  so that the only possible outcome is a rollback.
  278       */
  279      public void setRollbackOnly()
  280            throws IllegalStateException, SystemException
  281      {
  282         ThreadInfo ti = getThreadInfo();
  283         TransactionImpl current = ti.tx;
  284   
  285         if (current != null)
  286         {
  287            if (!current.isDone())
  288            {
  289               current.setRollbackOnly();
  290   
  291               if (trace)
  292                  log.trace("tx marked for rollback only: " + current);
  293               return;
  294            }
  295            ti.tx = null;
  296         }
  297         throw new IllegalStateException("No transaction.");
  298      }
  299   
  300      /**
  301       *  Set the transaction timeout for new transactions started by the
  302       *  calling thread.
  303       */
  304      public void setTransactionTimeout(int seconds)
  305            throws SystemException
  306      {
  307         getThreadInfo().timeout = 1000 * seconds;
  308   
  309         if (trace)
  310            log.trace("tx timeout is now: " + seconds + "s");
  311      }
  312   
  313      /**
  314       *  Set the default transaction timeout for new transactions.
  315       *  This default value is used if <code>setTransactionTimeout()</code>
  316       *  was never called, or if it was called with a value of <code>0</code>.
  317       */
  318      public void setDefaultTransactionTimeout(int seconds)
  319      {
  320         timeOut = 1000L * seconds;
  321   
  322         if (trace)
  323            log.trace("default tx timeout is now: " + seconds + "s");
  324      }
  325   
  326      /**
  327       *  Get the default transaction timeout.
  328       *
  329       *  @return Default transaction timeout in seconds.
  330       */
  331      public int getDefaultTransactionTimeout()
  332      {
  333         return (int) (timeOut / 1000);
  334      }
  335   
  336      /**
  337       *  The following 2 methods are here to provide association and
  338       *  disassociation of the thread.
  339       */
  340      public Transaction disassociateThread()
  341      {
  342         return disassociateThread(getThreadInfo());
  343      }
  344   
  345      private Transaction disassociateThread(ThreadInfo ti) {
  346         TransactionImpl current = ti.tx;
  347         ti.tx=null;
  348         current.disassociateCurrentThread();
  349         return current;
  350      }   
  351   
  352      public void associateThread(Transaction transaction)
  353      {
  354         if (transaction != null && !(transaction instanceof TransactionImpl))
  355            throw new RuntimeException("Not a TransactionImpl, but a " +
  356                  transaction.getClass().getName());
  357   
  358         // Associate with the thread
  359         TransactionImpl transactionImpl = (TransactionImpl) transaction;
  360         ThreadInfo ti = getThreadInfo();
  361         ti.tx = transactionImpl;
  362         transactionImpl.associateCurrentThread();
  363      }
  364   
  365      private void associateThread(ThreadInfo ti, TransactionImpl transaction)
  366      {
  367         // Associate with the thread
  368         ti.tx = transaction;
  369         transaction.associateCurrentThread();
  370      }
  371   
  372      /**
  373       * Return the number of active transactions
  374       */
  375      public int getTransactionCount()
  376      {
  377         return globalIdTx.size();
  378      }
  379      /** A count of the transactions that have been committed */
  380      public long getCommitCount()
  381      {
  382         return commitCount;
  383      }
  384      /** A count of the transactions that have been rolled back */
  385      public long getRollbackCount()
  386      {
  387         return rollbackCount;
  388      }
  389   
  390      // Implements TransactionPropagationContextImporter ---------------
  391   
  392      /**
  393       *  Import a transaction propagation context into this TM.
  394       *  The TPC is loosely typed, as we may (at a later time) want to
  395       *  import TPCs that come from other transaction domains without
  396       *  offloading the conversion to the client.
  397       *
  398       *  @param tpc The transaction propagation context that we want to
  399       *             import into this TM. Currently this is an instance
  400       *             of GlobalId. At some later time this may be an instance
  401       *             of a transaction propagation context from another
  402       *             transaction domain like
  403       *             org.omg.CosTransactions.PropagationContext.
  404       *
  405       *  @return A transaction representing this transaction propagation
  406       *          context, or null if this TPC cannot be imported.
  407       */
  408      public Transaction importTransactionPropagationContext(Object tpc)
  409      {
  410         if (tpc instanceof GlobalId)
  411         {
  412            GlobalId id = (GlobalId) tpc;
  413            return (Transaction) globalIdTx.get(id);
  414         }
  415   
  416         log.warn("Cannot import transaction propagation context: " + tpc);
  417         return null;
  418      }
  419   
  420      // Implements TransactionPropagationContextFactory ---------------
  421   
  422      /**
  423       *  Return a TPC for the current transaction.
  424       */
  425      public Object getTransactionPropagationContext()
  426      {
  427         return getTransactionPropagationContext(getThreadInfo().tx);
  428      }
  429   
  430      /**
  431       *  Return a TPC for the argument transaction.
  432       */
  433      public Object getTransactionPropagationContext(Transaction tx)
  434      {
  435         // If no transaction or unknown transaction class, return null.
  436         if (tx == null)
  437            return null;
  438         if (!(tx instanceof TransactionImpl))
  439         {
  440            log.warn("Cannot export transaction propagation context: " + tx);
  441            return null;
  442         }
  443   
  444         return ((TransactionImpl) tx).getGlobalId();
  445      }
  446   
  447      // Implements TransactionLocalDelegate ----------------------
  448   
  449      /**
  450       * get the transaction local value.  Pull it from the TransactionImpl object
  451       */
  452      public Object getValue(TransactionLocal local, Transaction tx)
  453      {
  454         TransactionImpl tximpl = (TransactionImpl) tx;
  455         return tximpl.getTransactionLocalValue(local);
  456      }
  457   
  458      /**
  459       * put the value in the TransactionImpl map
  460       */
  461      public void storeValue(TransactionLocal local, Transaction tx, Object value)
  462      {   
  463         TransactionImpl tximpl = (TransactionImpl) tx;
  464         tximpl.putTransactionLocalValue(local, value);
  465      }
  466   
  467      /**
  468       * does TransactionImpl contain object?
  469       */
  470      public boolean containsValue(TransactionLocal local, Transaction tx)
  471      {
  472         TransactionImpl tximpl = (TransactionImpl) tx;
  473         return tximpl.containsTransactionLocal(local);
  474      }
  475   
  476      // Package protected ---------------------------------------------
  477   
  478      /**
  479       *  Release the given TransactionImpl.
  480       */
  481      void releaseTransactionImpl(TransactionImpl tx)
  482      {
  483         globalIdTx.remove(tx.getGlobalId());
  484      }
  485   
  486      /**
  487       * Increment the commit count
  488       */
  489      void incCommitCount()
  490      {
  491         ++commitCount;
  492      }
  493   
  494      /**
  495       * Increment the rollback count
  496       */
  497      void incRollbackCount()
  498      {
  499         ++rollbackCount;
  500      }
  501   
  502      // Protected -----------------------------------------------------
  503   
  504      // Private -------------------------------------------------------
  505   
  506      /**
  507       *  This keeps track of the thread association with transactions
  508       *  and timeout values.
  509       *  In some cases terminated transactions may not be cleared here.
  510       */
  511      private ThreadLocal threadTx = new ThreadLocal();
  512   
  513      /**
  514       *  This map contains the active transactions as values.
  515       *  The keys are the <code>GlobalId</code>s of the transactions.
  516       */
  517      private Map globalIdTx = Collections.synchronizedMap(new HashMap());
  518   
  519   
  520      /**
  521       *  Return the ThreadInfo for the calling thread, and create if not
  522       *  found.
  523       */
  524      private ThreadInfo getThreadInfo()
  525      {
  526         ThreadInfo ret = (ThreadInfo) threadTx.get();
  527   
  528         if (ret == null)
  529         {
  530            ret = new ThreadInfo();
  531            ret.timeout = timeOut;
  532            threadTx.set(ret);
  533         }
  534   
  535         return ret;
  536      }
  537   
  538   
  539      // Inner classes -------------------------------------------------
  540   
  541      /**
  542       *  A simple aggregate of a thread-associated timeout value
  543       *  and a thread-associated transaction.
  544       */
  545      static class ThreadInfo
  546      {
  547         long timeout;
  548         TransactionImpl tx;
  549      }
  550   }

Save This Page
Home » jboss-5.0.0.CR1-src » org » jboss » tm » [javadoc | source]