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.ejb;
23
24 import java.lang.reflect.Method;
25 import java.util.HashMap;
26 import java.util.Hashtable;
27 import java.util.Iterator;
28 import java.util.Map;
29
30 import javax.ejb.CreateException;
31 import javax.ejb.EJBMetaData;
32 import javax.ejb.EJBObject;
33 import javax.ejb.Handle;
34 import javax.ejb.HomeHandle;
35 import javax.ejb.RemoveException;
36 import javax.ejb.TimedObject;
37 import javax.ejb.Timer;
38 import javax.ejb.EJBException;
39 import javax.management.ObjectName;
40
41 import org.jboss.invocation.Invocation;
42 import org.jboss.metadata.MessageDrivenMetaData;
43 import org.jboss.util.NullArgumentException;
44
45 /**
46 * The container for <em>MessageDriven</em> beans.
47 *
48 * @author <a href="mailto:peter.antman@tim.se">Peter Antman</a>.
49 * @author <a href="mailto:rickard.oberg@telkel.com">Rickard Oberg</a>
50 * @author <a href="mailto:marc.fleury@telkel.com">Marc Fleury</a>
51 * @author <a href="mailto:docodan@mvcsoft.com">Daniel OConnor</a>
52 * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
53 * @author <a href="mailto:Scott.Stark@jboss.org">Scott Stark</a>
54 * @version $Revision: 66439 $
55 *
56 * @jmx:mbean extends="org.jboss.ejb.ContainerMBean"
57 */
58 public class MessageDrivenContainer
59 extends Container
60 implements EJBProxyFactoryContainer, InstancePoolContainer, MessageDrivenContainerMBean
61 {
62 /**
63 * These are the mappings between the remote interface methods
64 * and the bean methods.
65 */
66 protected Map beanMapping;
67
68 /** This is the instancepool that is to be used. */
69 protected InstancePool instancePool;
70
71 /**
72 * This is the first interceptor in the chain.
73 * The last interceptor must be provided by the container itself.
74 */
75 protected Interceptor interceptor;
76
77 protected long messageCount;
78
79 public LocalProxyFactory getLocalProxyFactory()
80 {
81 return localProxyFactory;
82 }
83
84 public void setInstancePool(final InstancePool instancePool)
85 {
86 if (instancePool == null)
87 throw new NullArgumentException("instancePool");
88
89 this.instancePool = instancePool;
90 this.instancePool.setContainer(this);
91 }
92
93 public InstancePool getInstancePool()
94 {
95 return instancePool;
96 }
97
98 public void addInterceptor(Interceptor in)
99 {
100 if (interceptor == null)
101 {
102 interceptor = in;
103 }
104 else
105 {
106 Interceptor current = interceptor;
107
108 while (current.getNext() != null)
109 {
110 current = current.getNext();
111 }
112
113 current.setNext(in);
114 }
115 }
116
117 public Interceptor getInterceptor()
118 {
119 return interceptor;
120 }
121
122 /**
123 * @jmx:managed-attribute
124 * @return the number of messages delivered
125 */
126 public long getMessageCount()
127 {
128 return messageCount;
129 }
130
131 /**
132 * EJBProxyFactoryContainer - not needed, should we skip inherit this
133 * or just throw Error??
134 */
135 public Class getHomeClass()
136 {
137 //throw new Error("HomeClass not valid for MessageDriven beans");
138 return null;
139 }
140
141 public Class getRemoteClass()
142 {
143 //throw new Error("RemoteClass not valid for MessageDriven beans");
144 return null;
145 }
146
147 public Class getLocalClass()
148 {
149 return null;
150 }
151
152 public Class getLocalHomeClass()
153 {
154 //throw new Error("LocalHomeClass not valid for MessageDriven beans");
155 return null;
156 }
157
158 // Container implementation - overridden here ----------------------
159
160 protected void createService() throws Exception
161 {
162 // Associate thread with classloader
163 ClassLoader oldCl = SecurityActions.getContextClassLoader();
164 SecurityActions.setContextClassLoader(getClassLoader());
165 pushENC();
166 try
167 {
168 // Call default init
169 super.createService();
170
171 // Map the bean methods
172 Map map = new HashMap();
173 MessageDrivenMetaData mdMetaData = (MessageDrivenMetaData)metaData;
174 String type = mdMetaData.getMessagingType();
175 if(type == null || type.length() == 0)
176 type = MessageDrivenMetaData.DEFAULT_MESSAGING_TYPE;
177 Class clazz = getClassLoader().loadClass(type);
178 Method[] methods = clazz.getDeclaredMethods();
179 for (int i = 0; i < methods.length; i++)
180 {
181 Method m = methods[i];
182 map.put(m, beanClass.getMethod(m.getName(), m.getParameterTypes()));
183 log.debug("Mapped " + m.getName() + " " + m.hashCode() + " to " + map.get(m));
184 }
185 if( TimedObject.class.isAssignableFrom( beanClass ) ) {
186 // Map ejbTimeout
187 map.put(
188 TimedObject.class.getMethod( "ejbTimeout", new Class[] { Timer.class } ),
189 beanClass.getMethod( "ejbTimeout", new Class[] { Timer.class } )
190 );
191 }
192 beanMapping = map;
193
194 // Try to register the instance pool as an MBean
195 try
196 {
197 ObjectName containerName = super.getJmxName();
198 Hashtable props = containerName.getKeyPropertyList();
199 props.put("plugin", "pool");
200 ObjectName poolName = new ObjectName(containerName.getDomain(), props);
201 server.registerMBean(instancePool, poolName);
202 }
203 catch(Throwable t)
204 {
205 log.debug("Failed to register pool as mbean", t);
206 }
207 // Initialize pool
208 instancePool.create();
209
210 for (Iterator it = proxyFactories.keySet().iterator(); it.hasNext();)
211 {
212 String invokerBinding = (String) it.next();
213 EJBProxyFactory ci = (EJBProxyFactory) proxyFactories.get(invokerBinding);
214 // Try to register the container invoker as an MBean
215 try
216 {
217 ObjectName containerName = super.getJmxName();
218 Hashtable props = containerName.getKeyPropertyList();
219 props.put("plugin", "invoker");
220 props.put("binding", invokerBinding);
221 ObjectName invokerName = new ObjectName(containerName.getDomain(), props);
222 server.registerMBean(ci, invokerName);
223 }
224 catch(Throwable t)
225 {
226 log.debug("Failed to register invoker binding as mbean", t);
227 }
228 ci.create();
229 }
230
231 // Initialize the interceptor by calling the chain
232 Interceptor in = interceptor;
233 while (in != null)
234 {
235 in.setContainer(this);
236 in.create();
237 in = in.getNext();
238 }
239
240 }
241 finally
242 {
243 popENC();
244 // Reset classloader
245 SecurityActions.setContextClassLoader(oldCl);
246 }
247 }
248
249 protected void startService() throws Exception
250 {
251 // Associate thread with classloader
252 ClassLoader oldCl = SecurityActions.getContextClassLoader();
253 SecurityActions.setContextClassLoader(getClassLoader());
254 pushENC();
255 try
256 {
257 // Call default start
258 super.startService();
259
260 // Start the instance pool
261 instancePool.start();
262
263 // Start all interceptors in the chain
264 Interceptor in = interceptor;
265 while (in != null)
266 {
267 in.start();
268 in = in.getNext();
269 }
270
271 // Start container invoker
272 for (Iterator it = proxyFactories.keySet().iterator(); it.hasNext();)
273 {
274 String invokerBinding = (String) it.next();
275 EJBProxyFactory ci = (EJBProxyFactory) proxyFactories.get(invokerBinding);
276 ci.start();
277 }
278
279 // Restore persisted ejb timers
280 restoreTimers();
281 }
282 finally
283 {
284 popENC();
285 // Reset classloader
286 SecurityActions.setContextClassLoader(oldCl);
287 }
288 }
289
290 protected void stopService() throws Exception
291 {
292 // Associate thread with classloader
293 ClassLoader oldCl = SecurityActions.getContextClassLoader();
294 SecurityActions.setContextClassLoader(getClassLoader());
295 pushENC();
296 try
297 {
298 // Call default stop
299 super.stopService();
300
301 // Stop container invoker
302 for (Iterator it = proxyFactories.keySet().iterator(); it.hasNext();)
303 {
304 String invokerBinding = (String) it.next();
305 EJBProxyFactory ci = (EJBProxyFactory) proxyFactories.get(invokerBinding);
306 ci.stop();
307 }
308
309 // Stop the instance pool
310 instancePool.stop();
311
312 // Stop all interceptors in the chain
313 Interceptor in = interceptor;
314 while (in != null)
315 {
316 in.stop();
317 in = in.getNext();
318 }
319 }
320 finally
321 {
322 popENC();
323 // Reset classloader
324 SecurityActions.setContextClassLoader(oldCl);
325 }
326 }
327
328 protected void destroyService() throws Exception
329 {
330 // Associate thread with classloader
331 ClassLoader oldCl = SecurityActions.getContextClassLoader();
332 SecurityActions.setContextClassLoader(getClassLoader());
333 pushENC();
334 try
335 {
336 // Destroy container invoker
337 for (Iterator it = proxyFactories.keySet().iterator(); it.hasNext();)
338 {
339 String invokerBinding = (String) it.next();
340 EJBProxyFactory ci = (EJBProxyFactory) proxyFactories.get(invokerBinding);
341 ci.destroy();
342 ci.setContainer(null);
343 try
344 {
345 ObjectName containerName = super.getJmxName();
346 Hashtable props = containerName.getKeyPropertyList();
347 props.put("plugin", "invoker");
348 props.put("binding", invokerBinding);
349 ObjectName invokerName = new ObjectName(containerName.getDomain(), props);
350 server.unregisterMBean(invokerName);
351 }
352 catch(Throwable ignore)
353 {
354 }
355 }
356
357 // Destroy the pool
358 instancePool.destroy();
359 instancePool.setContainer(null);
360 try
361 {
362 ObjectName containerName = super.getJmxName();
363 Hashtable props = containerName.getKeyPropertyList();
364 props.put("plugin", "pool");
365 ObjectName poolName = new ObjectName(containerName.getDomain(), props);
366 server.unregisterMBean(poolName);
367 }
368 catch(Throwable ignore)
369 {
370 }
371
372 // Destroy all the interceptors in the chain
373 Interceptor in = interceptor;
374 while (in != null)
375 {
376 in.destroy();
377 in.setContainer(null);
378 in = in.getNext();
379 }
380
381 // Call default destroy
382 super.destroyService();
383 }
384 finally
385 {
386 popENC();
387 // Reset classloader
388 SecurityActions.setContextClassLoader(oldCl);
389 }
390 }
391
392 /**
393 * @throws Error Not valid for MDB
394 */
395 public Object internalInvokeHome(Invocation mi)
396 throws Exception
397 {
398 throw new Error("invokeHome not valid for MessageDriven beans");
399 }
400
401 /**
402 * This method does invocation interpositioning of tx and security,
403 * retrieves the instance from an object table, and invokes the method
404 * on the particular instance
405 */
406 public Object internalInvoke(Invocation mi) throws Exception
407 {
408 // Invoke through interceptors
409 return getInterceptor().invoke(mi);
410 }
411
412
413 // EJBHome implementation ----------------------------------------
414
415 public EJBObject createHome()
416 throws java.rmi.RemoteException, CreateException
417 {
418 throw new Error("createHome not valid for MessageDriven beans");
419 }
420
421
422 public void removeHome(Handle handle)
423 throws java.rmi.RemoteException, RemoveException
424 {
425 throw new Error("removeHome not valid for MessageDriven beans");
426 // TODO
427 }
428
429 public void removeHome(Object primaryKey)
430 throws java.rmi.RemoteException, RemoveException
431 {
432 throw new Error("removeHome not valid for MessageDriven beans");
433 // TODO
434 }
435
436 public EJBMetaData getEJBMetaDataHome()
437 throws java.rmi.RemoteException
438 {
439 // TODO
440 //return null;
441 throw new Error("getEJBMetaDataHome not valid for MessageDriven beans");
442 }
443
444 public HomeHandle getHomeHandleHome()
445 throws java.rmi.RemoteException
446 {
447 // TODO
448 //return null;
449 throw new Error("getHomeHandleHome not valid for MessageDriven beans");
450 }
451
452 Interceptor createContainerInterceptor()
453 {
454 return new ContainerInterceptor();
455 }
456
457 /**
458 * This is the last step before invocation - all interceptors are done
459 */
460 class ContainerInterceptor
461 extends AbstractContainerInterceptor
462 {
463 /**
464 * @throws Error Not valid for MDB
465 */
466 public Object invokeHome(Invocation mi) throws Exception
467 {
468 throw new Error("invokeHome not valid for MessageDriven beans");
469 }
470
471 /**
472 * FIXME Design problem, who will do the acknowledging for
473 * beans with bean managed transaction?? Probably best done in the
474 * listener "proxys"
475 */
476 public Object invoke(Invocation mi)
477 throws Exception
478 {
479 EnterpriseContext ctx = (EnterpriseContext) mi.getEnterpriseContext();
480
481 // wire the transaction on the context,
482 // this is how the instance remember the tx
483 if (ctx.getTransaction() == null)
484 {
485 ctx.setTransaction(mi.getTransaction());
486 }
487
488 // Get method and instance to invoke upon
489 Method m = (Method) beanMapping.get(mi.getMethod());
490 if( m == null )
491 {
492 // This is a configuration error that should have been caught earlier
493 String msg = MessageDrivenContainer.this.getBeanMetaData().getEjbName()
494 + " Invalid invocation, check your deployment packaging, interfaces, method=" + mi.getMethod();
495 throw new EJBException(msg);
496 }
497
498 // we have a method that needs to be done by a bean instance
499 try
500 {
501 messageCount++;
502 return mi.performCall(ctx.getInstance(), m, mi.getArguments());
503 }
504 catch (Exception e)
505 {
506 rethrow(e);
507 }
508
509 // We will never get this far, but the compiler does not know that
510 throw new org.jboss.util.UnreachableStatementException();
511 }
512 }
513 }