package org.jboss.ejb;
import java.lang.reflect.Method;
import java.rmi.RemoteException;
import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Collection;
import java.util.Hashtable;
import java.util.Enumeration;
import javax.ejb.Handle;
import javax.ejb.HomeHandle;
import javax.ejb.EJBObject;
import javax.ejb.EJBLocalObject;
import javax.ejb.EJBHome;
import javax.ejb.EJBLocalHome;
import javax.ejb.EJBMetaData;
import javax.ejb.RemoveException;
import javax.ejb.EJBException;
import javax.ejb.TimedObject;
import javax.ejb.Timer;
import javax.transaction.Transaction;
import javax.management.ObjectName;
import org.jboss.invocation.Invocation;
import org.jboss.invocation.InvocationType;
import org.jboss.invocation.MarshalledInvocation;
import org.jboss.monitor.StatisticsProvider;
import org.jboss.metadata.EntityMetaData;
import org.jboss.metadata.ConfigurationMetaData;
import org.jboss.util.collection.SerializableEnumeration;
public class EntityContainer
extends Container
implements EJBProxyFactoryContainer, InstancePoolContainer,
EntityContainerMBean
{
protected Map homeMapping = new HashMap();
protected Map beanMapping = new HashMap();
protected EntityPersistenceManager persistenceManager;
protected InstanceCache instanceCache;
protected InstancePool instancePool;
protected Interceptor interceptor;
protected boolean readOnly = false;
protected static GlobalTxEntityMap globalTxEntityMap = new GlobalTxEntityMap();
public static GlobalTxEntityMap getGlobalTxEntityMap()
{
return globalTxEntityMap;
}
public static void synchronizeEntitiesWithinTransaction(Transaction tx)
{
if(tx != null)
{
getGlobalTxEntityMap().synchronizeEntities(tx);
}
}
public boolean isReadOnly()
{
return readOnly;
}
public LocalProxyFactory getLocalProxyFactory()
{
return localProxyFactory;
}
public void setInstancePool(InstancePool ip)
{
if (ip == null)
throw new IllegalArgumentException("Null pool");
this.instancePool = ip;
ip.setContainer(this);
}
public InstancePool getInstancePool()
{
return instancePool;
}
public void setInstanceCache(InstanceCache ic)
{
if (ic == null)
throw new IllegalArgumentException("Null cache");
this.instanceCache = ic;
ic.setContainer(this);
}
public InstanceCache getInstanceCache()
{
return instanceCache;
}
public EntityPersistenceManager getPersistenceManager()
{
return persistenceManager;
}
public void setPersistenceManager(EntityPersistenceManager pm)
{
if (pm == null)
throw new IllegalArgumentException("Null persistence manager");
persistenceManager = pm;
pm.setContainer(this);
}
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;
}
public Class getHomeClass()
{
return homeInterface;
}
public Class getRemoteClass()
{
return remoteInterface;
}
public Object createBeanClassInstance() throws Exception {
return persistenceManager.createBeanClassInstance();
}
protected void createService() throws Exception
{
ClassLoader oldCl = SecurityActions.getContextClassLoader();
SecurityActions.setContextClassLoader(getClassLoader());
try
{
if (metaData.getHome() != null)
homeInterface = classLoader.loadClass(metaData.getHome());
if (metaData.getRemote() != null)
remoteInterface = classLoader.loadClass(metaData.getRemote());
super.createService();
checkCoherency ();
setupBeanMapping();
setupHomeMapping();
setupMarshalledInvocationMapping();
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 cache as mbean", t);
}
instancePool.create();
for (Iterator it = proxyFactories.keySet().iterator(); it.hasNext(); )
{
String invokerBinding = (String)it.next();
EJBProxyFactory ci = (EJBProxyFactory)proxyFactories.get(invokerBinding);
ci.create();
}
try
{
ObjectName containerName = super.getJmxName();
Hashtable props = containerName.getKeyPropertyList();
props.put("plugin", "cache");
ObjectName cacheName = new ObjectName(containerName.getDomain(), props);
server.registerMBean(instanceCache, cacheName);
}
catch(Throwable t)
{
log.debug("Failed to register cache as mbean", t);
}
instanceCache.create();
persistenceManager.create();
Interceptor in = interceptor;
while (in != null)
{
in.setContainer(this);
in.create();
in = in.getNext();
}
readOnly = ((EntityMetaData)metaData).isReadOnly();
}
finally
{
SecurityActions.setContextClassLoader(oldCl);
}
}
protected void startService() throws Exception
{
ClassLoader oldCl = SecurityActions.getContextClassLoader();
SecurityActions.setContextClassLoader(getClassLoader());
try
{
super.startService();
for (Iterator it = proxyFactories.keySet().iterator(); it.hasNext(); )
{
String invokerBinding = (String)it.next();
EJBProxyFactory ci = (EJBProxyFactory)proxyFactories.get(invokerBinding);
ci.start();
}
instanceCache.start();
persistenceManager.start();
instancePool.start();
Interceptor in = interceptor;
while (in != null)
{
in.start();
in = in.getNext();
}
}
finally
{
SecurityActions.setContextClassLoader(oldCl);
}
}
protected void stopService() throws Exception
{
ClassLoader oldCl = SecurityActions.getContextClassLoader();
SecurityActions.setContextClassLoader(getClassLoader());
try
{
Interceptor in = interceptor;
while (in != null)
{
in.stop();
in = in.getNext();
}
instancePool.stop();
persistenceManager.stop();
instanceCache.stop();
for (Iterator it = proxyFactories.keySet().iterator(); it.hasNext(); )
{
String invokerBinding = (String)it.next();
EJBProxyFactory ci = (EJBProxyFactory)proxyFactories.get(invokerBinding);
ci.stop();
}
super.stopService();
}
finally
{
SecurityActions.setContextClassLoader(oldCl);
}
}
protected void destroyService() throws Exception
{
ClassLoader oldCl = SecurityActions.getContextClassLoader();
SecurityActions.setContextClassLoader(getClassLoader());
try
{
for (Iterator it = proxyFactories.keySet().iterator(); it.hasNext(); )
{
String invokerBinding = (String)it.next();
EJBProxyFactory ci = (EJBProxyFactory)proxyFactories.get(invokerBinding);
ci.destroy();
}
instanceCache.destroy();
instanceCache.setContainer(null);
try
{
ObjectName containerName = super.getJmxName();
Hashtable props = containerName.getKeyPropertyList();
props.put("plugin", "cache");
ObjectName cacheName = new ObjectName(containerName.getDomain(), props);
server.unregisterMBean(cacheName);
}
catch(Throwable ignore)
{
}
persistenceManager.destroy();
persistenceManager.setContainer(null);
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)
{
}
Interceptor in = interceptor;
while (in != null)
{
in.destroy();
in.setContainer(null);
in = in.getNext();
}
MarshalledInvocation.removeHashes(homeInterface);
MarshalledInvocation.removeHashes(remoteInterface);
super.destroyService();
}
finally
{
SecurityActions.setContextClassLoader(oldCl);
}
}
public Object internalInvokeHome(Invocation mi) throws Exception
{
Method method = mi.getMethod();
if (method != null && method.getName().equals("remove"))
{
InvocationType type = mi.getType();
if (type == InvocationType.HOME)
mi.setType(InvocationType.REMOTE);
else if (type == InvocationType.LOCALHOME)
mi.setType(InvocationType.LOCAL);
mi.setMethod(EJBOBJECT_REMOVE);
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();
mi.setId(ejbObject.getPrimaryKey());
}
else
mi.setId(arg);
mi.setArguments(new Object[0]);
return getInterceptor().invoke(mi);
}
return getInterceptor().invokeHome(mi);
}
public Object internalInvoke(Invocation mi) throws Exception
{
return getInterceptor().invoke(mi);
}
public void remove(Invocation mi)
throws RemoteException, RemoveException
{
Transaction tx = mi.getTransaction();
if (!getBeanMetaData().getContainerConfiguration().getSyncOnCommitOnly())
synchronizeEntitiesWithinTransaction(tx);
EntityEnterpriseContext ctx = (EntityEnterpriseContext)mi.getEnterpriseContext();
getPersistenceManager().removeEntity(ctx);
Object pk = ctx.getId();
removeTimerService(pk);
ctx.setId(null);
removeCount++;
}
public Handle getHandle(Invocation mi)
throws RemoteException
{
throw new Error("Not yet implemented");
}
public Object getPrimaryKey(Invocation mi)
throws RemoteException
{
return mi.getId();
}
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();
}
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 EJBLocalHome getEJBLocalHome(Invocation mi)
{
return localProxyFactory.getEJBLocalHome();
}
public void removeLocalHome(Invocation mi)
throws RemoteException, RemoveException
{
throw new Error("Not Yet Implemented");
}
public EJBLocalObject createLocalHome(Invocation mi)
throws Exception
{
final EntityEnterpriseContext ctx = (EntityEnterpriseContext)mi.getEnterpriseContext();
getPersistenceManager().createEntity(mi.getMethod(), mi.getArguments(), ctx);
createCount++;
return localProxyFactory.getEntityEJBLocalObject(ctx.getId(), true);
}
public void postCreateLocalHome(Invocation mi) throws Exception
{
getPersistenceManager().postCreateEntity(mi.getMethod(),mi.getArguments(),
(EntityEnterpriseContext) mi.getEnterpriseContext());
}
public Object findLocal(Invocation mi)
throws Exception
{
Method method = mi.getMethod();
Object[] args = mi.getArguments();
EntityEnterpriseContext instance = (EntityEnterpriseContext)mi.getEnterpriseContext();
boolean syncOnCommitOnly = metaData.getContainerConfiguration().getSyncOnCommitOnly();
Transaction tx = mi.getTransaction();
Class returnType = method.getReturnType();
if (Collection.class.isAssignableFrom(returnType) || returnType == Enumeration.class)
{
if (!syncOnCommitOnly)
{
synchronizeEntitiesWithinTransaction(tx);
}
Collection c = getPersistenceManager().findEntities(method, args, instance, localProxyFactory);
if (returnType == Enumeration.class)
{
return java.util.Collections.enumeration(c);
}
else
{
return c;
}
}
else
{
return findSingleObject(tx, method, args, instance, localProxyFactory);
}
}
public Object find(Invocation mi) throws Exception
{
EJBProxyFactory ci = getProxyFactory();
if (ci == null)
{
String msg = "No ProxyFactory, check for ProxyFactoryFinderInterceptor";
throw new IllegalStateException(msg);
}
Method method = mi.getMethod();
Object[] args = mi.getArguments();
EntityEnterpriseContext instance = (EntityEnterpriseContext)mi.getEnterpriseContext();
boolean syncOnCommitOnly = metaData.getContainerConfiguration().getSyncOnCommitOnly();
Transaction tx = mi.getTransaction();
Class returnType = method.getReturnType();
if (Collection.class.isAssignableFrom(returnType) || returnType == Enumeration.class)
{
if (!syncOnCommitOnly)
{
synchronizeEntitiesWithinTransaction(tx);
}
Collection c = getPersistenceManager().findEntities(method, args, instance, ci);
if (returnType == Enumeration.class)
{
return new SerializableEnumeration(c);
}
else
{
return c;
}
}
else
{
return findSingleObject(tx, method, args, instance, ci);
}
}
public void invokeEjbStore(EntityEnterpriseContext ctx) throws Exception
{
if (ctx.getId() != null)
{
final EntityPersistenceManager pm = getPersistenceManager();
pm.invokeEjbStore(ctx);
}
}
public void storeEntity(EntityEnterpriseContext ctx) throws Exception
{
if (ctx.getId() != null)
{
final EntityPersistenceManager pm = getPersistenceManager();
if(pm.isStoreRequired(ctx))
{
pm.storeEntity(ctx);
}
}
}
public void postCreateHome(Invocation mi) throws Exception
{
getPersistenceManager().postCreateEntity(mi.getMethod(),mi.getArguments(),
(EntityEnterpriseContext) mi.getEnterpriseContext());
}
public EJBObject createHome(Invocation mi)
throws Exception
{
getPersistenceManager().createEntity(mi.getMethod(),mi.getArguments(),
(EntityEnterpriseContext) mi.getEnterpriseContext());
createCount++;
return ((EntityEnterpriseContext)mi.getEnterpriseContext()).getEJBObject();
}
public EJBObject getEJBObject(Invocation mi)
throws RemoteException
{
EJBProxyFactory ci = getProxyFactory();
if (ci == null)
{
String msg = "No ProxyFactory, check for ProxyFactoryFinderInterceptor";
throw new IllegalStateException(msg);
}
return (EJBObject)ci.getEntityEJBObject(((EntityCache) instanceCache).createCacheKey(mi.getId()));
}
public void removeHome(Invocation mi)
throws RemoteException, RemoveException
{
throw new Error("Not yet implemented");
}
public EJBMetaData getEJBMetaDataHome(Invocation mi)
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
{
throw new Error("Not yet implemented");
}
public long getCacheSize()
{
return instanceCache.getCacheSize();
}
public void flushCache()
{
instanceCache.flush();
}
public Map retrieveStatistic()
{
Map lStatistics = new HashMap();
StatisticsProvider lProvider = (StatisticsProvider) getPersistenceManager();
lStatistics.putAll( lProvider.retrieveStatistic() );
lProvider = (StatisticsProvider) getInstancePool();
lStatistics.putAll( lProvider.retrieveStatistic() );
return lStatistics;
}
public void resetStatistic()
{
}
private void setupHomeMappingImpl(Method[] m,
String finderName,
String append)
throws Exception
{
boolean isEJB1x = metaData.getApplicationMetaData().isEJB1x();
for (int i = 0; i < m.length; i++)
{
String methodName = m[i].getName();
try
{
try {
String ejbHomeMethodName = "ejbHome" + methodName.substring(0,1).toUpperCase() + methodName.substring(1);
homeMapping.put(m[i], beanClass.getMethod(ejbHomeMethodName, m[i].getParameterTypes()));
continue;
}
catch (NoSuchMethodException ignore) {}
if (methodName.startsWith("find"))
{
homeMapping.put(m[i], this.getClass().getMethod(finderName, new Class[] { Invocation.class }));
}
else if (methodName.equals("create") ||
(isEJB1x == false && methodName.startsWith("create")))
{
homeMapping.put(m[i], this.getClass().getMethod("create"+append, new Class[] { Invocation.class }));
beanMapping.put(m[i], this.getClass().getMethod("postCreate"+append, new Class[] { Invocation.class }));
}
else
{
homeMapping.put(m[i], this.getClass().getMethod(methodName+append, new Class[] { Invocation.class }));
}
}
catch (NoSuchMethodException e)
{
throw new NoSuchMethodException("Could not find matching method for "+m[i]);
}
}
}
protected void setupHomeMapping() throws Exception
{
try {
if (homeInterface != null)
{
Method[] m = homeInterface.getMethods();
setupHomeMappingImpl( m, "find", "Home" );
}
if (localHomeInterface != null)
{
Method[] m = localHomeInterface.getMethods();
setupHomeMappingImpl( m, "findLocal", "LocalHome" );
}
Class handleClass = Class.forName("javax.ejb.Handle");
Method[] handleMethods = handleClass.getMethods();
for (int j=0; j<handleMethods.length ;j++)
{
if (handleMethods[j].getName().equals("getEJBObject"))
{
homeMapping.put(handleMethods[j],
this.getClass().getMethod("getEJBObject",
new Class[] {Invocation.class}));
}
}
}
catch (Exception e)
{
homeMapping.clear();
beanMapping.clear();
throw e;
}
}
private void setupBeanMappingImpl( Method[] m, String intfName )
throws Exception
{
for (int i = 0; i < m.length; i++)
{
if (!m[i].getDeclaringClass().getName().equals(intfName))
{
beanMapping.put(m[i], beanClass.getMethod(m[i].getName(), m[i].getParameterTypes()));
}
else
{
beanMapping.put(m[i], getClass().getMethod(m[i].getName(),
new Class[] { Invocation.class }));
}
}
}
protected void setupBeanMapping() throws Exception
{
try {
if (remoteInterface != null)
{
Method[] m = remoteInterface.getMethods();
setupBeanMappingImpl( m, "javax.ejb.EJBObject" );
}
if (localInterface != null)
{
Method[] m = localInterface.getMethods();
setupBeanMappingImpl( m, "javax.ejb.EJBLocalObject" );
}
if( TimedObject.class.isAssignableFrom( beanClass ) ) {
beanMapping.put(
TimedObject.class.getMethod( "ejbTimeout", new Class[] { Timer.class } ),
beanClass.getMethod( "ejbTimeout", new Class[] { Timer.class } )
);
}
}
catch (Exception e)
{
homeMapping.clear();
beanMapping.clear();
throw e;
}
}
protected void setupMarshalledInvocationMapping() throws Exception
{
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]);
}
}
Method getEJBObjectMethod = Class.forName("javax.ejb.Handle").getMethod("getEJBObject", new Class[0]);
marshalledInvocationMapping.put(new Long(MarshalledInvocation.calculateHash(getEJBObjectMethod)),getEJBObjectMethod);
}
Interceptor createContainerInterceptor()
{
return new ContainerInterceptor();
}
protected void checkCoherency () throws Exception
{
if (metaData.isClustered())
{
boolean clusteredProxyFactoryFound = false;
for (Iterator it = proxyFactories.keySet().iterator(); 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 ***");
}
}
}
private Object findSingleObject(Transaction tx,
Method method,
Object[] args,
EntityEnterpriseContext instance,
GenericEntityObjectFactory factory)
throws Exception
{
if(method.getName().equals("findByPrimaryKey"))
{
if(args[0] == null)
throw new IllegalArgumentException("findByPrimaryKey called with null argument.");
if(metaData.getContainerConfiguration().getCommitOption() != ConfigurationMetaData.B_COMMIT_OPTION)
{
Object key = instance.getCacheKey();
if(key == null)
{
key = ((EntityCache)instanceCache).createCacheKey(args[0]);
}
if(instanceCache.isActive(key))
{
return factory.getEntityEJBObject(key);
}
}
}
else if(!metaData.getContainerConfiguration().getSyncOnCommitOnly())
{
EntityContainer.synchronizeEntitiesWithinTransaction(tx);
}
return getPersistenceManager().findEntity(method, args, instance, factory);
}
class ContainerInterceptor
extends AbstractContainerInterceptor
{
public Object invokeHome(Invocation mi) throws Exception
{
Method miMethod = mi.getMethod();
Method m = (Method) homeMapping.get(miMethod);
if( m == null )
{
String msg = "Invalid invocation, check your deployment packaging"
+", method="+miMethod;
throw new EJBException(msg);
}
if (m.getDeclaringClass().equals(EntityContainer.class))
{
try
{
return mi.performCall(EntityContainer.this, m, new Object[] { mi });
}
catch (Exception e)
{
rethrow(e);
}
}
else {
EnterpriseContext ctx = (EnterpriseContext) mi.getEnterpriseContext();
try
{
AllowedOperationsAssociation.pushInMethodFlag(AllowedOperationsAssociation.IN_EJB_HOME);
return mi.performCall(ctx.getInstance(), m, mi.getArguments());
}
catch (Exception e)
{
rethrow(e);
}
finally{
AllowedOperationsAssociation.popInMethodFlag();
}
}
throw new org.jboss.util.UnreachableStatementException();
}
public Object invoke(Invocation mi) throws Exception
{
Method miMethod = mi.getMethod();
Method m = (Method) beanMapping.get(miMethod);
if( m == null )
{
String msg = "Invalid invocation, check your deployment packaging"
+", method="+miMethod;
throw new EJBException(msg);
}
if (m.getDeclaringClass().equals(EntityContainer.class))
{
try
{
return mi.performCall(EntityContainer.this, m, new Object[]{ mi });
}
catch (Exception e)
{
rethrow(e);
}
}
else
{
try
{
EnterpriseContext ctx = (EnterpriseContext) mi.getEnterpriseContext();
Object instance = ctx.getInstance();
return mi.performCall(instance, m, mi.getArguments());
}
catch (Exception e)
{
rethrow(e);
}
}
throw new org.jboss.util.UnreachableStatementException();
}
}
}