FileDocCategorySizeDatePackage
StatefulBeanContext.javaAPI DocJBoss 4.2.126195Fri Jul 13 20:53:50 BST 2007org.jboss.ejb3.stateful

StatefulBeanContext

public class StatefulBeanContext extends org.jboss.ejb3.BaseContext implements Externalizable
BeanContext for a stateful session bean.
author
Bill Burke
author
Brian Stansberry
version
$Revision: 61595 $

Fields Summary
private static final long
serialVersionUID
The serialVersionUID
protected Object
id
protected boolean
txSynchronized
protected boolean
inInvocation
protected org.jboss.serial.io.MarshalledObject
beanMO
protected ReentrantLock
lock
protected boolean
discarded
public static org.jboss.ejb3.ThreadLocalStack
propagatedContainedIn
public static org.jboss.ejb3.ThreadLocalStack
currentBean
protected StatefulBeanContext
containedIn
protected List
contains
protected HashMap
persistenceContexts
protected boolean
removed
protected String
containerName
protected boolean
replicationIsPassivation
protected transient boolean
passivated
public volatile boolean
markedForPassivation
public volatile boolean
markedForReplication
private volatile boolean
inUse
public long
lastUsed
Constructors Summary
public StatefulBeanContext()


    
   
    
   
Methods Summary
public voidactivateAfterReplication()

      if (!removed && passivated)
      {         
         getInstance(); // make sure we're unmarshalled
         getContainer().invokePostActivate(this);
         passivated = false;
      }
      
      // Pass the call on to any nested children
      List<StatefulBeanContext> children = getThreadSafeContains();
      if (children != null)
      {
         for (StatefulBeanContext contained : children)
         {
            contained.activateAfterReplication();
         }
      }
   
public voidaddContains(org.jboss.ejb3.stateful.StatefulBeanContext ctx)

      if (getContains() == null)
         contains = new ArrayList<StatefulBeanContext>();
      
      synchronized (contains)
      {
         contains.add(ctx);
         ctx.containedIn = this;
      }      
   
public voidaddExtendedPersistenceContext(java.lang.String id, javax.persistence.EntityManager pc)

      Map<String, EntityManager> extendedPCS = getExtendedPersistenceContexts();
      if (extendedPCS == null)
      {
         extendedPCS = persistenceContexts = new HashMap<String, EntityManager>();
      }
      extendedPCS.put(id, pc);
   
private voidcleanExtendedPCs()

    
      try
      {
         Transaction tx = TxUtil.getTransactionManager().getTransaction();
         if (tx != null && TxUtils.isActive(tx))
         {
            tx.registerSynchronization(new XPCCloseSynchronization(this));
         }
         else
         {
            closeExtendedPCs();
         }
      }
      catch (RuntimeException e)
      {
         throw e;
      }
      catch (Exception e)
      {
         throw new RuntimeException("Error cleaning PersistenceContexts in SFSB removal", e);
      }
   
private voidcloseExtendedPCs()

      Map<String, EntityManager> extendedPCS = getExtendedPersistenceContexts();
      if (extendedPCS != null)
      {
         RuntimeException exceptionThrown = null;
         
         List<String> closedXPCs = new ArrayList<String>();
         StatefulBeanContext topCtx = getUltimateContainedIn();
         
         for (Iterator<Map.Entry<String,EntityManager>> iter = extendedPCS.entrySet().iterator(); 
               iter.hasNext();)
         {
            Map.Entry<String,EntityManager> entry = iter.next();
            String id = entry.getKey();
            EntityManager xpc = entry.getValue();
            
            // Only close the XPC if our live parent(s) or cousins 
            // don't also have a ref to it
            boolean canClose = topCtx.scanForExtendedPersistenceContext(id, this);
            
            if (canClose && getContains() != null)
            {
               // Only close the XPC if our live childrenScan don't have a ref
               synchronized (contains)
               {
                  for (StatefulBeanContext contained : contains)
                  {
                     if (contained.scanForExtendedPersistenceContext(id, null))
                     {
                        canClose = false;
                        break;
                     }
                  }
               }
            }
            
            if (canClose)
            {
               try
               {
                  xpc.close();
                  closedXPCs.add(id);
               }
               catch (RuntimeException e)
               {
                  exceptionThrown = e;
               }
            }
         }
         
         // Clean all refs to the closed XPCs from the tree
         for (String id : closedXPCs)
         {
            topCtx.removeExtendedPersistenceContext(id);
         }            
         
         if (exceptionThrown != null) throw new RuntimeException("Error closing PersistenceContexts in SFSB removal", exceptionThrown);
      }
   
protected synchronized voidextractBeanAndInterceptors()

      if (beanMO == null)
         return;
      
      try
      {
         Object[] beanAndInterceptors = (Object[]) beanMO.get();
         bean = beanAndInterceptors[0];
         persistenceContexts = (HashMap<String, EntityManager>) beanAndInterceptors[1];
         ArrayList list = (ArrayList) beanAndInterceptors[2];
         interceptorInstances = new HashMap<Class, Object>();
         if (list != null)
         {
            for (Object o : list)
            {
               interceptorInstances.put(o.getClass(), o);
            }
         }
         contains = (List<StatefulBeanContext>) beanAndInterceptors[3];
         // Reestablish links to our children; if they serialize a link
         // to us for some reason serialization blows up
         if (contains != null)
         {
            for (StatefulBeanContext contained : contains)
            {
               contained.containedIn = this;
            }
         }
         
         // Don't hold onto the beanMo, as its contents are mutable
         // and we don't want to serialize a stale version of them
         beanMO = null;
      }
      catch (IOException e)
      {
         throw new RuntimeException(e);
      }
      catch (ClassNotFoundException e)
      {
         throw new RuntimeException(e);
      }
   
public booleangetCanPassivate()
Checks whether this context or any of its children are in use.

      boolean canPassivate = (removed || !inUse);
      
      // Just check contains directly; don't call getContains() since
      // getContains() will deserialize the beanMO. If the beanMO isn't 
      // deserialized it's safe to assume the children aren't in use
      if (canPassivate && contains != null)
      {
         synchronized (contains)
         {
            for (StatefulBeanContext contained : contains)
            {
               if (!contained.getCanPassivate())
               {
                  canPassivate = false;
                  break;
               }
            }
         }
      }
      
      return canPassivate;
   
public booleangetCanRemoveFromCache()

      boolean canRemove = removed;
      
      if (canRemove && getContains() != null) // call getContains() to ensure unmarshalling
      {
         synchronized (contains)
         {
            canRemove = (contains.size() == 0);
         }
      }
      
      return canRemove;
   
public org.jboss.ejb3.stateful.StatefulBeanContextgetContainedIn()

      return containedIn;
   
public org.jboss.ejb3.ContainergetContainer()

      if (container == null)
      {
         container = Ejb3Registry.getContainer(containerName);
      }
      return container;
   
public java.util.ListgetContains()

      if (bean == null)
         extractBeanAndInterceptors();
      return contains;
   
public javax.ejb.EJBLocalObjectgetEJBLocalObject()

 
      try
      {
         Object proxy = ((StatefulContainer)container).createLocalProxy(getId());
         return (EJBLocalObject)proxy;
      }
      catch (Exception e)
      {
         throw new IllegalStateException(e);
      }
   
public javax.persistence.EntityManagergetExtendedPersistenceContext(java.lang.String id)

      EntityManager found = null;
      Map<String, EntityManager> extendedPCS = getExtendedPersistenceContexts();
      if (extendedPCS != null)
      {
         found = extendedPCS.get(id);
      }
      if (found != null)
         return found;
      if (containedIn != null)
      {
         found = containedIn.getExtendedPersistenceContext(id);
      }
      return found;
   
public java.util.MapgetExtendedPersistenceContexts()

      if (persistenceContexts == null)
      {
         if (bean == null)
            extractBeanAndInterceptors(); // unmarshall
      }
      return persistenceContexts;
   
public java.lang.ObjectgetId()

      return id;
   
public java.lang.ObjectgetInstance()

      if (bean == null)
      {
         extractBeanAndInterceptors();
      }
      return bean;
   
public java.lang.Object[]getInterceptorInstances(org.jboss.ejb3.interceptor.InterceptorInfo[] interceptorInfos)


   
      
   
      if (bean == null)
      {
         extractBeanAndInterceptors();
      }
      return super.getInterceptorInstances(interceptorInfos);
   
public java.lang.ObjectgetInvokedMethodKey()

      return this.getId();
   
public java.util.concurrent.locks.ReentrantLockgetLock()

      return lock;
   
public org.jboss.aop.metadata.SimpleMetaDatagetMetaData()

      return super.getMetaData();
   
public booleangetReplicationIsPassivation()

      return replicationIsPassivation;
   
private java.util.ListgetThreadSafeContains()
Makes a copy of the contains list so nested callback iterators can iterate over it without concern that another thread will remove the context. TODO replace contains list with a concurrent collection

      // Call getContains() to ensure unmarshalling
      List<StatefulBeanContext> orig = getContains();
      List<StatefulBeanContext> copy = null;
      if (orig != null)
      {
         synchronized (orig)
         {
            copy = new ArrayList<StatefulBeanContext>(orig);
         }
      }
      return copy;
   
public org.jboss.ejb3.stateful.StatefulBeanContextgetUltimateContainedIn()

      StatefulBeanContext child = this;
      StatefulBeanContext parent = containedIn;
      
      while (parent != null)
      {
         child = parent;
         parent = parent.getContainedIn();
      }
      
      if (parent == null && this != child)
      {
         // Don't hand out a ref to our parent obtained by walking the
         // tree. Rather, get it from its cache.  This gives the cache
         // a chance to activate it if it hasn't been.  We don't want
         // to mark the parent as in use though.
         StatefulCache ultimateCache = ((StatefulContainer)child.getContainer()).getCache();
         child = ultimateCache.get(child.getId(), false);
      }
      
      return child;
   
public booleanisDiscarded()

      return discarded;
   
public booleanisInInvocation()

      return inInvocation;
   
public booleanisInUse()

      return inUse;
   
public booleanisRemoved()

      return removed;
   
public booleanisTxSynchronized()

      return txSynchronized;
   
public voidpassivateAfterReplication()
Notification from a ClusteredStatefulCache to inform that a bean that is stored in the distributed cache is now being passivated as well. Something of a misnomer as it is possible the bean wasn't replicated (if it implements Optimized it may have been activated and then a reference left in the cache without the bean ever being replicated).

      if (!removed && !passivated)
      {  
         getInstance(); // make sure we're unmarshalled
         getContainer().invokePrePassivate(this);
         passivated = true;
      }
      
      // Only bother informing children if we aren't already serialized.
      // If we're serialized, so are they and there's no point.
      // Notifying them would cause us to deserialize a beanMO to no purpose.
      if (contains != null)
      {
         // Pass the call on to any nested children
         List<StatefulBeanContext> children = getThreadSafeContains();
         if (children != null)
         {
            for (StatefulBeanContext contained : children)
            {
               contained.passivateAfterReplication();
            }
         }
      }
   
public voidpopContainedIn()

      propagatedContainedIn.pop();
   
public voidpostActivate()
Notification from a non-clustered StatefulCache to inform that we have been activated.

      if (!removed && passivated)
      {
         getContainer().invokePostActivate(this);
         passivated = false;
      }
      
      // Pass the call on to any nested children
      List<StatefulBeanContext> children = getThreadSafeContains();
      if (children != null)
      {  
         for (StatefulBeanContext contained : children)
         {
            contained.postActivate();
         }
      }
   
public voidpostReplicate()
Notification from a ClusteredStatefulCache after the bean is fetched from the distributed cache. Something of a misnomer as it is possible the bean wasn't replicated (if it implements Optimized it can be fetched from the cache twice without ever being replicated).

      // We may not have been replicated, so only invoke @PostActivate
      // if we are marked as passivated
      if (!removed && passivated)
      {
         getContainer().invokePostActivate(this);
         passivated = false;
      }
      
      // Pass the call on to any nested children
      List<StatefulBeanContext> children = getThreadSafeContains();
      if (children != null)
      {
         for (StatefulBeanContext contained : children)
         {
            contained.postReplicate();
         }
      }
   
public voidprePassivate()
Notification from a non-clustered StatefulCache to inform that we are about to be passivated.

      if (!removed && !passivated)
      {
         getContainer().invokePrePassivate(this);
         passivated = true;
      }
      
      // Pass the call on to any nested children
      List<StatefulBeanContext> children = getThreadSafeContains();
      if (children != null)
      {
         for (StatefulBeanContext contained : children)
         {
            contained.prePassivate();
         }
      }
   
public voidpreReplicate()
Notification from a ClusteredStatefulCache before a bean is replicated.

      if (!removed && replicationIsPassivation && !passivated)
      {
         getContainer().invokePrePassivate(this);
         passivated = true;
      }
      
      // Pass the call on to any nested children
      List<StatefulBeanContext> children = getThreadSafeContains();
      if (children != null)
      {
         for (StatefulBeanContext contained : children)
         {
            contained.preReplicate();
         }
      }
   
public org.jboss.ejb3.stateful.StatefulBeanContextpushContainedIn()

      StatefulBeanContext thisPtr = this;
      if (propagatedContainedIn.getList() != null)
      {
         // This is a nested stateful bean, within another stateful bean.
         // We need to create a nested bean context. The nested one will 
         // be put in the parent's list and owned by it. It is a special 
         // class because we do not want to put its state in a separate
         // marshalled object as we want to maintain object references 
         // between it and its parent. 
         
         // We also do not want to put the nested context within its container's
         // cache. If placed in the cache, it could be independently passivated,
         // activated and replicated, again breaking object references due to
         // independent marshalling. Instead, we return a proxy to it that will 
         // be stored in its container's cache         
         containedIn = propagatedContainedIn.get();
         NestedStatefulBeanContext nested = new NestedStatefulBeanContext();
         nested.id = id;
         nested.container = getContainer();
         nested.containerName = containerName;
         nested.bean = bean;
         nested.replicationIsPassivation = replicationIsPassivation;
         containedIn.addContains(nested);
         thisPtr = new ProxiedStatefulBeanContext(nested);
      }
      propagatedContainedIn.push(thisPtr);
      return thisPtr;
   
public voidreadExternal(java.io.ObjectInput in)

      containerName = in.readUTF();
      id = in.readObject();
      metadata = (SimpleMetaData) in.readObject();
      lastUsed = in.readLong();
      
      beanMO = (MarshalledObject) in.readObject();
      removed = in.readBoolean();
      replicationIsPassivation = in.readBoolean();
      
      // If we've just been deserialized, we are passivated
      passivated = true;
   
public voidremove()

      if (removed)
         return;
      removed = true;
      RuntimeException exceptionThrown = null;
      
      // Close any XPCs that haven't been injected into live 
      // beans in our family
      try
      {
         cleanExtendedPCs();
      }
      catch (RuntimeException e)
      {
         // we still need to remove ourself from any parent, so save
         // the thrown exception and rethrow it after we have cleaned up.
         if (exceptionThrown == null)
            exceptionThrown = e;
      }
      
      if (containedIn != null && getCanRemoveFromCache())
      {
         try
         {
            containedIn.removeContains(this);
         }
         catch (RuntimeException e)
         {
            // we still need to clean internal state, so save the
            // thrown exception and rethrow it after we have cleaned up.
            if (exceptionThrown == null)
               exceptionThrown = e;
         }
      }
            
      // Clear out refs to our bean and interceptors, to reduce our footprint
      // in case we are still cached for our refs to any XPCs
      bean = null;
      interceptorInstances = null;
      
      if (exceptionThrown != null) throw new RuntimeException("exception thrown while removing SFSB", exceptionThrown);
   
public voidremoveContains(org.jboss.ejb3.stateful.StatefulBeanContext ctx)

      if (getContains() != null) // call getContains() to ensure unmarshalling
      {
         // Need to be thread safe
         synchronized (contains)
         {
            if (contains.remove(ctx))
            {             
               ctx.containedIn = null;
            }
         }
         
         if (removed)
         {
            // Close out any XPCs that are no longer referenced
            cleanExtendedPCs();
         }
         
         if (getCanRemoveFromCache())
         {  
            if (containedIn != null)
            {
               containedIn.removeContains(this);               
            }
            
            //  Notify our cache to remove us as we no longer have children            
            ((StatefulContainer) getContainer()).getCache().remove(getId());
         }
      }
   
public voidremoveExtendedPersistenceContext(java.lang.String id)

      Map<String, EntityManager> extendedPCS = getExtendedPersistenceContexts();
      if (extendedPCS != null)
      {
         extendedPCS.remove(id);
      }
      
      if (getContains() != null)
      {
         synchronized (contains)
         {
            for (StatefulBeanContext contained: contains)
            {
               contained.removeExtendedPersistenceContext(id);
            }
         }
      }
   
public booleanscanForExtendedPersistenceContext(java.lang.String id, org.jboss.ejb3.stateful.StatefulBeanContext ignore)

      if (this.equals(ignore))
         return false;
      
      if (!removed)
      {
         Map<String, EntityManager> extendedPCS = getExtendedPersistenceContexts();
         if (extendedPCS != null && extendedPCS.containsKey(id))
            return true;
      }
      
      if (getContains() != null)
      {
         synchronized (contains)
         {
            for (StatefulBeanContext contained : contains)
            {
               if (!contained.equals(ignore))
               {
                  if (contained.scanForExtendedPersistenceContext(id, ignore))
                     return true;
               }
            }
         }
      }
      
      return false;
   
public voidsetContainer(org.jboss.ejb3.Container container)

      super.setContainer(container);
      containerName = container.getObjectName().getCanonicalName();
   
public voidsetDiscarded(boolean discarded)

      this.discarded = discarded;
   
public voidsetId(java.lang.Object id)

      this.id = id;
   
public voidsetInInvocation(boolean inInvocation)

      this.inInvocation = inInvocation;
   
public voidsetInUse(boolean inUse)

      this.inUse = inUse;
   
public voidsetReplicationIsPassivation(boolean replicationIsPassivation)

      this.replicationIsPassivation = replicationIsPassivation;
   
public voidsetTxSynchronized(boolean txSynchronized)

      this.txSynchronized = txSynchronized;
   
public voidwriteExternal(java.io.ObjectOutput out)

      out.writeUTF(containerName);
      out.writeObject(id);
      out.writeObject(metadata);
      out.writeLong(lastUsed);
      
      if (beanMO == null)
      {
         Object[] beanAndInterceptors = new Object[4];
         beanAndInterceptors[0] = bean;
         beanAndInterceptors[1] = persistenceContexts;
         if (interceptorInstances != null && interceptorInstances.size() > 0)
         {
            ArrayList list = new ArrayList();
            list.addAll(interceptorInstances.values());
            beanAndInterceptors[2] = list;
         }
         beanAndInterceptors[3] = contains;
         
         // BES 2007/02/12 Previously we were trying to hold a ref to
         // beanMO after we created it, but that exposes the risk of
         // two different versions of the constituent state that
         // can fall out of sync.  So now we just write a local variable.
         
         MarshalledObject mo = new MarshalledObject(beanAndInterceptors);
         out.writeObject(mo);         
      }
      else
      {
         // We've been deserialized and are now being re-serialized, but
         // extractBeanAndInterceptors hasn't been called in between.
         // This can happen if a passivated session is involved in a 
         // JBoss Cache state transfer to a newly deployed node.
         out.writeObject(beanMO);
      }
      

      out.writeBoolean(removed);
      out.writeBoolean(replicationIsPassivation);