/* * JBoss, Home of Professional Open Source. * Copyright 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.ejb; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Hashtable; import java.util.Iterator; import java.util.Map; import javax.ejb.CreateException; import javax.ejb.EJBMetaData; import javax.ejb.EJBObject; import javax.ejb.Handle; import javax.ejb.HomeHandle; import javax.ejb.RemoveException; import javax.ejb.TimedObject; import javax.ejb.Timer; import javax.ejb.EJBException; import javax.management.ObjectName; import org.jboss.invocation.Invocation; import org.jboss.metadata.MessageDrivenMetaData; import org.jboss.security.SecurityContext; import org.jboss.util.NullArgumentException; /** * The container for <em>MessageDriven</em> beans. * * @author <a href="mailto:peter.antman@tim.se">Peter Antman</a>. * @author <a href="mailto:rickard.oberg@telkel.com">Rickard Oberg</a> * @author <a href="mailto:marc.fleury@telkel.com">Marc Fleury</a> * @author <a href="mailto:docodan@mvcsoft.com">Daniel OConnor</a> * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> * @author <a href="mailto:Scott.Stark@jboss.org">Scott Stark</a> * @version $Revision: 89854 $ * * @jmx:mbean extends="org.jboss.ejb.ContainerMBean" */ public class MessageDrivenContainer extends Container implements EJBProxyFactoryContainer, InstancePoolContainer, MessageDrivenContainerMBean { /** * These are the mappings between the remote interface methods * and the bean methods. */ protected Map beanMapping; /** This is the instancepool that is to be used. */ protected InstancePool instancePool; /** * This is the first interceptor in the chain. * The last interceptor must be provided by the container itself. */ protected Interceptor interceptor; protected long messageCount; public LocalProxyFactory getLocalProxyFactory() { return localProxyFactory; } public void setInstancePool(final InstancePool instancePool) { if (instancePool == null) throw new NullArgumentException("instancePool"); this.instancePool = instancePool; } public InstancePool getInstancePool() { return instancePool; } public String getBeanTypeName() { return "MDB"; } public void addInterceptor(Interceptor in) { if (interceptor == null) { interceptor = in; } else { Interceptor current = interceptor; while (current.getNext() != null) { current = current.getNext(); } current.setNext(in); } } public Interceptor getInterceptor() { return interceptor; } /** * @jmx:managed-attribute * @return the number of messages delivered */ public long getMessageCount() { return messageCount; } /** * EJBProxyFactoryContainer - not needed, should we skip inherit this * or just throw Error?? */ public Class getHomeClass() { //throw new Error("HomeClass not valid for MessageDriven beans"); return null; } public Class getRemoteClass() { //throw new Error("RemoteClass not valid for MessageDriven beans"); return null; } public Class getLocalClass() { return null; } public Class getLocalHomeClass() { //throw new Error("LocalHomeClass not valid for MessageDriven beans"); return null; } // Container implementation - overridden here ---------------------- protected void createService() throws Exception { // Associate thread with classloader ClassLoader oldCl = SecurityActions.getContextClassLoader(); SecurityActions.setContextClassLoader(getClassLoader()); pushENC(); try { // Call default init super.createService(); // Map the bean methods Map map = new HashMap(); MessageDrivenMetaData mdMetaData = (MessageDrivenMetaData)metaData; String type = mdMetaData.getMessagingType(); if(type == null || type.length() == 0) type = MessageDrivenMetaData.DEFAULT_MESSAGING_TYPE; Class clazz = getClassLoader().loadClass(type); Method[] methods = clazz.getDeclaredMethods(); for (int i = 0; i < methods.length; i++) { Method m = methods[i]; map.put(m, beanClass.getMethod(m.getName(), m.getParameterTypes())); log.debug("Mapped " + m.getName() + " " + m.hashCode() + " to " + map.get(m)); } if( TimedObject.class.isAssignableFrom( beanClass ) ) { // Map ejbTimeout map.put( TimedObject.class.getMethod( "ejbTimeout", new Class[] { Timer.class } ), beanClass.getMethod( "ejbTimeout", new Class[] { Timer.class } ) ); } beanMapping = map; // Try to register the instance pool as an MBean try { ObjectName containerName = super.getJmxName(); Hashtable props = containerName.getKeyPropertyList(); props.put("plugin", "pool"); ObjectName poolName = new ObjectName(containerName.getDomain(), props); server.registerMBean(instancePool, poolName); } catch(Throwable t) { log.debug("Failed to register pool as mbean", t); } // Initialize pool instancePool.setContainer(this); instancePool.create(); for (Iterator it = proxyFactories.keySet().iterator(); it.hasNext();) { String invokerBinding = (String) it.next(); EJBProxyFactory ci = (EJBProxyFactory) proxyFactories.get(invokerBinding); // Try to register the container invoker as an MBean try { ObjectName containerName = super.getJmxName(); Hashtable props = containerName.getKeyPropertyList(); props.put("plugin", "invoker"); props.put("binding", invokerBinding); ObjectName invokerName = new ObjectName(containerName.getDomain(), props); server.registerMBean(ci, invokerName); } catch(Throwable t) { log.debug("Failed to register invoker binding as mbean", t); } ci.create(); } // Initialize the interceptor by calling the chain Interceptor in = interceptor; while (in != null) { in.setContainer(this); in.create(); in = in.getNext(); } } finally { popENC(); // Reset classloader SecurityActions.setContextClassLoader(oldCl); } } protected void startService() throws Exception { // Associate thread with classloader ClassLoader oldCl = SecurityActions.getContextClassLoader(); SecurityActions.setContextClassLoader(getClassLoader()); pushENC(); try { // Call default start super.startService(); // Start the instance pool instancePool.start(); // Start all interceptors in the chain Interceptor in = interceptor; while (in != null) { in.start(); in = in.getNext(); } // Start container invoker for (Iterator it = proxyFactories.keySet().iterator(); it.hasNext();) { String invokerBinding = (String) it.next(); EJBProxyFactory ci = (EJBProxyFactory) proxyFactories.get(invokerBinding); ci.start(); } // Restore persisted ejb timers restoreTimers(); } finally { popENC(); // Reset classloader SecurityActions.setContextClassLoader(oldCl); } } protected void stopService() throws Exception { // Associate thread with classloader ClassLoader oldCl = SecurityActions.getContextClassLoader(); SecurityActions.setContextClassLoader(getClassLoader()); pushENC(); try { // Call default stop super.stopService(); // Stop container invoker for (Iterator it = proxyFactories.keySet().iterator(); it.hasNext();) { String invokerBinding = (String) it.next(); EJBProxyFactory ci = (EJBProxyFactory) proxyFactories.get(invokerBinding); ci.stop(); } // Stop the instance pool instancePool.stop(); // Stop all interceptors in the chain Interceptor in = interceptor; while (in != null) { in.stop(); in = in.getNext(); } } finally { popENC(); // Reset classloader SecurityActions.setContextClassLoader(oldCl); } } protected void destroyService() throws Exception { // Associate thread with classloader ClassLoader oldCl = SecurityActions.getContextClassLoader(); SecurityActions.setContextClassLoader(getClassLoader()); pushENC(); try { // Destroy container invoker for (Iterator it = proxyFactories.keySet().iterator(); it.hasNext();) { String invokerBinding = (String) it.next(); EJBProxyFactory ci = (EJBProxyFactory) proxyFactories.get(invokerBinding); ci.destroy(); try { ObjectName containerName = super.getJmxName(); Hashtable props = containerName.getKeyPropertyList(); props.put("plugin", "invoker"); props.put("binding", invokerBinding); ObjectName invokerName = new ObjectName(containerName.getDomain(), props); server.unregisterMBean(invokerName); } catch(Throwable ignore) { } } // Destroy the pool instancePool.destroy(); instancePool.setContainer(null); try { ObjectName containerName = super.getJmxName(); Hashtable props = containerName.getKeyPropertyList(); props.put("plugin", "pool"); ObjectName poolName = new ObjectName(containerName.getDomain(), props); server.unregisterMBean(poolName); } catch(Throwable ignore) { } // Destroy all the interceptors in the chain Interceptor in = interceptor; while (in != null) { in.destroy(); in.setContainer(null); in = in.getNext(); } // Call default destroy super.destroyService(); } finally { popENC(); // Reset classloader SecurityActions.setContextClassLoader(oldCl); } } /** * @throws Error Not valid for MDB */ public Object internalInvokeHome(Invocation mi) throws Exception { throw new Error("invokeHome not valid for MessageDriven beans"); } /** * This method does invocation interpositioning of tx and security, * retrieves the instance from an object table, and invokes the method * on the particular instance */ public Object internalInvoke(Invocation mi) throws Exception { SecurityContext cachedContext = null; try { cachedContext = SecurityActions.getSecurityContext(); //For message driven beans, there is no security context SecurityActions.setSecurityContext(null); // Invoke through interceptors return getInterceptor().invoke(mi); } finally { SecurityActions.setSecurityContext(cachedContext); } } // EJBHome implementation ---------------------------------------- public EJBObject createHome() throws java.rmi.RemoteException, CreateException { throw new Error("createHome not valid for MessageDriven beans"); } public void removeHome(Handle handle) throws java.rmi.RemoteException, RemoveException { throw new Error("removeHome not valid for MessageDriven beans"); // TODO } public void removeHome(Object primaryKey) throws java.rmi.RemoteException, RemoveException { throw new Error("removeHome not valid for MessageDriven beans"); // TODO } public EJBMetaData getEJBMetaDataHome() throws java.rmi.RemoteException { // TODO //return null; throw new Error("getEJBMetaDataHome not valid for MessageDriven beans"); } public HomeHandle getHomeHandleHome() throws java.rmi.RemoteException { // TODO //return null; throw new Error("getHomeHandleHome not valid for MessageDriven beans"); } Interceptor createContainerInterceptor() { return new ContainerInterceptor(); } /** * This is the last step before invocation - all interceptors are done */ class ContainerInterceptor extends AbstractContainerInterceptor { /** * @throws Error Not valid for MDB */ public Object invokeHome(Invocation mi) throws Exception { throw new Error("invokeHome not valid for MessageDriven beans"); } /** * FIXME Design problem, who will do the acknowledging for * beans with bean managed transaction?? Probably best done in the * listener "proxys" */ public Object invoke(Invocation mi) throws Exception { EnterpriseContext ctx = (EnterpriseContext) mi.getEnterpriseContext(); // wire the transaction on the context, // this is how the instance remember the tx if (ctx.getTransaction() == null) { ctx.setTransaction(mi.getTransaction()); } // Get method and instance to invoke upon Method m = (Method) beanMapping.get(mi.getMethod()); if( m == null ) { // This is a configuration error that should have been caught earlier String msg = MessageDrivenContainer.this.getBeanMetaData().getEjbName() + " Invalid invocation, check your deployment packaging, interfaces, method=" + mi.getMethod(); throw new EJBException(msg); } // we have a method that needs to be done by a bean instance try { messageCount++; return mi.performCall(ctx.getInstance(), m, mi.getArguments()); } catch (Exception e) { rethrow(e); } // We will never get this far, but the compiler does not know that throw new org.jboss.util.UnreachableStatementException(); } } }