FileDocCategorySizeDatePackage
StatelessSessionContainer.javaAPI DocGlassfish v2 API32610Tue Jul 24 05:01:32 BST 2007com.sun.ejb.containers

StatelessSessionContainer.java

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 * 
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 * 
 * Contributor(s):
 * 
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */
package com.sun.ejb.containers;

import java.util.Collection;
import java.util.Iterator;
import java.util.logging.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;

import java.lang.reflect.Method;
import java.rmi.Remote;
import java.rmi.RemoteException;

import javax.ejb.*;
import javax.transaction.*;
import javax.rmi.PortableRemoteObject;

import com.sun.ejb.*;

import com.sun.ejb.containers.util.pool.*;

import com.sun.enterprise.util.Utility; 
import com.sun.enterprise.util.LocalStringManagerImpl; 
import com.sun.enterprise.*;
import com.sun.enterprise.deployment.*;
import static com.sun.enterprise.deployment.LifecycleCallbackDescriptor.CallbackType;
import com.sun.enterprise.deployment.runtime.BeanPoolDescriptor;
import com.sun.enterprise.deployment.runtime.BeanCacheDescriptor;
import com.sun.enterprise.deployment.ServiceReferenceDescriptor;
import com.sun.enterprise.deployment.runtime.IASEjbExtraDescriptors;

import com.sun.enterprise.webservice.EjbRuntimeEndpointInfo;
import com.sun.enterprise.webservice.WebServiceEjbEndpointRegistry;
import com.sun.enterprise.webservice.ServiceInterfaceGenerator;
import com.sun.enterprise.webservice.WsUtil;
import com.sun.enterprise.webservice.ClientPipeCloser;
import java.lang.reflect.Proxy;

import com.sun.enterprise.log.Log;

import com.sun.logging.*;

import com.sun.enterprise.admin.monitor.*;
import com.sun.enterprise.config.ConfigException;
import com.sun.enterprise.config.serverbeans.*;
import com.sun.enterprise.server.ServerContext;
import com.sun.enterprise.server.ApplicationServer;
import com.sun.enterprise.util.io.FileUtils;

import com.sun.enterprise.appverification.factory.AppVerification;
import com.sun.enterprise.admin.monitor.callflow.ComponentType;

import com.sun.ejb.spi.stats.StatelessSessionBeanStatsProvider;

/** This class provides container functionality specific to stateless 
 *  SessionBeans.
 *  At deployment time, one instance of the StatelessSessionContainer is created
 *  for each stateless SessionBean type (i.e. deployment descriptor) in a JAR. 
 * <P>
 * The 3 states of a Stateless EJB (an EJB can be in only 1 state at a time):
 * 1. POOLED : ready for invocations, no transaction in progress
 * 2. INVOKING : processing an invocation
 * 3. DESTROYED : does not exist  
 * <P>
 * This container services invocations using a pool of EJB instances.
 * An instance is returned to the pool immediately after the invocation
 * completes, so the # of instances needed = # of concurrent invocations.
 * <P>
 * A Stateless Bean can hold open DB connections across invocations.
 * Its assumed that the Resource Manager can handle
 * multiple incomplete transactions on the same
 * connection.
 *
 */    

public final class StatelessSessionContainer
    extends BaseContainer 
    implements StatelessSessionBeanStatsProvider
{
    
    private static final Logger _logger =
        LogDomains.getLogger(LogDomains.EJB_LOGGER);

    private static LocalStringManagerImpl localStrings =
	new LocalStringManagerImpl(StatelessSessionContainer.class);

    private static final int POOLED=1, INVOKING=2, DESTROYED=3;
    private static final int LOW_WATER_MARK = 100;

    private static final byte[] statelessInstanceKey = {0, 0, 0, 1};

    // All stateless EJBs have the same instanceKey, since all stateless EJBs
    // are identical. Note: the first byte of instanceKey must be left empty.
    private Method homeCreateMethod 	 = null;
    private Method localHomeCreateMethod = null;

    // All stateless EJB instances of a particular class (i.e. all bean 
    // instances created by this container instance) have the same 
    // EJBObject/EJBLocalObject instance since they are all identical.
    private EJBLocalObjectImpl theEJBLocalObjectImpl = null;
    private EJBLocalObjectImpl theEJBLocalBusinessObjectImpl = null;

    // Data members for RemoteHome view
    private EJBObjectImpl theEJBObjectImpl = null;
    private EJBObject theEJBObject = null;
    private EJBObject theEJBStub = null;

    // Data members for Remote business view. Any objects representing the
    // Remote business interface are not subtypes of EJBObject.
    private EJBObjectImpl theRemoteBusinessObjectImpl = null;
    private java.rmi.Remote theRemoteBusinessObject = null;
    private Map<String, java.rmi.Remote> theRemoteBusinessStubs = 
        new HashMap<String, java.rmi.Remote>();

    // Information about a web service ejb endpoint.  Used as a conduit
    // between webservice runtime and ejb container.  Contains a Remote
    // servant used by jaxrpc to call web service business method.
    private EjbRuntimeEndpointInfo webServiceEndpoint;


	private boolean isPoolClosed = false;    
	protected AbstractPool pool;

    private IASEjbExtraDescriptors iased 	 = null;
    private BeanCacheDescriptor beanCacheDes = null;
    private BeanPoolDescriptor beanPoolDes   = null;
    private Server svr 						 = null;
    private Config cfg 						 = null;
    private EjbContainer ejbContainer 		 = null;

    private PoolProperties poolProp 		 = null;

    /** 
     * This constructor is called from the JarManager when a Jar is deployed.
     * @exception Exception on error
     */
    StatelessSessionContainer(EjbDescriptor desc, ClassLoader loader) 
	throws Exception
    {
        super(desc, loader);


        try {
            // get the ejbCreate method for stateless beans
            if ( hasLocalHomeView ) {
                localHomeCreateMethod = 
                    localHomeIntf.getMethod("create", NO_PARAMS);
            }
            if ( hasRemoteHomeView ) {
                homeCreateMethod = 
                    homeIntf.getMethod("create", NO_PARAMS);
            }
        } catch (Exception ex) {
            if(_logger.isLoggable(Level.SEVERE)) {
                _logger.log(Level.SEVERE,
                    "ejb.get_ejbcreate_method_exception",logParams);
                _logger.log(Level.SEVERE,"",ex);
            }
            throw ex;
        }

        EjbSessionDescriptor ed = (EjbSessionDescriptor)desc;
        iased = ed.getIASEjbExtraDescriptors();
        if( iased != null) {
            beanPoolDes = iased.getBeanPool();
        }
        try {
            ServerContext sc = ApplicationServer.getServerContext();
            //ROB: config changes
            //svr = ServerBeansFactory.getServerBean(sc.getConfigContext());
            cfg = ServerBeansFactory.getConfigBean(sc.getConfigContext());
        }  catch (ConfigException ex) {
            ex.printStackTrace();
        }
	   //ROB: config changes
        //ejbContainer = svr.getEjbContainer();
 	   ejbContainer = cfg.getEjbContainer(); 

        super.setMonitorOn(ejbContainer.isMonitoringEnabled());

        super.createCallFlowAgent(ComponentType.SLSB);
    }

    public String getMonitorAttributeValues() {
        StringBuffer sbuf = new StringBuffer();
        sbuf.append("STATELESS ").append(ejbDescriptor.getName());
        sbuf.append(pool.getAllAttrValues());
        sbuf.append("]");

        return sbuf.toString();
    }

    protected void initializeHome()
        throws Exception
    {

        super.initializeHome();

        if ( isRemote ) {

            if( hasRemoteHomeView ) {
                // Create theEJBObjectImpl
                theEJBObjectImpl = instantiateEJBObjectImpl();
                theEJBObject = (EJBObject) theEJBObjectImpl.getEJBObject();
                
                // connect the EJBObject to the ProtocolManager 
                // (creates the stub 
                // too). Note: cant do this in constructor above because 
                // containerId is not set at that time.
                theEJBStub = (EJBObject) 
                    remoteHomeRefFactory.createRemoteReference
                       (statelessInstanceKey);
                
                theEJBObjectImpl.setStub(theEJBStub);
            }

            if( hasRemoteBusinessView ) {

                theRemoteBusinessObjectImpl = 
                    instantiateRemoteBusinessObjectImpl();

                theRemoteBusinessObject = 
                    theRemoteBusinessObjectImpl.getEJBObject();
                
                for(RemoteBusinessIntfInfo next : 
                        remoteBusinessIntfInfo.values()) {
                    java.rmi.Remote stub = next.referenceFactory.
                        createRemoteReference(statelessInstanceKey);
                    theRemoteBusinessStubs.put
                        (next.generatedRemoteIntf.getName(), stub);
                    theRemoteBusinessObjectImpl.setStub
                        (next.generatedRemoteIntf.getName(), stub);
                }

            }

        }

        if ( isLocal ) {
            if( hasLocalHomeView ) {
                theEJBLocalObjectImpl = instantiateEJBLocalObjectImpl();
            }
            if( hasLocalBusinessView ) {
                theEJBLocalBusinessObjectImpl = 
                    instantiateEJBLocalBusinessObjectImpl();
            }
        }

        if( isWebServiceEndpoint ) {

            EjbBundleDescriptor bundle = 
                ejbDescriptor.getEjbBundleDescriptor();
            WebServicesDescriptor webServices = bundle.getWebServices();
            Collection myEndpoints = 
                webServices.getEndpointsImplementedBy(ejbDescriptor);
            //FindBugs [Deadstore] Long ejbId = new Long(ejbDescriptor.getUniqueId());
            
            // An ejb can only be exposed through 1 web service endpoint
            Iterator iter = myEndpoints.iterator();
            com.sun.enterprise.deployment.WebServiceEndpoint next = 
 					(com.sun.enterprise.deployment.WebServiceEndpoint) iter.next();

            Class serviceEndpointIntfClass = 
                    loader.loadClass(next.getServiceEndpointInterface());            

            if (!serviceEndpointIntfClass.isInterface()) {
                ServiceInterfaceGenerator generator = new ServiceInterfaceGenerator(loader, ejbClass);
                serviceEndpointIntfClass = WsUtil.generateAndLoad(generator, loader);
                if (serviceEndpointIntfClass==null) {
                    throw new RuntimeException("Error generating the SEI");
                }
            }
            
            Class tieClass=null;
            
            WebServiceInvocationHandler invocationHandler =
                    new WebServiceInvocationHandler(ejbClass, next,
                                                    serviceEndpointIntfClass,
                    webServiceInvocationInfoMap);
            
            
            invocationHandler.setContainer(this);
            Object servant = (Object) Proxy.newProxyInstance
                    (loader, new Class[] { serviceEndpointIntfClass },
                    invocationHandler);
            
            // starting in 2.0, there is no more generated Ties
            if (next.getTieClassName()!=null) {                
                tieClass = loader.loadClass(next.getTieClassName());                
            }
                    
            webServiceEndpoint = WebServiceEjbEndpointRegistry.getRegistry().createEjbEndpointInfo(next, this, servant, tieClass);
                                           
            WebServiceEjbEndpointRegistry.getRegistry().
                registerEjbWebServiceEndpoint(webServiceEndpoint);
        }
      
        ObjectFactory sessionCtxFactory = new SessionContextFactory();
        poolProp = new PoolProperties();
        pool= new NonBlockingPool(ejbDescriptor.getName(),
           sessionCtxFactory, poolProp.steadyPoolSize, 
           poolProp.poolResizeQuantity, poolProp.maxPoolSize, 
           poolProp.poolIdleTimeoutInSeconds, loader);

	registerMonitorableComponents();
    }

    protected void registerMonitorableComponents() {
	registryMediator.registerProvider(this);
	registryMediator.registerProvider(pool);
        super.registerMonitorableComponents();
	super.populateMethodMonitorMap();
        _logger.log(Level.FINE, "[SLSB Container] registered monitorable");
    }

    public void onReady() {
    }

    public EJBObjectImpl createRemoteBusinessObjectImpl()
        throws CreateException, RemoteException
    {
        // No access check since this is an internal operation.

	statCreateCount++;

        return theRemoteBusinessObjectImpl;
    }

	
    /**
     *
     */
    public EJBObjectImpl createEJBObjectImpl()
        throws CreateException, RemoteException
    {
        // Need to do access control check here because BaseContainer.preInvoke
        // is not called for stateless sessionbean creates.
        authorizeRemoteMethod(EJBHome_create);

        if ( AppVerification.doInstrument() ) {
            AppVerification.getInstrumentLogger().doInstrumentForEjb(
                ejbDescriptor, homeCreateMethod, null);
        }

	statCreateCount++;

        // For stateless EJBs, EJB2.0 Section 7.8 says that 
        // Home.create() need not do any real creation.
        // If necessary, a stateless bean is created below during getContext().
        return theEJBObjectImpl;
    }

    /**
     * Called during client creation request through EJB LocalHome view.
     */
    public EJBLocalObjectImpl createEJBLocalObjectImpl()
        throws CreateException
    {	
        // Need to do access control check here because BaseContainer.preInvoke
        // is not called for stateless sessionbean creates.
        authorizeLocalMethod(EJBLocalHome_create);

        if ( AppVerification.doInstrument() ) {
            AppVerification.getInstrumentLogger().doInstrumentForEjb(
                ejbDescriptor, localHomeCreateMethod, null);
        }

        // For stateless EJBs, EJB2.0 Section 7.8 says that 
        // Home.create() need not do any real creation.
        // If necessary, a stateless bean is created below during getContext().
        return theEJBLocalObjectImpl;
    }

    /**
     * Called during internal creation of session bean
     */
    public EJBLocalObjectImpl createEJBLocalBusinessObjectImpl()
        throws CreateException
    {	
        // No access checks needed because this is called as a result
        // of an internal creation, not a user-visible create method.
        return theEJBLocalBusinessObjectImpl;
    }


    // Called from EJBObjectImpl.remove, EJBLocalObjectImpl.remove,
    // EJBHomeImpl.remove(Handle).
    void removeBean(EJBLocalRemoteObject ejbo, Method removeMethod,
	    boolean local)
	throws RemoveException, EJBException, RemoteException
    {
        if( local ) {
            authorizeLocalMethod(BaseContainer.EJBLocalObject_remove);
        } else {
            authorizeRemoteMethod(BaseContainer.EJBObject_remove);
        }
	statRemoveCount++;
    }

    /**
     * Force destroy the EJB. Called from postInvokeTx.
     * Note: EJB2.0 section 18.3.1 says that discarding an EJB
     * means that no methods other than finalize() should be invoked on it.
     */
    void forceDestroyBean(EJBContextImpl sc) {
        if ( sc.getState() == DESTROYED )
                return;

        // mark context as destroyed
        sc.setState(DESTROYED);

        //sessionCtxPool.destroyObject(sc);
        pool.destroyObject(sc);
    }


    /**
     * Called when a remote invocation arrives for an EJB.
     */
    EJBObjectImpl getEJBObjectImpl(byte[] instanceKey) {
        return theEJBObjectImpl;
    }
    
    EJBObjectImpl getEJBRemoteBusinessObjectImpl(byte[] instanceKey) {
        return theRemoteBusinessObjectImpl;
    }

    /**
    * Called from EJBLocalObjectImpl.getLocalObject() while deserializing
    * a local object reference.
    */
    EJBLocalObjectImpl getEJBLocalObjectImpl(Object key) {
        return theEJBLocalObjectImpl;
    }

    /**
    * Called from EJBLocalObjectImpl.getLocalObject() while deserializing
    * a local business object reference.
    */
    EJBLocalObjectImpl getEJBLocalBusinessObjectImpl(Object key) {
        return theEJBLocalBusinessObjectImpl;
    }


    /**
    * Called from preInvoke which is called from the EJBObject
    * for local and remote invocations.
    */
    protected ComponentContext _getContext(Invocation inv) {
        try {
            SessionContextImpl sessionCtx = 
                (SessionContextImpl) pool.getObject(null);
            sessionCtx.setState(INVOKING);
            return sessionCtx;
        } catch (Exception ex) {
            throw new EJBException(ex);
        }
    }


    /**
    * called when an invocation arrives and there are no instances
    * left to deliver the invocation to.
    * Called from SessionContextFactory.create() !
    */
    private SessionContextImpl createStatelessEJB()
        throws CreateException
    { 
        ComponentInvocation ci = null;
        SessionContextImpl context;

        try {
            // create new stateless EJB
            Object ejb = ejbClass.newInstance();

            // create SessionContext and set it in the EJB
            context = new SessionContextImpl(ejb, this);
            context.setInterceptorInstances(
                    interceptorManager.createInterceptorInstances());
            
            // this allows JNDI lookups from setSessionContext, ejbCreate
            ci = new ComponentInvocation(ejb, this, context);
            invocationManager.preInvoke(ci);

            // setSessionContext will be called without a Tx as required
            // by the spec, because the EJBHome.create would have been called
            // after the container suspended any client Tx.
            // setSessionContext is also called before context.setEJBStub
            // because the bean is not allowed to do EJBContext.getEJBObject
            if( ejb instanceof SessionBean ) {
                ((SessionBean)ejb).setSessionContext(context);
            }

            // Perform injection right after where setSessionContext
            // would be called.  This is important since injection methods
            // have the same "operations allowed" permissions as
            // setSessionContext.
            injectionManager.injectInstance(ejb, ejbDescriptor, false);
            for (Object interceptorInstance : context.getInterceptorInstances()) {
                injectionManager.injectInstance(interceptorInstance,
                        ejbDescriptor, false);
            }

            if ( isRemote ) {
                if( hasRemoteHomeView ) {
                    context.setEJBObjectImpl(theEJBObjectImpl);
                    context.setEJBStub(theEJBStub);
                }
                if( hasRemoteBusinessView ) {
                    context.setEJBRemoteBusinessObjectImpl
                        (theRemoteBusinessObjectImpl);
                }
            }
            if ( isLocal ) {
                if( hasLocalHomeView ) {
                    context.setEJBLocalObjectImpl(theEJBLocalObjectImpl);
                }
                if( hasLocalBusinessView ) {
                    context.setEJBLocalBusinessObjectImpl
                        (theEJBLocalBusinessObjectImpl);
                }
            }

            // all stateless beans have the same id and same InstanceKey
            context.setInstanceKey(statelessInstanceKey); 

            //Call ejbCreate() or @PostConstruct method
            interceptorManager.intercept(
                    CallbackType.POST_CONSTRUCT, context);

            // Set the state to POOLED after ejbCreate so that 
            // EJBContext methods not allowed will throw exceptions
            context.setState(POOLED);
        } catch ( Throwable th ) {
            _logger.log(Level.INFO, "ejb.stateless_ejbcreate_exception", th);
            CreateException creEx = new CreateException("Could not create stateless EJB");
            creEx.initCause(th);
            throw creEx;
        } finally {
            if ( ci != null ) {
                invocationManager.postInvoke(ci);
            }
        }
        context.touch();
        return context;
    }

    void doTimerInvocationInit(Invocation inv, RuntimeTimerState timerState) 
        throws Exception 
	{
        if( isRemote ) {
            inv.ejbObject = theEJBObjectImpl;
            inv.isLocal = false;
        } else {
            inv.ejbObject = theEJBLocalObjectImpl;
            inv.isLocal = true;
        }
    }

    public boolean userTransactionMethodsAllowed(ComponentInvocation inv) {
        boolean utMethodsAllowed = false;
        if( isBeanManagedTran ) {
            if( inv instanceof Invocation ) {
                Invocation i = (Invocation) inv;
                EJBContextImpl sc = (EJBContextImpl) i.context;
                // If Invocation, only ejbRemove not allowed.
                utMethodsAllowed = !sc.isInEjbRemove();
            } else {
                // This will prevent setSessionContext/ejbCreate access
                utMethodsAllowed = false;
            }
        }
        return utMethodsAllowed;
    }

    /**
     * Called from preInvoke which is called from the EJBObject
     * for local and remote invocations.
     */
    public void releaseContext(Invocation inv) {
        SessionContextImpl sc = (SessionContextImpl)inv.context;

        // check if the bean was destroyed
        if ( sc.getState()==DESTROYED )
            return;

            sc.setState(POOLED);

            // Stateless beans cant have transactions across invocations
            sc.setTransaction(null);
            sc.touch();

            pool.returnObject(sc);
    }


    boolean isIdentical(EJBObjectImpl ejbo, EJBObject other)
        throws RemoteException
    {
        if ( other == ejbo.getStub() ) {
            return true;
        }else {
            try {
                // other may be a stub for a remote object.
                // Although all stateless sessionbeans for a bean type
                // are identical, we dont know whether other is of the
                // same bean type as ejbo.
                if ( protocolMgr.isIdentical(ejbo.getStub(), other) )
                        return true;
                else
                        return false;
            } catch ( Exception ex ) {
                if(_logger.isLoggable(Level.SEVERE)) {
                    _logger.log(Level.SEVERE,"ejb.ejb_getstub_exception",
                        logParams);
                    _logger.log(Level.SEVERE,"",ex);
                }
                throw new RemoteException("Error during isIdentical.", ex);
            }
        }
    }

    /**
    * Check if the given EJBObject/LocalObject has been removed.
    * @exception NoSuchObjectLocalException if the object has been removed.
    */
    void checkExists(EJBLocalRemoteObject ejbObj) 
    {
        // For stateless session beans, EJBObject/EJBLocalObj are never removed.
        // So do nothing.
    }


    void afterBegin(EJBContextImpl context) {
        // Stateless SessionBeans cannot implement SessionSynchronization!!
        // EJB2.0 Spec 7.8.
    }

    void beforeCompletion(EJBContextImpl context) {
        // Stateless SessionBeans cannot implement SessionSynchronization!!
        // EJB2.0 Spec 7.8.
    }

    void afterCompletion(EJBContextImpl ctx, int status) {
        // Stateless SessionBeans cannot implement SessionSynchronization!!
        // EJB2.0 Spec 7.8.

        // We dissociate the transaction from the bean in releaseContext above
    }

    // default
    public boolean passivateEJB(ComponentContext context) {
        return false;
    }   
    
    // default
    public void activateEJB(Object ctx, Object instanceKey) {}

    public void appendStats(StringBuffer sbuf) {
	sbuf.append("\nStatelessContainer: ")
	    .append("CreateCount=").append(statCreateCount).append("; ")
	    .append("RemoveCount=").append(statRemoveCount).append("; ")
	    .append("]");
    }

    public void undeploy() {
        //Change the container state to ensure that all new invocations will be rejected
        super.setUndeployedState();

        try {
            if( isWebServiceEndpoint && (webServiceEndpoint != null) ) {
                String endpointAddress = 
                    webServiceEndpoint.getEndpointAddressUri();
                WebServiceEjbEndpointRegistry.getRegistry().
                    unregisterEjbWebServiceEndpoint(endpointAddress);
            }
            
            EjbBundleDescriptor desc = ejbDescriptor.getEjbBundleDescriptor();
            if (desc != null && desc.getServiceReferenceDescriptors()!= null) {
                for (Object srd : desc.getServiceReferenceDescriptors()) {
                    ClientPipeCloser.getInstance()
                    .cleanupClientPipe((ServiceReferenceDescriptor)srd);
                }
            }
            
            if ( hasRemoteHomeView ) {
                    // destroy EJBObject refs
                    // XXX invocations still in progress will get exceptions ??
                remoteHomeRefFactory.destroyReference
                    (theEJBObjectImpl.getStub(), 
                     theEJBObjectImpl.getEJBObject());
            }
            if ( hasRemoteBusinessView ) {
                for(RemoteBusinessIntfInfo next : 
                        remoteBusinessIntfInfo.values()) {
                    next.referenceFactory.destroyReference
                        (theRemoteBusinessObjectImpl.getStub
                            (next.generatedRemoteIntf.getName()),
                         theRemoteBusinessObjectImpl.getEJBObject
                            (next.generatedRemoteIntf.getName()));
                }
            }

            isPoolClosed = true;

            pool.close();

        } finally {
            super.undeploy();

            this.homeCreateMethod      = null;
            this.localHomeCreateMethod = null;
            this.theEJBLocalObjectImpl = null;
            this.theEJBObjectImpl      = null;
            this.theEJBStub            = null;
            this.pool                  = null;
            this.iased                 = null;
            this.beanCacheDes          = null;
            this.beanPoolDes           = null;
            this.svr                   = null;
            this.ejbContainer          = null;
            this.poolProp              = null;

        }
    }

    public long getMethodReadyCount() {
	return pool.getSize();
    }

    private class SessionContextFactory
        implements ObjectFactory
    {

        public Object create(Object param) {
            try {
                    return createStatelessEJB();
            } catch (CreateException ex) {
                    throw new EJBException(ex);
            }
        }

        public void destroy(Object obj) {
            SessionContextImpl sessionCtx = (SessionContextImpl) obj;
            // Note: stateless SessionBeans cannot have incomplete transactions
            // in progress. So it is ok to destroy the EJB.

            Object sb = sessionCtx.getEJB();
            if (sessionCtx.getState() != DESTROYED) {
                //Called from pool implementation to reduce the pool size.
                //So need to call ejb.ejbRemove()
                // mark context as destroyed
                sessionCtx.setState(DESTROYED);
                Invocation inv = null;
                try {
                    // NOTE : Context class-loader is already set by Pool
                    inv = new Invocation(sb, StatelessSessionContainer.this);
                    inv.context = sessionCtx;
                    invocationManager.preInvoke(inv);
                    sessionCtx.setInEjbRemove(true);        
   
                    interceptorManager.intercept(
                            CallbackType.PRE_DESTROY, sessionCtx);

                } catch ( Throwable t ) {
                     _logger.log(Level.FINE, "ejbRemove exception", t);
                } finally {
                    sessionCtx.setInEjbRemove(false);
                    if( inv != null ) {
                        invocationManager.postInvoke(inv);
                    }
                }
            } else {
                //Called from forceDestroyBean
                //So NO need to call ejb.ejbRemove()
                // mark the context's transaction for rollback
                Transaction tx = sessionCtx.getTransaction();
                try {
                    if ( (tx != null) && 
                        (tx.getStatus() != Status.STATUS_NO_TRANSACTION ) )  {
                        tx.setRollbackOnly();
                    }	
                } catch ( Exception ex ) {
                     _logger.log(Level.FINE,"forceDestroyBean exception", ex);
                }
            }

            // tell the TM to release resources held by the bean
            transactionManager.ejbDestroyed(sessionCtx);

            sessionCtx.setTransaction(null);

            sessionCtx.deleteAllReferences();
            sessionCtx = null;
        }
    } // SessionContextFactory{}

    private class PoolProperties {
        int maxPoolSize;
        int poolIdleTimeoutInSeconds;
        int poolResizeQuantity;
        int steadyPoolSize;

        public PoolProperties() {

            maxPoolSize = new Integer(ejbContainer.getMaxPoolSize()).intValue();
            poolIdleTimeoutInSeconds = new Integer(
                ejbContainer.getPoolIdleTimeoutInSeconds()).intValue();
            poolResizeQuantity = new Integer(
                ejbContainer.getPoolResizeQuantity()).intValue();
            steadyPoolSize = new Integer(
                ejbContainer.getSteadyPoolSize()).intValue();
            if(beanPoolDes != null) {
                int temp = 0;
                if (( temp = beanPoolDes.getMaxPoolSize()) != -1) {
                        maxPoolSize = temp;
                }
                if (( temp = beanPoolDes.getPoolIdleTimeoutInSeconds()) != -1) {
                        poolIdleTimeoutInSeconds = temp;
                }

                if (( temp = beanPoolDes.getPoolResizeQuantity()) != -1) {
                        poolResizeQuantity = temp;
                }
                if (( temp = beanPoolDes.getSteadyPoolSize()) != -1) {
                        steadyPoolSize = temp;
                }
            }
        }
    } // PoolProperties{}

    //Methods for StatelessSessionBeanStatsProvider
    public int getMaxPoolSize() {
        return (poolProp.maxPoolSize <= 0)
	    ? Integer.MAX_VALUE
	    : poolProp.maxPoolSize;
    }

    public int getSteadyPoolSize() {
        return (poolProp.steadyPoolSize <= 0)
	    ? 0
	    : poolProp.steadyPoolSize;
    }
    
} // StatelessSessionContainer.java