FileDocCategorySizeDatePackage
MessageBeanContainer.javaAPI DocGlassfish v2 API49345Fri May 04 22:32:58 BST 2007com.sun.ejb.containers

MessageBeanContainer

public final class MessageBeanContainer extends BaseContainer implements MessageBeanProtocolManager, com.sun.ejb.spi.stats.MessageDrivenBeanStatsProvider
This class provides container functionality specific to message-driven EJBs. At deployment time, one instance of the MessageDrivenBeanContainer is created for each message-driven bean in an application.

The 3 states of a Message-driven 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 A Message-driven Bean can hold open DB connections across invocations. It's assumed that the Resource Manager can handle multiple incomplete transactions on the same connection.

author
Kenneth Saks

Fields Summary
private static final Logger
_logger
private String
appEJBName_
private static com.sun.enterprise.util.LocalStringManagerImpl
localStrings
private static final int
POOLED
private static final int
INVOKING
private static final int
DESTROYED
private MessageBeanClient
messageBeanClient_
private com.sun.ejb.containers.util.pool.AbstractPool
messageBeanPool_
private com.sun.enterprise.deployment.runtime.BeanPoolDescriptor
beanPoolDesc_
private int
maxMessageBeanListeners_
private int
numMessageBeanListeners_
private static final String
MESSAGE_BEAN_CLIENT_FACTORY_PROP
private static final String
DEFAULT_MESSAGE_BEAN_CLIENT_FACTORY
private static final int
DEFAULT_RESIZE_QUANTITY
private static final int
DEFAULT_STEADY_SIZE
private static final int
DEFAULT_MAX_POOL_SIZE
private static final int
DEFAULT_IDLE_TIMEOUT
private static final int
MIN_IDLE_TIMEOUT
private int
statMessageCount
Constructors Summary
MessageBeanContainer(com.sun.enterprise.deployment.EjbDescriptor desc, ClassLoader loader)


        
	 
    
	super(desc, loader);

        appEJBName_ = 
            desc.getApplication().getRegistrationName() + ":" +desc.getName();

        EjbMessageBeanDescriptor msgBeanDesc = 
            (EjbMessageBeanDescriptor) desc;

        try {

            // Register the tx attribute for each method on MessageListener
            // interface. NOTE : These method objects MUST come from the
            // MessageListener interface, NOT the bean class itself.  This
            // is because the message bean container clients do not have
            // access to the message bean class.
            Method[] msgListenerMethods = 
                msgBeanDesc.getMessageListenerInterfaceMethods(loader);

            for(int i = 0; i < msgListenerMethods.length; i++) {
                Method next = msgListenerMethods[i];
                super.registerTxAttrForMethod(next, MethodDescriptor.EJB_BEAN);
            }
            // NOTE : No need to register tx attribute for ejbTimeout.  It's
            // done in BaseContainer intialization.
            // Message-driven beans can be timed objects.


            // Bootstrap message bean client factory.  If the class name is
            // specified as a system property, that value takes precedence.
            // Otherwise use default client factory.  The default is set to 
            // a client factory that uses the S1AS 7 style JMS connection 
            // consumer contracts.  This will be changed once the Connector 1.5
            // implementation is ready.
            String factoryClassName = System.getProperty
                (MESSAGE_BEAN_CLIENT_FACTORY_PROP, 
                 DEFAULT_MESSAGE_BEAN_CLIENT_FACTORY);

            Class clientFactoryClass = Class.forName(factoryClassName);
            MessageBeanClientFactory clientFactory = (MessageBeanClientFactory)
                clientFactoryClass.newInstance();

            _logger.log(Level.FINE, "Using " + factoryClassName + 
                " for message bean client factory in " + appEJBName_);

            // Create message bean pool before calling setup on
            // Message-bean client, since pool properties can be retrieved
            // through MessageBeanProtocolManager interface.
            createMessageBeanPool(msgBeanDesc);

            // Set resource limit for message bean listeners created through
            // Protocol Manager.  For now, just use max pool size.  However,
            // we might want to bump this up once the ejb timer service is
            // integrated.  
            maxMessageBeanListeners_ = beanPoolDesc_.getMaxPoolSize();
            numMessageBeanListeners_ = 0;

            messageBeanClient_ = 
                clientFactory.createMessageBeanClient(msgBeanDesc);

            messageBeanClient_.setup(this);

	    registerMonitorableComponents(msgListenerMethods);

            createCallFlowAgent(ComponentType.MDB);
	} 
	catch (Exception ex) {

            if (messageBeanClient_ != null) {
                messageBeanClient_.close();
            }

            _logger.log(Level.SEVERE,
                        "containers.mdb.create_container_exception", 
                        new Object[]{desc.getName(), ex.toString()});
            _logger.log(Level.SEVERE, ex.getClass().getName(), ex);
	    throw ex;
	}
    
Methods Summary
protected ComponentContext_getContext(Invocation inv)

        MessageBeanContextImpl context = null;
        try {
            context = (MessageBeanContextImpl) messageBeanPool_.getObject(null);
            context.setState(INVOKING);
        } catch(Exception e) {
            throw new EJBException(e);
        }
        return context;
    
public voidactivateEJB(java.lang.Object ctx, java.lang.Object instanceKey)

   
    
voidafterBegin(EJBContextImpl context)

	// Message-driven Beans cannot implement SessionSynchronization!!
    
voidafterCompletion(EJBContextImpl ctx, int status)

	// Message-driven Beans cannot implement SessionSynchronization!!
    
public voidafterMessageDelivery(com.sun.enterprise.resource.ResourceHandle resourceHandle)

        afterMessageDeliveryInternal(resourceHandle);
    
private booleanafterMessageDeliveryInternal(com.sun.enterprise.resource.ResourceHandle resourceHandle)

        // return value.  assume failure until proven otherwise.
        boolean success = false;

        Invocation invocation = null;

        invocation = (Invocation) invocationManager.getCurrentInvocation();
        if (invocation == null) {
            _logger.log(Level.SEVERE, "containers.mdb.no_invocation",
                        new Object[]{appEJBName_, ""});         
        } else {
            MessageBeanContextImpl beanContext = 
                (MessageBeanContextImpl) invocation.context;

            try {
                if( invocation.containerStartsTx ) {
                    // Unregister the session associated with 
                    // the message-driven bean's destination.
                    unregisterMessageBeanResource(resourceHandle);
                }

                // counterpart of invocationManager.preInvoke
                invocationManager.postInvoke(invocation);

                // Commit/Rollback container-managed transaction.
		postInvokeTx(invocation);
                
                // Consider successful delivery.  Commit failure will be
                // checked below.
                success = true;

		//TODO: Check if Tx existed / committed
		statMessageCount++;
                
            } catch(Throwable ce) {
                _logger.log(Level.SEVERE,
                            "containers.mdb.postinvoke_exception", 
                            new Object[]{appEJBName_, ce.toString()});
                _logger.log(Level.SEVERE,ce.getClass().getName(), ce);
            } finally {
                releaseContext(invocation);
            }

            // Reset original class loader
            Utility.setContextClassLoader(invocation.originalContextClassLoader);

            if( invocation.exception != null ) {

                if( isSystemUncheckedException(invocation.exception) ) {
                    success = false;
                }

                // Log system exceptions by default and application exceptions 
                // only when log level is FINE or higher.
                Level exLogLevel = 
                    isSystemUncheckedException(invocation.exception) ?
                        Level.INFO : Level.FINE;
                
                //START OF IASRI 4660565
                _logger.log(exLogLevel, 
                            "containers.mdb.invocation_exception", 
                            new Object[]{appEJBName_, 
                                             invocation.exception.toString()});
                _logger.log(exLogLevel, invocation.exception.getClass().
                            getName(), invocation.exception);
            }
        }

        return success;
    
public voidappendStats(java.lang.StringBuffer sbuf)

	sbuf.append("\nMessageBeanContainer: ")
	    .append("CreateCount=").append(statCreateCount).append("; ")
	    .append("RemoveCount=").append(statRemoveCount).append("; ")
	    .append("MsgCount=").append(statMessageCount).append("; ");
	sbuf.append("]");
    
voidbeforeCompletion(EJBContextImpl context)

	// Message-driven beans cannot implement SessionSynchronization!!
    
public voidbeforeMessageDelivery(java.lang.reflect.Method method, boolean txImported, com.sun.enterprise.resource.ResourceHandle resourceHandle)
Actual message delivery happens in three steps : 1) beforeMessageDelivery(Message, MessageListener) This is our chance to make the message delivery itself part of the instance's global transaction. 2) onMessage(Message, MessageListener) This is where the container delegates to the actual ejb instance's onMessage method. 3) afterMessageDelivery(Message, MessageListener) Perform transaction cleanup and error handling. We use the Invocation manager's thread-specific state to track the invocation across these three calls.

                                      
      
        if (containerState != CONTAINER_STARTED) { // i.e. no invocation
            String errorMsg = localStrings.getLocalString
               ("containers.mdb.invocation_closed", appEJBName_ +
                ": Message-driven bean invocation closed by container",
                new Object[] { appEJBName_ });

	    throw new EJBException(errorMsg);
        }

        Invocation invocation = new Invocation();
        
	try {

            MessageBeanContextImpl context = 
                (MessageBeanContextImpl) getContext(invocation);
                
            // Set the context class loader here so that message producer will
            // have access to application class loader during message processing.
            // The previous context class loader will be restored in
            // afterMessageDelivery.

            invocation.originalContextClassLoader = 
                Utility.setContextClassLoader(getClassLoader());
            invocation.isMessageDriven = true;
            invocation.method = method;
            
            context.setState(INVOKING);

	    invocation.context   = context;
	    invocation.instance  = context.getEJB();
            invocation.ejb       = context.getEJB();
	    invocation.container = this;

            // Message Bean Container only starts a new transaction if
            // there is no imported transaction and the message listener
            // method has tx attribute TX_REQUIRED or the ejbTimeout has
            // tx attribute TX_REQUIRES_NEW/TX_REQUIRED
            boolean startTx = false;
            if( !txImported ) {
                startTx = containerStartsTx(method);
            }

            // keep track of whether tx was started for later.
            invocation.containerStartsTx = startTx;

	    this.invocationManager.preInvoke(invocation);
            
            if( startTx ) {
                // Register the session associated with the message-driven 
                // bean's destination so the message delivery will be 
                // part of the container-managed transaction.
                registerMessageBeanResource(resourceHandle);
            }

            preInvokeTx(invocation);

	} catch(Throwable c) {
            if (containerState != CONTAINER_STARTED ) {
                _logger.log(Level.SEVERE,"containers.mdb.preinvoke_exception", 
                            new Object[]{appEJBName_, c.toString()});
                _logger.log(Level.SEVERE, c.getClass().getName(), c);
            }
            invocation.exception = c;
        }        
    
booleancallEJBTimeout(RuntimeTimerState timerState, EJBTimerService timerService)
Override callEJBTimeout from BaseContainer since delivery to message driven beans is a bit different from session/entity.

     
        boolean redeliver = false;
     
        // There is no resource associated with the delivery of the timeout.
        ResourceHandle nullResourceHandle = null;

        try {

            // Do pre-invoke logic for message bean with tx import = false
            // and a null resource handle.
            beforeMessageDelivery(ejbTimeoutMethod, false, nullResourceHandle);
     
            // Application must be passed a TimerWrapper.
            Object[] args  = { new TimerWrapper(timerState.getTimerId(),
                                                timerService) };

            deliverMessage(args);
     
        } catch(Throwable t) {
            // A runtime exception thrown from ejbTimeout, independent of
            // its transactional setting(CMT, BMT, etc.), should result in
            // a redelivery attempt.  The instance that threw the runtime
            // exception will be destroyed, as per the EJB spec.
            redeliver = true;

            _logger.log(Level.FINE, "ejbTimeout threw Runtime exception", t);

        } finally {
            if( !isBeanManagedTran && (transactionManager.getStatus() ==
                                       Status.STATUS_MARKED_ROLLBACK) ) {
                redeliver = true;
                _logger.log(Level.FINE, "ejbTimeout called setRollbackOnly");
            }

            // Only call postEjbTimeout if there are no errors so far. 
            if( !redeliver ) {
                boolean successfulPostEjbTimeout = 
                    timerService.postEjbTimeout(timerState.getTimerId());
                redeliver = !successfulPostEjbTimeout;
            }
          
            // afterMessageDelivery takes care of postInvoke and postInvokeTx
            // processing.   If any portion of that work fails, mark
            // timer for redelivery.
            boolean successfulAfterMessageDelivery = 
                afterMessageDeliveryInternal(nullResourceHandle);
            if( !redeliver && !successfulAfterMessageDelivery) {
                redeliver = true;
            }
        }
     
        return redeliver;
    
private voidcleanupResources()


        ASyncClientShutdownTask task = new ASyncClientShutdownTask(appEJBName_,
                messageBeanClient_, loader, messageBeanPool_);
        int timeout = 0;
        try {
            timeout = ResourcesUtil.createInstance().getShutdownTimeout();
        } catch (Throwable th) {
            _logger.log(Level.WARNING, "[MDBContainer] Got exception while trying "
                    + " to get shutdown timeout", th);
        }
        try {
            boolean addedAsyncTask = false;
            if (timeout > 0) {
                try {
                    ContainerWorkPool.addLast(task);
                    addedAsyncTask = true;
                } catch (Throwable th) {
                    //Since we got an exception while trying to add the async task
                    //  we will have to do the cleanup in the current thread itself.
                    addedAsyncTask = false;
                    _logger.log(Level.WARNING, "[MDBContainer] Got exception while trying "
                            + "to add task to ContainerWorkPool. Will execute "
                            + "cleanupResources on current thread", th);
                }
            }

            if (addedAsyncTask) {
                synchronized (task) {
                    if (! task.isDone()) {
                        _logger.log(Level.FINE, "[MDBContainer] "
                                + "Going to wait for a maximum of " + timeout + " seconds.");
                        task.wait(timeout * 1000);
                    }
    
                    if (! task.isDone()) {
                        _logger.log(Level.WARNING, "[MDBContainer] ASync task has not finished. "
                                + "Giving up after " + timeout + " seconds.");
                    } else {
                        _logger.log(Level.FINE, "[MDBContainer] ASync task has completed");
                    }
                }
            } else {
                //Execute in the same thread
                _logger.log(Level.FINE, "[MDBContainer] Attempting to do cleanup()in current thread...");
                task.run();
                _logger.log(Level.WARNING, "[MDBContainer] Current thread done cleanup()... ");
            }
        } catch (InterruptedException inEx) {
            _logger.log(Level.SEVERE, "containers.mdb.cleanup_exception",
                new Object[]{appEJBName_, inEx.toString()});
        } catch (Exception ex) {
            _logger.log(Level.SEVERE, "containers.mdb.cleanup_exception",
                new Object[]{appEJBName_, ex.toString()});
        }
    
private booleancontainerStartsTx(java.lang.reflect.Method method)

        int txMode = getTxAttr(method, MethodDescriptor.EJB_BEAN);

        return  method.equals(ejbTimeoutMethod) ?
            ( (txMode == TX_REQUIRES_NEW) || (txMode == TX_REQUIRED) )
            : (txMode == TX_REQUIRED);
    
public EJBObjectImplcreateEJBObjectImpl()

        throw new EJBException("No EJBObject for message-driven beans");
    
public MessageBeanListenercreateMessageBeanListener(com.sun.enterprise.resource.ResourceHandle resource)
The following are implementation for methods required by the * MessageBeanProtocalManager interface. *


        boolean resourcesExceeded = false;

        synchronized (this) {
            if( numMessageBeanListeners_ < maxMessageBeanListeners_ ) {
                numMessageBeanListeners_++;
            } else {
                resourcesExceeded = true;
            }
        }
        
        if( resourcesExceeded ) {
            ResourcesExceededException ree = 
                new ResourcesExceededException("Message Bean Resources " +
               "exceeded for message bean " + appEJBName_);
            _logger.log(Level.FINE, "exceeded max of " + 
                        maxMessageBeanListeners_, ree);
            throw ree;
        }

        //
        // Message bean context/instance creation is decoupled from 
        // MessageBeanListener instance creation.  This typically means
        // the message bean instances are instantiated lazily upon actual
        // message delivery.  In addition, MessageBeanListener instances 
        // are not pooled since they are currently very small objects without 
        // much initialization overhead.  This is the simplest approach since
        // there is minimal state to track between invocations and upon 
        // error conditions such as message bean instance failure. However,
        // it could be optimized in the following ways :
        //
        // 1. Implement MessageBeanListener within MessageBeanContextImpl.  
        // This reduces the number of objects created per thread of delivery.
        //
        // 2. Associate message bean context/instance with MessageBeanListener
        // across invocations.  This saves one pool retrieval and one
        // pool replacement operation for each invocation.  
        // 
        // 
        return new MessageBeanListenerImpl(this, resource);
    
private voidcreateMessageBeanPool(com.sun.enterprise.deployment.EjbMessageBeanDescriptor descriptor)

        
        beanPoolDesc_ = descriptor.getIASEjbExtraDescriptors().getBeanPool();
            
        if (beanPoolDesc_ == null) {
            beanPoolDesc_ = new BeanPoolDescriptor();
        }

        ServerContext sc = ApplicationServer.getServerContext();
        MdbContainer mdbc = ServerBeansFactory.
            getConfigBean(sc.getConfigContext()).getMdbContainer();
                                       
        int maxPoolSize = beanPoolDesc_.getMaxPoolSize();
        if (maxPoolSize < 0) {
            maxPoolSize = 
                stringToInt(mdbc.getMaxPoolSize(), appEJBName_, _logger); 
        }
        maxPoolSize = validateValue(maxPoolSize, 1, -1, DEFAULT_MAX_POOL_SIZE,
                                    "max-pool-size", appEJBName_, _logger);
        beanPoolDesc_.setMaxPoolSize(maxPoolSize);

        int value =  beanPoolDesc_.getSteadyPoolSize();
        if (value < 0) {
            value = stringToInt
                (mdbc.getSteadyPoolSize(), appEJBName_, _logger); 
        }
        value = validateValue(value, 0, maxPoolSize, DEFAULT_STEADY_SIZE, 
                              "steady-pool-size", appEJBName_, _logger);
        beanPoolDesc_.setSteadyPoolSize(value);

        value = beanPoolDesc_.getPoolResizeQuantity(); 
        if (value < 0 ) {
            value = stringToInt
                (mdbc.getPoolResizeQuantity(), appEJBName_, _logger); 
        }
        value = validateValue(value, 1, maxPoolSize, DEFAULT_RESIZE_QUANTITY, 
                              "pool-resize-quantity", appEJBName_, _logger);
        beanPoolDesc_.setPoolResizeQuantity(value);

        value = beanPoolDesc_.getPoolIdleTimeoutInSeconds();
        if (value <= 0) {
            value = stringToInt(mdbc.getIdleTimeoutInSeconds(),
                                appEJBName_, _logger);
        }
        value = validateValue(value, MIN_IDLE_TIMEOUT, -1, 
                              DEFAULT_IDLE_TIMEOUT, "idle-timeout-in-seconds",
                              appEJBName_, _logger);
        beanPoolDesc_.setPoolIdleTimeoutInSeconds(value);

        if (_logger.isLoggable(Level.FINE)) {
            _logger.log(Level.FINE, appEJBName_ + 
                ": Setting message-driven bean pool max-pool-size=" +
                beanPoolDesc_.getMaxPoolSize() +
                ", steady-pool-size=" + beanPoolDesc_.getSteadyPoolSize() +
                ", pool-resize-quantity=" + 
                beanPoolDesc_.getPoolResizeQuantity() +
                ", idle-timeout-in-seconds=" + 
                beanPoolDesc_.getPoolIdleTimeoutInSeconds());
        }

        // Create a non-blocking pool of message bean instances.
        // The protocol manager implementation enforces a limit 
        // on message bean resources independent of the pool.
        ObjectFactory objFactory = new MessageBeanContextFactory();
        messageBeanPool_ = new NonBlockingPool
            (appEJBName_, 
             objFactory, 
             beanPoolDesc_.getSteadyPoolSize(),
             beanPoolDesc_.getPoolResizeQuantity(),
             beanPoolDesc_.getMaxPoolSize(),
             beanPoolDesc_.getPoolIdleTimeoutInSeconds(), 
             loader);

	registryMediator.registerProvider(messageBeanPool_);
        //super.setMonitorOn(mdbc.isMonitoringEnabled());
    
private MessageBeanContextImplcreateMessageDrivenEJB()
Instantiate and initialize a message-driven bean instance.


	Invocation inv = null;
	MessageBeanContextImpl context  = null;
        ClassLoader originalClassLoader = null;
        boolean methodCalled = false;
        boolean methodCallFailed = false;

	try {
            // Set application class loader before invoking instance.
            originalClassLoader = Utility.setContextClassLoader(getClassLoader());

	    // create new message-driven ejb
	    Object ejb = ejbClass.newInstance();

            // create MessageDrivenContext and set it in the EJB
            context = new MessageBeanContextImpl(ejb, this);
            context.setInterceptorInstances(
                    interceptorManager.createInterceptorInstances());

	    // java:comp/env lookups are allowed from here on...
	    inv = new Invocation(ejb, this);
            inv.context = context;

            inv.isMessageDriven = true;
	    invocationManager.preInvoke(inv);

            if( ejb instanceof MessageDrivenBean ) {
                // setMessageDrivenContext will be called without a Tx 
                // as required by the spec
                ((MessageDrivenBean)ejb).setMessageDrivenContext(context);
            }

            // Perform injection right after where setMessageDrivenContext
            // would be called.  This is important since injection methods
            // have the same "operations allowed" permissions as
            // setMessageDrivenContext.
            injectionManager.injectInstance(ejb, ejbDescriptor, false);
            for (Object interceptorInstance : context.getInterceptorInstances()) {
                injectionManager.injectInstance(interceptorInstance,
                        ejbDescriptor, false);
            }
            
            // Set flag in context so UserTransaction can
            // be used from ejbCreate.  Didn't want to add
            // a new state to lifecycle since that would
            // require either changing lots of code in 
            // EJBContextImpl or re-implementing all the
            // context methods within MessageBeanContextImpl.
            context.setContextCalled();

            // Call ejbCreate OR @PostConstruct on the bean.
            interceptorManager.intercept(
                    CallbackType.POST_CONSTRUCT, context);

	    statCreateCount++;
            
	    // Set the state to POOLED after ejbCreate so that 
	    // EJBContext methods not allowed will throw exceptions
	    context.setState(POOLED);
	} 
	catch ( Throwable t ) {
            _logger.log(Level.SEVERE,
                        "containers.mdb.ejb_creation_exception", 
                        new Object[]{appEJBName_, t.toString()});
            if (t instanceof InvocationTargetException) {
                _logger.log(Level.SEVERE, t.getClass().getName(), 
                            t.getCause());
            }
            _logger.log(Level.SEVERE, t.getClass().getName(), t);

            CreateException ce = 
                new CreateException("Could not create Message-Driven EJB");
            ce.initCause(t);
	    throw ce;

	} finally {
            if( originalClassLoader != null ) {
                Utility.setContextClassLoader(originalClassLoader);
            }
	    if ( inv != null ) {
		invocationManager.postInvoke(inv);
            }
	}

        return context;
    
public java.lang.ObjectdeliverMessage(java.lang.Object[] params)


        Invocation invocation = null;
        boolean methodCalled = false; //for monitoring
        Object result = null;

        invocation = (Invocation) invocationManager.getCurrentInvocation();

        if (invocation == null && _logger.isLoggable(Level.FINEST)) {
            if (containerState != CONTAINER_STARTED) {
                _logger.log(Level.FINEST, "No invocation in onMessage " + 
                            " (container closing)");
            }
            else {
                _logger.log(Level.FINEST, "No invocation in onMessage : ");
            }
        }

        if( ( invocation != null ) && ( invocation.exception == null ) ) {

            try {

                // NOTE : Application classloader already set in 
                // beforeMessageDelivery
                
                methodCalled = true;
		if (ejbMethodStatsManager.isMethodMonitorOn()){
		    ejbMethodStatsManager.preInvoke(invocation.method);
		}
                
                invocation.methodParams = params;

                if( isTimedObject() &&
                    invocation.method.equals(ejbTimeoutMethod) ) {
                    invocation.beanMethod = invocation.method;
                    invokeTargetBeanMethod(ejbTimeoutMethod, invocation, 
                                           invocation.ejb,
                                           invocation.methodParams, null);
                } else {
                    // invocation.beanMethod is the actual target method from 
                    // the bean class.  The bean class is not required to be 
                    // a formal subtype of the message listener interface, so 
                    // we need to be careful to invoke through the bean class 
                    // method itself.  This info is also returned from the 
                    // interceptor context info.

                    invocation.beanMethod = invocation.ejb.getClass().getMethod
                        (invocation.method.getName(), 
                         invocation.method.getParameterTypes());

                    result = super.intercept(invocation);
                }

            } catch(InvocationTargetException ite) {

                //
                // In EJB 2.1, message listener method signatures do not have 
                // any restrictions on what kind of exceptions can be thrown.
                // This was not the case in J2EE 1.3, since JMS message driven
                // beans could only implement 
                // void javax.jms.MessageListener.onMessage() , which does
                // not declare any exceptions.
                // 
                // In the J2EE 1.3 implementation, exceptions were only 
                // propagated when the message driven bean was not configured
                // with CMT/Required transaction mode.  This has been changed
                // due to the Connector 1.5 integration.  Now, all exceptions
                // are propagated regardless of the tx mode.  (18.2.2)
                // Application exceptions are propagated as is, while system
                // exceptions are wrapped in an EJBException.
                //
                // If an exception is thrown and there is a container-started 
                // transaction, the semantics are the same as for other ejb 
                // types whose business methods throw an exception.  
                // Specifically, if the exception thrown is an Application
                // exception(defined in 18.2.1), it does not automatically 
                // result in a rollback of the container-started transaction. 
                // 

                Throwable cause = ite.getCause();
                // set cause on invocation , rather than the propagated 
                // EJBException
                invocation.exception = cause;

                if( isSystemUncheckedException(cause) ) {
                    EJBException ejbEx = new EJBException
                        ("message-driven bean method " + invocation.method + 
                         " system exception");
                    ejbEx.initCause(cause);
                    cause = ejbEx;
                }
                throw cause;
            } catch(Throwable t) {
                EJBException ejbEx = 
                    new EJBException("message-bean container dispatch error");
                ejbEx.initCause(t);
                invocation.exception = ejbEx;
                throw ejbEx;
            } finally {
                if (methodCalled && (ejbMethodStatsManager.isMethodMonitorOn())) {
		    ejbMethodStatsManager.postInvoke(
			invocation.method, invocation.exception);
                }

                if ( AppVerification.doInstrument() ) {
                    AppVerification.getInstrumentLogger().doInstrumentForEjb
                        (getEjbDescriptor(), invocation.method, 
                         invocation.exception);
                }
            }

        } // End if -- invoke instance's onMessage method 
        else {
            if (invocation == null) {
                String errorMsg = localStrings.getLocalString
                    ("containers.mdb.invocation_closed", 
                     appEJBName_ + ": Message-driven bean invocation " + 
                     "closed by container", new Object[] { appEJBName_}); 
                throw new EJBException(errorMsg);
            }
            else {
                _logger.log(Level.SEVERE, 
                            "containers.mdb.invocation_exception",
                   new Object[]{appEJBName_, invocation.exception.toString()});
                _logger.log(Level.SEVERE, invocation.exception.getClass().
                            getName(), invocation.exception);
                EJBException ejbEx = new EJBException();
                ejbEx.initCause(invocation.exception);
                throw ejbEx;
            }
        }

        return result;
    
public voiddestroyMessageBeanListener(MessageBeanListener listener)

        synchronized (this) {
            numMessageBeanListeners_--;
        } 
    
public voiddoAfterApplicationDeploy()
Called when the application containing this message-bean has successfully deployed.

        super.doAfterApplicationDeploy();

        // Start delivery of messages to message bean instances.
        try {
            messageBeanClient_.start();

        }
        catch(Exception e) {
            _logger.log(Level.SEVERE,
                        "containers.mdb.start_message_delivery_exception",
                        new Object[]{appEJBName_, e.toString()});
            _logger.log(Level.SEVERE, e.getClass().getName(), e);
        }
    
voidforceDestroyBean(EJBContextImpl sc)
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.

        if ( sc.getState() == DESTROYED )
            return;

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

        messageBeanPool_.destroyObject(sc);
    
public longgetCreateCount()

	return statCreateCount;
    
public EJBObjectImplgetEJBObjectImpl(byte[] instanceKey)

        throw new EJBException("No EJBObject for message-driven beans");
    
public longgetMessageCount()

	return statMessageCount;
    
public java.lang.StringgetMonitorAttributeValues()

        StringBuffer sbuf = new StringBuffer();
        sbuf.append("MESSAGEDRIVEN ");
        sbuf.append(appEJBName_);

        sbuf.append(messageBeanPool_.getAllAttrValues());
        sbuf.append("]");
        return sbuf.toString();
    
public com.sun.enterprise.deployment.runtime.BeanPoolDescriptorgetPoolDescriptor()

        return beanPoolDesc_;
    
public longgetRemoveCount()

	return statRemoveCount;
    
public booleanisDeliveryTransacted(java.lang.reflect.Method method)

param
method One of the methods used to deliver messages, e.g. onMessage method for javax.jms.MessageListener. Note that if the method is not one of the methods for message delivery, the behavior of this method is not defined.

        return containerStartsTx(method);
    
public voidonShutdown()
Called when server instance is shuting down

        _logger.log(Level.FINE, 
                    "containers.mdb.shutdown_cleanup_start", appEJBName_);
        setStoppedState();
        monitorOn = false;
        cleanupResources();
        _logger.log(Level.FINE, 
                    "containers.mdb.shutdown_cleanup_end", appEJBName_);
    
public booleanpassivateEJB(ComponentContext context)

		return false;
    
public voidpostInvoke(Invocation inv)

        throw new EJBException("postInvoke(Invocation) not supported " +
                                  "in message-driven bean container");
    
public voidpreInvoke(Invocation inv)

        throw new EJBException("preInvoke(Invocation) not supported");
    
private voidregisterMessageBeanResource(com.sun.enterprise.resource.ResourceHandle resourceHandle)
Make the work performed by a message-bean instance's associated XA resource part of any global transaction

        if( resourceHandle != null ) {
            Switch theSwitch    = Switch.getSwitch();
            PoolManager poolMgr = theSwitch.getPoolManager();
            poolMgr.registerResource(resourceHandle);
        }
    
protected voidregisterMonitorableComponents(java.lang.reflect.Method[] msgListenerMethods)

	registryMediator.registerProvider(this);
        super.registerMonitorableComponents();
	super.populateMethodMonitorMap(msgListenerMethods);
        _logger.log(Level.FINE, "[Entity Container] registered monitorable");
    
public voidreleaseContext(Invocation inv)
Return instance to a pooled state.

        MessageBeanContextImpl beanContext = (MessageBeanContextImpl)
            inv.context;
 
        if( beanContext.getState() == DESTROYED ) {
            return;
        }

        beanContext.setState(POOLED);

	// Message-driven beans can't have transactions across invocations.
	beanContext.setTransaction(null);

        // Update last access time so pool's time-based logic will work best
        beanContext.touch();

        messageBeanPool_.returnObject(beanContext);
    
voidremoveBean(EJBLocalRemoteObject ejbo, java.lang.reflect.Method removeMethod, boolean local)

        throw new EJBException("not used in message-driven beans");
    
public voidsetEJBHome(EJBHome ejbHome)

        throw new Exception("Can't set EJB Home on Message-driven bean");
    
protected static intstringToInt(java.lang.String val, java.lang.String appName, java.util.logging.Logger logger)

        int value = -1;
        try {
            value  = Integer.parseInt(val);
        } catch (Exception e) {
            _logger.log(Level.WARNING,"containers.mdb.invalid_value" , 
                new Object[]{appName, new Integer(val) , e.toString(), 
                                 new Integer(0)} );
            _logger.log(Level.WARNING, "", e);
        }
        return value;
    
public voidundeploy()

        // This will cause all new invocations to be rejected.
        super.setUndeployedState();

        _logger.log(Level.FINE, "containers.mdb.undeploy", appEJBName_);

        cleanupResources();

        super.undeploy();
    
private voidunregisterMessageBeanResource(com.sun.enterprise.resource.ResourceHandle resourceHandle)


        // resource handle may be null if preInvokeTx error caused 
        // ResourceAllocator.destroyResource()
        if (resourceHandle != null) { 
            Switch theSwitch    = Switch.getSwitch();
            PoolManager poolMgr = theSwitch.getPoolManager();
            poolMgr.unregisterResource(resourceHandle, XAResource.TMSUCCESS);
        }
    
public booleanuserTransactionMethodsAllowed(ComponentInvocation inv)

        boolean utMethodsAllowed = false;
        if( isBeanManagedTran ) {
            if( inv instanceof Invocation ) {
                Invocation i = (Invocation) inv;
                EJBContextImpl mdc = (EJBContextImpl) i.context;
                utMethodsAllowed = (mdc.isUnitialized() || mdc.isInEjbRemove())
                    ? false : true;
            } 
        }
        return utMethodsAllowed;
    
protected intvalidateValue(int value, int lowLimit, int highLimit, int deft, java.lang.String emsg, java.lang.String appName, java.util.logging.Logger logger)


        if (value < lowLimit ) {
            _logger.log(Level.WARNING,"containers.mdb.invalid_value" , 
                new Object[]{appName, new Integer(value) , emsg , 
                                 new Integer(lowLimit)} );
            value = deft;
        }

        if ((highLimit >= 0) && (value > highLimit))  {
           _logger.log(Level.WARNING,"containers.mdb.invalid_value" , 
               new Object[]{appName, new Integer(value) , emsg , 
                                new Integer(highLimit)} );
           value = highLimit;
        }

        return value;