KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > ejb > containers > MessageBeanContainer


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the License). You may not use this file except in
5  * compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * https://glassfish.dev.java.net/public/CDDLv1.0.html or
9  * glassfish/bootstrap/legal/CDDLv1.0.txt.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * Header Notice in each file and include the License file
15  * at glassfish/bootstrap/legal/CDDLv1.0.txt.
16  * If applicable, add the following below the CDDL Header,
17  * with the fields enclosed by brackets [] replaced by
18  * you own identifying information:
19  * "Portions Copyrighted [year] [name of copyright owner]"
20  *
21  * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
22  */

23 package com.sun.ejb.containers;
24
25 import java.lang.reflect.Constructor JavaDoc;
26 import java.lang.reflect.Method JavaDoc;
27
28 import java.util.*;
29 import java.lang.reflect.InvocationTargetException JavaDoc;
30
31 import javax.ejb.*;
32
33 import javax.ejb.MessageDriven JavaDoc;
34
35 import javax.naming.InitialContext JavaDoc;
36 import javax.naming.Context JavaDoc;
37
38 import javax.transaction.*;
39 import javax.transaction.xa.*;
40
41 import com.sun.ejb.*;
42
43 import com.sun.ejb.containers.util.pool.Pool;
44 import com.sun.ejb.containers.util.pool.AbstractPool;
45 import com.sun.ejb.containers.util.pool.NonBlockingPool;
46 import com.sun.ejb.containers.util.pool.ObjectFactory;
47 import com.sun.enterprise.util.LocalStringManagerImpl;
48 import com.sun.enterprise.*;
49 import com.sun.enterprise.deployment.EjbDescriptor;
50 import com.sun.enterprise.deployment.MethodDescriptor;
51 import com.sun.enterprise.deployment.EjbMessageBeanDescriptor;
52 import static com.sun.enterprise.deployment.LifecycleCallbackDescriptor.CallbackType;
53 import com.sun.enterprise.deployment.runtime.BeanPoolDescriptor;
54 import com.sun.enterprise.server.ServerContext;
55 import com.sun.enterprise.server.ApplicationServer;
56 import com.sun.enterprise.config.serverbeans.ServerBeansFactory;
57 import com.sun.enterprise.config.serverbeans.MdbContainer;
58 import com.sun.enterprise.config.ConfigException;
59 import com.sun.enterprise.resource.ResourceHandle;
60 import com.sun.enterprise.log.Log;
61 import com.sun.enterprise.ServerConfiguration;
62 import com.sun.enterprise.admin.monitor.callflow.ComponentType;
63
64 import com.sun.enterprise.appverification.factory.AppVerification;
65 import com.sun.enterprise.admin.monitor.*;
66 import com.sun.ejb.containers.util.pool.AbstractPool;
67
68 import java.util.logging.*;
69
70 import com.sun.logging.*;
71
72 import com.sun.ejb.spi.stats.MessageDrivenBeanStatsProvider;
73
74 /** This class provides container functionality specific to message-driven
75  * EJBs.
76  * At deployment time, one instance of the MessageDrivenBeanContainer is
77  * created for each message-driven bean in an application.
78  * <P>
79  * The 3 states of a Message-driven EJB (an EJB can be in only 1 state
80  * at a time):
81  * 1. POOLED : ready for invocations, no transaction in progress
82  * 2. INVOKING : processing an invocation
83  * 3. DESTROYED : does not exist
84  *
85  * A Message-driven Bean can hold open DB connections across invocations.
86  * It's assumed that the Resource Manager can handle
87  * multiple incomplete transactions on the same
88  * connection.
89  *
90  * @author Kenneth Saks
91  */

92 public final class MessageBeanContainer extends BaseContainer
93     implements MessageBeanProtocolManager, MessageDrivenBeanStatsProvider
94 {
95     private static Logger _logger;
96     static {
97         _logger=LogDomains.getLogger(LogDomains.MDB_LOGGER);
98     }
99     
100     private String JavaDoc appEJBName_;
101
102     private static LocalStringManagerImpl localStrings =
103     new LocalStringManagerImpl(MessageBeanContainer.class);
104
105     // Message-bean instance states
106
private static final int POOLED=1, INVOKING=2, DESTROYED=3;
107
108     private MessageBeanClient messageBeanClient_ = null;
109
110     private AbstractPool messageBeanPool_ = null;
111
112     private BeanPoolDescriptor beanPoolDesc_ = null;
113     private int maxMessageBeanListeners_;
114     private int numMessageBeanListeners_;
115
116     // Property used to bootstrap message bean client factory for inbound
117
// message delivery.
118
private static final String JavaDoc MESSAGE_BEAN_CLIENT_FACTORY_PROP =
119         "com.sun.enterprise.MessageBeanClientFactory";
120
121     private static final String JavaDoc DEFAULT_MESSAGE_BEAN_CLIENT_FACTORY =
122         "com.sun.enterprise.connectors.inflow.ConnectorMessageBeanClientFactory";
123
124     private static final int DEFAULT_RESIZE_QUANTITY = 1;
125     private static final int DEFAULT_STEADY_SIZE = 10;
126     private static final int DEFAULT_MAX_POOL_SIZE = 60;
127     private static final int DEFAULT_IDLE_TIMEOUT = 600;
128     private static final int MIN_IDLE_TIMEOUT = 1;
129
130     private int statMessageCount = 0;
131
132     MessageBeanContainer(EjbDescriptor desc, ClassLoader JavaDoc loader)
133     throws Exception JavaDoc
134     {
135     super(desc, loader);
136
137         appEJBName_ =
138             desc.getApplication().getRegistrationName() + ":" +desc.getName();
139
140         EjbMessageBeanDescriptor msgBeanDesc =
141             (EjbMessageBeanDescriptor) desc;
142
143         try {
144
145             // Register the tx attribute for each method on MessageListener
146
// interface. NOTE : These method objects MUST come from the
147
// MessageListener interface, NOT the bean class itself. This
148
// is because the message bean container clients do not have
149
// access to the message bean class.
150
Method JavaDoc[] msgListenerMethods =
151                 msgBeanDesc.getMessageListenerInterfaceMethods(loader);
152
153             for(int i = 0; i < msgListenerMethods.length; i++) {
154                 Method JavaDoc next = msgListenerMethods[i];
155                 super.registerTxAttrForMethod(next, MethodDescriptor.EJB_BEAN);
156             }
157             // NOTE : No need to register tx attribute for ejbTimeout. It's
158
// done in BaseContainer intialization.
159
// Message-driven beans can be timed objects.
160

161
162             // Bootstrap message bean client factory. If the class name is
163
// specified as a system property, that value takes precedence.
164
// Otherwise use default client factory. The default is set to
165
// a client factory that uses the S1AS 7 style JMS connection
166
// consumer contracts. This will be changed once the Connector 1.5
167
// implementation is ready.
168
String JavaDoc factoryClassName = System.getProperty
169                 (MESSAGE_BEAN_CLIENT_FACTORY_PROP,
170                  DEFAULT_MESSAGE_BEAN_CLIENT_FACTORY);
171
172             Class JavaDoc clientFactoryClass = Class.forName(factoryClassName);
173             MessageBeanClientFactory clientFactory = (MessageBeanClientFactory)
174                 clientFactoryClass.newInstance();
175
176             _logger.log(Level.FINE, "Using " + factoryClassName +
177                 " for message bean client factory in " + appEJBName_);
178
179             // Create message bean pool before calling setup on
180
// Message-bean client, since pool properties can be retrieved
181
// through MessageBeanProtocolManager interface.
182
createMessageBeanPool(msgBeanDesc);
183
184             // Set resource limit for message bean listeners created through
185
// Protocol Manager. For now, just use max pool size. However,
186
// we might want to bump this up once the ejb timer service is
187
// integrated.
188
maxMessageBeanListeners_ = beanPoolDesc_.getMaxPoolSize();
189             numMessageBeanListeners_ = 0;
190
191             messageBeanClient_ =
192                 clientFactory.createMessageBeanClient(msgBeanDesc);
193
194             messageBeanClient_.setup(this);
195
196         registerMonitorableComponents(msgListenerMethods);
197
198             createCallFlowAgent(ComponentType.MDB);
199     }
200     catch (Exception JavaDoc ex) {
201
202             if (messageBeanClient_ != null) {
203                 messageBeanClient_.close();
204             }
205
206             _logger.log(Level.SEVERE,
207                         "containers.mdb.create_container_exception",
208                         new Object JavaDoc[]{desc.getName(), ex.toString()});
209             _logger.log(Level.SEVERE, ex.getClass().getName(), ex);
210         throw ex;
211     }
212     }
213
214     protected void registerMonitorableComponents(Method JavaDoc[] msgListenerMethods) {
215     registryMediator.registerProvider(this);
216         super.registerMonitorableComponents();
217     super.populateMethodMonitorMap(msgListenerMethods);
218         _logger.log(Level.FINE, "[Entity Container] registered monitorable");
219     }
220
221     private void createMessageBeanPool(EjbMessageBeanDescriptor descriptor)
222         throws ConfigException {
223         
224         beanPoolDesc_ = descriptor.getIASEjbExtraDescriptors().getBeanPool();
225             
226         if (beanPoolDesc_ == null) {
227             beanPoolDesc_ = new BeanPoolDescriptor();
228         }
229
230         ServerContext sc = ApplicationServer.getServerContext();
231         MdbContainer mdbc = ServerBeansFactory.
232             getConfigBean(sc.getConfigContext()).getMdbContainer();
233                                        
234         int maxPoolSize = beanPoolDesc_.getMaxPoolSize();
235         if (maxPoolSize < 0) {
236             maxPoolSize =
237                 stringToInt(mdbc.getMaxPoolSize(), appEJBName_, _logger);
238         }
239         maxPoolSize = validateValue(maxPoolSize, 1, -1, DEFAULT_MAX_POOL_SIZE,
240                                     "max-pool-size", appEJBName_, _logger);
241         beanPoolDesc_.setMaxPoolSize(maxPoolSize);
242
243         int value = beanPoolDesc_.getSteadyPoolSize();
244         if (value < 0) {
245             value = stringToInt
246                 (mdbc.getSteadyPoolSize(), appEJBName_, _logger);
247         }
248         value = validateValue(value, 0, maxPoolSize, DEFAULT_STEADY_SIZE,
249                               "steady-pool-size", appEJBName_, _logger);
250         beanPoolDesc_.setSteadyPoolSize(value);
251
252         value = beanPoolDesc_.getPoolResizeQuantity();
253         if (value < 0 ) {
254             value = stringToInt
255                 (mdbc.getPoolResizeQuantity(), appEJBName_, _logger);
256         }
257         value = validateValue(value, 1, maxPoolSize, DEFAULT_RESIZE_QUANTITY,
258                               "pool-resize-quantity", appEJBName_, _logger);
259         beanPoolDesc_.setPoolResizeQuantity(value);
260
261         value = beanPoolDesc_.getPoolIdleTimeoutInSeconds();
262         if (value <= 0) {
263             value = stringToInt(mdbc.getIdleTimeoutInSeconds(),
264                                 appEJBName_, _logger);
265         }
266         value = validateValue(value, MIN_IDLE_TIMEOUT, -1,
267                               DEFAULT_IDLE_TIMEOUT, "idle-timeout-in-seconds",
268                               appEJBName_, _logger);
269         beanPoolDesc_.setPoolIdleTimeoutInSeconds(value);
270
271         if (_logger.isLoggable(Level.FINE)) {
272             _logger.log(Level.FINE, appEJBName_ +
273                 ": Setting message-driven bean pool max-pool-size=" +
274                 beanPoolDesc_.getMaxPoolSize() +
275                 ", steady-pool-size=" + beanPoolDesc_.getSteadyPoolSize() +
276                 ", pool-resize-quantity=" +
277                 beanPoolDesc_.getPoolResizeQuantity() +
278                 ", idle-timeout-in-seconds=" +
279                 beanPoolDesc_.getPoolIdleTimeoutInSeconds());
280         }
281
282         // Create a non-blocking pool of message bean instances.
283
// The protocol manager implementation enforces a limit
284
// on message bean resources independent of the pool.
285
ObjectFactory objFactory = new MessageBeanContextFactory();
286         messageBeanPool_ = new NonBlockingPool
287             (appEJBName_,
288              objFactory,
289              beanPoolDesc_.getSteadyPoolSize(),
290              beanPoolDesc_.getPoolResizeQuantity(),
291              beanPoolDesc_.getMaxPoolSize(),
292              beanPoolDesc_.getPoolIdleTimeoutInSeconds(),
293              loader);
294
295     registryMediator.registerProvider(messageBeanPool_);
296         //super.setMonitorOn(mdbc.isMonitoringEnabled());
297
}
298
299     protected static int stringToInt(String JavaDoc val, String JavaDoc appName,
300                                      Logger logger) {
301         int value = -1;
302         try {
303             value = Integer.parseInt(val);
304         } catch (Exception JavaDoc e) {
305             _logger.log(Level.WARNING,"containers.mdb.invalid_value" ,
306                 new Object JavaDoc[]{appName, new Integer JavaDoc(val) , e.toString(),
307                                  new Integer JavaDoc(0)} );
308             _logger.log(Level.WARNING, "", e);
309         }
310         return value;
311     }
312
313     //deft should always >= lowLimit
314
protected int validateValue(int value, int lowLimit, int highLimit,
315        int deft, String JavaDoc emsg, String JavaDoc appName, Logger logger) {
316
317         if (value < lowLimit ) {
318             _logger.log(Level.WARNING,"containers.mdb.invalid_value" ,
319                 new Object JavaDoc[]{appName, new Integer JavaDoc(value) , emsg ,
320                                  new Integer JavaDoc(lowLimit)} );
321             value = deft;
322         }
323
324         if ((highLimit >= 0) && (value > highLimit)) {
325            _logger.log(Level.WARNING,"containers.mdb.invalid_value" ,
326                new Object JavaDoc[]{appName, new Integer JavaDoc(value) , emsg ,
327                                 new Integer JavaDoc(highLimit)} );
328            value = highLimit;
329         }
330
331         return value;
332     }
333
334     private boolean containerStartsTx(Method JavaDoc method) {
335         int txMode = getTxAttr(method, MethodDescriptor.EJB_BEAN);
336
337         return method.equals(ejbTimeoutMethod) ?
338             ( (txMode == TX_REQUIRES_NEW) || (txMode == TX_REQUIRED) )
339             : (txMode == TX_REQUIRED);
340     }
341
342     public String JavaDoc getMonitorAttributeValues() {
343         StringBuffer JavaDoc sbuf = new StringBuffer JavaDoc();
344         sbuf.append("MESSAGEDRIVEN ");
345         sbuf.append(appEJBName_);
346
347         sbuf.append(messageBeanPool_.getAllAttrValues());
348         sbuf.append("]");
349         return sbuf.toString();
350     }
351
352     public boolean userTransactionMethodsAllowed(ComponentInvocation inv)
353     {
354         boolean utMethodsAllowed = false;
355         if( isBeanManagedTran ) {
356             if( inv instanceof Invocation ) {
357                 Invocation i = (Invocation) inv;
358                 EJBContextImpl mdc = (EJBContextImpl) i.context;
359                 utMethodsAllowed = (mdc.isUnitialized() || mdc.isInEjbRemove())
360                     ? false : true;
361             }
362         }
363         return utMethodsAllowed;
364     }
365
366     public void setEJBHome(EJBHome ejbHome)
367     throws Exception JavaDoc
368     {
369         throw new Exception JavaDoc("Can't set EJB Home on Message-driven bean");
370     }
371
372     public EJBObjectImpl getEJBObjectImpl(byte[] instanceKey)
373     {
374         throw new EJBException("No EJBObject for message-driven beans");
375     }
376     
377     public EJBObjectImpl createEJBObjectImpl()
378     throws CreateException
379     {
380         throw new EJBException("No EJBObject for message-driven beans");
381     }
382
383     void removeBean(EJBLocalRemoteObject ejbo, Method JavaDoc removeMethod,
384                              boolean local)
385         throws RemoveException, EJBException
386     {
387         throw new EJBException("not used in message-driven beans");
388     }
389
390
391     /**
392      * Override callEJBTimeout from BaseContainer since delivery to
393      * message driven beans is a bit different from session/entity.
394      */

395     boolean callEJBTimeout(RuntimeTimerState timerState,
396                            EJBTimerService timerService) throws Exception JavaDoc {
397      
398         boolean redeliver = false;
399      
400         // There is no resource associated with the delivery of the timeout.
401
ResourceHandle nullResourceHandle = null;
402
403         try {
404
405             // Do pre-invoke logic for message bean with tx import = false
406
// and a null resource handle.
407
beforeMessageDelivery(ejbTimeoutMethod, false, nullResourceHandle);
408      
409             // Application must be passed a TimerWrapper.
410
Object JavaDoc[] args = { new TimerWrapper(timerState.getTimerId(),
411                                                 timerService) };
412
413             deliverMessage(args);
414      
415         } catch(Throwable JavaDoc t) {
416             // A runtime exception thrown from ejbTimeout, independent of
417
// its transactional setting(CMT, BMT, etc.), should result in
418
// a redelivery attempt. The instance that threw the runtime
419
// exception will be destroyed, as per the EJB spec.
420
redeliver = true;
421
422             _logger.log(Level.FINE, "ejbTimeout threw Runtime exception", t);
423
424         } finally {
425             if( !isBeanManagedTran && (transactionManager.getStatus() ==
426                                        Status.STATUS_MARKED_ROLLBACK) ) {
427                 redeliver = true;
428                 _logger.log(Level.FINE, "ejbTimeout called setRollbackOnly");
429             }
430
431             // Only call postEjbTimeout if there are no errors so far.
432
if( !redeliver ) {
433                 boolean successfulPostEjbTimeout =
434                     timerService.postEjbTimeout(timerState.getTimerId());
435                 redeliver = !successfulPostEjbTimeout;
436             }
437           
438             // afterMessageDelivery takes care of postInvoke and postInvokeTx
439
// processing. If any portion of that work fails, mark
440
// timer for redelivery.
441
boolean successfulAfterMessageDelivery =
442                 afterMessageDeliveryInternal(nullResourceHandle);
443             if( !redeliver && !successfulAfterMessageDelivery) {
444                 redeliver = true;
445             }
446         }
447      
448         return redeliver;
449     }
450
451
452     /**
453      * Force destroy the EJB. Called from postInvokeTx.
454      * Note: EJB2.0 section 18.3.1 says that discarding an EJB
455      * means that no methods other than finalize() should be invoked on it.
456      */

457     void forceDestroyBean(EJBContextImpl sc)
458     {
459         if ( sc.getState() == DESTROYED )
460             return;
461
462         // mark context as destroyed
463
sc.setState(DESTROYED);
464
465         messageBeanPool_.destroyObject(sc);
466     }
467
468     // This particular preInvoke signature not used
469
public void preInvoke(Invocation inv) {
470         throw new EJBException("preInvoke(Invocation) not supported");
471     }
472
473     private class MessageBeanContextFactory implements ObjectFactory {
474
475         public Object JavaDoc create(Object JavaDoc param) {
476             try {
477                 return createMessageDrivenEJB();
478             } catch (CreateException ex) {
479                 throw new EJBException(ex);
480             }
481         }
482
483         public void destroy(Object JavaDoc obj) {
484
485             MessageBeanContextImpl beanContext =
486                 (MessageBeanContextImpl) obj;
487                 
488             Object JavaDoc ejb = beanContext.getEJB();
489
490             if( beanContext.getState() != DESTROYED ) {
491
492                 // Called from pool implementation to reduce the pool size.
493
// So we need to call ejb.ejbRemove() and
494
// mark context as destroyed.
495
Invocation inv = null;
496
497                 try {
498                     // NOTE : Context class-loader is already set by Pool
499

500                     inv = new Invocation(ejb, MessageBeanContainer.this);
501                     inv.context = beanContext;
502
503                     inv.isMessageDriven = true;
504                     invocationManager.preInvoke(inv);
505                     
506                     beanContext.setInEjbRemove(true);
507                     interceptorManager.intercept(
508                                 CallbackType.PRE_DESTROY, beanContext);
509             statRemoveCount++;
510                 } catch ( Throwable JavaDoc t ) {
511                     _logger.log(Level.SEVERE,
512                                 "containers.mdb_preinvoke_exception_indestroy",
513                                 new Object JavaDoc[]{appEJBName_, t.toString()});
514                     _logger.log(Level.SEVERE, t.getClass().getName(), t);
515                 } finally {
516                     beanContext.setInEjbRemove(false);
517                     if ( inv != null ) {
518                         invocationManager.postInvoke(inv);
519                     }
520                 }
521
522                 beanContext.setState(DESTROYED);
523                 
524             }
525
526             // tell the TM to release resources held by the bean
527
transactionManager.ejbDestroyed(beanContext);
528
529             // Message-driven beans can't have transactions across
530
// invocations.
531
beanContext.setTransaction(null);
532         }
533
534     }
535
536     protected ComponentContext _getContext(Invocation inv)
537     {
538         MessageBeanContextImpl context = null;
539         try {
540             context = (MessageBeanContextImpl) messageBeanPool_.getObject(null);
541             context.setState(INVOKING);
542         } catch(Exception JavaDoc e) {
543             throw new EJBException(e);
544         }
545         return context;
546     }
547
548     /**
549      * Return instance to a pooled state.
550      */

551     public void releaseContext(Invocation inv)
552     {
553         MessageBeanContextImpl beanContext = (MessageBeanContextImpl)
554             inv.context;
555  
556         if( beanContext.getState() == DESTROYED ) {
557             return;
558         }
559
560         beanContext.setState(POOLED);
561
562     // Message-driven beans can't have transactions across invocations.
563
beanContext.setTransaction(null);
564
565         // Update last access time so pool's time-based logic will work best
566
beanContext.touch();
567
568         messageBeanPool_.returnObject(beanContext);
569     }
570
571     public void appendStats(StringBuffer JavaDoc sbuf) {
572     sbuf.append("\nMessageBeanContainer: ")
573         .append("CreateCount=").append(statCreateCount).append("; ")
574         .append("RemoveCount=").append(statRemoveCount).append("; ")
575         .append("MsgCount=").append(statMessageCount).append("; ");
576     sbuf.append("]");
577     }
578
579     // This particular postInvoke signature not used
580
public void postInvoke(Invocation inv) {
581         throw new EJBException("postInvoke(Invocation) not supported " +
582                                   "in message-driven bean container");
583     }
584
585     /****************************************************************
586      * The following are implementation for methods required by the *
587      * MessageBeanProtocalManager interface. *
588      ****************************************************************/

589
590     public MessageBeanListener createMessageBeanListener
591         (ResourceHandle resource) throws ResourcesExceededException {
592
593         boolean resourcesExceeded = false;
594
595         synchronized (this) {
596             if( numMessageBeanListeners_ < maxMessageBeanListeners_ ) {
597                 numMessageBeanListeners_++;
598             } else {
599                 resourcesExceeded = true;
600             }
601         }
602         
603         if( resourcesExceeded ) {
604             ResourcesExceededException ree =
605                 new ResourcesExceededException("Message Bean Resources " +
606                "exceeded for message bean " + appEJBName_);
607             _logger.log(Level.FINE, "exceeded max of " +
608                         maxMessageBeanListeners_, ree);
609             throw ree;
610         }
611
612         //
613
// Message bean context/instance creation is decoupled from
614
// MessageBeanListener instance creation. This typically means
615
// the message bean instances are instantiated lazily upon actual
616
// message delivery. In addition, MessageBeanListener instances
617
// are not pooled since they are currently very small objects without
618
// much initialization overhead. This is the simplest approach since
619
// there is minimal state to track between invocations and upon
620
// error conditions such as message bean instance failure. However,
621
// it could be optimized in the following ways :
622
//
623
// 1. Implement MessageBeanListener within MessageBeanContextImpl.
624
// This reduces the number of objects created per thread of delivery.
625
//
626
// 2. Associate message bean context/instance with MessageBeanListener
627
// across invocations. This saves one pool retrieval and one
628
// pool replacement operation for each invocation.
629
//
630
//
631
return new MessageBeanListenerImpl(this, resource);
632     }
633
634     public void destroyMessageBeanListener(MessageBeanListener listener) {
635         synchronized (this) {
636             numMessageBeanListeners_--;
637         }
638     }
639
640     /**
641      * @param method One of the methods used to deliver messages, e.g.
642      * onMessage method for javax.jms.MessageListener.
643      * Note that if the <code>method</code> is not one
644      * of the methods for message delivery, the behavior
645      * of this method is not defined.
646      */

647     public boolean isDeliveryTransacted (Method JavaDoc method) {
648         return containerStartsTx(method);
649     }
650
651     public BeanPoolDescriptor getPoolDescriptor() {
652         return beanPoolDesc_;
653     }
654
655     /**
656      * Instantiate and initialize a message-driven bean instance.
657      */

658     private MessageBeanContextImpl createMessageDrivenEJB()
659         throws CreateException {
660
661     Invocation inv = null;
662     MessageBeanContextImpl context = null;
663         ClassLoader JavaDoc originalClassLoader = null;
664         boolean methodCalled = false;
665         boolean methodCallFailed = false;
666
667     try {
668             // Set application class loader before invoking instance.
669
originalClassLoader = setContextClassLoader(getClassLoader());
670
671         // create new message-driven ejb
672
Object JavaDoc ejb = ejbClass.newInstance();
673
674             // create MessageDrivenContext and set it in the EJB
675
context = new MessageBeanContextImpl(ejb, this);
676             context.setInterceptorInstances(
677                     interceptorManager.createInterceptorInstances());
678
679         // java:comp/env lookups are allowed from here on...
680
inv = new Invocation(ejb, this);
681             inv.context = context;
682
683             inv.isMessageDriven = true;
684         invocationManager.preInvoke(inv);
685
686             if( ejb instanceof MessageDrivenBean ) {
687                 // setMessageDrivenContext will be called without a Tx
688
// as required by the spec
689
((MessageDrivenBean)ejb).setMessageDrivenContext(context);
690             }
691
692             // Perform injection right after where setMessageDrivenContext
693
// would be called. This is important since injection methods
694
// have the same "operations allowed" permissions as
695
// setMessageDrivenContext.
696
injectionManager.injectInstance(ejb, ejbDescriptor, false);
697             for (Object JavaDoc interceptorInstance : context.getInterceptorInstances()) {
698                 injectionManager.injectInstance(interceptorInstance,
699                         ejbDescriptor, false);
700             }
701             
702             // Set flag in context so UserTransaction can
703
// be used from ejbCreate. Didn't want to add
704
// a new state to lifecycle since that would
705
// require either changing lots of code in
706
// EJBContextImpl or re-implementing all the
707
// context methods within MessageBeanContextImpl.
708
context.setContextCalled();
709
710             // Call ejbCreate OR @PostConstruct on the bean.
711
interceptorManager.intercept(
712                     CallbackType.POST_CONSTRUCT, context);
713
714         statCreateCount++;
715             
716         // Set the state to POOLED after ejbCreate so that
717
// EJBContext methods not allowed will throw exceptions
718
context.setState(POOLED);
719     }
720     catch ( Throwable JavaDoc t ) {
721             _logger.log(Level.SEVERE,
722                         "containers.mdb.ejb_creation_exception",
723                         new Object JavaDoc[]{appEJBName_, t.toString()});
724             if (t instanceof InvocationTargetException JavaDoc) {
725                 _logger.log(Level.SEVERE, t.getClass().getName(),
726                             t.getCause());
727             }
728             _logger.log(Level.SEVERE, t.getClass().getName(), t);
729
730             CreateException ce =
731                 new CreateException("Could not create Message-Driven EJB");
732             ce.initCause(t);
733         throw ce;
734
735     } finally {
736             if( originalClassLoader != null ) {
737                 setContextClassLoader(originalClassLoader);
738             }
739         if ( inv != null ) {
740         invocationManager.postInvoke(inv);
741             }
742     }
743
744         return context;
745     }
746
747     /**
748      * Make the work performed by a message-bean instance's
749      * associated XA resource part of any global transaction
750      */

751     private void registerMessageBeanResource(ResourceHandle resourceHandle)
752         throws Exception JavaDoc {
753         if( resourceHandle != null ) {
754             Switch theSwitch = Switch.getSwitch();
755             PoolManager poolMgr = theSwitch.getPoolManager();
756             poolMgr.registerResource(resourceHandle);
757         }
758     }
759
760     private void unregisterMessageBeanResource(ResourceHandle resourceHandle) {
761
762         // resource handle may be null if preInvokeTx error caused
763
// ResourceAllocator.destroyResource()
764
if (resourceHandle != null) {
765             Switch theSwitch = Switch.getSwitch();
766             PoolManager poolMgr = theSwitch.getPoolManager();
767             poolMgr.unregisterResource(resourceHandle, XAResource.TMSUCCESS);
768         }
769     }
770
771     void afterBegin(EJBContextImpl context)
772     {
773     // Message-driven Beans cannot implement SessionSynchronization!!
774
}
775     
776     void beforeCompletion(EJBContextImpl context)
777     {
778     // Message-driven beans cannot implement SessionSynchronization!!
779
}
780
781     void afterCompletion(EJBContextImpl ctx, int status)
782     {
783     // Message-driven Beans cannot implement SessionSynchronization!!
784
}
785
786     // default
787
public boolean passivateEJB(ComponentContext context)
788     {
789         return false;
790     }
791     
792     // default
793
public void activateEJB(Object JavaDoc ctx, Object JavaDoc instanceKey)
794     {
795     }
796
797     /**
798      * Called when the application containing this message-bean
799      * has successfully deployed.
800      */

801     public void doAfterApplicationDeploy() {
802         super.doAfterApplicationDeploy();
803
804         // Start delivery of messages to message bean instances.
805
try {
806             messageBeanClient_.start();
807
808         }
809         catch(Exception JavaDoc e) {
810             _logger.log(Level.SEVERE,
811                         "containers.mdb.start_message_delivery_exception",
812                         new Object JavaDoc[]{appEJBName_, e.toString()});
813             _logger.log(Level.SEVERE, e.getClass().getName(), e);
814         }
815     }
816
817     private void cleanupResources() {
818
819         // Cleanup the message bean client resources.
820
try {
821             messageBeanClient_.close();
822         }
823         catch(Exception JavaDoc e) {
824             _logger.log(Level.SEVERE, "containers.mdb.cleanup_exception",
825                         new Object JavaDoc[]{appEJBName_, e.toString()});
826             _logger.log(Level.SEVERE, e.getClass().getName(), e);
827         }
828
829         messageBeanPool_.close();
830     }
831
832
833     public void undeploy()
834     {
835         // This will cause all new invocations to be rejected.
836
super.setUndeployedState();
837
838         _logger.log(Level.FINE, "containers.mdb.undeploy", appEJBName_);
839
840         cleanupResources();
841
842         super.undeploy();
843     }
844
845     /**
846      * Called when server instance is shuting down
847      */

848     public void onShutdown() {
849         _logger.log(Level.FINE,
850                     "containers.mdb.shutdown_cleanup_start", appEJBName_);
851         setStoppedState();
852         monitorOn = false;
853         cleanupResources();
854         _logger.log(Level.FINE,
855                     "containers.mdb.shutdown_cleanup_end", appEJBName_);
856     }
857
858     /**
859      * Actual message delivery happens in three steps :
860      *
861      * 1) beforeMessageDelivery(Message, MessageListener)
862      * This is our chance to make the message delivery
863      * itself part of the instance's global transaction.
864      *
865      * 2) onMessage(Message, MessageListener)
866      * This is where the container delegates to the
867      * actual ejb instance's onMessage method.
868      *
869      * 3) afterMessageDelivery(Message, MessageListener)
870      * Perform transaction cleanup and error handling.
871      *
872      * We use the Invocation manager's thread-specific state
873      * to track the invocation across these three calls.
874      *
875      */

876
877     public void beforeMessageDelivery(Method JavaDoc method, boolean txImported,
878                                       ResourceHandle resourceHandle) {
879                                       
880       
881         if (containerState != CONTAINER_STARTED) { // i.e. no invocation
882
String JavaDoc errorMsg = localStrings.getLocalString
883                ("containers.mdb.invocation_closed", appEJBName_ +
884                 ": Message-driven bean invocation closed by container",
885                 new Object JavaDoc[] { appEJBName_ });
886
887         throw new EJBException(errorMsg);
888         }
889
890         Invocation invocation = new Invocation();
891         
892     try {
893
894             MessageBeanContextImpl context =
895                 (MessageBeanContextImpl) getContext(invocation);
896                 
897             // Set the context class loader here so that message producer will
898
// have access to application class loader during message processing.
899
// The previous context class loader will be restored in
900
// afterMessageDelivery.
901

902             invocation.originalContextClassLoader =
903                 setContextClassLoader(getClassLoader());
904             invocation.isMessageDriven = true;
905             invocation.method = method;
906             
907             context.setState(INVOKING);
908
909         invocation.context = context;
910         invocation.instance = context.getEJB();
911             invocation.ejb = context.getEJB();
912         invocation.container = this;
913
914             // Message Bean Container only starts a new transaction if
915
// there is no imported transaction and the message listener
916
// method has tx attribute TX_REQUIRED or the ejbTimeout has
917
// tx attribute TX_REQUIRES_NEW/TX_REQUIRED
918
boolean startTx = false;
919             if( !txImported ) {
920                 startTx = containerStartsTx(method);
921             }
922
923             // keep track of whether tx was started for later.
924
invocation.containerStartsTx = startTx;
925
926         this.invocationManager.preInvoke(invocation);
927             
928             if( startTx ) {
929                 // Register the session associated with the message-driven
930
// bean's destination so the message delivery will be
931
// part of the container-managed transaction.
932
registerMessageBeanResource(resourceHandle);
933             }
934
935             preInvokeTx(invocation);
936
937     } catch(Throwable JavaDoc c) {
938             if (containerState != CONTAINER_STARTED ) {
939                 _logger.log(Level.SEVERE,"containers.mdb.preinvoke_exception",
940                             new Object JavaDoc[]{appEJBName_, c.toString()});
941                 _logger.log(Level.SEVERE, c.getClass().getName(), c);
942             }
943             invocation.exception = c;
944         }
945     }
946
947     public Object JavaDoc deliverMessage(Object JavaDoc[] params) throws Throwable JavaDoc {
948
949         Invocation invocation = null;
950         boolean methodCalled = false; //for monitoring
951
Object JavaDoc result = null;
952
953         invocation = (Invocation) invocationManager.getCurrentInvocation();
954
955         if (invocation == null && _logger.isLoggable(Level.FINEST)) {
956             if (containerState != CONTAINER_STARTED) {
957                 _logger.log(Level.FINEST, "No invocation in onMessage " +
958                             " (container closing)");
959             }
960             else {
961                 _logger.log(Level.FINEST, "No invocation in onMessage : ");
962             }
963         }
964
965         if( ( invocation != null ) && ( invocation.exception == null ) ) {
966
967             try {
968
969                 // NOTE : Application classloader already set in
970
// beforeMessageDelivery
971

972                 methodCalled = true;
973         if (ejbMethodStatsManager.isMethodMonitorOn()){
974             ejbMethodStatsManager.preInvoke(invocation.method);
975         }
976                 
977                 invocation.methodParams = params;
978
979                 if( isTimedObject() &&
980                     invocation.method.equals(ejbTimeoutMethod) ) {
981                     invocation.beanMethod = invocation.method;
982                     invokeTargetBeanMethod(ejbTimeoutMethod, invocation,
983                                            invocation.ejb,
984                                            invocation.methodParams, null);
985                 } else {
986                     // invocation.beanMethod is the actual target method from
987
// the bean class. The bean class is not required to be
988
// a formal subtype of the message listener interface, so
989
// we need to be careful to invoke through the bean class
990
// method itself. This info is also returned from the
991
// interceptor context info.
992

993                     invocation.beanMethod = invocation.ejb.getClass().getMethod
994                         (invocation.method.getName(),
995                          invocation.method.getParameterTypes());
996
997                     result = super.intercept(invocation);
998                 }
999
1000            } catch(InvocationTargetException JavaDoc ite) {
1001
1002                //
1003
// In EJB 2.1, message listener method signatures do not have
1004
// any restrictions on what kind of exceptions can be thrown.
1005
// This was not the case in J2EE 1.3, since JMS message driven
1006
// beans could only implement
1007
// void javax.jms.MessageListener.onMessage() , which does
1008
// not declare any exceptions.
1009
//
1010
// In the J2EE 1.3 implementation, exceptions were only
1011
// propagated when the message driven bean was not configured
1012
// with CMT/Required transaction mode. This has been changed
1013
// due to the Connector 1.5 integration. Now, all exceptions
1014
// are propagated regardless of the tx mode. (18.2.2)
1015
// Application exceptions are propagated as is, while system
1016
// exceptions are wrapped in an EJBException.
1017
//
1018
// If an exception is thrown and there is a container-started
1019
// transaction, the semantics are the same as for other ejb
1020
// types whose business methods throw an exception.
1021
// Specifically, if the exception thrown is an Application
1022
// exception(defined in 18.2.1), it does not automatically
1023
// result in a rollback of the container-started transaction.
1024
//
1025

1026                Throwable JavaDoc cause = ite.getCause();
1027                // set cause on invocation , rather than the propagated
1028
// EJBException
1029
invocation.exception = cause;
1030
1031                if( isSystemUncheckedException(cause) ) {
1032                    EJBException ejbEx = new EJBException
1033                        ("message-driven bean method " + invocation.method +
1034                         " system exception");
1035                    ejbEx.initCause(cause);
1036                    cause = ejbEx;
1037                }
1038                throw cause;
1039            } catch(Throwable JavaDoc t) {
1040                EJBException ejbEx =
1041                    new EJBException("message-bean container dispatch error");
1042                ejbEx.initCause(t);
1043                invocation.exception = ejbEx;
1044                throw ejbEx;
1045            } finally {
1046                if (methodCalled && (ejbMethodStatsManager.isMethodMonitorOn())) {
1047            ejbMethodStatsManager.postInvoke(
1048            invocation.method, invocation.exception);
1049                }
1050
1051                if ( AppVerification.doInstrument() ) {
1052                    AppVerification.getInstrumentLogger().doInstrumentForEjb
1053                        (getEjbDescriptor(), invocation.method,
1054                         invocation.exception);
1055                }
1056            }
1057
1058        } // End if -- invoke instance's onMessage method
1059
else {
1060            if (invocation == null) {
1061                String JavaDoc errorMsg = localStrings.getLocalString
1062                    ("containers.mdb.invocation_closed",
1063                     appEJBName_ + ": Message-driven bean invocation " +
1064                     "closed by container", new Object JavaDoc[] { appEJBName_});
1065                throw new EJBException(errorMsg);
1066            }
1067            else {
1068                _logger.log(Level.SEVERE,
1069                            "containers.mdb.invocation_exception",
1070                   new Object JavaDoc[]{appEJBName_, invocation.exception.toString()});
1071                _logger.log(Level.SEVERE, invocation.exception.getClass().
1072                            getName(), invocation.exception);
1073                EJBException ejbEx = new EJBException();
1074                ejbEx.initCause(invocation.exception);
1075                throw ejbEx;
1076            }
1077        }
1078
1079        return result;
1080    }
1081          
1082    public void afterMessageDelivery(ResourceHandle resourceHandle) {
1083        afterMessageDeliveryInternal(resourceHandle);
1084    }
1085
1086    private boolean afterMessageDeliveryInternal(ResourceHandle resourceHandle)
1087    {
1088        // return value. assume failure until proven otherwise.
1089
boolean success = false;
1090
1091        Invocation invocation = null;
1092
1093        invocation = (Invocation) invocationManager.getCurrentInvocation();
1094        if (invocation == null) {
1095            _logger.log(Level.SEVERE, "containers.mdb.no_invocation",
1096                        new Object JavaDoc[]{appEJBName_, ""});
1097        } else {
1098            MessageBeanContextImpl beanContext =
1099                (MessageBeanContextImpl) invocation.context;
1100
1101            try {
1102                if( invocation.containerStartsTx ) {
1103                    // Unregister the session associated with
1104
// the message-driven bean's destination.
1105
unregisterMessageBeanResource(resourceHandle);
1106                }
1107
1108                // counterpart of invocationManager.preInvoke
1109
invocationManager.postInvoke(invocation);
1110
1111                // Commit/Rollback container-managed transaction.
1112
postInvokeTx(invocation);
1113                
1114                // Consider successful delivery. Commit failure will be
1115
// checked below.
1116
success = true;
1117
1118        //TODO: Check if Tx existed / committed
1119
statMessageCount++;
1120                
1121            } catch(Throwable JavaDoc ce) {
1122                _logger.log(Level.SEVERE,
1123                            "containers.mdb.postinvoke_exception",
1124                            new Object JavaDoc[]{appEJBName_, ce.toString()});
1125                _logger.log(Level.SEVERE,ce.getClass().getName(), ce);
1126            } finally {
1127                releaseContext(invocation);
1128            }
1129
1130            // Reset original class loader
1131
setContextClassLoader(invocation.originalContextClassLoader);
1132
1133            if( invocation.exception != null ) {
1134
1135                if( isSystemUncheckedException(invocation.exception) ) {
1136                    success = false;
1137                }
1138
1139                // Log system exceptions by default and application exceptions
1140
// only when log level is FINE or higher.
1141
Level exLogLevel =
1142                    isSystemUncheckedException(invocation.exception) ?
1143                        Level.INFO : Level.FINE;
1144                
1145                //START OF IASRI 4660565
1146
_logger.log(exLogLevel,
1147                            "containers.mdb.invocation_exception",
1148                            new Object JavaDoc[]{appEJBName_,
1149                                             invocation.exception.toString()});
1150                _logger.log(exLogLevel, invocation.exception.getClass().
1151                            getName(), invocation.exception);
1152            }
1153        }
1154
1155        return success;
1156    }
1157
1158    /**
1159     * Utility routine for setting the context class loader.
1160     * Returns previous class loader.
1161     */

1162    private ClassLoader JavaDoc setContextClassLoader(ClassLoader JavaDoc newClassLoader) {
1163
1164        // Can only reference final local variables from dopriveleged block
1165
final ClassLoader JavaDoc classLoaderToSet = newClassLoader;
1166
1167        final Thread JavaDoc currentThread = Thread.currentThread();
1168        ClassLoader JavaDoc originalClassLoader =
1169            currentThread.getContextClassLoader();
1170
1171        java.security.AccessController.doPrivileged
1172            (new java.security.PrivilegedAction JavaDoc() {
1173        public java.lang.Object JavaDoc run() {
1174        currentThread.setContextClassLoader(classLoaderToSet);
1175        return null;
1176        }
1177    });
1178        return originalClassLoader;
1179    }
1180   
1181    public long getCreateCount() {
1182    return statCreateCount;
1183    }
1184
1185    public long getRemoveCount() {
1186    return statRemoveCount;
1187    }
1188
1189    public long getMessageCount() {
1190    return statMessageCount;
1191    }
1192
1193}
1194
Popular Tags