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.NoSuchObjectException;
   26   import java.rmi.RemoteException;
   27   
   28   import javax.ejb.EJBException;
   29   import javax.ejb.NoSuchObjectLocalException;
   30   import javax.ejb.TimedObject;
   31   import javax.ejb.Timer;
   32   import javax.transaction.Transaction;
   33   
   34   import org.jboss.ejb.AllowedOperationsAssociation;
   35   import org.jboss.ejb.BeanLock;
   36   import org.jboss.ejb.Container;
   37   import org.jboss.ejb.EntityContainer;
   38   import org.jboss.ejb.EntityEnterpriseContext;
   39   import org.jboss.ejb.GlobalTxEntityMap;
   40   import org.jboss.ejb.InstanceCache;
   41   import org.jboss.ejb.InstancePool;
   42   import org.jboss.invocation.Invocation;
   43   import org.jboss.invocation.InvocationType;
   44   import org.jboss.util.NestedRuntimeException;
   45   
   46   /**
   47    * The instance interceptors role is to acquire a context representing the
   48    * target object from the cache.
   49    *
   50    * <p>This particular container interceptor implements pessimistic locking on
   51    * the transaction that is associated with the retrieved instance.  If there is
   52    * a transaction associated with the target component and it is different from
   53    * the transaction associated with the Invocation coming in then the policy is
   54    * to wait for transactional commit.
   55    *
   56    * <p>We also implement serialization of calls in here (this is a spec
   57    * requirement). This is a fine grained notify, notifyAll mechanism. We notify
   58    * on ctx serialization locks and notifyAll on global transactional locks.
   59    *
   60    * <p><b>WARNING: critical code</b>, get approval from senior developers before
   61    * changing.
   62    * @author <a href="mailto:marc.fleury@jboss.org">Marc Fleury</a>
   63    * @author <a href="mailto:Scott.Stark@jboss.org">Scott Stark</a>
   64    * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
   65    * @author <a href="mailto:mkgarnek@hotmail.com">Jamie Burns</a>
   66    * @version $Revision: 69133 $
   67    */
   68   public class EntityInstanceInterceptor
   69      extends AbstractInterceptor
   70   {
   71      // Constants -----------------------------------------------------
   72   	
   73      // Attributes ----------------------------------------------------
   74   	
   75      protected EntityContainer container;
   76   
   77      // Static --------------------------------------------------------
   78   
   79      /** A reference to {@link javax.ejb.TimedObject#ejbTimeout}. */
   80      protected static final Method ejbTimeout;
   81   
   82      static
   83      {
   84         try
   85         {
   86            ejbTimeout = TimedObject.class.getMethod("ejbTimeout", new Class[]{Timer.class});
   87         }
   88         catch (Exception e)
   89         {
   90            throw new ExceptionInInitializerError(e);
   91         }
   92      }
   93   
   94      // Constructors --------------------------------------------------
   95   	
   96      // Public --------------------------------------------------------
   97   	
   98      public void setContainer(Container container)
   99      {
  100         this.container = (EntityContainer) container;
  101      }
  102   
  103      public Container getContainer()
  104      {
  105         return container;
  106      }
  107   
  108      // Interceptor implementation --------------------------------------
  109   
  110      public Object invokeHome(Invocation mi)
  111         throws Exception
  112      {
  113         // Get context
  114         EntityContainer container = (EntityContainer) getContainer();
  115         EntityEnterpriseContext ctx = (EntityEnterpriseContext) container.getInstancePool().get();
  116         ctx.setTxAssociation(GlobalTxEntityMap.NOT_READY);
  117         InstancePool pool = container.getInstancePool();
  118   
  119         // Pass it to the method invocation
  120         mi.setEnterpriseContext(ctx);
  121      
  122         // Give it the transaction
  123         ctx.setTransaction(mi.getTransaction());
  124      
  125         // Set the current security information
  126         ctx.setPrincipal(mi.getPrincipal());
  127   
  128         AllowedOperationsAssociation.pushInMethodFlag(IN_EJB_HOME);
  129         
  130         // Invoke through interceptors
  131         
  132         Object obj = null;
  133         Exception exception = null;
  134   
  135         try
  136         {
  137            obj = getNext().invokeHome(mi);
  138             
  139            // Is the context now with an identity? in which case we need to insert
  140            if (ctx.getId() != null)
  141            {
  142               BeanLock lock = container.getLockManager().getLock(ctx.getCacheKey());
  143               lock.sync(); // lock all access to BeanLock
  144               try
  145               {
  146                  // Check there isn't a context already in the cache
  147                  // e.g. commit-option B where the entity was
  148                  // created then removed externally
  149                  InstanceCache cache = container.getInstanceCache();
  150                  cache.remove(ctx.getCacheKey());
  151          
  152                  // marcf: possible race on creation and usage
  153                  // insert instance in cache,
  154                  cache.insert(ctx);
  155               }
  156               finally
  157               {
  158                  lock.releaseSync();
  159                  container.getLockManager().removeLockRef(ctx.getCacheKey());
  160               }
  161                
  162               // we are all done             
  163               return obj;
  164            }
  165         }
  166         catch (Exception e)
  167         {
  168            exception = e;
  169         }
  170         finally
  171         {
  172            AllowedOperationsAssociation.popInMethodFlag();
  173         }
  174   
  175         ctx.setTransaction(null);
  176         // EntityCreateInterceptor will access ctx if it is not null and call postCreate
  177         mi.setEnterpriseContext(null);
  178         
  179         // if we get to here with a null exception then our invocation is
  180         // just a home invocation. Return our instance to the instance pool   
  181         if (exception == null)
  182         {
  183            container.getInstancePool().free(ctx);
  184            return obj;
  185         }
  186         
  187         if (exception instanceof RuntimeException)
  188         {
  189            // if we get to here with a RuntimeException, we have a system exception.
  190            // EJB 2.1 section 18.3.1 says we need to discard our instance.
  191            pool.discard(ctx);
  192         }
  193         else
  194         {
  195            // if we get to here with an Exception, we have an application exception.
  196            // EJB 2.1 section 18.3.1 says we can keep the instance. We need to return 
  197            // our instance to the instance pool so we dont get a memory leak.  
  198            pool.free(ctx);         
  199         }
  200         
  201         throw exception;
  202      }
  203   
  204   
  205      public Object invoke(Invocation mi)
  206         throws Exception
  207      {
  208         boolean trace = log.isTraceEnabled();
  209   
  210         // The key
  211         Object key = mi.getId();
  212   
  213         // The context
  214         EntityEnterpriseContext ctx;
  215         try
  216         {
  217            ctx = (EntityEnterpriseContext) container.getInstanceCache().get(key);
  218         }
  219         catch (NoSuchObjectException e)
  220         {
  221            if (mi.isLocal())
  222               throw new NoSuchObjectLocalException(e.getMessage());
  223            else
  224               throw e;
  225         }
  226         catch (EJBException e)
  227         {
  228            throw e;
  229         }
  230         catch (RemoteException e)
  231         {
  232            throw e;
  233         }
  234         catch (Exception e)
  235         {
  236            InvocationType type = mi.getType();
  237            boolean isLocal = (type == InvocationType.LOCAL || type == InvocationType.LOCALHOME);
  238            if (isLocal)
  239               throw new EJBException("Unable to get an instance from the pool/cache", e);
  240            else
  241               throw new RemoteException("Unable to get an intance from the pool/cache", e);
  242         }
  243   
  244         if (trace) log.trace("Begin invoke, key=" + key);
  245   
  246         // Associate transaction, in the new design the lock already has the transaction from the
  247         // previous interceptor
  248   
  249         // Don't set the transction if a read-only method.  With a read-only method, the ctx can be shared
  250         // between multiple transactions.
  251         Transaction tx = mi.getTransaction();
  252         if (!container.isReadOnly())
  253         {
  254            Method method = mi.getMethod();
  255            if (method == null ||
  256               !container.getBeanMetaData().isMethodReadOnly(method.getName()))
  257            {
  258               ctx.setTransaction(tx);
  259            }
  260         } 
  261   
  262         // Set the current security information
  263         ctx.setPrincipal(mi.getPrincipal());
  264         // Set the JACC EnterpriseBean PolicyContextHandler data
  265         EnterpriseBeanPolicyContextHandler.setEnterpriseBean(ctx.getInstance());
  266   
  267         // Set context on the method invocation
  268         mi.setEnterpriseContext(ctx);
  269   
  270         if (ejbTimeout.equals(mi.getMethod()))
  271            AllowedOperationsAssociation.pushInMethodFlag(IN_EJB_TIMEOUT);
  272         else
  273            AllowedOperationsAssociation.pushInMethodFlag(IN_BUSINESS_METHOD);
  274   
  275         Throwable exceptionThrown = null;
  276         boolean discardContext = false;
  277         try
  278         {
  279            Object obj = getNext().invoke(mi);
  280            return obj;
  281         }
  282         catch (RemoteException e)
  283         {
  284            exceptionThrown = e;
  285            discardContext = true;
  286            throw e;
  287         }
  288         catch (RuntimeException e)
  289         {
  290            exceptionThrown = e;
  291            discardContext = true;
  292            throw e;
  293         }
  294         catch (Error e)
  295         {
  296            exceptionThrown = e;
  297            discardContext = true;
  298            throw e;
  299         }
  300         catch (Exception e)
  301         {
  302            exceptionThrown = e;
  303            throw e;
  304         }
  305         catch (Throwable e)
  306         {
  307            exceptionThrown = e;
  308            discardContext = true;
  309            throw new NestedRuntimeException(e);
  310         }
  311         finally
  312         {
  313            AllowedOperationsAssociation.popInMethodFlag();
  314   
  315            // Make sure we clear the transaction on an error before synchronization.
  316            // But avoid a race with a transaction rollback on a synchronization
  317            // that may have moved the context onto a different transaction
  318            if (exceptionThrown != null && tx != null)
  319            {
  320               Transaction ctxTx = ctx.getTransaction();
  321               if (tx.equals(ctxTx) && ctx.hasTxSynchronization() == false)
  322                  ctx.setTransaction(null);
  323            }
  324   
  325            // If an exception has been thrown,
  326            if (exceptionThrown != null &&
  327               // if tx, the ctx has been registered in an InstanceSynchronization.
  328               // that will remove the context, so we shouldn't.
  329               // if no synchronization then we need to do it by hand
  330               // But not for application exceptions
  331               !ctx.hasTxSynchronization() && discardContext)
  332            {
  333               // Discard instance
  334               // EJB 1.1 spec 12.3.1
  335               container.getInstanceCache().remove(key);
  336   
  337               if (trace) log.trace("Ending invoke, exceptionThrown, ctx=" + ctx, exceptionThrown);
  338            }
  339            else if (ctx.getId() == null)
  340            {
  341               // The key from the Invocation still identifies the right cachekey
  342               container.getInstanceCache().remove(key);
  343   
  344               if (trace) log.trace("Ending invoke, cache removal, ctx=" + ctx);
  345               // no more pool return
  346            }
  347            
  348            EnterpriseBeanPolicyContextHandler.setEnterpriseBean(null);
  349   
  350            if (trace) log.trace("End invoke, key=" + key + ", ctx=" + ctx);
  351         }// end finally
  352      }
  353   }
  354   

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