Save This Page
Home » jboss-5.0.0.CR1-src » org » jboss » ejb » plugins » [javadoc | source]
    1   /*
    2    * JBoss, Home of Professional Open Source.
    3    * Copyright 2006, Red Hat Middleware LLC, and individual contributors
    4    * as indicated by the @author tags. See the copyright.txt file in the
    5    * distribution for a full listing of individual contributors.
    6    *
    7    * This is free software; you can redistribute it and/or modify it
    8    * under the terms of the GNU Lesser General Public License as
    9    * published by the Free Software Foundation; either version 2.1 of
   10    * the License, or (at your option) any later version.
   11    *
   12    * This software is distributed in the hope that it will be useful,
   13    * but WITHOUT ANY WARRANTY; without even the implied warranty of
   14    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   15    * Lesser General Public License for more details.
   16    *
   17    * You should have received a copy of the GNU Lesser General Public
   18    * License along with this software; if not, write to the Free
   19    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
   20    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
   21    */
   22   package org.jboss.ejb.plugins;
   23   
   24   import java.util.Hashtable;
   25   
   26   import java.rmi.RemoteException;
   27   
   28   import javax.transaction.Transaction;
   29   import javax.transaction.Status;
   30   import javax.transaction.SystemException;
   31   
   32   import javax.naming.Context;
   33   import javax.naming.InitialContext;
   34   import javax.naming.Name;
   35   import javax.naming.Reference;
   36   import javax.naming.RefAddr;
   37   import javax.naming.spi.ObjectFactory;
   38   
   39   import org.jboss.ejb.EnterpriseContext;
   40   import org.jboss.ejb.AllowedOperationsAssociation;
   41   import org.jboss.invocation.Invocation;
   42   import org.jboss.tm.TxUtils;
   43   
   44   /**
   45    *  A common superclass for the BMT transaction interceptors.
   46    *
   47    *  @author <a href="mailto:osh@sparre.dk">Ole Husgaard</a>
   48    *  @version $Revision: 73911 $
   49    */
   50   abstract class AbstractTxInterceptorBMT extends AbstractTxInterceptor
   51   {
   52      // Attributes ----------------------------------------------------
   53   
   54      /**
   55       *  This associates the thread to the UserTransaction.
   56       *
   57       *  It is used to redirect lookups on java:comp/UserTransaction to
   58       *  the <code>getUserTransaction()</code> method of the context.
   59       */
   60      private ThreadLocal userTransaction = new ThreadLocal();
   61   
   62      /**
   63       *  If <code>false</code>, transactions may live across bean instance
   64       *  invocations, otherwise the bean instance should terminate any
   65       *  transaction before returning from the invocation.
   66       *  This attribute defaults to <code>true</code>.
   67       */
   68      protected boolean stateless = true;
   69   
   70      // Static --------------------------------------------------------
   71   
   72      // Constructors --------------------------------------------------
   73   
   74      // Public --------------------------------------------------------
   75   
   76      // Interceptor implementation --------------------------------------
   77   
   78      public void create() throws Exception
   79      {
   80         // Do initialization in superclass.
   81         super.create();
   82   
   83         // bind java:comp/UserTransaction
   84         RefAddr refAddr = new RefAddr("userTransaction")
   85         {
   86            /** This is never really serialized */
   87            private static final long serialVersionUID = -8228448967597474960L;
   88   
   89            public Object getContent()
   90            {
   91               return userTransaction;
   92            }
   93         };
   94   
   95         Reference ref = new Reference("javax.transaction.UserTransaction", refAddr, new UserTxFactory().getClass()
   96               .getName(), null);
   97         ((Context) new InitialContext().lookup("java:comp/")).bind("UserTransaction", ref);
   98      }
   99   
  100      public void stop()
  101      {
  102         // bind java:comp/UserTransaction
  103         try
  104         {
  105            ((Context) new InitialContext().lookup("java:comp/")).unbind("UserTransaction");
  106         }
  107         catch (Exception e)
  108         {
  109            //ignore
  110         }
  111      }
  112   
  113      // Protected  ----------------------------------------------------
  114   
  115      /*
  116       *  This method calls the next interceptor in the chain.
  117       *
  118       *  It handles the suspension of any client transaction, and the
  119       *  association of the calling thread with the instance transaction.
  120       *  And it takes care that any lookup of
  121       *  <code>java:comp/UserTransaction</code> will return the right
  122       *  UserTransaction for the bean instance.
  123       *
  124       *  @param remoteInvocation If <code>true</code> this is an invocation
  125       *                          of a method in the remote interface, otherwise
  126       *                          it is an invocation of a method in the home
  127       *                          interface.
  128       *  @param mi The <code>Invocation</code> of this call.
  129       */
  130      protected Object invokeNext(Invocation mi) throws Exception
  131      {
  132         // Save the transaction that comes with the MI
  133         Transaction oldTransaction = mi.getTransaction();
  134   
  135         // Get old threadlocal: It may be non-null if one BMT bean does a local
  136         // call to another.
  137         Object oldUserTx = userTransaction.get();
  138   
  139         // Suspend any transaction associated with the thread: It may be
  140         // non-null on optimized local calls.
  141         Transaction threadTx = tm.suspend();
  142   
  143         try
  144         {
  145            EnterpriseContext ctx = ((EnterpriseContext) mi.getEnterpriseContext());
  146   
  147            // Set the threadlocal to the userTransaction of the instance
  148            try
  149            {
  150               AllowedOperationsAssociation.pushInMethodFlag(IN_INTERCEPTOR_METHOD);
  151               userTransaction.set(ctx.getEJBContext().getUserTransaction());
  152            }
  153            finally
  154            {
  155               AllowedOperationsAssociation.popInMethodFlag();
  156            }
  157   
  158            // Get the bean instance transaction
  159            Transaction beanTx = ctx.getTransaction();
  160   
  161            // Resume the bean instance transaction
  162            // only if it not null, some TMs can't resume(null), e.g. Tyrex
  163            if (beanTx != null)
  164               tm.resume(beanTx);
  165   
  166            // Let the MI know about our new transaction
  167            mi.setTransaction(beanTx);
  168   
  169            try
  170            {
  171               // Let the superclass call next interceptor and do the exception
  172               // handling
  173               return super.invokeNext(mi, false);
  174            }
  175            finally
  176            {
  177               try
  178               {
  179                  if (stateless)
  180                     checkStatelessDone();
  181                  else
  182                     checkBadStateful();
  183               }
  184               finally
  185               {
  186                  tm.suspend();
  187               }
  188            }
  189         }
  190         finally
  191         {
  192            // Reset threadlocal to its old value
  193            userTransaction.set(oldUserTx);
  194   
  195            // Restore old MI transaction
  196            // OSH: Why ???
  197            mi.setTransaction(oldTransaction);
  198   
  199            // If we had a Tx associated with the thread reassociate
  200            if (threadTx != null)
  201               tm.resume(threadTx);
  202         }
  203      }
  204   
  205      private void checkStatelessDone() throws RemoteException
  206      {
  207         int status = Status.STATUS_NO_TRANSACTION;
  208   
  209         try
  210         {
  211            status = tm.getStatus();
  212         }
  213         catch (SystemException ex)
  214         {
  215            log.error("Failed to get status", ex);
  216         }
  217   
  218         try
  219         {
  220            switch (status)
  221            {
  222               case Status.STATUS_ACTIVE :
  223               case Status.STATUS_COMMITTING :
  224               case Status.STATUS_MARKED_ROLLBACK :
  225               case Status.STATUS_PREPARING :
  226               case Status.STATUS_ROLLING_BACK :
  227                  try
  228                  {
  229                     tm.rollback();
  230                  }
  231                  catch (Exception ex)
  232                  {
  233                     log.error("Failed to rollback", ex);
  234                  }
  235               // fall through...
  236               case Status.STATUS_PREPARED :
  237                  String msg = "Application error: BMT stateless bean " + container.getBeanMetaData().getEjbName()
  238                        + " should complete transactions before" + " returning (ejb1.1 spec, 11.6.1)";
  239                  log.error(msg);
  240   
  241                  // the instance interceptor will discard the instance
  242                  throw new RemoteException(msg);
  243            }
  244         }
  245         finally
  246         {
  247            Transaction tx = null;
  248            try
  249            {
  250               tx = tm.suspend();
  251            }
  252            catch (SystemException ex)
  253            {
  254               log.error("Failed to suspend transaction", ex);
  255            }
  256            if (tx != null)
  257            {
  258               String msg = "Application error: BMT stateless bean " + container.getBeanMetaData().getEjbName()
  259                      + " should complete transactions before " + " returning (ejb1.1 spec, 11.6.1), suspended tx=" + tx ;
  260               log.error(msg);
  261               throw new RemoteException(msg);
  262            }
  263         }
  264      }
  265   
  266      private void checkBadStateful() throws RemoteException
  267      {
  268         int status = Status.STATUS_NO_TRANSACTION;
  269   
  270         try
  271         {
  272            status = tm.getStatus();
  273         }
  274         catch (SystemException ex)
  275         {
  276            log.error("Failed to get status", ex);
  277         }
  278         switch (status)
  279         {
  280            case Status.STATUS_COMMITTING :
  281            case Status.STATUS_MARKED_ROLLBACK :
  282            case Status.STATUS_PREPARING :
  283            case Status.STATUS_ROLLING_BACK :
  284               try
  285               {
  286                  tm.rollback();
  287               }
  288               catch (Exception ex)
  289               {
  290                  log.error("Failed to rollback", ex);
  291               }
  292               String msg = "BMT stateful bean '" + container.getBeanMetaData().getEjbName()
  293                     + "' did not complete user transaction properly status=" + TxUtils.getStatusAsString(status);
  294               log.error(msg);
  295         }
  296      }
  297   
  298      // Inner classes -------------------------------------------------
  299   
  300      public static class UserTxFactory implements ObjectFactory
  301      {
  302         public Object getObjectInstance(Object ref, Name name, Context nameCtx, Hashtable environment) throws Exception
  303         {
  304            // The ref is a list with only one RefAddr ...
  305            RefAddr refAddr = ((Reference) ref).get(0);
  306            // ... whose content is the threadlocal
  307            ThreadLocal threadLocal = (ThreadLocal) refAddr.getContent();
  308   
  309            // The threadlocal holds the right UserTransaction
  310            return threadLocal.get();
  311         }
  312      }
  313   
  314   }

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