FileDocCategorySizeDatePackage
EntityContainer.javaAPI DocGlassfish v2 API116297Thu Jun 07 18:29:30 BST 2007com.sun.ejb.containers

EntityContainer

public class EntityContainer extends BaseContainer implements com.sun.ejb.spi.stats.EntityBeanStatsProvider, com.sun.appserv.util.cache.CacheListener
This class implements the Container interface for EntityBeans. It is responsible for instance & lifecycle management for BMP & CMP EntityBeans. The EntityContainer implements option B of the commit-time options described in the EJB2.0 spec section 10.5.9 It also implements optimistic concurrency (i.e. multiple non-exclusive bean instances per primary key) when there are multiple concurrent transactions on a EntityBean.

The following sequence of actions happens for the EntityContainer, for each EJB lifecycle stage (note: getEJBObject, getContext, releaseContext, preInvokeTx, postInvokeTx are called from BaseContainer). 1. EJB Creation homeImpl.create, container.getContext, container.preInvokeTx, ejb.ejbCreate, container.postCreate, ejb.ejbPostCreate, container.postInvokeTx, container.releaseContext 2. EJB Finding homeImpl.find---, container.getContext, container.preInvokeTx, ejb.ejbFind---, container.postFind, container.postInvokeTx, container.releaseContext 3. EJB Invocation container.getEJBObject, ejbObject.someMethod, container.getContext, container.preInvokeTx, ejb.someMethod, container.postInvokeTx, container.releaseContext

State Management: The EntityContainer manages collections of EJBs in different states. The 5 states of an EntityBean (an EJB can be in only 1 state at a time):

  • 1. POOLED : does not have identity. EJBs in the POOLED state are all identical, hence are maintained in a java.util.Vector, whose size is maintained below a HIGH_WATER_MARK (currently 100).
  • 2. READY : ready for invocations, no transaction in progress. EJBs in the READY state are associated with a primary key. To enhance reuse of EJB instances, only one READY EJB per primary key is stored. READY EJBs are managed by the ejbstore/EntityStore class. READY EJBs are looked up using a key consisting of the primary key and a null transaction context.
  • 3. INVOKING : processing an invocation. EJBs in the INVOKING state are not stored anywhere. Before transitioning from READY or INCOMPLETE_TX to INVOKING, the EJB is removed from the EntityStore.
  • 4. INCOMPLETE_TX : ready for invocations, transaction in progress. EJBs in the INCOMPLETE_TX state are associated with a primary key. INCOMPLETE_TX EJBs are managed by the ejbstore/EntityStore class. INCOMPLETE_TX EJBs are looked up using a composite key consisting of the primary key and the transaction context.
  • 5. DESTROYED : does not exist.
All READY bean instances are stored in the readyStore. All INCOMPLETE_TX bean instances are stored in the ActiveTxCache. Beans in the READY state are stored with key = ejbObject. Beans in the INCOMPLETE_TX state are stored with key = ejbObject+Tx. Instances in INVOKING state which have transactions associated with them are also in ActiveTxCache. All POOLED instances are stored in the pooledEJBs vector. Note on locking order: if both ready/ActiveTxCache and context are to be locked, always acquire the context lock first, then the Store lock. Note on locking order: if both ready/ActiveTxCache and ejbObject need locks, always acquire the ejbObject lock first, then the Store lock.
author
Mahesh Kannan
author
Shanker N
author
Pramod Gopinath

Fields Summary
protected static final Logger
_logger
private ThreadLocal
ejbServant
protected static final com.sun.enterprise.util.LocalStringManagerImpl
localStrings
static final int
POOLED
static final int
READY
static final int
INVOKING
static final int
INCOMPLETE_TX
static final int
DESTROYED
protected static final int
HIGH_WATER_MARK
private static final int
DEFAULT_TX_CACHE_BUCKETS
protected com.sun.ejb.containers.util.cache.EJBObjectCache
ejbObjectStore
protected com.sun.ejb.containers.util.cache.EJBObjectCache
ejbLocalObjectStore
protected Stack
passivationCandidates
protected com.sun.appserv.util.cache.Cache
readyStore
protected AbstractPool
entityCtxPool
protected boolean
isReentrant
protected boolean
isContainerManagedPers
protected final float
DEFAULT_LOAD_FACTOR
protected final int
DEFAULT_CACHE_SIZE
protected int
_maxBuckets
protected com.sun.enterprise.deployment.runtime.IASEjbExtraDescriptors
iased
protected com.sun.enterprise.deployment.runtime.BeanCacheDescriptor
beanCacheDes
protected com.sun.enterprise.deployment.runtime.BeanPoolDescriptor
beanPoolDes
protected Server
svr
protected Config
cfg
protected EjbContainer
ejbContainer
boolean
largeCache
CacheProperties
cacheProp
PoolProperties
poolProp
Object
asyncTaskSemaphore
boolean
addedASyncTask
protected IdleBeansPassivator
idleEJBObjectPassivator
protected IdleBeansPassivator
idleLocalEJBObjectPassivator
protected boolean
defaultCacheEJBO
IdleBeansPassivator
idleBeansPassivator
boolean
timerValid
long
idleTimeout
protected int
ejboRemoved
protected int
totalPassivations
protected int
totalPassivationErrors
private EntityCacheStatsProvider
cacheStatsProvider
Constructors Summary
protected EntityContainer(EjbDescriptor desc, ClassLoader loader)
This constructor is called from the JarManager when a Jar is deployed.

exception
Exception on error


     
        _logger.log(Level.FINE," Loading Entitycontainer...");
    
        super(desc, loader);
        EjbEntityDescriptor ed = (EjbEntityDescriptor)desc;
        isReentrant = ed.isReentrant();
        if ( ed.getPersistenceType().equals(
            EjbEntityDescriptor.BEAN_PERSISTENCE) ) {
            isContainerManagedPers = false;
        } else {
            isContainerManagedPers = true;
        }
        
        iased = ed.getIASEjbExtraDescriptors();
        if( iased != null) {
            beanCacheDes = iased.getBeanCache();
            beanPoolDes = iased.getBeanPool();
        }
        try {
            ServerContext sc = ApplicationServer.getServerContext();
	       //ROB: config changes
            //svr = ServerBeansFactory.getServerBean(sc.getConfigContext());
            cfg = ServerBeansFactory.getConfigBean(sc.getConfigContext()); 
        }  catch (ConfigException ex) {
            _logger.log(Level.WARNING, "ejb.entitycontainer_exception", ex);
        }
        //ROB: config changes
        //ejbContainer = svr.getEjbContainer();
        ejbContainer = cfg.getEjbContainer();

        super.setMonitorOn(ejbContainer.isMonitoringEnabled());
        
        createCaches();
        
        super.createCallFlowAgent(
                isContainerManagedPers ? ComponentType.CMP : ComponentType.BMP);
        _logger.log(Level.FINE,"[EntityContainer] Created EntityContainer: "
                + logParams[0]);
    
Methods Summary
protected ComponentContext_getContext(Invocation inv)
Called from BaseContainer.preInvoke which is called from the EJBObject for local and remote invocations, and from the EJBHome for create/find.

        String name = inv.method.getName();
        
        if ( inv.invocationInfo.isCreateHomeFinder ) { 
            // create*, find*, home methods
            // Note: even though CMP finders dont need an instance,
            // we still return a pooled instance, so that the Tx demarcation
            // in BaseContainer.pre+postInvoke can work.
            
            // get any pooled EJB
            EntityContextImpl context = getPooledEJB();
            
            // we're sure that no concurrent thread can be using this
            // context, so no need to synchronize.
            context.setState(INVOKING);
            
            if ( inv.invocationInfo.startsWithCreate )
                preCreate(inv, context);
            else if ( inv.invocationInfo.startsWithFind )
                preFind(inv, context);


            context.setLastTransactionStatus(-1);
            context.incrementCalls();
            
            return context;
        }
        
        // If we came here, it means this is a business method
        // and there is an EJBObject/LocalObject.
        
        // If we would invoke the EJB with the client's Tx,
        // try to get an EJB with that incomplete Tx.
        EntityContextImpl context = null;
        if ( willInvokeWithClientTx(inv) )
            context = getEJBWithIncompleteTx(inv);
        if ( context == null )
            context = getReadyEJB(inv);
        
        synchronized ( context ) {
            if ( context.getState() == INVOKING && !isReentrant )
                throw new EJBException(
                    "EJB is already executing another request");
            if (context.getState() == POOLED ||
                context.getState() == DESTROYED) {
                // somehow a concurrent thread must have changed state.
                // this is an internal error.
                throw new EJBException("Internal error: unknown EJB state");
            }
            
            context.setState(INVOKING);
        }
        
        context.setLastTransactionStatus(-1);
        context.incrementCalls();
        // A business method may modify the bean's state
        context.setDirty(true);
        
        return context;
    
protected EntityContextImplactivateEJBFromPool(java.lang.Object primaryKey, Invocation inv)
Called from getContext and getEJBWithIncompleteTx Get an EJB in the ready state (i.e. which is not doing any invocations and doesnt have any incomplete Tx), for the ejbObject provided in the Invocation. Concurrent invocations should get *different* instances.

        EntityContextImpl context = null;
        // get a pooled EJB and activate it.
        context = getPooledEJB();
        
        // we're sure that no concurrent thread can be using this
        // context, so no need to synchronize.
        
        // set EJBObject/LocalObject for the context
        if ( inv.isLocal ) {
            EJBLocalObjectImpl localObjImpl = 
                internalGetEJBLocalObjectImpl(primaryKey, true);
            inv.ejbObject = localObjImpl;
            context.setEJBLocalObjectImpl(localObjImpl);
            // No need to create/set EJBObject if this EJB isRemote too.
		    // This saves remote object creation overhead.
		    // The EJBObject and stub will get created lazily if needed
		    // when EntityContext.getEJBObjectImpl is called.
        } else { // remote invocation
            EJBObjectImpl ejbObjImpl = 
                internalGetEJBObjectImpl(primaryKey, null, true);
            inv.ejbObject = ejbObjImpl;
            context.setEJBObjectImpl(ejbObjImpl);
            context.setEJBStub((EJBObject)ejbObjImpl.getStub());
            
            if ( isLocal ) {
                // Create EJBLocalObject so EntityContext methods work
                context.setEJBLocalObjectImpl(
                    internalGetEJBLocalObjectImpl(primaryKey, true));
            }
        }
        
        context.setState(READY);
        
        EntityBean ejb = (EntityBean)context.getEJB();
        
        Invocation inv2 = new Invocation(ejb, this, context);
        inv2.method = ejbActivateMethod;
        invocationManager.preInvoke(inv2);
        
        try {
            ejb.ejbActivate();
            
            // Note: ejbLoad will be called during preInvokeTx
            // since this EJB instance is being associated with
            // a Tx for the first time.
            
        } catch ( Exception ex ) {
            // Error during ejbActivate, discard bean: EJB2.0 18.3.3
            forceDestroyBean(context);
            throw new EJBException(ex);
        } finally {
            invocationManager.postInvoke(inv2);
        }
        
        context.setNewlyActivated(true);
        //recycler.initSoftRef(context);
        
        afterNewlyActivated(context);
        
        return context;
    
private voidaddIncompleteTxEJB(EntityContextImpl context)
Called only from afterBegin. This EJB is invoked either with client's tx (in which case it would already be in table), or with new tx (in which case it would not be in table).

        J2EETransaction current = (J2EETransaction) context.getTransaction();
        if ( current == null ) {
            return;
        }
        if ( (context.getEJBObjectImpl() == null) &&
             (context.getEJBLocalObjectImpl() == null) ) {
            return;
        }

        // Its ok to add this context without checking if its already there.
	ActiveTxCache activeTxCache = (ActiveTxCache) current.getActiveTxCache();
	if (activeTxCache == null) {
	    activeTxCache = new ActiveTxCache(DEFAULT_TX_CACHE_BUCKETS);
	    current.setActiveTxCache(activeTxCache);
	}

	activeTxCache.add(context);
        
        Vector beans = containerFactory.getBeans(current);
        beans.add(context);
    
protected voidaddPooledEJB(EntityContextImpl context)

        if ( context.getState() == POOLED ) {
            return;
        }
        // we're sure that no concurrent thread can be using this
        // context, so no need to synchronize.
        context.setEJBLocalObjectImpl(null);
        context.setEJBObjectImpl(null);
        context.setEJBStub(null);
        context.setState(POOLED);
	context.clearCachedPrimaryKey();
        
        //context.cacheEntry = null;
        entityCtxPool.returnObject(context);
        
    
protected voidaddReadyEJB(EntityContextImpl context)

        // add to the cache (can have multiple instances of beans per key)
        Object primaryKey = context.getPrimaryKey();
        context.setState(READY);
        readyStore.add(primaryKey, context);
    
voidafterBegin(EJBContextImpl ctx)

        // Note: EntityBeans are not allowed to be TX_BEAN_MANAGED
        if ( ctx.getState() == DESTROYED )
            return;
        
        EntityContextImpl context  = (EntityContextImpl)ctx;
        
        if ( context.getEJBObjectImpl() != null
             || context.getEJBLocalObjectImpl() != null ) {
            // ejbLoad needed only for business methods and removes
            
            // Add EJB to INCOMPLETE_TX table so that concurrent/loopback
            // invocations will be correctly handled
            if ( context.getTransaction() != null ) {
                addIncompleteTxEJB(context);
            }
            
            // need to call ejbLoad since there can be more than
            // one active EJB instance per primaryKey. (Option B in 9.11.5).
            EntityBean e = (EntityBean)context.getEJB();
            try {
                callEJBLoad(e, context, true);
            } catch ( NoSuchEntityException ex ) {
                _logger.log(Level.FINE, "Exception in afterBegin()", ex);
                // Error during ejbLoad, so discard bean: EJB2.0 18.3.3
                forceDestroyBean(context);
                
                throw new NoSuchObjectLocalException(
         "NoSuchEntityException thrown by ejbLoad, EJB instance discarded", ex);
            } catch ( Exception ex ) {
                // Error during ejbLoad, so discard bean: EJB2.0 18.3.3
                forceDestroyBean(context);
                
                throw new EJBException(ex);
            }
            
            context.setNewlyActivated(false);
        }
    
voidafterCompletion(EJBContextImpl ctx, int status)

        if ( ctx.getState() == DESTROYED ) {
            return;
        }

        if (super.isUndeployed()) {
	    transactionManager.ejbDestroyed(ctx);
	    return;
	}

        EntityContextImpl context = (EntityContextImpl)ctx;
        EJBLocalRemoteObject ejbObjImpl = context.getEJBObjectImpl();
        EJBLocalRemoteObject ejbLocalObjImpl = context.getEJBLocalObjectImpl();

        // home methods, finders and remove dont need this
        if ( ((ejbObjImpl != null) && !ejbObjImpl.isRemoved())
             || ((ejbLocalObjImpl != null) && !ejbLocalObjImpl.isRemoved()) ) {
            // Remove bean from ActiveTxCache table if its there.
            // No need to remove it from txBeanTable because the table
            // gets updated in ContainerFactoryImpl.removeContainerSync.

	    //removeIncompleteTxEJB(context, false);
            
            context.setTransaction(null);
            context.setLastTransactionStatus(status);

            context.setCascadeDeleteAfterSuperEJBRemove(false);
            context.setCascadeDeleteBeforeEJBRemove(false);
            
            // Move context to ready state if tx commited, else to pooled state
            if ( context.getState() != INVOKING ) {
                if ( (status == Status.STATUS_COMMITTED)
                     || (status == Status.STATUS_NO_TRANSACTION) ) {
                    addReadyEJB(context);
                } else {
                    passivateAndPoolEJB(context);
                }
            }
        } else if ((ejbObjImpl == null) && (ejbLocalObjImpl == null)) {
            // This happens if an ejbcreate has an exception, in that case
            // we remove bean from ActiveTxCache table if its there.
            // and return it to the pool
            //removeIncompleteTxEJB(context, false);
            
            context.setTransaction(null);
            context.setLastTransactionStatus(status);

            context.setCascadeDeleteAfterSuperEJBRemove(false);
            context.setCascadeDeleteBeforeEJBRemove(false);
            
            if ( context.getState() != INVOKING ) {
                addPooledEJB(context);
            }
	} else if ( ((ejbObjImpl != null) && ejbObjImpl.isRemoved())
	    || ((ejbLocalObjImpl != null) && ejbLocalObjImpl.isRemoved()) )
	{
	    //removeIncompleteTxEJB(context, false);
	    context.setTransaction(null);
	    context.setLastTransactionStatus(status);

	    if (context.getState() == INCOMPLETE_TX) {
		addPooledEJB(context);
	    }
	}
    
protected voidafterNewlyActivated(EntityContextImpl context)

        //Noop for EntityContainer
    
private static voidappendStat(java.lang.StringBuffer sbuf, java.lang.String header, java.util.Map map)

        
        sbuf.append("\n\t[").append(header).append(": ");
        if (map != null) {
            Iterator iter = map.keySet().iterator();
            
            while (iter.hasNext()) {
                String name = (String)iter.next();
                sbuf.append(name).append("=").append(map.get(name))
		    .append("; ");
            }
        } else {
            sbuf.append("NONE");
        }
        sbuf.append("]");
    
public voidappendStats(java.lang.StringBuffer sbuf)

	sbuf.append("\nEntityContainer: ")
	    .append("CreateCount=").append(statCreateCount).append("; ")
	    .append("RemoveCount=").append(statRemoveCount).append("; ")
	    .append("PassQSize=")
	    .append(passivationCandidates.size()).append("]");
        Map stats = null;
        if (readyStore != null) {
            stats = readyStore.getStats();
        }
        appendStat(sbuf, "ReadyStore", stats);
        
        appendStat(sbuf, "EJBObjectStore", ejbObjectStore.getStats());
        appendStat(sbuf, "EJBLocalObjectStore",ejbLocalObjectStore.getStats());
    
voidbeforeCompletion(EJBContextImpl ctx)

        if ( ctx.getState() == DESTROYED ) {
            return;
        }
        
        EntityContextImpl context = (EntityContextImpl)ctx;
        EJBLocalRemoteObject ejbObjImpl = context.getEJBObjectImpl();
        EJBLocalRemoteObject ejbLocalObjImpl = context.getEJBLocalObjectImpl();
        
        // Call ejbStore as required by diagram in EJB2.0 section 10.9.4
        // home methods, finders and remove dont need ejbStore
        if ( ((ejbObjImpl != null) && !ejbObjImpl.isRemoved())
             || ((ejbLocalObjImpl != null) && !ejbLocalObjImpl.isRemoved()) ) {
            if ( context.isDirty() ) {
                enlistResourcesAndStore(context);
            }
        }
    
protected voidcallEJBLoad(EntityBean ejb, EntityContextImpl context, boolean activeTx)

        try {
            context.setInEjbLoad(true);
            ejb.ejbLoad();
            // Note: no need to do context.setDirty(false) because ejbLoad is
            // called immediately before a business method.
        } catch(Exception e) {
            throw e;
        } finally {
            context.setInEjbLoad(false);
        }
    
protected voidcallEJBRemove(EntityBean ejb, EntityContextImpl context)

        Exception exc = null;
        try {
            context.setInEjbRemove(true);
            ejb.ejbRemove();
        } catch ( Exception ex ) {
            exc = ex;
            throw ex;
        } finally {
            context.setInEjbRemove(false);
            context.setDirty(false); // bean is removed so doesnt need ejbStore
            if ( AppVerification.doInstrument() ) {
                AppVerification.getInstrumentLogger().doInstrumentForEjb(
                ejbDescriptor, ejbRemoveMethod, exc);
            }
        }
    
protected voidcallEJBStore(EntityBean ejb, EntityContextImpl context)

        try {
            context.setInEjbStore(true);
            ejb.ejbStore();
        } finally {
            context.setInEjbStore(false);
            context.setDirty(false); // bean's state is in sync with DB
        }
    
public voidcancelTimerTasks()
cancel a timer task to trim timed out entries in the cache.

        timerValid = false;
        if (idleBeansPassivator != null) {
            try {
                idleBeansPassivator.cancel();
                idleBeansPassivator.cache  = null;
            } catch (Exception e) {
                _logger.log(Level.FINE, "[EntityContainer] cancelTimerTask: " + 
                    e);
            }
        }
        
        if (idleEJBObjectPassivator != null) {
            try {
                idleEJBObjectPassivator.cancel();
                idleEJBObjectPassivator.cache  = null;
            } catch (Exception e) {
                _logger.log(Level.FINE, "[EntityContainer] cancelTimerTask: " +
                    e);
            }
        }
        
        if (idleLocalEJBObjectPassivator != null) {
            try {
                idleLocalEJBObjectPassivator.cancel();
                idleLocalEJBObjectPassivator.cache  = null;
            } catch (Exception e) {
                _logger.log(Level.FINE, "[EntityContainer] cancelTimerTask: " +
                    e);
            }
        }
        
        this.idleEJBObjectPassivator    = null;
        this.idleLocalEJBObjectPassivator    = null;
        this.idleBeansPassivator = null;
    
voidcheckExists(EJBLocalRemoteObject ejbObj)
Check if the given EJBObject/LocalObject has been removed. Called before executing non-business methods of EJBLocalObject.

exception
NoSuchObjectLocalException if the object has been removed.

        // Need to call ejbLoad to see if persistent state is removed.
        // However, the non-business methods dont have a transaction attribute.
        // So do nothing for now.
    
protected voidcheckUnfinishedTx(javax.transaction.Transaction prevTx, Invocation inv)

                                     
        try {
            if ( (prevTx != null) &&
                 prevTx.getStatus() != Status.STATUS_NO_TRANSACTION ) {
                // An unfinished tx exists for the bean.
                // so we cannot invoke the bean with no Tx or a new Tx.
                throw new IllegalStateException(
                  "Bean is associated with a different unfinished transaction");
            }
        } catch (SystemException ex) {
            throw new EJBException(ex);
        }
    
private voidcreateCaches()


        cacheProp = new CacheProperties();
        
        int cacheSize = cacheProp.maxCacheSize;
        int numberOfVictimsToSelect = cacheProp.numberOfVictimsToSelect;
        float loadFactor = DEFAULT_LOAD_FACTOR;
        idleTimeout = cacheProp.cacheIdleTimeoutInSeconds * 1000;
        
        createReadyStore(cacheSize, numberOfVictimsToSelect, loadFactor, 
                         idleTimeout);
        
        createEJBObjectStores(cacheSize, numberOfVictimsToSelect, 
                              idleTimeout);
        
    
EJBLocalObjectImplcreateEJBLocalObjectImpl()

        throw new EJBException(
          "INTERNAL ERROR: EntityContainer.createEJBLocalObjectImpl() called");
    
EJBObjectImplcreateEJBObjectImpl()
Implementation of BaseContainer method. This is never called.

        throw new EJBException(
            "INTERNAL ERROR: EntityContainer.createEJBObject() called");
    
protected voidcreateEJBObjectStores(int cacheSize, int numberOfVictimsToSelect, long idleTimeout)

        
        EJBObjectCache lru = null;
        String ejbName = ejbDescriptor.getName();
        idleTimeout = (idleTimeout <= 0) ? -1 : idleTimeout;
        
        if (cacheSize <= 0 && idleTimeout <= 0) {
            ejbObjectStore = new UnboundedEJBObjectCache(ejbName);
            ejbObjectStore.init(DEFAULT_CACHE_SIZE, numberOfVictimsToSelect, 0L, 
                (float)1.0, null);
            
            ejbLocalObjectStore = new UnboundedEJBObjectCache(ejbName);
            ejbLocalObjectStore.init(DEFAULT_CACHE_SIZE,
                numberOfVictimsToSelect, 0L, (float)1.0, null);
        } else {
            cacheSize = (cacheSize <= 0) ? DEFAULT_CACHE_SIZE : cacheSize;
            ejbObjectStore = new FIFOEJBObjectCache(ejbName);
            ejbObjectStore.init(cacheSize, numberOfVictimsToSelect, idleTimeout,
                (float)1.0, null);
            ejbObjectStore.setEJBObjectCacheListener(
                new EJBObjectCacheVictimHandler());
            
            ejbLocalObjectStore = new FIFOEJBObjectCache(ejbName);
            ejbLocalObjectStore.init(cacheSize, numberOfVictimsToSelect, 
                idleTimeout, (float)1.0, null);
            ejbLocalObjectStore.setEJBObjectCacheListener(
                new LocalEJBObjectCacheVictimHandler());
        }
        
        if (idleTimeout > 0) {
            idleEJBObjectPassivator = setupIdleBeansPassivator(ejbObjectStore);
            idleLocalEJBObjectPassivator = 
                setupIdleBeansPassivator(ejbLocalObjectStore);
        }
    
protected EntityContextImplcreateEntityContextInstance(EntityBean ejb, com.sun.ejb.containers.EntityContainer entityContainer)

        return new EntityContextImpl(ejb, entityContainer);
    
protected voidcreateReadyStore(int cacheSize, int numberOfVictimsToSelect, float loadFactor, long idleTimeout)

        idleTimeout = (idleTimeout <= 0) ? -1 : idleTimeout;
        if (cacheSize <= 0 && idleTimeout <= 0) {
            readyStore = new BaseCache();
            cacheSize = DEFAULT_CACHE_SIZE;
            readyStore.init(cacheSize, loadFactor, null);
        } else {
            cacheSize = (cacheSize <= 0) ? DEFAULT_CACHE_SIZE : cacheSize;
            LruCache lru = new LruCache(DEFAULT_CACHE_SIZE);
            if (numberOfVictimsToSelect >= 0) {
                loadFactor = (float) (1.0 - (1.0 *
                                             numberOfVictimsToSelect/cacheSize));
            }
            lru.init(cacheSize, idleTimeout, loadFactor, null);
            readyStore = lru;
            readyStore.addCacheListener(this);
        }
        
        if (idleTimeout > 0) {
            idleBeansPassivator = setupIdleBeansPassivator(readyStore);
        }
    
protected voiddestroyReadyStoreOnUndeploy()

        if (readyStore == null) {
            return;
        }
        
        // destroy all EJB instances in readyStore
        synchronized ( readyStore ) {
            
            Iterator beans = readyStore.values();
            while ( beans.hasNext() ) {
                EJBContextImpl ctx = (EJBContextImpl)beans.next();
                transactionManager.ejbDestroyed(ctx);
            }
        }
        readyStore.destroy();
        readyStore = null;
    
protected voiddoFlush(Invocation inv)

        if( !inv.invocationInfo.flushEnabled ||
            inv.exception != null )  {
            return;
        }

        if( !isContainerManagedPers ) {
            //NEED TO INTERNATIONALIZE THIS WARNING MESSAGE
            _logger.log(Level.WARNING, 
                "Cannot turn on flush-enabled-at-end-of-method for a bean with Bean Managed Persistence");
            return;
        }

        InvocationInfo invInfo = inv.invocationInfo;
        Throwable exception = inv.exception;
        EntityContextImpl context = (EntityContextImpl)inv.context;
        Transaction tx = context.getTransaction();

        //Since postInvoke(Tx) has been called before the releaseContext, the transaction
        //could be committed or rolledback. In that case there is no point to call flush
        if( tx == null) {
            return;
        }

        //return w/o doing anything if the transaction is marked for rollback 
        try {
            if( context.getRollbackOnly() ) {
                return;
            }
        } catch( Throwable ex ) {
            _logger.log(Level.WARNING, "Exception when calling getRollbackOnly", ex);
            return;
        }

        if ( invInfo.isBusinessMethod ) {
            try {
                //Store the state of all the beans that are part of this transaction
                storeAllBeansInTx( tx );
            } catch( Throwable ex ) {
                inv.exception = ex;
                return;
            }
        }

        try {
            BeanStateSynchronization pmcontract = (BeanStateSynchronization)inv.ejb;
            pmcontract.ejb__flush();
        } catch( Throwable ex ) {
            //check the type of the method and create the corresponding exception
            if( invInfo.startsWithCreate ) {
                CreateException ejbEx = new CreateException();
                ejbEx.initCause(ex);
                inv.exception = ejbEx;
            } else if( invInfo.startsWithRemove ) {
                RemoveException ejbEx = new RemoveException();
                ejbEx.initCause(ex);
                inv.exception = ejbEx;
            } else {
                EJBException ejbEx = new EJBException();
                ejbEx.initCause(ex);
                inv.exception = ejbEx;
            }

            return;
        }

    
voiddoTimerInvocationInit(Invocation inv, RuntimeTimerState timerState)

        Object primaryKey = timerState.getTimedObjectPrimaryKey();
        if( isRemote ) {
            inv.ejbObject = internalGetEJBObjectImpl(primaryKey, null);
            inv.isLocal = false;
        } else {
            inv.ejbObject = internalGetEJBLocalObjectImpl(primaryKey);
            inv.isLocal = true;
        }
        if( inv.ejbObject == null ) {
            throw new Exception("Timed object identity (" + primaryKey +
                " ) no longer exists " );
        }
    
private voidenlistResourcesAndStore(EntityContextImpl context)

        EntityBean e = (EntityBean)context.getEJB();
        // NOTE : Use invocation instead of ComponentInvocation since
        // the context is available.  It is needed in case ejbStore/ejbLoad
        // makes use of EJB timer service in order to perform operations allowed
        // checks
        Invocation inv = new Invocation(e, this);
        inv.context = context;
        invocationManager.preInvoke(inv);
        
        try {
            transactionManager.enlistComponentResources();
            
            callEJBStore(e, context);
            
        } catch ( NoSuchEntityException ex ) {
            // Error during ejbStore, so discard bean: EJB2.0 18.3.3
            forceDestroyBean(context);
            
            throw new NoSuchObjectLocalException(
        "NoSuchEntityException thrown by ejbStore, EJB instance discarded", ex);
        } catch ( Exception ex ) {
            // Error during ejbStore, so discard bean: EJB2.0 18.3.3
            forceDestroyBean(context);
            throw new EJBException(ex);
        } finally {
            invocationManager.postInvoke(inv);
        }
    
voidforceDestroyBean(EJBContextImpl ctx)
Discard the bean instance. The bean's persistent state is not removed. This is usually called when the bean instance throws a system exception, from BaseContainer.postInvokeTx, getReadyEJB, afterBegin, beforeCompletion, passivateEJB.

        // Something bad happened (such as a RuntimeException),
        // so kill the bean and let it be GC'ed
        // Note: EJB2.0 section 18.3.1 says that discarding an EJB
        // means that no methods other than finalize() should be invoked on it.
        
        if ( ctx.getState() == DESTROYED ) {
            entityCtxPool.destroyObject(null);
            return;
        }
        
        EntityContextImpl context = (EntityContextImpl)ctx;
        EntityBean ejb = (EntityBean)context.getEJB();
        // Start of IAS 4661771
        synchronized ( context ) {
            try {
                Object primaryKey = context.getPrimaryKey();
                if ( primaryKey != null ) {
                    if ( context.getTransaction() != null ) {
                        Transaction txCurrent = context.getTransaction();
			ActiveTxCache activeTxCache = (ActiveTxCache) 
			    (((J2EETransaction) txCurrent).getActiveTxCache());
                        if (activeTxCache !=  null) {
			    // remove the context from the store
			    activeTxCache.remove(this, primaryKey);
			}
                    }
                    
                    // remove the context from readyStore as well
                    removeContextFromReadyStore(primaryKey, context);
                    
                    if (context.getEJBObjectImpl() != null) {
                        removeEJBObjectFromStore(primaryKey);
                    }
                    if (context.getEJBLocalObjectImpl() != null) {
                        ejbLocalObjectStore.remove(primaryKey);
                    }
                    
                }
            } catch ( Exception ex ) {
                _logger.log(Level.FINE, "Exception in forceDestroyBean()", ex);
            } finally {
                try {
                    //Very importatnt to set the state as destroyed otherwise
                    //	the pool.destroy might wrongly call unsetEntityContext
                    context.setState(DESTROYED);
                    entityCtxPool.destroyObject(context);
                } catch (Exception ex) {
                    _logger.log(Level.FINE, "Exception in forceDestroyBean()", 
                        ex);
                }
            }
        }
        // End of IAS 4661771
    
public EJBLocalObjectgetEJBLocalObjectForPrimaryKey(java.lang.Object pkey, EJBContext ctx)
Called only from the Persistence Manager for EJB2.0 CMP EntityBeans. Called only during cascade delete...... This is a private API between the PM and Container because there is no standard API defined in EJB2.0 for the PM to get an EJBLocalObject for a primary key (findByPrimaryKey cant be used because it may not run in the same tx). Example 1: A cascadeDeletes B and B calls getA() (expected return value: null) In the above case, getA() eventualy calls getEJBLocalObjectForPrimaryKey(PK_of_A, Ctx_of_B) We first check if B is in the process of being cascade deleted by checking the cascadeDeleteBeforeEJBRemove flag. If this flag is true, only then we bother to check if the Context associated with the PK_of_A in this transaction is marked for cascade delete which can be figured out by checking isCascadeDeleteAfterSuperEJBRemove() in A's context. If A is marked for cascade delete then we return null else the EJBLocalObject associated with A. Example 2: C cascadeDeletes B and B calls getA() (expected return value: EJBLocalObject for PK_of_A) In the above case, getA() eventualy calls getEJBLocalObjectForPrimaryKey(PK_of_A, Ctx_of_B) We first check if B is in the process of being cascade deleted by checking the cascadeDeleteBeforeEJBRemove flag. This flag will be true, and hence we check if the Context associated with the PK_of_A in this transaction is marked for cascade delete which can be figured out by checking isCascadeDeleteAfterSuperEJBRemove() in A's context. In this case this flag will be false and hcen we return the ejbLocalObject Example 2: B is *NOT* cascade deleted and B calls getA() (expected return value: EJBLocalObject for PK_of_A) In the above case, getA() eventualy calls getEJBLocalObjectForPrimaryKey(PK_of_A, Ctx_of_B) We first check if B is in the process of being cascade deleted by checking the cascadeDeleteBeforeEJBRemove flag. This flag will be FALSE, and hence we do not make any further check and return the EJBLocalObject associated with A

param
pkey The primary key for which the EJBLocalObject is required
param
ctx The context associated with the bean from which the accessor method is invoked
return
The EJBLocalObject associated with the PK or null if it is cascade deleted.


        EntityContextImpl context = (EntityContextImpl) ctx;
        EJBLocalObjectImpl ejbLocalObjectImpl = 
            internalGetEJBLocalObjectImpl(pkey);

        if (context.isCascadeDeleteBeforeEJBRemove()) {
            J2EETransaction current = null;
            try {
                current = (J2EETransaction) transactionManager.getTransaction();
            } catch ( SystemException ex ) {
                throw new EJBException(ex);
            }
	    ActiveTxCache activeTxCache = (current == null) ? null :
		(ActiveTxCache) (current.getActiveTxCache());
            if (activeTxCache != null) {
		EntityContextImpl ctx2 = (EntityContextImpl)
			activeTxCache.get(this, pkey);
		if ((ctx2 != null) && 
		    (ctx2.isCascadeDeleteAfterSuperEJBRemove())) {
		    return null;
		}
	    }
	    return (EJBLocalObject) ejbLocalObjectImpl.getClientObject();
        }

        return (EJBLocalObject) ejbLocalObjectImpl.getClientObject();
    
public EJBLocalObjectgetEJBLocalObjectForPrimaryKey(java.lang.Object pkey)
Called only from the Persistence Manager for EJB2.0 CMP EntityBeans. This is a private API between the PM and Container because there is no standard API defined in EJB2.0 for the PM to get an EJBLocalObject for a primary key (findByPrimaryKey cant be used because it may not run in the same tx).

        EJBLocalObjectImpl localObjectImpl = 
            internalGetEJBLocalObjectImpl(pkey);
	return (localObjectImpl != null) ? 
            (EJBLocalObject) localObjectImpl.getClientObject() : null;
    
EJBLocalObjectImplgetEJBLocalObjectImpl(java.lang.Object key)
Called from EJBLocalObjectImpl.getLocalObject() while deserializing a local object reference.

        return internalGetEJBLocalObjectImpl(key);
    
public EJBObjectgetEJBObjectForPrimaryKey(java.lang.Object pkey)
Called only from the Persistence Manager for EJB2.0 CMP EntityBeans. This is a private API between the PM and Container because there is no standard API defined in EJB2.0 for the PM to get an EJBObject for a primary key (home.findByPrimaryKey cant be used because it may not run in the same tx).

        // create stub without creating EJBObject
        return getEJBObjectStub(pkey, null);
    
EJBObjectImplgetEJBObjectImpl(byte[] streamKey)
Called only from ContainerFactory when a remote invocation arrives for an EJB.

        // First get the primary key of the EJB
        Object primaryKey;
        try {
            primaryKey = EJBUtils.deserializeObject(streamKey, loader, false);
        } catch ( Exception ex ) {
            throw new EJBException(ex);
        }
        
        return internalGetEJBObjectImpl(primaryKey, streamKey);
    
EJBObjectgetEJBObjectStub(java.lang.Object primaryKey, byte[] streamKey)

	
        // primary key cant be null, streamkey may be null

        // check if the EJBObject exists in the store.
        try {
            EJBObjectImpl ejbObjImpl = 
                (EJBObjectImpl) ejbObjectStore.get(primaryKey);
            if ( (ejbObjImpl != null) && (ejbObjImpl.getStub() != null) ) {
                return (EJBObject) ejbObjImpl.getStub();
            }

            // create a new stub without creating the EJBObject itself
            if ( streamKey == null ) {
                streamKey = EJBUtils.serializeObject(primaryKey, false);
            }
            EJBObject ejbStub = (EJBObject)
                remoteHomeRefFactory.createRemoteReference(streamKey);
                                                           
            return ejbStub;
        } catch ( Exception ex ) {
            _logger.log(Level.FINE,"", ex);
            throw new EJBException(ex);
        }
    
private EntityContextImplgetEJBWithIncompleteTx(Invocation inv)
Get an EJB instance for this EJBObject and current client Tx Called only from getContext. Return null if there no INCOMPLETE_TX bean for the pkey & tx.

        // We need to make sure that two concurrent client
        // invocations with same primary key and same client tx
        // get the SAME EJB instance.
        // So we need to maintain exactly one copy of an EJB's state
        // per transaction.
        
        J2EETransaction current = null;
        try {
            current = (J2EETransaction) transactionManager.getTransaction();
        } catch ( SystemException ex ) {
            throw new EJBException(ex);
        }
        
        EntityContextImpl ctx = null;
	if (current != null) {
	    ActiveTxCache activeTxCache = (ActiveTxCache) 
		current.getActiveTxCache();
	    ctx = (activeTxCache == null)
		    ? null : activeTxCache.get(this, inv.ejbObject.getKey());
        inv.foundInTxCache = (ctx != null);
	}
	
	return ctx;
    
private intgetEjbObjectStoreSize()
/ //Methods of EntityBeanStatsProvider public int getMaxCacheSize() { int maxSize = 0; if (readyStore != null) { maxSize = (cacheProp.maxCacheSize <= 0) ? Integer.MAX_VALUE : cacheProp.maxCacheSize; } return maxSize; } public int getSteadyPoolSize() { return entityCtxPool.getSteadyPoolSize(); } public int getMaxPoolSize() { return entityCtxPool.getMaxPoolSize(); } public long getPooledCount() { return entityCtxPool.getSize(); } public long getReadyCount() { return (readyStore == null) ? 0 : readyStore.getEntryCount(); } /

        return ejbObjectStore.getEntryCount();
    
public java.lang.StringgetMonitorAttributeValues()

        StringBuffer sbuf = new StringBuffer();
	appendStats(sbuf);
	return sbuf.toString();
    
protected EntityContextImplgetPooledEJB()

        try {
            return (EntityContextImpl) entityCtxPool.getObject(true, null);
        } catch (com.sun.ejb.containers.util.pool.PoolException inEx) {
            throw new EJBException(inEx);
        }
    
protected EntityContextImplgetReadyEJB(Invocation inv)

        Object primaryKey = inv.ejbObject.getKey();
        EntityContextImpl context = null;
        // Try and get an EJB instance for this primaryKey from the
        // readyStore
        context = (EntityContextImpl)readyStore.remove(primaryKey);
        if (context == null || context.getState() != READY) {
            context = activateEJBFromPool(primaryKey, inv);
        }
        return context;
    
protected voidinitializeHome()
Called from the ContainerFactory during initialization.

        ObjectFactory entityCtxFactory = new EntityContextFactory(this);
        
        int steadyPoolSize = 0;
        int resizeQuantity = 10;
        int idleTimeoutInSeconds = Integer.MAX_VALUE-1;
        poolProp = new PoolProperties();
        
        super.initializeHome();

        entityCtxPool = new NonBlockingPool(ejbDescriptor.getName(),
        	entityCtxFactory, poolProp.steadyPoolSize,
            poolProp.poolResizeQuantity, poolProp.maxPoolSize,
            poolProp.poolIdleTimeoutInSeconds, loader);


	registerMonitorableComponents();
    
protected EJBLocalObjectImplinternalGetEJBLocalObjectImpl(java.lang.Object primaryKey)
The following are private methods for implementing internal logic for lifecyle and state management, in a reusable way.

        return internalGetEJBLocalObjectImpl(primaryKey, false, 
                                             defaultCacheEJBO);
    
protected EJBLocalObjectImplinternalGetEJBLocalObjectImpl(java.lang.Object primaryKey, boolean incrementRefCount)

        return internalGetEJBLocalObjectImpl(primaryKey, incrementRefCount, 
            defaultCacheEJBO);
    
protected EJBLocalObjectImplinternalGetEJBLocalObjectImpl(java.lang.Object primaryKey, boolean incrementRefCount, boolean cacheEJBO)

        // check if the EJBLocalObject exists in the store.
        try {
            EJBLocalObjectImpl localObjImpl = (EJBLocalObjectImpl)
                ejbLocalObjectStore.get(primaryKey, incrementRefCount);
            if ( localObjImpl == null ) {

                localObjImpl = instantiateEJBLocalObjectImpl();
                
                // associate the EJBLocalObjectImpl with the primary key
                localObjImpl.setKey(primaryKey);
                
                // add the EJBLocalObjectImpl to ejbLocalObjectStore
                if (incrementRefCount || cacheEJBO) {
                    ejbLocalObjectStore.put(primaryKey, localObjImpl, 
                        incrementRefCount);
                }
            }
            return localObjImpl;
        } catch ( Exception ex ) {
            _logger.log(Level.SEVERE,"ejb.get_ejb_local_object_exception",
                        logParams);
            _logger.log(Level.SEVERE,"",ex);
            throw new EJBException(ex);
        }
    
protected EJBObjectImplinternalGetEJBObjectImpl(java.lang.Object primaryKey, byte[] streamKey)

        return internalGetEJBObjectImpl(primaryKey, streamKey, false, 
                                        defaultCacheEJBO);
    
protected EJBObjectImplinternalGetEJBObjectImpl(java.lang.Object primaryKey, byte[] streamKey, boolean incrementRefCount)

        return internalGetEJBObjectImpl
            (primaryKey, streamKey, incrementRefCount, defaultCacheEJBO);
    
protected EJBObjectImplinternalGetEJBObjectImpl(java.lang.Object primaryKey, byte[] streamKey, boolean incrementRefCount, boolean cacheEJBO)

        // primary key cant be null, streamkey may be null
        
        // check if the EJBContext/EJBObject exists in the store.
        try {
            
            EJBObjectImpl ejbObjImpl = (EJBObjectImpl) 
                ejbObjectStore.get(primaryKey, incrementRefCount);

            if ( (ejbObjImpl != null) && (ejbObjImpl.getStub() != null) ) {
                return ejbObjImpl;
            }
            
            // check if the EJBContext/EJBObject exists in threadlocal
            // This happens if ejbo is in the process of being created.
            // This is necessary to prevent infinite recursion
            // because PRO.narrow calls is_a which calls the
            // ProtocolMgr which calls getEJBObject.
            ejbObjImpl = (EJBObjectImpl) ejbServant.get();
            if ( ejbObjImpl != null ) {
                return ejbObjImpl;
            }
            
            // create the EJBObject.
            ejbObjImpl = instantiateEJBObjectImpl();
            
            // associate the EJBObject with the primary key
            ejbObjImpl.setKey(primaryKey);
            
            // set ejbo in thread local to help recursive calls find the ejbo
            ejbServant.set(ejbObjImpl);
            
            // "Connect" the EJBObject to the Protocol Manager
            
            if ( streamKey == null ) {
                streamKey = EJBUtils.serializeObject(primaryKey, false);
            }
            EJBObject ejbStub = (EJBObject)
                remoteHomeRefFactory.createRemoteReference(streamKey);
                                                           
            ejbObjImpl.setStub(ejbStub);
            ejbServant.set(null);
            
            if ((incrementRefCount || cacheEJBO)) {
                EJBObjectImpl ejbo1 = 
                    (EJBObjectImpl) ejbObjectStore.put(primaryKey, ejbObjImpl, 
                        incrementRefCount);
                if ((ejbo1 != null) && (ejbo1 != ejbObjImpl)) {
                    remoteHomeRefFactory.destroyReference(ejbObjImpl.getStub(), 
                                                      ejbObjImpl);
                    ejbObjImpl = ejbo1;
                }
            }
            
            return ejbObjImpl;
        }
        catch ( Exception ex ) {
            _logger.log(Level.FINE, "ejb.get_ejb_context_exception", logParams);
            _logger.log(Level.FINE,"",ex);
            throw new EJBException(ex);
        }
    
private voidinternalRemoveBeanUnchecked(EJBLocalRemoteObject localRemoteObj, boolean local)
Remove a bean. Used by the PersistenceManager. This is needed because the PM's remove must bypass tx/security checks.

        Invocation inv = new Invocation();
        inv.ejbObject = localRemoteObj;
        inv.isLocal = local;
        Method method=null;
        try {
            method = EJBLocalObject.class.getMethod("remove", NO_PARAMS);
        } catch ( NoSuchMethodException e ) {
            _logger.log(Level.FINE, 
                "Exception in internalRemoveBeanUnchecked()", e);
        }
        inv.method = method;
        
        inv.invocationInfo = (InvocationInfo) invocationInfoMap.get(method);
        
        try {
            // First get a bean instance on which ejbRemove can be invoked.
            // This code must be in sync with getContext().
            // Can't call getContext() directly because it does stuff
            // based on remove's txAttr.
            // Assume there is a tx on the current thread.
            EntityContextImpl context = getEJBWithIncompleteTx(inv);
            if ( context == null ) {
                context = getReadyEJB(inv);
            }
            
            synchronized ( context ) {
                if ( context.getState() == INVOKING && !isReentrant ) {
                    throw new EJBException(
                        "EJB is already executing another request");
                }
                if (context.getState() == POOLED ||
                    context.getState() == DESTROYED) {
                    // somehow a concurrent thread must have changed state.
                    // this is an internal error.
                    throw new EJBException("Internal error: unknown EJB state");
                }
                
                context.setState(INVOKING);
            }
            inv.context = context;
            context.setLastTransactionStatus(-1);
            context.incrementCalls();
            
            inv.instance = inv.ejb = context.getEJB();
            inv.container = this;
            invocationManager.preInvoke(inv);
            
            // call ejbLoad if necessary
            useClientTx(context.getTransaction(), inv);
            
            try {
                context.setCascadeDeleteBeforeEJBRemove(true);
                removeBean(inv);
            } catch ( Exception ex ) {
                _logger.log(Level.FINE, 
                    "Exception in internalRemoveBeanUnchecked()", ex);
                // if system exception mark the tx for rollback
                inv.exception = checkExceptionClientTx(context, ex);
            }
            if ( inv.exception != null ) {
                throw inv.exception;
            }
        }
        catch ( RuntimeException ex ) {
            throw ex;
        }
        catch ( Exception ex ) {
            throw new EJBException(ex);
        }
        catch ( Throwable ex ) {
            EJBException ejbEx = new EJBException();
            ejbEx.initCause(ex);
            throw ejbEx;
        }
        finally {
            invocationManager.postInvoke(inv);
            releaseContext(inv);
        }
    
protected java.lang.ObjectinvokeFindByPrimaryKey(java.lang.reflect.Method method, Invocation inv, java.lang.Object[] args)

	Object pKeys = super.invokeTargetBeanMethod(method,
	    inv, inv.ejb, args, null);
	return postFind(inv, pKeys, null);
    
booleanisIdentical(EJBObjectImpl ejbObjImpl, EJBObject other)

        if ( other == ejbObjImpl.getStub() ) {
            return true;
        } else {
            try {
                // EJBObject may be a remote object.
                // Compare homes. See EJB2.0 spec section 9.8.
                if ( !protocolMgr.isIdentical(ejbHomeStub,
                                              other.getEJBHome()))
                    return false;
                
                // Compare primary keys.
                if (!ejbObjImpl.getPrimaryKey().equals(other.getPrimaryKey())) {
                    return false;
                }
                
                return true;
            } catch ( Exception ex ) {
                _logger.log(Level.INFO, "ejb.ejb_comparison_exception",
                            logParams);
                _logger.log(Level.INFO, "", ex);
                throw new RemoteException("Exception in isIdentical()", ex);
            }
        }
    
public voidonReady()

    
protected voidpassivateAndPoolEJB(EntityContextImpl context)

        if ( context.getState() == DESTROYED || context.getState() == POOLED )
            return;
        
        // if ( context.isPooled() ) {
        // context.isPooled(false);
        // return;
        // }
        EntityBean ejb = (EntityBean) context.getEJB();
        synchronized ( context ) {
            Invocation inv = new Invocation(ejb, this, context);
            inv.method = ejbPassivateMethod;
            invocationManager.preInvoke(inv);
            
            try {
                ejb.ejbPassivate();
            } catch ( Exception ex ) {
                _logger.log(Level.FINE,"Exception in passivateAndPoolEJB()",ex);
                forceDestroyBean(context);
                return;
            } finally {
                invocationManager.postInvoke(inv);
            }
            
            // remove EJB(Local)Object from ejb(Local)ObjectStore
            
            
            Object primaryKey = context.getPrimaryKey();
            if ( isRemote ) {
                removeEJBObjectFromStore(primaryKey);
            }
            if ( isLocal ) {
                ejbLocalObjectStore.remove(primaryKey);
            }
            
            addPooledEJB(context);
        }
    
booleanpassivateEJB(ComponentContext ctx)

        if (containerState != CONTAINER_STARTED) {
            return false;
        }
        
        EntityContextImpl context = (EntityContextImpl)ctx;
        
        if (context.getState() != READY) {
            return false;
        }
        
        if(_logger.isLoggable(Level.FINEST)) {
            _logger.log(Level.FINEST,"EntityContainer.passivateEJB(): context = (" +
                ctx + ")");
        }
        EntityBean ejb = (EntityBean)context.getEJB();
        
        Invocation inv = new Invocation(ejb, this, context);
        inv.method = ejbPassivateMethod;
        
        Object pkey = context.getPrimaryKey();
        boolean wasPassivated = false;
        
        // check state after locking ctx
        if ( context.getState() != READY )
            return false;
        try {
            invocationManager.preInvoke(inv);
            
            // remove EJB from readyStore
            removeContextFromReadyStore(pkey, context);
            
            // no Tx needed for ejbPassivate
            ejb.ejbPassivate();
            
            wasPassivated = true;
        } catch ( Exception ex ) {
            _logger.log(Level.FINE, "Exception in passivateEJB()", ex);
            // Error during ejbStore/Passivate, discard bean: EJB2.0 18.3.3
            forceDestroyBean(context);
            return false;
        } finally {
            invocationManager.postInvoke(inv);
        }
        
        // Remove the ejbObject/LocalObject from ejbObject/LocalObjectStore
        // If a future invocation arrives for them, they'll get recreated.
        if ( isRemote ) {
            removeEJBObjectFromStore(pkey);
        }
        if ( isLocal ) {
            ejbLocalObjectStore.remove(pkey);
        }
        
        // Note: ejbStore and ejbPassivate need the primarykey
        // so we should dissociate the context from EJBObject only
        // after calling ejbStore and ejbPassivate.
        synchronized (context) {
            addPooledEJB(context);
        }
        
        return wasPassivated;
    
public voidpostCreate(Invocation inv, java.lang.Object primaryKey)
This is called from the generated "HelloEJBHomeImpl" create* method, after ejb.ejbCreate() has been called and before ejb.ejbPostCreate() is called. Note: postCreate will not be called if ejbCreate throws an exception

        if ( primaryKey == null )
            throw new EJBException(
                "Null primary key returned by ejbCreate method");
        
        EntityContextImpl context = (EntityContextImpl)inv.context;
        EJBObjectImpl ejbObjImpl  = null;
        EJBLocalObjectImpl localObjImpl = null;
        
        if ( (isRemote) && (!inv.isLocal) ) {
            // remote invocation: create EJBObject
            ejbObjImpl = internalGetEJBObjectImpl(primaryKey, null, true);
            
            // associate the context with the ejbObject
            context.setEJBObjectImpl(ejbObjImpl);
            context.setEJBStub((EJBObject)ejbObjImpl.getStub());
        }
        
        if ( isLocal ) {
            // create EJBLocalObject irrespective of local/remote invocation
            // this is necessary to make EntityContext.getPrimaryKey and
            // EntityContext.getEJBObject work.
            localObjImpl = internalGetEJBLocalObjectImpl(primaryKey, true);
            
            // associate the context with the ejbLocalObject
            context.setEJBLocalObjectImpl(localObjImpl);
        }
        
        if ( inv.isLocal )
            inv.ejbObject = localObjImpl;
        else
            inv.ejbObject = ejbObjImpl;
        
        if ( context.getTransaction() != null ) {
            // Add EJB to INCOMPLETE_TX table so that concurrent/loopback
            // invocations will be correctly handled
            addIncompleteTxEJB(context);
        }
        
        context.setDirty(true); // ejbPostCreate could modify state
    
public java.lang.ObjectpostFind(Invocation inv, java.lang.Object primaryKeys, java.lang.Object[] findParams)
Convert a collection of primary keys to a collection of EJBObjects. (special case: single primary key). Note: the order of input & output collections must be maintained. Null values are preserved in both the single primary key return and collection-valued return cases. This is called from the generated "HelloEJBHomeImpl" find* method, after ejb.ejbFind**() has been called. Note: postFind will not be called if ejbFindXXX throws an exception

                
        if ( primaryKeys instanceof Enumeration ) {
            // create Enumeration of objrefs from Enumeration of primaryKeys
            Enumeration e = (Enumeration)primaryKeys;
            // this is a portable Serializable Enumeration
            ObjrefEnumeration objrefs = new ObjrefEnumeration();
            while ( e.hasMoreElements() ) {
                Object primaryKey = e.nextElement();
                Object ref;
                if( primaryKey != null ) {
                    if ( inv.isLocal )
                        ref = getEJBLocalObjectForPrimaryKey(primaryKey);
                    else
                        ref = getEJBObjectStub(primaryKey, null);
                    objrefs.add(ref);
                } else {
                    objrefs.add(null);
                }
            }
            return objrefs;
        } else if ( primaryKeys instanceof Collection ) {
            // create Collection of objrefs from Collection of primaryKeys
            Collection c = (Collection)primaryKeys;
            Iterator it = c.iterator();
            ArrayList objrefs = new ArrayList();  // a Serializable Collection
            while ( it.hasNext() ) {
                Object primaryKey = it.next();
                Object ref;
                if( primaryKey != null ) {
                    if ( inv.isLocal )
                        ref = getEJBLocalObjectForPrimaryKey(primaryKey);
                    else
                        ref = getEJBObjectStub(primaryKey, null);
                    objrefs.add(ref);
                } else {
                    objrefs.add(null);
                }
            }
            return objrefs;
        } else {
            if( primaryKeys != null ) {
                if ( inv.isLocal )
                    return getEJBLocalObjectForPrimaryKey(primaryKeys);
                else
                    return getEJBObjectStub(primaryKeys, null);
            } else {
                return null;
            }
        }
    
voidpostInvokeNoTx(Invocation inv)

        // This calls ejbStore to allow bean to flush any state to database.
        // This is also sufficient for compliance with EJB2.0 section 12.1.6.1
        // (ejbStore must be called between biz method and ejbPassivate).
        beforeCompletion((EJBContextImpl)inv.context);
    
protected InvocationInfopostProcessInvocationInfo(InvocationInfo invInfo)

        Method method = invInfo.method;
        boolean isCMPField = isContainerManagedPers && invInfo.isBusinessMethod
                && invInfo.methodIntf.equals(MethodDescriptor.EJB_LOCAL);
        if (isCMPField) {
            String methodName = method.getName();
            isCMPField = methodName.startsWith("get") 
                    || methodName.startsWith("set");
            if (isCMPField) {
                try {
                    //ejbClass is the container-generated implementation class.
                    //Need to get its superclass, which is provided by the bean provider.
                    Method methodInBeanClass = ejbClass.getSuperclass().getMethod(
                            methodName, method.getParameterTypes());
                    isCMPField = Modifier.isAbstract(methodInBeanClass.getModifiers());
                } catch (NoSuchMethodException ignore) {
                    isCMPField = false;
                }
            }
        }
        invInfo.isTxRequiredLocalCMPField = isCMPField
                && (invInfo.txAttr == TX_REQUIRED);
        return invInfo;
    
protected voidpreCreate(Invocation inv, EntityContextImpl context)
Called from getContext before the ejb.ejbCreate is called

	statCreateCount++;
    
protected voidpreFind(Invocation inv, EntityContextImpl context)
Called from getContext before the ejb.ejbFind* is called

        // if the finder is being invoked with the client's transaction,
        // call ejbStore on all dirty bean instances associated with that
        // transaction. This ensures that the finder results will include
        // all updates done previously in the client's tx.
        if ( willInvokeWithClientTx(inv) &&
        !inv.method.getName().equals("findByPrimaryKey") ) {
            Transaction tx = null;
            try {
                tx = transactionManager.getTransaction();
            } catch ( SystemException ex ) {
                throw new EJBException(ex);
            }
            
            storeAllBeansInTx( tx );
        }
        
    
protected voidpreInitialize(EjbDescriptor desc, java.lang.ClassLoader loader)

        EjbEntityDescriptor ed = (EjbEntityDescriptor)desc;
        isReentrant = ed.isReentrant();
        if ( ed.getPersistenceType().equals(
            EjbEntityDescriptor.BEAN_PERSISTENCE) ) {
            isContainerManagedPers = false;
        } else {
            isContainerManagedPers = true;
        }
        _logger.log(Level.FINE,"[EntityContainer] preInitialize==>isContainerManagedPers: "
                + isContainerManagedPers);
    
voidpreInvokeNoTx(Invocation inv)

        EntityContextImpl context = (EntityContextImpl)inv.context;
        
        if ( context.getState() == DESTROYED ) {
            return;
        }
        
        if ( context.isNewlyActivated() && 
            !inv.invocationInfo.isCreateHomeFinder ) {
            // follow EJB2.0 section 12.1.6.1
            EntityBean e = (EntityBean)context.getEJB();
            try {
                callEJBLoad(e, context, false);
            } catch ( NoSuchEntityException ex ) {
                // Error during ejbLoad, so discard bean: EJB2.0 18.3.3
                forceDestroyBean(context);
                
                throw new NoSuchObjectLocalException(
         "NoSuchEntityException thrown by ejbLoad, EJB instance discarded", ex);
            } catch ( Exception ex ) {
                // Error during ejbLoad, so discard bean: EJB2.0 18.3.3
                forceDestroyBean(context);
                
                throw new EJBException(ex);
            }
            
            context.setNewlyActivated(false);
        }
    
public voidpreSelect()
Called from CMP PersistentManager

	// if the ejbSelect is being invoked with the client's transaction,
        // call ejbStore on all dirty bean instances associated with that
        // transaction. This ensures that the select results will include
        // all updates done previously in the client's tx.
	_logger.fine(" inside preSelect...");
	Transaction tx = null;
	try {
	    _logger.fine("PRESELECT : getting transaction...");
	    tx = transactionManager.getTransaction();
	} catch ( SystemException ex ) {
	    throw new EJBException(ex);
	}
	_logger.fine("PRESELECT : calling storeAllBeansInTx()...");
	storeAllBeansInTx( tx );                
    
protected voidregisterMonitorableComponents()

	registryMediator.registerProvider(this);
	registryMediator.registerProvider(entityCtxPool);
	if (readyStore != null) {
	    int confMaxCacheSize = cacheProp.maxCacheSize;
	    if (confMaxCacheSize <= 0) {
		confMaxCacheSize = Integer.MAX_VALUE;
	    }
	    this.cacheStatsProvider = new EntityCacheStatsProvider(
		    (BaseCache) readyStore, confMaxCacheSize);
	    registryMediator.registerProvider(cacheStatsProvider);
	}
        super.registerMonitorableComponents();
	super.populateMethodMonitorMap();
        _logger.log(Level.FINE, "[Entity Container] registered monitorable");
    
public voidreleaseContext(Invocation inv)
This is called from BaseContainer.postInvoke after EntityContainer.preInvokeTx has been called.

        EntityContextImpl context = (EntityContextImpl)inv.context;
        boolean decrementedCalls = false; // End of IAS 4661771
        
        if ( context.getState()==DESTROYED )
            return;
        
        try {
            if ( context.hasReentrantCall() ) {
                // For biz->biz or postCreate->biz, the bean instance will
                // remain in the incomplete-tx table.
                if ( inv.ejbObject.isRemoved() ) {
                    // biz -> remove case (biz method invoked reentrant remove)
                    // Remove from IncompleteTx table, to prevent further
                    // reentrant calls.
                    removeIncompleteTxEJB(context, true);
                    
                    // disconnect context from EJB(Local)Object so that
                    // context.getEJBObject() will throw exception.
                    if ( context.getEJBObjectImpl() != null ) {
                        // reset flag in case EJBObject is used again
                        context.getEJBObjectImpl().setRemoved(false);
                        context.setEJBObjectImpl(null);
                        context.setEJBStub(null);
                    }
                    if ( context.getEJBLocalObjectImpl() != null ) {
                        // reset flag in case EJBLocalObject is used again
                        context.getEJBLocalObjectImpl().setRemoved(false);
                        context.setEJBLocalObjectImpl(null);
                    }
                } else {
                    if ( context.getState() == INVOKING )  {
                        doFlush( inv );
                    }
                }
                
                // Note: at this point context.getState() is INVOKING.
            } else if ( context.getEJBObjectImpl()==null
                && context.getEJBLocalObjectImpl()==null ) {
                // This can only happen if the method was ejbFind
                // OR if the method was ejbCreate which threw an application
                // exception (so postCreate was not called)
                // OR after a biz method which called a reentrant remove.
                // So bean instance goes back into pool.
                // We dont care if any Tx has completed or not.
                //context.setTransaction(null);
                decrementedCalls = true;
                context.decrementCalls();
                if (!(inv.invocationInfo.startsWithCreate)) {
                    context.setTransaction(null);
                    addPooledEJB(context);
                }else if(context.getTransaction() == null) {
                    addPooledEJB(context);
                } else {
                    // Set the state to incomplete as the transaction
                    // is not done still and afterCompletion will
                    // handle stuff
                    context.setState(INCOMPLETE_TX);
                }
            } else if ( inv.ejbObject.isRemoved() ) {
                // EJBObject/LocalObject was removed, so bean instance
                // goes back into pool.
                // We dont care if any Tx has completed or not.
                removeIncompleteTxEJB(context, true);
                // unset the removed flag, in case the EJB(Local)Object
                // ref is held by the client and is used again
                if ( context.getEJBObjectImpl() != null )
                    context.getEJBObjectImpl().setRemoved(false);
                if ( context.getEJBLocalObjectImpl() != null )
                    context.getEJBLocalObjectImpl().setRemoved(false);
                
                decrementedCalls = true;
                context.decrementCalls();
                if(context.getTransaction() == null) {
                    addPooledEJB(context);
                } else {
                    // Set the state to incomplete as the transaction
                    // is not done still and afterCompletion will
                    // handle stuff
                    context.setState(INCOMPLETE_TX);
                }
                
            } else if ( context.getTransaction() == null ) {
                // biz methods and ejbCreate
                // Either the EJB was called with no tx,
                // or it was called with a tx which finished,
                // so afterCompletion was already called.
                
                // If no tx or tx committed, then move the EJB to READY state
                // else pool the bean
                int status = context.getLastTransactionStatus();
                decrementedCalls = true;
                context.decrementCalls();
                context.setLastTransactionStatus(-1);
                if ( status == -1 || status == Status.STATUS_COMMITTED
                || status == Status.STATUS_NO_TRANSACTION )
                    addReadyEJB(context);
                else
                    passivateAndPoolEJB(context);
            } else {
                // biz methods and ejbCreate
                // The EJB is still associated with a Tx.
                // It will already be in the INCOMPLETE_TX table.
                context.setState(INCOMPLETE_TX);

                doFlush( inv );
            }
        } catch ( Exception ex ) {
            _logger.log(Level.FINE, "ejb.release_context_exception",
                        logParams);
            _logger.log(Level.FINE, "",ex);
            throw new EJBException(ex);
        } finally {
            if (decrementedCalls == false) {
                context.decrementCalls();
            }
            context.touch();
        }
    
protected voidremoveBean(java.lang.Object primaryKey, java.lang.reflect.Method removeMethod, boolean local)

        EJBLocalRemoteObject ejbo;
        if ( local ) {
            ejbo = internalGetEJBLocalObjectImpl(primaryKey, false, true);
        }
        else { // may be remote-only bean
            ejbo = internalGetEJBObjectImpl(primaryKey, null, false, true);
        }
        removeBean(ejbo, removeMethod, local);
    
protected voidremoveBean(EJBLocalRemoteObject ejbo, java.lang.reflect.Method removeMethod, boolean local)

        Invocation i = new Invocation();
        i.ejbObject = ejbo;
        i.isLocal = local;
        i.method = removeMethod;
        
        // Method must be a remove method defined on one of :
        // javax.ejb.EJBHome, javax.ejb.EJBObject, javax.ejb.EJBLocalHome,
        // javax.ejb.EJBLocalObject
        Class declaringClass = removeMethod.getDeclaringClass();
        i.isHome = ( (declaringClass == javax.ejb.EJBHome.class) ||
                     (declaringClass == javax.ejb.EJBLocalHome.class) );

        try {
            preInvoke(i);
            removeBean(i);
        } catch(Exception e) {
            _logger.log(Level.SEVERE,"ejb.preinvoke_exception",logParams);
            _logger.log(Level.SEVERE,"",e);
            i.exception = e;
        } finally {
            postInvoke(i);
        }
        
        if(i.exception != null) {
            if(i.exception instanceof RemoveException) {
                throw (RemoveException)i.exception;
            }
            else if(i.exception instanceof RuntimeException) {
                throw (RuntimeException)i.exception;
            }
            else if(i.exception instanceof Exception) {
                throw new EJBException((Exception)i.exception);
            }
            else {
                EJBException ejbEx = new EJBException();
                ejbEx.initCause(i.exception);
                throw ejbEx;
            }
        }
    
protected voidremoveBean(Invocation inv)
container.preInvoke() must already be done. So this will be called with the proper Tx context.

exception
RemoveException if an error occurs while removing the bean

        try {
	    statRemoveCount++;
            // Note: if there are concurrent invocations/transactions in
            // progress for this ejbObject, they will be serialized along with
            // this remove by the database. So we optimistically do ejbRemove.
            
            // call ejbRemove on the EJB
            // the EJB is allowed to veto the remove by throwing RemoveException
            EntityBean ejb = (EntityBean)inv.ejb;
            EntityContextImpl context = (EntityContextImpl)inv.context;
            callEJBRemove(ejb, context);
            
            // inv.ejbObject could be a EJBObject or a EJBLocalObject
            Object primaryKey = inv.ejbObject.getKey();
            if ( isRemote ) {
                removeEJBObjectFromStore(primaryKey);
                
                // Mark EJB as removed. Now releaseContext will add bean to pool
                if ( context.getEJBObjectImpl() != null ) {
                    context.getEJBObjectImpl().setRemoved(true);
                }
            }
            
            if ( isLocal ) {
                // Remove the EJBLocalObject from ejbLocalObjectStore
                ejbLocalObjectStore.remove(primaryKey);
                // Mark EJB as removed. Now releaseContext will add bean to pool
                if ( context.getEJBLocalObjectImpl() != null ) {
                    context.getEJBLocalObjectImpl().setRemoved(true);
                }
            }
            
            // Remove any timers for this entity bean identity.
            EJBTimerService ejbTimerService =
                containerFactory.getEJBTimerService();
            if( (ejbTimerService != null) && isTimedObject() ) {
                ejbTimerService.cancelEntityBeanTimers(getContainerId(),
                                                       primaryKey);
            } 

        } catch ( RemoveException ex ) {
            if(_logger.isLoggable(Level.FINE)) {
                _logger.log(Level.FINE,"ejb.local_remove_exception",logParams);
                _logger.log(Level.FINE,"",ex);
            }
            throw ex;
        }
        catch ( Exception ex ) {
            if(_logger.isLoggable(Level.FINE)) {
                _logger.log(Level.FINE,"ejb.remove_bean_exception",logParams);
                _logger.log(Level.FINE,"",ex);
            }
            throw new EJBException(ex);
        }
    
public voidremoveBeanUnchecked(EJBLocalObject localObj)
Remove a bean. Used by the PersistenceManager. This is needed because the PM's remove must bypass tx/security checks.

        // First convert client EJBLocalObject to EJBLocalObjectImpl
        EJBLocalObjectImpl localObjectImpl = 
            EJBLocalObjectImpl.toEJBLocalObjectImpl(localObj);
        internalRemoveBeanUnchecked(localObjectImpl, true);
    
public voidremoveBeanUnchecked(java.lang.Object primaryKey)
Remove a bean. Used by the PersistenceManager. This is needed because the PM's remove must bypass tx/security checks.

        EJBLocalRemoteObject ejbo;
        if ( isLocal ) {
            ejbo = internalGetEJBLocalObjectImpl(primaryKey);
            internalRemoveBeanUnchecked(ejbo, true);
        }
        else { // remote-only bean
            ejbo = internalGetEJBObjectImpl(primaryKey, null);
            internalRemoveBeanUnchecked(ejbo, false);
        }
    
protected voidremoveContextFromReadyStore(java.lang.Object primaryKey, EntityContextImpl context)

        readyStore.remove(primaryKey, context);
    
private voidremoveEJBObjectFromStore(java.lang.Object primaryKey)

        removeEJBObjectFromStore(primaryKey, true);
    
private voidremoveEJBObjectFromStore(java.lang.Object primaryKey, boolean decrementRefCount)

        // Remove the EJBObject from ejbObjectStore so future lookups
        // in internalGetEJBObject will not get it.
        EJBObjectImpl ejbObjImpl = 
            (EJBObjectImpl)ejbObjectStore.remove(primaryKey, decrementRefCount);
                                                 
        if ( ejbObjImpl != null ) {
            synchronized ( ejbObjImpl ) {
                // disconnect the EJBObject from the ProtocolManager
                // so that no remote invocations can reach the EJBObject
                remoteHomeRefFactory.destroyReference(ejbObjImpl.getStub(), 
                                                  ejbObjImpl.getEJBObject());
            }
        }
    
protected voidremoveIncompleteTxEJB(EntityContextImpl context, boolean updateTxBeanTable)
Called from releaseContext if ejb is removed, from afterCompletion, and from passivateEJB.

        J2EETransaction current = (J2EETransaction) context.getTransaction();

        if (current == null) {
            return;
        }
        if ( (context.getEJBObjectImpl() == null) &&
             (context.getEJBLocalObjectImpl() == null) ) {
            return;
        }
        
	ActiveTxCache activeTxCache = (ActiveTxCache) 
	    (((J2EETransaction) current).getActiveTxCache());
	if (activeTxCache != null) {
	    activeTxCache.remove(this, context.getPrimaryKey());
	}

        if ( updateTxBeanTable ) {
            Vector beans = containerFactory.getBeans(current);
            beans.remove(context); // this is a little expensive...
        }
    
public com.sun.ejb.containers.EntityContainer$IdleBeansPassivatorsetupIdleBeansPassivator(com.sun.appserv.util.cache.Cache cache)
setup a timer task to trim timed out entries in the cache.

param
cache cache which is used to setup the timer task
return
the passivator object

        
        IdleBeansPassivator idleBeansPassivator =
            new IdleBeansPassivator(cache);

        ContainerFactoryImpl.getTimer().
            scheduleAtFixedRate(idleBeansPassivator, idleTimeout, idleTimeout);
        
        return idleBeansPassivator;
    
private voidstoreAllBeansInTx(javax.transaction.Transaction tx)

        // Call ejbStore on all entitybeans in tx for all EntityContainers
        Vector beans = containerFactory.getBeans(tx);
        if ( beans.isEmpty() ) {
            // No beans associated with the current transaction
            return;
        }

        Iterator itr = beans.iterator();
        while ( itr.hasNext() ) {
            EntityContextImpl ctx = (EntityContextImpl)itr.next();
            if ( ctx.getState() == INCOMPLETE_TX && ctx.isDirty() ) {
                // Call ejbStore on the bean
                // Note: the bean may be in a different container instance
                EntityContainer cont = (EntityContainer)ctx.getContainer();
                cont.enlistResourcesAndStore(ctx);
            }
        }
    
public voidtrimEvent(java.lang.Object primaryKey, java.lang.Object context)

        boolean addTask = false;
        synchronized (asyncTaskSemaphore) {
            passivationCandidates.add(context);
            if (addedASyncTask == true) {
                return;
            }
            addTask = addedASyncTask = true;
        }
        
        try {
            ASyncPassivator work = new ASyncPassivator();
            ContainerWorkPool.addLast(work);
        } catch (Exception ex) {
            addedASyncTask = false;
            _logger.log(Level.WARNING, "ejb.add_cleanup_task_error",ex);
        }
    
public voidundeploy()

        
        //Change the container state to ensure that all new invocations will be rejected
        super.setUndeployedState();
        
        String ejbName = ejbDescriptor.getName();
        
        if(_logger.isLoggable(Level.FINE)) {
            _logger.log(Level.FINE,"[EntityContainer]: Undeploying " + ejbName +
                " ...");
        } 
        // destroy all EJBObject refs
        
        try {
            Iterator elements = ejbObjectStore.values();
            while ( elements.hasNext() ) {
                EJBObjectImpl ejbObjImpl = (EJBObjectImpl) elements.next();
                try {
                    if ( isRemote ) {
                        remoteHomeRefFactory.destroyReference
                            (ejbObjImpl.getStub(), ejbObjImpl.getEJBObject());
                                                    
                    }
                } catch ( Exception ex ) {
                    _logger.log(Level.FINE, "Exception in undeploy()", ex);
                }
            }
            
            ejbObjectStore.destroy();  //store must set the listern to null
            ejbObjectStore = null;
            
            ejbLocalObjectStore.destroy(); //store must set the listern to null
            ejbLocalObjectStore = null;
            
            // destroy all EJB instances in readyStore
            destroyReadyStoreOnUndeploy(); //cache must set the listern to null
            
            // destroy all EJB instances in ActiveTxCache
	    /*
            synchronized ( incompleteTxStore ) {
                Iterator beans = incompleteTxStore.values();
                while ( beans.hasNext() ) {
                    EJBContextImpl ctx = (EJBContextImpl)beans.next();
                    transactionManager.ejbDestroyed(ctx);
                }
            }
	    */
            
            entityCtxPool.close();
            
            // stops the idle bean passivator and also removes the link
            // to the cache; note that cancel() method of timertask
            // does not remove the task from the timer's queue
            if (idleBeansPassivator != null) {
                try {
                    idleBeansPassivator.cancel();
                } catch (Exception e) {
                    _logger.log(Level.FINE,
                                "[EntityContainer] cancelTimerTask: ", e);
                }
                this.idleBeansPassivator.cache  = null;
            }
	    cancelTimerTasks();
        }
        finally {
            
            super.undeploy();
            
            // helps garbage collection
            this.ejbObjectStore         = null;
            this.ejbLocalObjectStore    = null;
            this.passivationCandidates  = null;
            this.readyStore             = null;
            this.entityCtxPool          = null;
            this.iased                  = null;
            this.beanCacheDes           = null;
            this.beanPoolDes            = null;
            this.svr                    = null;
            this.cfg                    = null;
            this.ejbContainer           = null;
            this.cacheProp              = null;
            this.poolProp               = null;
            this.asyncTaskSemaphore     = null;
            this.idleBeansPassivator    = null;
            
        }
        
        if(_logger.isLoggable(Level.FINE)) {
            _logger.log(Level.FINE," [EntityContainer]: Successfully Undeployed " +
                ejbName);
        }
    
protected booleanwillInvokeWithClientTx(Invocation inv)

        int status = Status.STATUS_UNKNOWN;
        try {
            Integer preInvokeTxStatus = inv.getPreInvokeTxStatus();
            status = (preInvokeTxStatus != null) ?
                preInvokeTxStatus.intValue() : transactionManager.getStatus();
        } catch ( SystemException ex ) {
            throw new EJBException(ex);
        }
        if ( status != Status.STATUS_NO_TRANSACTION ) {
            int txAttr = inv.invocationInfo.txAttr;
            switch (txAttr) {
                case TX_SUPPORTS:
                case TX_REQUIRED:
                case TX_MANDATORY:
                    return true;
            }
        }
        return false;