KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > ejb3 > mdb > MessagingContainer


1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */

22 package org.jboss.ejb3.mdb;
23
24 import org.jboss.annotation.ejb.ResourceAdapter;
25 import org.jboss.aop.AspectManager;
26 import org.jboss.aop.MethodInfo;
27 import org.jboss.aop.advice.Interceptor;
28 import org.jboss.deployment.DeploymentException;
29 import org.jboss.ejb3.*;
30 import org.jboss.ejb3.mdb.inflow.JBossMessageEndpointFactory;
31 import org.jboss.ejb3.interceptor.InterceptorInfoRepository;
32 import org.jboss.ejb3.jms.JMSDestinationFactory;
33 import org.jboss.ejb3.timerservice.TimedObjectInvoker;
34 import org.jboss.ejb3.timerservice.TimerServiceFactory;
35 import org.jboss.jms.jndi.JMSProviderAdapter;
36 import org.jboss.logging.Logger;
37 import org.jboss.metadata.ActivationConfigPropertyMetaData;
38
39 import javax.ejb.*;
40 import javax.ejb.Timer JavaDoc;
41 import javax.jms.*;
42 import javax.jms.Queue JavaDoc;
43 import javax.management.MalformedObjectNameException JavaDoc;
44 import javax.management.ObjectName JavaDoc;
45 import javax.naming.Context JavaDoc;
46 import javax.naming.InitialContext JavaDoc;
47 import javax.naming.NamingException JavaDoc;
48 import java.lang.reflect.Field JavaDoc;
49 import java.lang.reflect.Method JavaDoc;
50 import java.util.*;
51
52 /**
53  * @version <tt>$Revision: 58397 $</tt>
54  * @author <a HREF="mailto:bdecoste@jboss.com">William DeCoste</a>
55  */

56 public abstract class MessagingContainer extends EJBContainer implements TimedObjectInvoker
57 {
58    private static final Logger log = Logger.getLogger(MessagingContainer.class);
59    
60    protected TimerService timerService;
61    protected ActivationSpec activationSpec = new ActivationSpec();
62    protected JBossMessageEndpointFactory messageEndpointFactory;
63
64    /**
65     * Default destination type. Used when no message-driven-destination is given
66     * in ejb-jar, and a lookup of destinationJNDI from jboss.xml is not
67     * successfull. Default value: javax.jms.Topic.
68     */

69    protected final static String JavaDoc DEFAULT_DESTINATION_TYPE = "javax.jms.Topic";
70
71    public MessagingContainer(String JavaDoc ejbName, AspectManager manager, ClassLoader JavaDoc cl, String JavaDoc beanClassName, Hashtable ctxProperties,
72               InterceptorInfoRepository interceptorRepository, Ejb3Deployment deployment)
73    {
74       super(Ejb3Module.BASE_EJB3_JMX_NAME + ",name=" + ejbName, manager, cl, beanClassName, ejbName, ctxProperties, interceptorRepository, deployment);
75       
76       beanContextClass = MDBContext.class;
77       
78       messageEndpointFactory = new JBossMessageEndpointFactory();
79       messageEndpointFactory.setContainer(this);
80    }
81    
82    public abstract Class JavaDoc getMessagingType();
83    
84    public abstract Map getActivationConfigProperties();
85    
86    protected abstract void populateActivationSpec();
87    
88    public abstract MethodInfo getMethodInfo(Method JavaDoc method);
89
90    public void setMessageEndpointFactory(JBossMessageEndpointFactory messageEndpointFactory)
91    {
92       this.messageEndpointFactory = messageEndpointFactory;
93    }
94    
95    public String JavaDoc getResourceAdaptorName()
96    {
97       ResourceAdapter annotation = (ResourceAdapter) resolveAnnotation(ResourceAdapter.class);
98       if (annotation == null)
99          return JMS_ADAPTOR;
100       
101       return annotation.value();
102    }
103    
104    protected void addActivationSpecProperty(Map result, ActivationConfigProperty property)
105    {
106       if (!property.propertyName().equals("messagingType"))
107       {
108          ActivationConfigPropertyMetaData metaData = new ActivationConfigPropertyMetaData();
109          try
110          {
111             Field JavaDoc nameField = ActivationConfigPropertyMetaData.class.getDeclaredField("name");
112             nameField.setAccessible(true);
113             nameField.set(metaData, property.propertyName());
114             Field JavaDoc valueField = ActivationConfigPropertyMetaData.class.getDeclaredField("value");
115             valueField.setAccessible(true);
116             valueField.set(metaData, property.propertyValue());
117          }
118          catch (Exception JavaDoc e)
119          {
120             throw new RuntimeException JavaDoc(e);
121          }
122    
123          /*
124           * Older versions don't have this
125           * TODO revert to this after we ditch 4.0.3SP1 as supported EJB3 platform
126          metaData.setName(property.propertyName());
127          metaData.setValue(property.propertyValue());
128          */

129          result.put(property.propertyName(), metaData);
130       }
131    }
132
133    /**
134     * Initialize the container invoker. Sets up a connection, a server session
135     * pool and a connection consumer for the configured destination.
136     * <p/>
137     * Any JMSExceptions produced while initializing will be assumed to be
138     * caused due to JMS Provider failure.
139     *
140     * @throws Exception Failed to initalize.
141     */

142    public void start() throws Exception JavaDoc
143    {
144       super.start();
145
146       populateActivationSpec();
147          
148       innerStart();
149
150       timerService = TimerServiceFactory.getInstance().createTimerService(this.getObjectName(), this);
151
152       startProxies();
153       
154       TimerServiceFactory.getInstance().restoreTimerService(timerService);
155    }
156
157    protected void innerStart() throws Exception JavaDoc
158    {
159       log.debug("Initializing");
160
161       if (getResourceAdaptorName().equals(JMS_ADAPTOR))
162          jmsCreate();
163    }
164
165    public ObjectName JavaDoc getJmxName()
166    {
167       ObjectName JavaDoc jmxName = null;
168       String JavaDoc jndiName = ProxyFactoryHelper.getLocalJndiName(this);
169       // The name must be escaped since the jndiName may be arbitrary
170
String JavaDoc name = org.jboss.ejb.Container.BASE_EJB_CONTAINER_NAME + ",jndiName=" + jndiName;
171       try
172       {
173          jmxName = org.jboss.mx.util.ObjectNameConverter.convert(name);
174       }
175       catch (MalformedObjectNameException JavaDoc e)
176       {
177          e.printStackTrace();
178          throw new RuntimeException JavaDoc("Failed to create ObjectName, msg=" + e.getMessage());
179       }
180
181       return jmxName;
182    }
183
184    protected void startProxies() throws Exception JavaDoc
185    {
186       messageEndpointFactory.start();
187    }
188
189    /**
190     * Parse the JNDI suffix from the given JNDI name.
191     *
192     * @param jndiname The JNDI name used to lookup the destination.
193     * @param defautSuffix Description of Parameter
194     * @return The parsed suffix or the defaultSuffix
195     */

196    protected String JavaDoc parseJndiSuffix(final String JavaDoc jndiname,
197                                     final String JavaDoc defautSuffix)
198    {
199       // jndiSuffix is merely the name that the user has given the MDB.
200
// since the jndi name contains the message type I have to split
201
// at the "/" if there is no slash then I use the entire jndi name...
202
String JavaDoc jndiSuffix = "";
203
204       if (jndiname != null)
205       {
206          int indexOfSlash = jndiname.indexOf("/");
207          if (indexOfSlash != -1)
208          {
209             jndiSuffix = jndiname.substring(indexOfSlash + 1);
210          }
211          else
212          {
213             jndiSuffix = jndiname;
214          }
215       }
216       else
217       {
218          // if the jndi name from jboss.xml is null then lets use the ejbName
219
jndiSuffix = defautSuffix;
220       }
221
222       return jndiSuffix;
223    }
224
225    public Object JavaDoc localInvoke(Method JavaDoc method, Object JavaDoc[] args) throws Throwable JavaDoc
226    {
227       MethodInfo info = getMethodInfo(method);
228       if (info == null)
229       {
230          throw new RuntimeException JavaDoc("Could not resolve beanClass method from proxy call: " + method.toString());
231       }
232       return localInvoke(info, args);
233
234    }
235
236    public Object JavaDoc localInvoke(MethodInfo info, Object JavaDoc[] args) throws Throwable JavaDoc
237    {
238       ClassLoader JavaDoc oldLoader = Thread.currentThread().getContextClassLoader();
239       ThreadLocalENCFactory.push(enc);
240       try
241       {
242          Interceptor[] aspects = info.getInterceptors();
243          EJBContainerInvocation nextInvocation = new EJBContainerInvocation(info, aspects);
244          nextInvocation.setAdvisor(this);
245          nextInvocation.setArguments(args);
246          return nextInvocation.invokeNext();
247       }
248       finally
249       {
250          Thread.currentThread().setContextClassLoader(oldLoader);
251          ThreadLocalENCFactory.pop();
252       }
253    }
254
255    public TimerService getTimerService()
256    {
257       return timerService;
258    }
259
260    public TimerService getTimerService(Object JavaDoc pKey)
261    {
262       assert timerService != null : "Timer Service not yet initialized";
263       return timerService;
264    }
265    
266    public void callTimeout(Timer JavaDoc timer) throws Exception JavaDoc
267    {
268       Method JavaDoc timeout = callbackHandler.getTimeoutCallback();
269       if (timeout == null) throw new EJBException("No method has been annotated with @Timeout");
270       Object JavaDoc[] args = {timer};
271       try
272       {
273          localInvoke(timeout, args);
274       }
275       catch (Throwable JavaDoc throwable)
276       {
277          if (throwable instanceof Exception JavaDoc) throw (Exception JavaDoc) throwable;
278          throw new RuntimeException JavaDoc(throwable);
279       }
280    }
281
282
283    public void stop() throws Exception JavaDoc
284    {
285       if (timerService != null)
286       {
287          TimerServiceFactory.getInstance().removeTimerService(timerService);
288       }
289
290       stopProxies();
291    }
292
293    protected void stopProxies() throws Exception JavaDoc
294    {
295       messageEndpointFactory.stop();
296    }
297
298    public void destroy() throws Exception JavaDoc
299    {
300    }
301    
302    // ********* JMS Specific
303
protected static final String JavaDoc JMS_ADAPTOR = "jms-ra.rar";
304    protected static final String JavaDoc DESTINATION = "destination";
305    protected static final String JavaDoc DESTINATION_TYPE = "destinationType";
306    protected static final String JavaDoc PROVIDER_ADAPTER_JNDI = "providerAdapterJNDI";
307    protected static final String JavaDoc MAX_SESSION = "maxSession";
308    
309    public void initializePool() throws Exception JavaDoc
310    {
311      super.initializePool();
312      
313      String JavaDoc maxSession = getMaxSession();
314      if (maxSession != null)
315      {
316         pool.setMaxSize(Integer.parseInt(maxSession));
317      }
318    }
319    
320    protected String JavaDoc getProviderAdapterJNDI()
321    {
322       ActivationConfigPropertyMetaData property = (ActivationConfigPropertyMetaData)getActivationConfigProperties().get(PROVIDER_ADAPTER_JNDI);
323       if (property != null)
324          return property.getValue();
325       return "java:/DefaultJMSProvider";
326    }
327    
328    protected String JavaDoc getMaxSession()
329    {
330       ActivationConfigPropertyMetaData property = (ActivationConfigPropertyMetaData)getActivationConfigProperties().get(MAX_SESSION);
331       if (property != null)
332          return property.getValue();
333       return null;
334    }
335    
336    protected String JavaDoc getDestination()
337    {
338       ActivationConfigPropertyMetaData property = (ActivationConfigPropertyMetaData)getActivationConfigProperties().get(DESTINATION);
339       if (property != null)
340          return property.getValue();
341       return null;
342    }
343    
344    protected String JavaDoc getDestinationType()
345    {
346       ActivationConfigPropertyMetaData property = (ActivationConfigPropertyMetaData)getActivationConfigProperties().get(DESTINATION_TYPE);
347       if (property != null)
348          return property.getValue();
349       return null;
350    }
351
352    protected void jmsCreate() throws Exception JavaDoc
353    {
354       // Get the JMS provider
355
// todo get rid of server module dependency
356
JMSProviderAdapter adapter = getJMSProviderAdapter();
357       log.debug("Provider adapter: " + adapter);
358   
359       // Connect to the JNDI server and get a reference to root context
360
Context JavaDoc context = adapter.getInitialContext();
361       log.debug("context: " + context);
362
363       // if we can't get the root context then exit with an exception
364
if (context == null)
365       {
366          throw new RuntimeException JavaDoc("Failed to get the root context");
367       }
368
369       // Unfortunately the destination is optional, so if we do not have one
370
// here we have to look it up if we have a destinationJNDI, else give it
371
// a default.
372
String JavaDoc destinationType = getDestinationType();
373       if (destinationType == null)
374       {
375          log.warn("No message-driven-destination given; using; guessing type");
376          destinationType = getDestinationType(context, getDestination());
377       }
378
379       if ("javax.jms.Topic".equals(destinationType))
380       {
381          innerCreateTopic(context);
382
383       }
384       else if ("javax.jms.Queue".equals(destinationType))
385       {
386          innerCreateQueue(context);
387
388       }
389       else
390          throw new DeploymentException("Unknown destination-type " + destinationType);
391
392       log.debug("Initialized with config " + toString());
393
394       context.close();
395    }
396    
397    protected void innerCreateQueue(Context JavaDoc context)
398            throws Exception JavaDoc
399    {
400       log.debug("Got destination type Queue for " + ejbName);
401
402       // Get the JNDI suffix of the destination
403
String JavaDoc jndiSuffix = parseJndiSuffix(getDestination(), ejbName);
404       log.debug("jndiSuffix: " + jndiSuffix);
405
406       // lookup or create the destination queue
407
Queue JavaDoc queue = null;
408       try
409       {
410          // First we try the specified queue
411
if (getDestination() != null)
412             queue = (Queue JavaDoc) context.lookup(getDestination());
413       }
414       catch (NamingException JavaDoc e)
415       {
416          log.warn("Could not find the queue destination-jndi-name=" + getDestination());
417       }
418       catch (ClassCastException JavaDoc e)
419       {
420          throw new DeploymentException("Expected a Queue destination-jndi-name=" + getDestination());
421       }
422
423       if (queue == null)
424          queue = (Queue JavaDoc) createDestination(Queue JavaDoc.class,
425                  context,
426                  "queue/" + jndiSuffix,
427                  jndiSuffix);
428    }
429
430    protected void innerCreateTopic(Context JavaDoc context)
431            throws Exception JavaDoc
432    {
433       log.debug("Got destination type Topic for " + ejbName);
434
435       // Get the JNDI suffix of the destination
436
String JavaDoc jndiSuffix = parseJndiSuffix(getDestination(), ejbName);
437       log.debug("jndiSuffix: " + jndiSuffix);
438
439       // lookup or create the destination topic
440
Topic topic = null;
441       try
442       {
443          // First we try the specified topic
444
if (getDestination() != null)
445             topic = (Topic) context.lookup(getDestination());
446       }
447       catch (NamingException JavaDoc e)
448       {
449          log.warn("Could not find the topic destination-jndi-name=" + getDestination());
450       }
451       catch (ClassCastException JavaDoc e)
452       {
453          throw new DeploymentException("Expected a Topic destination-jndi-name=" + getDestination());
454       }
455
456       if (topic == null)
457          topic = (Topic) createDestination(Topic.class,
458                  context,
459                  "topic/" + jndiSuffix,
460                  jndiSuffix);
461    }
462
463    /**
464     * Create and or lookup a JMS destination.
465     *
466     * @param type Either javax.jms.Queue or javax.jms.Topic.
467     * @param ctx The naming context to lookup destinations from.
468     * @param jndiName The name to use when looking up destinations.
469     * @param jndiSuffix The name to use when creating destinations.
470     * @return The destination.
471     * @throws IllegalArgumentException Type is not Queue or Topic.
472     * @throws Exception Description of Exception
473     */

474    private Destination createDestination(final Class JavaDoc<? extends Destination> type,
475                                            final Context JavaDoc ctx,
476                                            final String JavaDoc jndiName,
477                                            final String JavaDoc jndiSuffix)
478            throws Exception JavaDoc
479    {
480       try
481       {
482          // first try to look it up
483
return (Destination) ctx.lookup(jndiName);
484       }
485       catch (NamingException JavaDoc e)
486       {
487          // is JMS?
488
if (getDestination() == null)
489          {
490             return null;
491          }
492          else
493          {
494             // if the lookup failes, the try to create it
495
log.warn("destination not found: " + jndiName + " reason: " + e);
496             log.warn("creating a new temporary destination: " + jndiName);
497             
498             createTemporaryDestination(type, jndiSuffix);
499        
500             // try to look it up again
501
return (Destination) ctx.lookup(jndiName);
502          }
503       }
504    }
505    
506    private void createTemporaryDestination(Class JavaDoc<? extends Destination> type, String JavaDoc jndiSuffix) throws Exception JavaDoc
507    {
508       //
509
// jason: we should do away with this...
510
//
511
// attempt to create the destination (note, this is very
512
// very, very unportable).
513
//
514

515       // MBeanServer server = org.jboss.mx.util.MBeanServerLocator.locateJBoss();
516

517 // String methodName;
518
// String destinationContext;
519
// if (type == Topic.class)
520
// {
521
// destinationContext = "topic";
522
// methodName = "createTopic";
523
// }
524
// else if (type == Queue.class)
525
// {
526
// destinationContext = "queue";
527
// methodName = "createQueue";
528
// }
529
// else
530
// {
531
// // type was not a Topic or Queue, bad user
532
// throw new IllegalArgumentException
533
// ("Expected javax.jms.Queue or javax.jms.Topic: " + type);
534
// }
535

536       /* No longer supported (AS 4.x)
537       ObjectName destinationManagerName = new ObjectName("jboss.mq:service=DestinationManager");
538       
539       KernelAbstraction kernel = KernelAbstractionFactory.getInstance();
540       // invoke the server to create the destination
541       Object result = kernel.invoke(destinationManagerName,
542               methodName,
543               new Object[]{jndiSuffix},
544               new String[]{"java.lang.String"});
545       
546       InitialContext jndiContext = InitialContextFactory.getInitialContext();
547       String binding = destinationContext + "/" + jndiSuffix;
548       try
549       {
550          jndiContext.lookup(binding);
551       }
552       catch (NamingException e)
553       {
554          jndiContext.rebind(binding, result);
555       }
556       */

557       
558       //throw new UnsupportedOperationException("Can't create destination " + destinationContext + "/" + jndiSuffix);
559
JMSDestinationFactory.getInstance().createDestination(type, jndiSuffix);
560    }
561    
562    /**
563     * Return the JMSProviderAdapter that should be used.
564     *
565     * @return The JMSProviderAdapter to use.
566     */

567    protected JMSProviderAdapter getJMSProviderAdapter()
568            throws NamingException JavaDoc
569    {
570       Context JavaDoc context = getInitialContext();
571       //todo make this pluggable
572
String JavaDoc providerAdapterJNDI = getProviderAdapterJNDI();
573       try
574       {
575          log.debug("Looking up provider adapter: " + providerAdapterJNDI);
576      
577          return (JMSProviderAdapter) context.lookup(providerAdapterJNDI);
578       }
579       finally
580       {
581          context.close();
582       }
583    }
584
585    /**
586     * Try to get a destination type by looking up the destination JNDI, or
587     * provide a default if there is not destinationJNDI or if it is not possible
588     * to lookup.
589     *
590     * @param ctx The naming context to lookup destinations from.
591     * @param destinationJNDI The name to use when looking up destinations.
592     * @return The destination type, either derived from destinationJDNI or
593     * DEFAULT_DESTINATION_TYPE
594     */

595    protected String JavaDoc getDestinationType(Context JavaDoc ctx, String JavaDoc destinationJNDI)
596    {
597       String JavaDoc destType = null;
598
599       if (destinationJNDI != null)
600       {
601          try
602          {
603             Destination dest = (Destination) ctx.lookup(destinationJNDI);
604             if (dest instanceof javax.jms.Topic JavaDoc)
605             {
606                destType = "javax.jms.Topic";
607             }
608             else if (dest instanceof javax.jms.Queue JavaDoc)
609             {
610                destType = "javax.jms.Queue";
611             }
612          }
613          catch (NamingException JavaDoc ex)
614          {
615             log.debug("Could not do heristic lookup of destination ", ex);
616          }
617
618       }
619       if (destType == null)
620       {
621          log.warn("Could not determine destination type, defaults to: " +
622                  DEFAULT_DESTINATION_TYPE);
623
624          destType = DEFAULT_DESTINATION_TYPE;
625       }
626
627       return destType;
628    }
629 }
Popular Tags