/*
* 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;
// $Id: SessionContainer.java 81030 2008-11-14 12:59:42Z dimitris@jboss.org $
import java.lang.reflect.Method;
import java.rmi.RemoteException;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import javax.ejb.EJBHome;
import javax.ejb.EJBLocalHome;
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.management.ObjectName;
import org.jboss.invocation.Invocation;
import org.jboss.invocation.MarshalledInvocation;
import org.jboss.metadata.SessionMetaData;
/**
* <p>
* Container dedicated to session beans. Contains factored out
* redundancies between stateless and stateful treatments, because
* (extending the spec) we would like to also support stateful
* web services.
* </p>
* @author <a href="mailto:Christoph.Jung@infor.de">Christoph G. Jung</a>
* @version $Revision: 81030 $
* @since 30.10.2003
*/
public abstract class SessionContainer extends Container
{
/**
* These are the mappings between the home interface methods and the
* container methods.
*/
protected Map homeMapping;
/**
* These are the mappings between the remote interface methods and the
* bean methods.
*/
protected Map beanMapping;
/**
* This is the first interceptor in the chain. The last interceptor must
* be provided by the container itself
*/
protected Interceptor interceptor;
/** this is the service endpoint class */
protected Class serviceEndpoint;
/** This is the instancepool that is to be used */
protected InstancePool instancePool;
/** set the instance pool */
public void setInstancePool(InstancePool ip)
{
if (ip == null)
throw new IllegalArgumentException("Null pool");
this.instancePool = ip;
}
/** return instance pool */
public InstancePool getInstancePool()
{
return instancePool;
}
/** return local proxy factory */
public LocalProxyFactory getLocalProxyFactory()
{
return localProxyFactory;
}
/** add an additional interceptor to the chain */
public void addInterceptor(Interceptor in)
{
if (interceptor == null)
{
interceptor = in;
}
else
{
Interceptor current = interceptor;
while (current.getNext() != null)
{
current = current.getNext();
}
current.setNext(in);
}
}
/** return first interceptor */
public Interceptor getInterceptor()
{
return interceptor;
}
/** return service endpoint */
public Class getServiceEndpoint()
{
return serviceEndpoint;
}
// Container stuff
protected void createService() throws Exception
{
// Associate thread with classloader
ClassLoader oldCl = SecurityActions.getContextClassLoader();
SecurityActions.setContextClassLoader(getClassLoader());
pushENC();
try
{
// Acquire classes from CL
if (metaData.getHome() != null)
homeInterface = classLoader.loadClass(metaData.getHome());
if (metaData.getRemote() != null)
remoteInterface = classLoader.loadClass(metaData.getRemote());
if (((SessionMetaData) metaData).getServiceEndpoint() != null)
{
serviceEndpoint =
classLoader.loadClass(((SessionMetaData) metaData).getServiceEndpoint());
}
// Call default init
super.createService();
// Make some additional validity checks with regards to the container configuration
checkCoherency();
// Map the bean methods
setupBeanMapping();
// Map the home methods
setupHomeMapping();
// Map the interfaces to Long
setupMarshalledInvocationMapping();
createInvokers();
createInstanceCache();
createInstancePool();
createPersistenceManager();
createInterceptors();
}
finally
{
popENC();
// Reset classloader
SecurityActions.setContextClassLoader(oldCl);
}
}
/**
* how home methods are treated by container
*/
protected abstract void setupHomeMapping() throws Exception;
/** loop through methods and setup mapping */
protected void setUpBeanMappingImpl(Map map, Method[] methods, String declaringClass)
throws NoSuchMethodException
{
for (int i = 0; i < methods.length; i++)
{
Method m = methods[i];
if (m.getDeclaringClass().getName().equals(declaringClass) == false)
{
// Implemented by bean
try
{
Method beanMethod = beanClass.getMethod(m.getName(), m.getParameterTypes());
map.put(m, beanMethod);
}
catch (NoSuchMethodException ex)
{
throw new NoSuchMethodException("Not found in bean class: " + m);
}
log.debug("Mapped " + m.getName() + " HASH " + m.hashCode() + "to " + map.get(m));
}
else
{
// Implemented by container
try
{
Method containerMethod = getClass().getMethod(m.getName(), new Class[]{Invocation.class});
map.put(m, containerMethod);
}
catch (NoSuchMethodException e)
{
throw new NoSuchMethodException("Not found in container class: " + m);
}
log.debug("Mapped Container method " + m.getName() + " HASH " + m.hashCode());
}
}
}
/** build bean mappings for application logic */
protected void setupBeanMapping() throws NoSuchMethodException
{
Map map = new HashMap();
if (remoteInterface != null)
{
Method[] m = remoteInterface.getMethods();
setUpBeanMappingImpl(map, m, "javax.ejb.EJBObject");
}
if (localInterface != null)
{
Method[] m = localInterface.getMethods();
setUpBeanMappingImpl(map, m, "javax.ejb.EJBLocalObject");
}
if (TimedObject.class.isAssignableFrom(beanClass))
{
Method[] m = new Method[]{TimedObject.class.getMethod("ejbTimeout", new Class[]{Timer.class})};
setUpBeanMappingImpl(map, m, "javax.ejb.Timer");
}
if (serviceEndpoint != null)
{
Method[] m = serviceEndpoint.getMethods();
setUpBeanMappingImpl(map, m, "java.rmi.Remote");
}
beanMapping = map;
}
/**
* sets up marshalled invocation mappings
* @throws Exception
*/
protected void setupMarshalledInvocationMapping() throws Exception
{
// Create method mappings for container invoker
if (homeInterface != null)
{
Method[] m = homeInterface.getMethods();
for (int i = 0; i < m.length; i++)
{
marshalledInvocationMapping.put(new Long(MarshalledInvocation.calculateHash(m[i])), m[i]);
}
}
if (remoteInterface != null)
{
Method[] m = remoteInterface.getMethods();
for (int j = 0; j < m.length; j++)
{
marshalledInvocationMapping.put(new Long(MarshalledInvocation.calculateHash(m[j])), m[j]);
}
}
// Get the getEJBObjectMethod
Method getEJBObjectMethod =
Class.forName("javax.ejb.Handle").getMethod("getEJBObject",
new Class[0]);
// Hash it
marshalledInvocationMapping.put(new Long(MarshalledInvocation.calculateHash(getEJBObjectMethod)), getEJBObjectMethod);
}
protected void checkCoherency() throws Exception
{
// Check clustering cohrency wrt metadata
//
if (metaData.isClustered())
{
boolean clusteredProxyFactoryFound = false;
Iterator it = proxyFactories.keySet().iterator();
while (it.hasNext())
{
String invokerBinding = (String) it.next();
EJBProxyFactory ci = (EJBProxyFactory) proxyFactories.get(invokerBinding);
if (ci instanceof org.jboss.proxy.ejb.ClusterProxyFactory)
clusteredProxyFactoryFound = true;
}
if (!clusteredProxyFactoryFound)
{
log.warn("*** EJB '"
+ this.metaData.getEjbName()
+ "' deployed as CLUSTERED but not a single clustered-invoker is bound to container ***");
}
}
}
/** creates a new instance pool */
protected void createInstancePool() throws Exception
{
// 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();
}
/**
* no instance cache per default
*/
protected void createInstanceCache() throws Exception
{
}
/** creates the invokers */
protected void createInvokers() throws Exception
{
// Init container invoker
for (Iterator it = proxyFactories.keySet().iterator(); it.hasNext();)
{
String invokerBinding = (String) it.next();
EJBProxyFactory ci = (EJBProxyFactory) proxyFactories.get(invokerBinding);
ci.create();
}
}
/** Initialize the interceptors by calling the chain */
protected void createInterceptors() throws Exception
{
Interceptor in = interceptor;
while (in != null)
{
in.setContainer(this);
in.create();
in = in.getNext();
}
}
/**
* no persistence manager per default
*/
protected void createPersistenceManager() throws Exception
{
}
protected void startService() throws Exception
{
// Associate thread with classloader
ClassLoader oldCl = SecurityActions.getContextClassLoader();
SecurityActions.setContextClassLoader(getClassLoader());
pushENC();
try
{
// Call default start
super.startService();
startInvokers();
startInstanceCache();
startInstancePool();
startPersistenceManager();
startInterceptors();
// Restore persisted ejb timers
restoreTimers();
}
finally
{
popENC();
// Reset classloader
SecurityActions.setContextClassLoader(oldCl);
}
}
/**
* no persistence manager per default
*/
protected void startPersistenceManager() throws Exception
{
}
/**
* no instance cache per default
*/
protected void startInstanceCache() throws Exception
{
}
/** Start container invokers */
protected void startInvokers() throws Exception
{
for (Iterator it = proxyFactories.keySet().iterator(); it.hasNext();)
{
String invokerBinding = (String) it.next();
EJBProxyFactory ci = (EJBProxyFactory) proxyFactories.get(invokerBinding);
ci.start();
}
}
/** Start pool */
protected void startInstancePool() throws Exception
{
instancePool.start();
}
/** Start all interceptors in the chain **/
protected void startInterceptors() throws Exception
{
Interceptor in = interceptor;
while (in != null)
{
in.start();
in = in.getNext();
}
}
protected void stopService() throws Exception
{
// Associate thread with classloader
ClassLoader oldCl = SecurityActions.getContextClassLoader();
SecurityActions.setContextClassLoader(getClassLoader());
pushENC();
try
{
// Call default stop
super.stopService();
stopInvokers();
stopInstanceCache();
stopInstancePool();
stopPersistenceManager();
stopInterceptors();
}
finally
{
popENC();
// Reset classloader
SecurityActions.setContextClassLoader(oldCl);
}
}
/** Stop all interceptors in the chain */
protected void stopInterceptors()
{
Interceptor in = interceptor;
while (in != null)
{
in.stop();
in = in.getNext();
}
}
/** no persistence */
protected void stopPersistenceManager()
{
}
/** Stop pool */
protected void stopInstancePool()
{
instancePool.stop();
}
/** no instance cache */
protected void stopInstanceCache()
{
}
/** Stop container invoker */
protected void stopInvokers()
{
for (Iterator it = proxyFactories.keySet().iterator(); it.hasNext();)
{
String invokerBinding = (String) it.next();
EJBProxyFactory ci = (EJBProxyFactory) proxyFactories.get(invokerBinding);
ci.stop();
}
}
protected void destroyService() throws Exception
{
// Associate thread with classloader
ClassLoader oldCl = SecurityActions.getContextClassLoader();
SecurityActions.setContextClassLoader(getClassLoader());
pushENC();
try
{
destroyInvokers();
destroyInstanceCache();
destroyInstancePool();
destroyPersistenceManager();
destroyInterceptors();
destroyMarshalledInvocationMapping();
homeInterface = null;
remoteInterface = null;
serviceEndpoint = null;
beanMapping.clear();
// Call default destroy
super.destroyService();
}
finally
{
popENC();
// Reset classloader
SecurityActions.setContextClassLoader(oldCl);
}
}
protected void destroyMarshalledInvocationMapping()
{
MarshalledInvocation.removeHashes(homeInterface);
MarshalledInvocation.removeHashes(remoteInterface);
}
protected void destroyInterceptors()
{
// Destroy all the interceptors in the chain
Interceptor in = interceptor;
while (in != null)
{
in.destroy();
in.setContainer(null);
in = in.getNext();
}
}
protected void destroyPersistenceManager()
{
}
protected void destroyInstancePool()
{
// Destroy 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)
{
}
}
protected void destroyInstanceCache()
{
}
protected void destroyInvokers()
{
// Destroy container invoker
for (Iterator it = proxyFactories.keySet().iterator(); it.hasNext();)
{
String invokerBinding = (String) it.next();
EJBProxyFactory ci = (EJBProxyFactory) proxyFactories.get(invokerBinding);
ci.destroy();
}
}
public Object internalInvokeHome(Invocation mi) throws Exception
{
Method method = mi.getMethod();
if (method != null && method.getName().equals("remove"))
{
// Handle or primary key?
Object arg = mi.getArguments()[0];
if (arg instanceof Handle)
{
if (arg == null)
throw new RemoteException("Null handle");
Handle handle = (Handle) arg;
EJBObject ejbObject = handle.getEJBObject();
ejbObject.remove();
return null;
}
else
throw new RemoveException("EJBHome.remove(Object) not allowed for session beans");
}
// Invoke through interceptors
return getInterceptor().invokeHome(mi);
}
/**
* 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
{
// Invoke through interceptors
return getInterceptor().invoke(mi);
}
// EJBObject implementation --------------------------------------
/**
* While the following methods are implemented in the client in the case
* of JRMP we would need to implement them to fully support other transport
* protocols
*
* @return Always null
*/
public Handle getHandle(Invocation mi) throws RemoteException
{
// TODO
return null;
}
public Object getPrimaryKey(Invocation mi) throws RemoteException
{
return getPrimaryKey();
}
public Object getPrimaryKey() throws RemoteException
{
throw new RemoteException("Call to getPrimaryKey not allowed on session bean");
}
public EJBHome getEJBHome(Invocation mi) throws RemoteException
{
EJBProxyFactory ci = getProxyFactory();
if (ci == null)
{
String msg = "No ProxyFactory, check for ProxyFactoryFinderInterceptor";
throw new IllegalStateException(msg);
}
return (EJBHome) ci.getEJBHome();
}
/**
* @return Always false
*/
public boolean isIdentical(Invocation mi) throws RemoteException
{
EJBProxyFactory ci = getProxyFactory();
if (ci == null)
{
String msg = "No ProxyFactory, check for ProxyFactoryFinderInterceptor";
throw new IllegalStateException(msg);
}
return ci.isIdentical(this, mi);
}
public EJBMetaData getEJBMetaDataHome(Invocation mi) throws RemoteException
{
return getEJBMetaDataHome();
}
public EJBMetaData getEJBMetaDataHome() throws RemoteException
{
EJBProxyFactory ci = getProxyFactory();
if (ci == null)
{
String msg = "No ProxyFactory, check for ProxyFactoryFinderInterceptor";
throw new IllegalStateException(msg);
}
return ci.getEJBMetaData();
}
public HomeHandle getHomeHandleHome(Invocation mi) throws RemoteException
{
return getHomeHandleHome();
}
public HomeHandle getHomeHandleHome() throws RemoteException
{
EJBProxyFactory ci = getProxyFactory();
if (ci == null)
{
String msg = "No ProxyFactory, check for ProxyFactoryFinderInterceptor";
throw new IllegalStateException(msg);
}
EJBHome home = (EJBHome) ci.getEJBHome();
return home.getHomeHandle();
}
// Home interface implementation ---------------------------------
// local object interface implementation
public EJBLocalHome getEJBLocalHome(Invocation mi)
{
return localProxyFactory.getEJBLocalHome();
}
/**
* needed for sub-inner-class access (old jdk compiler bug)
* @return
*/
protected Map getHomeMapping()
{
return homeMapping;
}
/**
* needed for sub-inner-class access (old jdk compiler bug)
* @return
*/
protected Map getBeanMapping()
{
return beanMapping;
}
}