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 2005, JBoss Inc., and individual contributors as indicated
    4   * by the @authors tag. See the copyright.txt in the distribution for a
    5   * 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.lang.reflect.Method;
   25   import java.rmi.RemoteException;
   26   import javax.ejb.EJBObject;
   27   import javax.ejb.EJBException;
   28   import javax.transaction.Transaction;
   29   import javax.transaction.Status;
   30   
   31   import org.jboss.invocation.Invocation;
   32   import org.jboss.invocation.InvocationType;
   33   import org.jboss.ejb.Container;
   34   import org.jboss.ejb.EntityEnterpriseContext;
   35   import org.jboss.metadata.EntityMetaData;
   36   import org.jboss.ejb.plugins.lock.Entrancy;
   37   import org.jboss.ejb.plugins.lock.NonReentrantLock;
   38   import org.jboss.ejb.plugins.cmp.jdbc.bridge.CMRInvocation;
   39   
   40   /**
   41    * The role of this interceptor is to check for reentrancy.
   42    * Per the spec, throw an exception if instance is not marked
   43    * as reentrant.  We do not check to see if same Tx is
   44    * accessing object at the same time as we assume that
   45    * any transactional locks will handle this.
   46    *
   47    * <p><b>WARNING: critical code</b>, get approval from senior developers
   48    *    before changing.
   49    *
   50    * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
   51    * @version $Revision: 37459 $
   52    */
   53   public class EntityReentranceInterceptor
   54           extends AbstractInterceptor
   55   {
   56      protected boolean reentrant = false;
   57   
   58      // Public --------------------------------------------------------
   59   
   60      public void setContainer(Container container)
   61      {
   62         super.setContainer(container);
   63         if (container != null)
   64         {
   65            EntityMetaData meta = (EntityMetaData) container.getBeanMetaData();
   66            reentrant = meta.isReentrant();
   67         }
   68      }
   69   
   70      protected boolean isTxExpired(Transaction miTx) throws Exception
   71      {
   72         if (miTx != null && miTx.getStatus() == Status.STATUS_MARKED_ROLLBACK)
   73         {
   74            return true;
   75         }
   76         return false;
   77      }
   78   
   79      public Object invoke(Invocation mi)
   80              throws Exception
   81      {
   82         // We are going to work with the context a lot
   83         EntityEnterpriseContext ctx = (EntityEnterpriseContext) mi.getEnterpriseContext();
   84         boolean nonReentrant = !(reentrant || isReentrantMethod(mi));
   85   
   86         // Not a reentrant method like getPrimaryKey
   87         NonReentrantLock methodLock = ctx.getMethodLock();
   88         Transaction miTx = ctx.getTransaction();
   89         boolean locked = false;
   90         try
   91         {
   92            while (!locked)
   93            {
   94               if (methodLock.attempt(5000, miTx, nonReentrant))
   95               {
   96                  locked = true;
   97               }
   98               else
   99               {
  100                  if (isTxExpired(miTx))
  101                  {
  102                     log.error("Saw rolled back tx=" + miTx);
  103                     throw new RuntimeException("Transaction marked for rollback, possibly a timeout");
  104                  }
  105               }
  106            }
  107         }
  108         catch (NonReentrantLock.ReentranceException re)
  109         {
  110            if (mi.getType() == InvocationType.REMOTE)
  111            {
  112               throw new RemoteException("Reentrant method call detected: "
  113                       + container.getBeanMetaData().getEjbName() + " "
  114                       + ctx.getId().toString());
  115            }
  116            else
  117            {
  118               throw new EJBException("Reentrant method call detected: "
  119                       + container.getBeanMetaData().getEjbName() + " "
  120                       + ctx.getId().toString());
  121            }
  122         }
  123         try
  124         {
  125            ctx.lock();
  126            return getNext().invoke(mi);
  127         }
  128         finally
  129         {
  130            ctx.unlock();
  131            methodLock.release(nonReentrant);
  132         }
  133      }
  134   
  135      // Private ------------------------------------------------------
  136   
  137      private static final Method getEJBHome;
  138      private static final Method getHandle;
  139      private static final Method getPrimaryKey;
  140      private static final Method isIdentical;
  141      private static final Method remove;
  142   
  143      static
  144      {
  145         try
  146         {
  147            Class[] noArg = new Class[0];
  148            getEJBHome = EJBObject.class.getMethod("getEJBHome", noArg);
  149            getHandle = EJBObject.class.getMethod("getHandle", noArg);
  150            getPrimaryKey = EJBObject.class.getMethod("getPrimaryKey", noArg);
  151            isIdentical = EJBObject.class.getMethod("isIdentical", new Class[]{EJBObject.class});
  152            remove = EJBObject.class.getMethod("remove", noArg);
  153         }
  154         catch (Exception e)
  155         {
  156            e.printStackTrace();
  157            throw new ExceptionInInitializerError(e);
  158         }
  159      }
  160   
  161      protected boolean isReentrantMethod(Invocation mi)
  162      {
  163         // is this a known non-entrant method
  164         Method m = mi.getMethod();
  165         if (m != null && (
  166                 m.equals(getEJBHome) ||
  167                 m.equals(getHandle) ||
  168                 m.equals(getPrimaryKey) ||
  169                 m.equals(isIdentical) ||
  170                 m.equals(remove)))
  171         {
  172            return true;
  173         }
  174   
  175         // if this is a non-entrant message to the container let it through
  176         if (mi instanceof CMRInvocation)
  177         {
  178            Entrancy entrancy = ((CMRInvocation) mi).getEntrancy();
  179            if (entrancy == Entrancy.NON_ENTRANT)
  180            {
  181               log.trace("NON_ENTRANT invocation");
  182               return true;
  183            }
  184         }
  185   
  186         return false;
  187      }
  188   
  189   }

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