001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.ejb;
023:
024: import java.lang.reflect.Method;
025: import java.util.HashMap;
026: import java.util.Hashtable;
027: import java.util.Iterator;
028: import java.util.Map;
029:
030: import javax.ejb.CreateException;
031: import javax.ejb.EJBMetaData;
032: import javax.ejb.EJBObject;
033: import javax.ejb.Handle;
034: import javax.ejb.HomeHandle;
035: import javax.ejb.RemoveException;
036: import javax.ejb.TimedObject;
037: import javax.ejb.Timer;
038: import javax.ejb.EJBException;
039: import javax.management.ObjectName;
040:
041: import org.jboss.invocation.Invocation;
042: import org.jboss.metadata.MessageDrivenMetaData;
043: import org.jboss.util.NullArgumentException;
044:
045: /**
046: * The container for <em>MessageDriven</em> beans.
047: *
048: * @author <a href="mailto:peter.antman@tim.se">Peter Antman</a>.
049: * @author <a href="mailto:rickard.oberg@telkel.com">Rickard Öberg</a>
050: * @author <a href="mailto:marc.fleury@telkel.com">Marc Fleury</a>
051: * @author <a href="mailto:docodan@mvcsoft.com">Daniel OConnor</a>
052: * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
053: * @author <a href="mailto:Scott.Stark@jboss.org">Scott Stark</a>
054: * @version $Revision: 57209 $
055: *
056: * @jmx:mbean extends="org.jboss.ejb.ContainerMBean"
057: */
058: public class MessageDrivenContainer extends Container implements
059: EJBProxyFactoryContainer, InstancePoolContainer,
060: MessageDrivenContainerMBean {
061: /**
062: * These are the mappings between the remote interface methods
063: * and the bean methods.
064: */
065: protected Map beanMapping;
066:
067: /** This is the instancepool that is to be used. */
068: protected InstancePool instancePool;
069:
070: /**
071: * This is the first interceptor in the chain.
072: * The last interceptor must be provided by the container itself.
073: */
074: protected Interceptor interceptor;
075:
076: protected long messageCount;
077:
078: public LocalProxyFactory getLocalProxyFactory() {
079: return localProxyFactory;
080: }
081:
082: public void setInstancePool(final InstancePool instancePool) {
083: if (instancePool == null)
084: throw new NullArgumentException("instancePool");
085:
086: this .instancePool = instancePool;
087: this .instancePool.setContainer(this );
088: }
089:
090: public InstancePool getInstancePool() {
091: return instancePool;
092: }
093:
094: public void addInterceptor(Interceptor in) {
095: if (interceptor == null) {
096: interceptor = in;
097: } else {
098: Interceptor current = interceptor;
099:
100: while (current.getNext() != null) {
101: current = current.getNext();
102: }
103:
104: current.setNext(in);
105: }
106: }
107:
108: public Interceptor getInterceptor() {
109: return interceptor;
110: }
111:
112: /**
113: * @jmx:managed-attribute
114: * @return the number of messages delivered
115: */
116: public long getMessageCount() {
117: return messageCount;
118: }
119:
120: /**
121: * EJBProxyFactoryContainer - not needed, should we skip inherit this
122: * or just throw Error??
123: */
124: public Class getHomeClass() {
125: //throw new Error("HomeClass not valid for MessageDriven beans");
126: return null;
127: }
128:
129: public Class getRemoteClass() {
130: //throw new Error("RemoteClass not valid for MessageDriven beans");
131: return null;
132: }
133:
134: public Class getLocalClass() {
135: return null;
136: }
137:
138: public Class getLocalHomeClass() {
139: //throw new Error("LocalHomeClass not valid for MessageDriven beans");
140: return null;
141: }
142:
143: // Container implementation - overridden here ----------------------
144:
145: protected void createService() throws Exception {
146: // Associate thread with classloader
147: ClassLoader oldCl = SecurityActions.getContextClassLoader();
148: SecurityActions.setContextClassLoader(getClassLoader());
149:
150: try {
151: // Call default init
152: super .createService();
153:
154: // Map the bean methods
155: Map map = new HashMap();
156: MessageDrivenMetaData mdMetaData = (MessageDrivenMetaData) metaData;
157: Class clazz = getClassLoader().loadClass(
158: mdMetaData.getMessagingType());
159: Method[] methods = clazz.getDeclaredMethods();
160: for (int i = 0; i < methods.length; i++) {
161: Method m = methods[i];
162: map.put(m, beanClass.getMethod(m.getName(), m
163: .getParameterTypes()));
164: log.debug("Mapped " + m.getName() + " " + m.hashCode()
165: + " to " + map.get(m));
166: }
167: if (TimedObject.class.isAssignableFrom(beanClass)) {
168: // Map ejbTimeout
169: map.put(TimedObject.class.getMethod("ejbTimeout",
170: new Class[] { Timer.class }), beanClass
171: .getMethod("ejbTimeout",
172: new Class[] { Timer.class }));
173: }
174: beanMapping = map;
175:
176: // Try to register the instance pool as an MBean
177: try {
178: ObjectName containerName = super .getJmxName();
179: Hashtable props = containerName.getKeyPropertyList();
180: props.put("plugin", "pool");
181: ObjectName poolName = new ObjectName(containerName
182: .getDomain(), props);
183: server.registerMBean(instancePool, poolName);
184: } catch (Throwable t) {
185: log.debug("Failed to register pool as mbean", t);
186: }
187: // Initialize pool
188: instancePool.create();
189:
190: for (Iterator it = proxyFactories.keySet().iterator(); it
191: .hasNext();) {
192: String invokerBinding = (String) it.next();
193: EJBProxyFactory ci = (EJBProxyFactory) proxyFactories
194: .get(invokerBinding);
195: // Try to register the container invoker as an MBean
196: try {
197: ObjectName containerName = super .getJmxName();
198: Hashtable props = containerName
199: .getKeyPropertyList();
200: props.put("plugin", "invoker");
201: props.put("binding", invokerBinding);
202: ObjectName invokerName = new ObjectName(
203: containerName.getDomain(), props);
204: server.registerMBean(ci, invokerName);
205: } catch (Throwable t) {
206: log
207: .debug(
208: "Failed to register invoker binding as mbean",
209: t);
210: }
211: ci.create();
212: }
213:
214: // Initialize the interceptor by calling the chain
215: Interceptor in = interceptor;
216: while (in != null) {
217: in.setContainer(this );
218: in.create();
219: in = in.getNext();
220: }
221:
222: } finally {
223: // Reset classloader
224: SecurityActions.setContextClassLoader(oldCl);
225: }
226: }
227:
228: protected void startService() throws Exception {
229: // Associate thread with classloader
230: ClassLoader oldCl = SecurityActions.getContextClassLoader();
231: SecurityActions.setContextClassLoader(getClassLoader());
232:
233: try {
234: // Call default start
235: super .startService();
236:
237: // Start the instance pool
238: instancePool.start();
239:
240: // Start all interceptors in the chain
241: Interceptor in = interceptor;
242: while (in != null) {
243: in.start();
244: in = in.getNext();
245: }
246:
247: // Start container invoker
248: for (Iterator it = proxyFactories.keySet().iterator(); it
249: .hasNext();) {
250: String invokerBinding = (String) it.next();
251: EJBProxyFactory ci = (EJBProxyFactory) proxyFactories
252: .get(invokerBinding);
253: ci.start();
254: }
255:
256: // Restore persisted ejb timers
257: restoreTimers();
258: } finally {
259: // Reset classloader
260: SecurityActions.setContextClassLoader(oldCl);
261: }
262: }
263:
264: protected void stopService() throws Exception {
265: // Associate thread with classloader
266: ClassLoader oldCl = SecurityActions.getContextClassLoader();
267: SecurityActions.setContextClassLoader(getClassLoader());
268:
269: try {
270: // Call default stop
271: super .stopService();
272:
273: // Stop container invoker
274: for (Iterator it = proxyFactories.keySet().iterator(); it
275: .hasNext();) {
276: String invokerBinding = (String) it.next();
277: EJBProxyFactory ci = (EJBProxyFactory) proxyFactories
278: .get(invokerBinding);
279: ci.stop();
280: }
281:
282: // Stop the instance pool
283: instancePool.stop();
284:
285: // Stop all interceptors in the chain
286: Interceptor in = interceptor;
287: while (in != null) {
288: in.stop();
289: in = in.getNext();
290: }
291: } finally {
292: // Reset classloader
293: SecurityActions.setContextClassLoader(oldCl);
294: }
295: }
296:
297: protected void destroyService() throws Exception {
298: // Associate thread with classloader
299: ClassLoader oldCl = SecurityActions.getContextClassLoader();
300: SecurityActions.setContextClassLoader(getClassLoader());
301:
302: try {
303: // Destroy container invoker
304: for (Iterator it = proxyFactories.keySet().iterator(); it
305: .hasNext();) {
306: String invokerBinding = (String) it.next();
307: EJBProxyFactory ci = (EJBProxyFactory) proxyFactories
308: .get(invokerBinding);
309: ci.destroy();
310: ci.setContainer(null);
311: try {
312: ObjectName containerName = super .getJmxName();
313: Hashtable props = containerName
314: .getKeyPropertyList();
315: props.put("plugin", "invoker");
316: props.put("binding", invokerBinding);
317: ObjectName invokerName = new ObjectName(
318: containerName.getDomain(), props);
319: server.unregisterMBean(invokerName);
320: } catch (Throwable ignore) {
321: }
322: }
323:
324: // Destroy the pool
325: instancePool.destroy();
326: instancePool.setContainer(null);
327: try {
328: ObjectName containerName = super .getJmxName();
329: Hashtable props = containerName.getKeyPropertyList();
330: props.put("plugin", "pool");
331: ObjectName poolName = new ObjectName(containerName
332: .getDomain(), props);
333: server.unregisterMBean(poolName);
334: } catch (Throwable ignore) {
335: }
336:
337: // Destroy all the interceptors in the chain
338: Interceptor in = interceptor;
339: while (in != null) {
340: in.destroy();
341: in.setContainer(null);
342: in = in.getNext();
343: }
344:
345: // Call default destroy
346: super .destroyService();
347: } finally {
348: // Reset classloader
349: SecurityActions.setContextClassLoader(oldCl);
350: }
351: }
352:
353: /**
354: * @throws Error Not valid for MDB
355: */
356: public Object internalInvokeHome(Invocation mi) throws Exception {
357: throw new Error("invokeHome not valid for MessageDriven beans");
358: }
359:
360: /**
361: * This method does invocation interpositioning of tx and security,
362: * retrieves the instance from an object table, and invokes the method
363: * on the particular instance
364: */
365: public Object internalInvoke(Invocation mi) throws Exception {
366: // Invoke through interceptors
367: return getInterceptor().invoke(mi);
368: }
369:
370: // EJBHome implementation ----------------------------------------
371:
372: public EJBObject createHome() throws java.rmi.RemoteException,
373: CreateException {
374: throw new Error("createHome not valid for MessageDriven beans");
375: }
376:
377: public void removeHome(Handle handle)
378: throws java.rmi.RemoteException, RemoveException {
379: throw new Error("removeHome not valid for MessageDriven beans");
380: // TODO
381: }
382:
383: public void removeHome(Object primaryKey)
384: throws java.rmi.RemoteException, RemoveException {
385: throw new Error("removeHome not valid for MessageDriven beans");
386: // TODO
387: }
388:
389: public EJBMetaData getEJBMetaDataHome()
390: throws java.rmi.RemoteException {
391: // TODO
392: //return null;
393: throw new Error(
394: "getEJBMetaDataHome not valid for MessageDriven beans");
395: }
396:
397: public HomeHandle getHomeHandleHome()
398: throws java.rmi.RemoteException {
399: // TODO
400: //return null;
401: throw new Error(
402: "getHomeHandleHome not valid for MessageDriven beans");
403: }
404:
405: Interceptor createContainerInterceptor() {
406: return new ContainerInterceptor();
407: }
408:
409: /**
410: * This is the last step before invocation - all interceptors are done
411: */
412: class ContainerInterceptor extends AbstractContainerInterceptor {
413: /**
414: * @throws Error Not valid for MDB
415: */
416: public Object invokeHome(Invocation mi) throws Exception {
417: throw new Error(
418: "invokeHome not valid for MessageDriven beans");
419: }
420:
421: /**
422: * FIXME Design problem, who will do the acknowledging for
423: * beans with bean managed transaction?? Probably best done in the
424: * listener "proxys"
425: */
426: public Object invoke(Invocation mi) throws Exception {
427: EnterpriseContext ctx = (EnterpriseContext) mi
428: .getEnterpriseContext();
429:
430: // wire the transaction on the context,
431: // this is how the instance remember the tx
432: if (ctx.getTransaction() == null) {
433: ctx.setTransaction(mi.getTransaction());
434: }
435:
436: // Get method and instance to invoke upon
437: Method m = (Method) beanMapping.get(mi.getMethod());
438: if (m == null) {
439: // This is a configuration error that should have been caught earlier
440: String msg = MessageDrivenContainer.this
441: .getBeanMetaData().getEjbName()
442: + " Invalid invocation, check your deployment packaging, interfaces, method="
443: + mi.getMethod();
444: throw new EJBException(msg);
445: }
446:
447: // we have a method that needs to be done by a bean instance
448: try {
449: messageCount++;
450: return mi.performCall(ctx.getInstance(), m, mi
451: .getArguments());
452: } catch (Exception e) {
453: rethrow(e);
454: }
455:
456: // We will never get this far, but the compiler does not know that
457: throw new org.jboss.util.UnreachableStatementException();
458: }
459: }
460: }
|