0001: /*
0002: * JBoss, Home of Professional Open Source.
0003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
0004: * as indicated by the @author tags. See the copyright.txt file in the
0005: * distribution for a full listing of individual contributors.
0006: *
0007: * This is free software; you can redistribute it and/or modify it
0008: * under the terms of the GNU Lesser General Public License as
0009: * published by the Free Software Foundation; either version 2.1 of
0010: * the License, or (at your option) any later version.
0011: *
0012: * This software is distributed in the hope that it will be useful,
0013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0015: * Lesser General Public License for more details.
0016: *
0017: * You should have received a copy of the GNU Lesser General Public
0018: * License along with this software; if not, write to the Free
0019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
0020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
0021: */
0022: package org.jboss.ejb;
0023:
0024: import java.lang.reflect.Method;
0025: import java.rmi.RemoteException;
0026: import java.util.Map;
0027: import java.util.HashMap;
0028: import java.util.Iterator;
0029: import java.util.Collection;
0030: import java.util.Hashtable;
0031: import java.util.Enumeration;
0032:
0033: import javax.ejb.Handle;
0034: import javax.ejb.HomeHandle;
0035: import javax.ejb.EJBObject;
0036: import javax.ejb.EJBLocalObject;
0037: import javax.ejb.EJBHome;
0038: import javax.ejb.EJBLocalHome;
0039: import javax.ejb.EJBMetaData;
0040: import javax.ejb.RemoveException;
0041: import javax.ejb.EJBException;
0042: import javax.ejb.TimedObject;
0043: import javax.ejb.Timer;
0044:
0045: import javax.transaction.Transaction;
0046: import javax.management.ObjectName;
0047:
0048: import org.jboss.invocation.Invocation;
0049: import org.jboss.invocation.InvocationType;
0050: import org.jboss.invocation.MarshalledInvocation;
0051: import org.jboss.monitor.StatisticsProvider;
0052: import org.jboss.metadata.EntityMetaData;
0053: import org.jboss.metadata.ConfigurationMetaData;
0054: import org.jboss.util.collection.SerializableEnumeration;
0055:
0056: /**
0057: * This is a Container for EntityBeans (both BMP and CMP).
0058: *
0059: * @see Container
0060: * @see EntityEnterpriseContext
0061: *
0062: * @author <a href="mailto:rickard.oberg@telkel.com">Rickard �berg</a>
0063: * @author <a href="mailto:marc.fleury@telkel.com">Marc Fleury</a>
0064: * @author <a href="mailto:sebastien.alborini@m4x.org">Sebastien Alborini</a>
0065: * @author <a href="mailto:docodan@mvcsoft.com">Daniel OConnor</a>
0066: * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
0067: * @author <a href="mailto:andreas.schaefer@madplanet.com">Andreas Schaefer</a>
0068: * @author <a href="mailto:dain@daingroup.com">Dain Sundstrom</a>
0069: * @version $Revision: 57209 $
0070: *
0071: * @jmx.mbean extends="org.jboss.ejb.ContainerMBean"
0072: */
0073: public class EntityContainer extends Container implements
0074: EJBProxyFactoryContainer, InstancePoolContainer,
0075: EntityContainerMBean {
0076: /**
0077: * These are the mappings between the home interface methods and the
0078: * container methods.
0079: */
0080: protected Map homeMapping = new HashMap();
0081:
0082: /**
0083: * These are the mappings between the remote/local interface methods and the
0084: * bean methods.
0085: */
0086: protected Map beanMapping = new HashMap();
0087:
0088: /** This is the persistence manager for this container */
0089: protected EntityPersistenceManager persistenceManager;
0090:
0091: /** This is the instance cache for this container */
0092: protected InstanceCache instanceCache;
0093:
0094: /** This is the instancepool that is to be used */
0095: protected InstancePool instancePool;
0096:
0097: /**
0098: * This is the first interceptor in the chain. The last interceptor must
0099: * be provided by the container itself.
0100: */
0101: protected Interceptor interceptor;
0102:
0103: /**
0104: * <code>readOnly</code> determines if state can be written to resource manager.
0105: */
0106: protected boolean readOnly = false;
0107:
0108: /**
0109: * This provides a way to find the entities that are part of a given
0110: * transaction EntitySynchronizationInterceptor and InstanceSynchronization
0111: * manage this instance.
0112: */
0113: protected static GlobalTxEntityMap globalTxEntityMap = new GlobalTxEntityMap();
0114:
0115: public static GlobalTxEntityMap getGlobalTxEntityMap() {
0116: return globalTxEntityMap;
0117: }
0118:
0119: /**
0120: * Stores all of the entities associated with the specified transaction.
0121: * As per the spec 9.6.4, entities must be synchronized with the datastore
0122: * when an ejbFind<METHOD> is called.
0123: * Also, all entities within entire transaction should be synchronized before
0124: * a remove, otherwise there may be problems with 'cascade delete'.
0125: *
0126: * @param tx the transaction that associated entites will be stored
0127: */
0128: public static void synchronizeEntitiesWithinTransaction(
0129: Transaction tx) {
0130: // If there is no transaction, there is nothing to synchronize.
0131: if (tx != null) {
0132: getGlobalTxEntityMap().synchronizeEntities(tx);
0133: }
0134: }
0135:
0136: // Public --------------------------------------------------------
0137:
0138: public boolean isReadOnly() {
0139: return readOnly;
0140: }
0141:
0142: public LocalProxyFactory getLocalProxyFactory() {
0143: return localProxyFactory;
0144: }
0145:
0146: public void setInstancePool(InstancePool ip) {
0147: if (ip == null)
0148: throw new IllegalArgumentException("Null pool");
0149:
0150: this .instancePool = ip;
0151: ip.setContainer(this );
0152: }
0153:
0154: public InstancePool getInstancePool() {
0155: return instancePool;
0156: }
0157:
0158: public void setInstanceCache(InstanceCache ic) {
0159: if (ic == null)
0160: throw new IllegalArgumentException("Null cache");
0161:
0162: this .instanceCache = ic;
0163: ic.setContainer(this );
0164: }
0165:
0166: public InstanceCache getInstanceCache() {
0167: return instanceCache;
0168: }
0169:
0170: public EntityPersistenceManager getPersistenceManager() {
0171: return persistenceManager;
0172: }
0173:
0174: public void setPersistenceManager(EntityPersistenceManager pm) {
0175: if (pm == null)
0176: throw new IllegalArgumentException(
0177: "Null persistence manager");
0178:
0179: persistenceManager = pm;
0180: pm.setContainer(this );
0181: }
0182:
0183: public void addInterceptor(Interceptor in) {
0184: if (interceptor == null) {
0185: interceptor = in;
0186: } else {
0187: Interceptor current = interceptor;
0188: while (current.getNext() != null) {
0189: current = current.getNext();
0190: }
0191:
0192: current.setNext(in);
0193: }
0194: }
0195:
0196: public Interceptor getInterceptor() {
0197: return interceptor;
0198: }
0199:
0200: public Class getHomeClass() {
0201: return homeInterface;
0202: }
0203:
0204: public Class getRemoteClass() {
0205: return remoteInterface;
0206: }
0207:
0208: /**
0209: * Returns a new instance of the bean class or a subclass of the bean class.
0210: * If this is 1.x cmp, simply return a new instance of the bean class.
0211: * If this is 2.x cmp, return a subclass that provides an implementation
0212: * of the abstract accessors.
0213: *
0214: * @see java.lang.Class#newInstance
0215: *
0216: * @return The new instance.
0217: */
0218: public Object createBeanClassInstance() throws Exception {
0219: return persistenceManager.createBeanClassInstance();
0220: }
0221:
0222: // Container implementation --------------------------------------
0223:
0224: protected void createService() throws Exception {
0225: // Associate thread with classloader
0226: ClassLoader oldCl = SecurityActions.getContextClassLoader();
0227: SecurityActions.setContextClassLoader(getClassLoader());
0228:
0229: try {
0230: // Acquire classes from CL
0231: if (metaData.getHome() != null)
0232: homeInterface = classLoader.loadClass(metaData
0233: .getHome());
0234: if (metaData.getRemote() != null)
0235: remoteInterface = classLoader.loadClass(metaData
0236: .getRemote());
0237:
0238: // Call default init
0239: super .createService();
0240:
0241: // Make some additional validity checks with regards to the container configuration
0242: checkCoherency();
0243:
0244: // Map the bean methods
0245: setupBeanMapping();
0246:
0247: // Map the home methods
0248: setupHomeMapping();
0249:
0250: // Map the interfaces to Long
0251: setupMarshalledInvocationMapping();
0252:
0253: // Try to register the instance pool as an MBean
0254: try {
0255: ObjectName containerName = super .getJmxName();
0256: Hashtable props = containerName.getKeyPropertyList();
0257: props.put("plugin", "pool");
0258: ObjectName poolName = new ObjectName(containerName
0259: .getDomain(), props);
0260: server.registerMBean(instancePool, poolName);
0261: } catch (Throwable t) {
0262: log.debug("Failed to register cache as mbean", t);
0263: }
0264: // Initialize pool
0265: instancePool.create();
0266:
0267: for (Iterator it = proxyFactories.keySet().iterator(); it
0268: .hasNext();) {
0269: String invokerBinding = (String) it.next();
0270: EJBProxyFactory ci = (EJBProxyFactory) proxyFactories
0271: .get(invokerBinding);
0272: ci.create();
0273: }
0274:
0275: // Try to register the instance cache as an MBean
0276: try {
0277: ObjectName containerName = super .getJmxName();
0278: Hashtable props = containerName.getKeyPropertyList();
0279: props.put("plugin", "cache");
0280: ObjectName cacheName = new ObjectName(containerName
0281: .getDomain(), props);
0282: server.registerMBean(instanceCache, cacheName);
0283: } catch (Throwable t) {
0284: log.debug("Failed to register cache as mbean", t);
0285: }
0286: // Init instance cache
0287: instanceCache.create();
0288:
0289: // Init persistence
0290: persistenceManager.create();
0291:
0292: // Initialize the interceptor by calling the chain
0293: Interceptor in = interceptor;
0294: while (in != null) {
0295: in.setContainer(this );
0296: in.create();
0297: in = in.getNext();
0298: }
0299: readOnly = ((EntityMetaData) metaData).isReadOnly();
0300: } finally {
0301: // Reset classloader
0302: SecurityActions.setContextClassLoader(oldCl);
0303: }
0304: }
0305:
0306: protected void startService() throws Exception {
0307: // Associate thread with classloader
0308: ClassLoader oldCl = SecurityActions.getContextClassLoader();
0309: SecurityActions.setContextClassLoader(getClassLoader());
0310:
0311: try {
0312: // Call default start
0313: super .startService();
0314:
0315: // Start container invokers
0316: for (Iterator it = proxyFactories.keySet().iterator(); it
0317: .hasNext();) {
0318: String invokerBinding = (String) it.next();
0319: EJBProxyFactory ci = (EJBProxyFactory) proxyFactories
0320: .get(invokerBinding);
0321: ci.start();
0322: }
0323:
0324: // Start instance cache
0325: instanceCache.start();
0326:
0327: // Start the instance pool
0328: instancePool.start();
0329:
0330: Interceptor i = interceptor;
0331: while (i != null) {
0332: i.start();
0333: i = i.getNext();
0334: }
0335:
0336: // Restore persisted ejb timers
0337: restoreTimers();
0338: } finally {
0339: // Reset classloader
0340: SecurityActions.setContextClassLoader(oldCl);
0341: }
0342: }
0343:
0344: protected void stopService() throws Exception {
0345: // Associate thread with classloader
0346: ClassLoader oldCl = SecurityActions.getContextClassLoader();
0347: SecurityActions.setContextClassLoader(getClassLoader());
0348:
0349: try {
0350: //Stop items in reverse order from start
0351: //This assures that CachedConnectionInterceptor will get removed
0352: //from in between this and the pm before the pm is stopped.
0353: // Stop all interceptors in the chain
0354: Interceptor in = interceptor;
0355: while (in != null) {
0356: in.stop();
0357: in = in.getNext();
0358: }
0359:
0360: // Stop the instance pool
0361: instancePool.stop();
0362:
0363: // Stop persistence
0364: persistenceManager.stop();
0365:
0366: // Stop instance cache
0367: instanceCache.stop();
0368:
0369: // Stop container invoker
0370: for (Iterator it = proxyFactories.keySet().iterator(); it
0371: .hasNext();) {
0372: String invokerBinding = (String) it.next();
0373: EJBProxyFactory ci = (EJBProxyFactory) proxyFactories
0374: .get(invokerBinding);
0375: ci.stop();
0376: }
0377:
0378: // Call default stop
0379: super .stopService();
0380: } finally {
0381: // Reset classloader
0382: SecurityActions.setContextClassLoader(oldCl);
0383: }
0384: }
0385:
0386: protected void destroyService() throws Exception {
0387: // Associate thread with classloader
0388: ClassLoader oldCl = SecurityActions.getContextClassLoader();
0389: SecurityActions.setContextClassLoader(getClassLoader());
0390:
0391: try {
0392: // Destroy container invoker
0393: for (Iterator it = proxyFactories.keySet().iterator(); it
0394: .hasNext();) {
0395: String invokerBinding = (String) it.next();
0396: EJBProxyFactory ci = (EJBProxyFactory) proxyFactories
0397: .get(invokerBinding);
0398: ci.destroy();
0399: }
0400:
0401: // Destroy instance cache
0402: instanceCache.destroy();
0403: instanceCache.setContainer(null);
0404: try {
0405: ObjectName containerName = super .getJmxName();
0406: Hashtable props = containerName.getKeyPropertyList();
0407: props.put("plugin", "cache");
0408: ObjectName cacheName = new ObjectName(containerName
0409: .getDomain(), props);
0410: server.unregisterMBean(cacheName);
0411: } catch (Throwable ignore) {
0412: }
0413:
0414: // Destroy persistence
0415: persistenceManager.destroy();
0416: persistenceManager.setContainer(null);
0417:
0418: // Destroy the pool
0419: instancePool.destroy();
0420: instancePool.setContainer(null);
0421: try {
0422: ObjectName containerName = super .getJmxName();
0423: Hashtable props = containerName.getKeyPropertyList();
0424: props.put("plugin", "pool");
0425: ObjectName poolName = new ObjectName(containerName
0426: .getDomain(), props);
0427: server.unregisterMBean(poolName);
0428: } catch (Throwable ignore) {
0429: }
0430:
0431: // Destroy all the interceptors in the chain
0432: Interceptor in = interceptor;
0433: while (in != null) {
0434: in.destroy();
0435: in.setContainer(null);
0436: in = in.getNext();
0437: }
0438:
0439: MarshalledInvocation.removeHashes(homeInterface);
0440: MarshalledInvocation.removeHashes(remoteInterface);
0441:
0442: // Call default destroy
0443: super .destroyService();
0444: } finally {
0445: // Reset classloader
0446: SecurityActions.setContextClassLoader(oldCl);
0447: }
0448: }
0449:
0450: public Object internalInvokeHome(Invocation mi) throws Exception {
0451: Method method = mi.getMethod();
0452: if (method != null && method.getName().equals("remove")) {
0453: // Map to EJBHome.remove(Object) to EJBObject.remove()
0454: InvocationType type = mi.getType();
0455: if (type == InvocationType.HOME)
0456: mi.setType(InvocationType.REMOTE);
0457: else if (type == InvocationType.LOCALHOME)
0458: mi.setType(InvocationType.LOCAL);
0459: mi.setMethod(EJBOBJECT_REMOVE);
0460:
0461: // Handle or primary key?
0462: Object arg = mi.getArguments()[0];
0463: if (arg instanceof Handle) {
0464: if (arg == null)
0465: throw new RemoteException("Null handle");
0466: Handle handle = (Handle) arg;
0467: EJBObject ejbObject = handle.getEJBObject();
0468: mi.setId(ejbObject.getPrimaryKey());
0469: } else
0470: mi.setId(arg);
0471:
0472: mi.setArguments(new Object[0]);
0473: return getInterceptor().invoke(mi);
0474: }
0475: return getInterceptor().invokeHome(mi);
0476: }
0477:
0478: public Object internalInvoke(Invocation mi) throws Exception {
0479: // Invoke through interceptors
0480: return getInterceptor().invoke(mi);
0481: }
0482:
0483: // EJBObject implementation --------------------------------------
0484:
0485: public void remove(Invocation mi) throws RemoteException,
0486: RemoveException {
0487: // synchronize entities with the datastore before the bean is removed
0488: // this will write queued updates so datastore will be consistent before removal
0489: Transaction tx = mi.getTransaction();
0490: if (!getBeanMetaData().getContainerConfiguration()
0491: .getSyncOnCommitOnly())
0492: synchronizeEntitiesWithinTransaction(tx);
0493:
0494: // Get the persistence manager to do the dirty work
0495: EntityEnterpriseContext ctx = (EntityEnterpriseContext) mi
0496: .getEnterpriseContext();
0497: getPersistenceManager().removeEntity(ctx);
0498:
0499: Object pk = ctx.getId();
0500: removeTimerService(pk);
0501:
0502: // We signify "removed" with a null id
0503: // There is no need to synchronize on the context since all the threads reaching here have
0504: // gone through the InstanceInterceptor so the instance is locked and we only have one thread
0505: // the case of reentrant threads is unclear (would you want to delete an instance in reentrancy)
0506: ctx.setId(null);
0507: removeCount++;
0508: }
0509:
0510: /**
0511: * @throws Error Not yet implemented.
0512: */
0513: public Handle getHandle(Invocation mi) throws RemoteException {
0514: // TODO
0515: throw new Error("Not yet implemented");
0516: }
0517:
0518: public Object getPrimaryKey(Invocation mi) throws RemoteException {
0519: return mi.getId();
0520: }
0521:
0522: /**
0523: * @throws IllegalStateException If container invoker is null.
0524: */
0525: public EJBHome getEJBHome(Invocation mi) throws RemoteException {
0526: EJBProxyFactory ci = getProxyFactory();
0527: if (ci == null) {
0528: String msg = "No ProxyFactory, check for ProxyFactoryFinderInterceptor";
0529: throw new IllegalStateException(msg);
0530: }
0531: return (EJBHome) ci.getEJBHome();
0532: }
0533:
0534: public boolean isIdentical(Invocation mi) throws RemoteException {
0535: EJBProxyFactory ci = getProxyFactory();
0536: if (ci == null) {
0537: String msg = "No ProxyFactory, check for ProxyFactoryFinderInterceptor";
0538: throw new IllegalStateException(msg);
0539: }
0540:
0541: return ci.isIdentical(this , mi);
0542: }
0543:
0544: /**
0545: * MF FIXME these are implemented on the client
0546: */
0547: public EJBLocalHome getEJBLocalHome(Invocation mi) {
0548: return localProxyFactory.getEJBLocalHome();
0549: }
0550:
0551: /**
0552: * @throws Error Not yet implemented.
0553: */
0554: public void removeLocalHome(Invocation mi) throws RemoteException,
0555: RemoveException {
0556: throw new Error("Not Yet Implemented");
0557: }
0558:
0559: /**
0560: * Local home interface implementation
0561: */
0562: public EJBLocalObject createLocalHome(Invocation mi)
0563: throws Exception {
0564: // The persistence manager takes care of the wiring and creating the EJBLocalObject
0565: final EntityEnterpriseContext ctx = (EntityEnterpriseContext) mi
0566: .getEnterpriseContext();
0567: getPersistenceManager().createEntity(mi.getMethod(),
0568: mi.getArguments(), ctx);
0569:
0570: // The context implicitely carries the EJBObject
0571: createCount++;
0572: return localProxyFactory.getEntityEJBLocalObject(ctx.getId(),
0573: true);
0574: }
0575:
0576: /**
0577: * Delegates to the persistence manager postCreateEntityMethod.
0578: */
0579: public void postCreateLocalHome(Invocation mi) throws Exception {
0580: // The persistence manager takes care of the post create step
0581: getPersistenceManager().postCreateEntity(mi.getMethod(),
0582: mi.getArguments(),
0583: (EntityEnterpriseContext) mi.getEnterpriseContext());
0584: }
0585:
0586: public Object findLocal(Invocation mi) throws Exception {
0587: Method method = mi.getMethod();
0588: Object[] args = mi.getArguments();
0589: EntityEnterpriseContext instance = (EntityEnterpriseContext) mi
0590: .getEnterpriseContext();
0591:
0592: boolean syncOnCommitOnly = metaData.getContainerConfiguration()
0593: .getSyncOnCommitOnly();
0594: Transaction tx = mi.getTransaction();
0595:
0596: Class returnType = method.getReturnType();
0597: if (Collection.class.isAssignableFrom(returnType)
0598: || returnType == Enumeration.class) {
0599: // as per the spec 9.6.4, entities must be synchronized with the datastore when an ejbFind<METHOD> is called.
0600: if (!syncOnCommitOnly) {
0601: synchronizeEntitiesWithinTransaction(tx);
0602: }
0603:
0604: // Iterator finder
0605: Collection c = getPersistenceManager().findEntities(method,
0606: args, instance, localProxyFactory);
0607:
0608: // BMP entity finder methods are allowed to return java.util.Enumeration.
0609: if (returnType == Enumeration.class) {
0610: return java.util.Collections.enumeration(c);
0611: } else {
0612: return c;
0613: }
0614: } else {
0615: return findSingleObject(tx, method, args, instance,
0616: localProxyFactory);
0617: }
0618: }
0619:
0620: // Home interface implementation ---------------------------------
0621:
0622: /**
0623: * This methods finds the target instances by delegating to the persistence
0624: * manager It then manufactures EJBObject for all the involved instances
0625: * found.
0626: */
0627: public Object find(Invocation mi) throws Exception {
0628: EJBProxyFactory ci = getProxyFactory();
0629: if (ci == null) {
0630: String msg = "No ProxyFactory, check for ProxyFactoryFinderInterceptor";
0631: throw new IllegalStateException(msg);
0632: }
0633:
0634: Method method = mi.getMethod();
0635: Object[] args = mi.getArguments();
0636: EntityEnterpriseContext instance = (EntityEnterpriseContext) mi
0637: .getEnterpriseContext();
0638:
0639: boolean syncOnCommitOnly = metaData.getContainerConfiguration()
0640: .getSyncOnCommitOnly();
0641: Transaction tx = mi.getTransaction();
0642:
0643: Class returnType = method.getReturnType();
0644: if (Collection.class.isAssignableFrom(returnType)
0645: || returnType == Enumeration.class) {
0646: // as per the spec 9.6.4, entities must be synchronized with the datastore when an ejbFind<METHOD> is called.
0647: if (!syncOnCommitOnly) {
0648: synchronizeEntitiesWithinTransaction(tx);
0649: }
0650:
0651: // Iterator finder
0652: Collection c = getPersistenceManager().findEntities(method,
0653: args, instance, ci);
0654:
0655: // BMP entity finder methods are allowed to return java.util.Enumeration.
0656: // We need a serializable Enumeration, so we can't use Collections.enumeration()
0657: if (returnType == Enumeration.class) {
0658: return new SerializableEnumeration(c);
0659: } else {
0660: return c;
0661: }
0662: } else {
0663: return findSingleObject(tx, method, args, instance, ci);
0664: }
0665: }
0666:
0667: /**
0668: * Invokes ejbStore method on the instance
0669: * @param ctx the instance to invoke ejbStore on
0670: * @throws Exception
0671: */
0672: public void invokeEjbStore(EntityEnterpriseContext ctx)
0673: throws Exception {
0674: if (ctx.getId() != null) {
0675: final EntityPersistenceManager pm = getPersistenceManager();
0676: pm.invokeEjbStore(ctx);
0677: }
0678: }
0679:
0680: /**
0681: * For CMP actually stores the instance
0682: */
0683: public void storeEntity(EntityEnterpriseContext ctx)
0684: throws Exception {
0685: if (ctx.getId() != null) {
0686: final EntityPersistenceManager pm = getPersistenceManager();
0687: if (pm.isStoreRequired(ctx)) {
0688: pm.storeEntity(ctx);
0689: }
0690: }
0691: }
0692:
0693: /**
0694: * Delegates to the persistence manager postCreateEntityMethod.
0695: */
0696: public void postCreateHome(Invocation mi) throws Exception {
0697: // The persistence manager takes care of the post create step
0698: getPersistenceManager().postCreateEntity(mi.getMethod(),
0699: mi.getArguments(),
0700: (EntityEnterpriseContext) mi.getEnterpriseContext());
0701: }
0702:
0703: /**
0704: * This method takes care of the wiring of the "EJBObject" trio
0705: * (target, context, proxy). It delegates to the persistence manager.
0706: */
0707: public EJBObject createHome(Invocation mi) throws Exception {
0708: // The persistence manager takes care of the wiring and creating the EJBObject
0709: getPersistenceManager().createEntity(mi.getMethod(),
0710: mi.getArguments(),
0711: (EntityEnterpriseContext) mi.getEnterpriseContext());
0712:
0713: // The context implicitely carries the EJBObject
0714: createCount++;
0715: return ((EntityEnterpriseContext) mi.getEnterpriseContext())
0716: .getEJBObject();
0717: }
0718:
0719: /**
0720: * A method for the getEJBObject from the handle
0721: */
0722: public EJBObject getEJBObject(Invocation mi) throws RemoteException {
0723: EJBProxyFactory ci = getProxyFactory();
0724: if (ci == null) {
0725: String msg = "No ProxyFactory, check for ProxyFactoryFinderInterceptor";
0726: throw new IllegalStateException(msg);
0727: }
0728: // All we need is an EJBObject for this Id;
0729: return (EJBObject) ci
0730: .getEntityEJBObject(((EntityCache) instanceCache)
0731: .createCacheKey(mi.getId()));
0732: }
0733:
0734: // EJBHome implementation ----------------------------------------
0735:
0736: /**
0737: * @throws Error Not yet implemented.
0738: */
0739: public void removeHome(Invocation mi) throws RemoteException,
0740: RemoveException {
0741: throw new Error("Not yet implemented");
0742: }
0743:
0744: public EJBMetaData getEJBMetaDataHome(Invocation mi)
0745: throws RemoteException {
0746: EJBProxyFactory ci = getProxyFactory();
0747: if (ci == null) {
0748: String msg = "No ProxyFactory, check for ProxyFactoryFinderInterceptor";
0749: throw new IllegalStateException(msg);
0750: }
0751: return ci.getEJBMetaData();
0752: }
0753:
0754: /**
0755: * @throws Error Not yet implemented.
0756: */
0757: public HomeHandle getHomeHandleHome(Invocation mi)
0758: throws RemoteException {
0759: // TODO
0760: throw new Error("Not yet implemented");
0761: }
0762:
0763: /**
0764: * @jmx.managed-attribute
0765: * @return the current cache size
0766: */
0767: public long getCacheSize() {
0768: return instanceCache.getCacheSize();
0769: }
0770:
0771: /** Flush the cache
0772: * @jmx.managed-operation
0773: */
0774: public void flushCache() {
0775: instanceCache.flush();
0776: }
0777:
0778: // StatisticsProvider implementation ------------------------------------
0779:
0780: public Map retrieveStatistic() {
0781: // Loop through all Interceptors and add statistics
0782: Map lStatistics = new HashMap();
0783: StatisticsProvider lProvider = (StatisticsProvider) getPersistenceManager();
0784: lStatistics.putAll(lProvider.retrieveStatistic());
0785: lProvider = (StatisticsProvider) getInstancePool();
0786: lStatistics.putAll(lProvider.retrieveStatistic());
0787: return lStatistics;
0788: }
0789:
0790: public void resetStatistic() {
0791: }
0792:
0793: // Private -------------------------------------------------------
0794:
0795: private void setupHomeMappingImpl(Method[] m, String finderName,
0796: String append) throws Exception {
0797: // Adrian Brock: This should go away when we don't support EJB1x
0798: boolean isEJB1x = metaData.getApplicationMetaData().isEJB1x();
0799:
0800: for (int i = 0; i < m.length; i++) {
0801: String methodName = m[i].getName();
0802: try {
0803: try // Try home method
0804: {
0805: String ejbHomeMethodName = "ejbHome"
0806: + methodName.substring(0, 1).toUpperCase()
0807: + methodName.substring(1);
0808: homeMapping.put(m[i], beanClass
0809: .getMethod(ejbHomeMethodName, m[i]
0810: .getParameterTypes()));
0811:
0812: continue;
0813: } catch (NoSuchMethodException ignore) {
0814: } // just go on with other types of methods
0815:
0816: // Implemented by container (in both cases)
0817: if (methodName.startsWith("find")) {
0818: homeMapping.put(m[i], this .getClass().getMethod(
0819: finderName,
0820: new Class[] { Invocation.class }));
0821: } else if (methodName.equals("create")
0822: || (isEJB1x == false && methodName
0823: .startsWith("create"))) {
0824: homeMapping.put(m[i], this .getClass().getMethod(
0825: "create" + append,
0826: new Class[] { Invocation.class }));
0827: beanMapping.put(m[i], this .getClass().getMethod(
0828: "postCreate" + append,
0829: new Class[] { Invocation.class }));
0830: } else {
0831: homeMapping.put(m[i], this .getClass().getMethod(
0832: methodName + append,
0833: new Class[] { Invocation.class }));
0834: }
0835: } catch (NoSuchMethodException e) {
0836: throw new NoSuchMethodException(
0837: "Could not find matching method for " + m[i]);
0838: }
0839: }
0840: }
0841:
0842: protected void setupHomeMapping() throws Exception {
0843: try {
0844: if (homeInterface != null) {
0845: Method[] m = homeInterface.getMethods();
0846: setupHomeMappingImpl(m, "find", "Home");
0847: }
0848: if (localHomeInterface != null) {
0849: Method[] m = localHomeInterface.getMethods();
0850: setupHomeMappingImpl(m, "findLocal", "LocalHome");
0851: }
0852:
0853: // Special methods
0854:
0855: // Get the One on Handle (getEJBObject), get the class
0856: Class handleClass = Class.forName("javax.ejb.Handle");
0857:
0858: // Get the methods (there is only one)
0859: Method[] handleMethods = handleClass.getMethods();
0860:
0861: //Just to make sure let's iterate
0862: for (int j = 0; j < handleMethods.length; j++) {
0863: //Get only the one called handle.getEJBObject
0864: if (handleMethods[j].getName().equals("getEJBObject")) {
0865: //Map it in the home stuff
0866: homeMapping.put(handleMethods[j], this .getClass()
0867: .getMethod("getEJBObject",
0868: new Class[] { Invocation.class }));
0869: }
0870: }
0871: } catch (Exception e) {
0872: // ditch the half built mappings
0873: homeMapping.clear();
0874: beanMapping.clear();
0875:
0876: throw e;
0877: }
0878: }
0879:
0880: private void setupBeanMappingImpl(Method[] m, String intfName)
0881: throws Exception {
0882: for (int i = 0; i < m.length; i++) {
0883: if (!m[i].getDeclaringClass().getName().equals(intfName)) {
0884: // Implemented by bean
0885: beanMapping.put(m[i], beanClass.getMethod(m[i]
0886: .getName(), m[i].getParameterTypes()));
0887: } else {
0888: // Implemented by container
0889: beanMapping.put(m[i], getClass().getMethod(
0890: m[i].getName(),
0891: new Class[] { Invocation.class }));
0892: }
0893: }
0894: }
0895:
0896: protected void setupBeanMapping() throws Exception {
0897: try {
0898: if (remoteInterface != null) {
0899: Method[] m = remoteInterface.getMethods();
0900: setupBeanMappingImpl(m, "javax.ejb.EJBObject");
0901: }
0902: if (localInterface != null) {
0903: Method[] m = localInterface.getMethods();
0904: setupBeanMappingImpl(m, "javax.ejb.EJBLocalObject");
0905: }
0906: if (TimedObject.class.isAssignableFrom(beanClass)) {
0907: // Map ejbTimeout
0908: beanMapping.put(TimedObject.class.getMethod(
0909: "ejbTimeout", new Class[] { Timer.class }),
0910: beanClass.getMethod("ejbTimeout",
0911: new Class[] { Timer.class }));
0912: }
0913: } catch (Exception e) {
0914: // ditch the half built mappings
0915: homeMapping.clear();
0916: beanMapping.clear();
0917:
0918: throw e;
0919: }
0920: }
0921:
0922: protected void setupMarshalledInvocationMapping() throws Exception {
0923: // Create method mappings for container invoker
0924: if (homeInterface != null) {
0925: Method[] m = homeInterface.getMethods();
0926: for (int i = 0; i < m.length; i++) {
0927: marshalledInvocationMapping
0928: .put(new Long(MarshalledInvocation
0929: .calculateHash(m[i])), m[i]);
0930: }
0931: }
0932:
0933: if (remoteInterface != null) {
0934: Method[] m = remoteInterface.getMethods();
0935: for (int j = 0; j < m.length; j++) {
0936: marshalledInvocationMapping
0937: .put(new Long(MarshalledInvocation
0938: .calculateHash(m[j])), m[j]);
0939: }
0940: }
0941:
0942: // Get the getEJBObjectMethod
0943: Method getEJBObjectMethod = Class.forName("javax.ejb.Handle")
0944: .getMethod("getEJBObject", new Class[0]);
0945:
0946: // Hash it
0947: marshalledInvocationMapping
0948: .put(new Long(MarshalledInvocation
0949: .calculateHash(getEJBObjectMethod)),
0950: getEJBObjectMethod);
0951: }
0952:
0953: Interceptor createContainerInterceptor() {
0954: return new ContainerInterceptor();
0955: }
0956:
0957: protected void checkCoherency() throws Exception {
0958: // Check clustering cohrency wrt metadata
0959: //
0960: if (metaData.isClustered()) {
0961: boolean clusteredProxyFactoryFound = false;
0962: for (Iterator it = proxyFactories.keySet().iterator(); it
0963: .hasNext();) {
0964: String invokerBinding = (String) it.next();
0965: EJBProxyFactory ci = (EJBProxyFactory) proxyFactories
0966: .get(invokerBinding);
0967: if (ci instanceof org.jboss.proxy.ejb.ClusterProxyFactory)
0968: clusteredProxyFactoryFound = true;
0969: }
0970:
0971: if (!clusteredProxyFactoryFound) {
0972: log
0973: .warn("*** EJB '"
0974: + this .metaData.getEjbName()
0975: + "' deployed as CLUSTERED but not a single clustered-invoker is bound to container ***");
0976: }
0977: }
0978: }
0979:
0980: private Object findSingleObject(Transaction tx, Method method,
0981: Object[] args, EntityEnterpriseContext instance,
0982: GenericEntityObjectFactory factory) throws Exception {
0983: if (method.getName().equals("findByPrimaryKey")) {
0984: if (args[0] == null)
0985: throw new IllegalArgumentException(
0986: "findByPrimaryKey called with null argument.");
0987:
0988: if (metaData.getContainerConfiguration().getCommitOption() != ConfigurationMetaData.B_COMMIT_OPTION) {
0989: Object key = instance.getCacheKey();
0990: if (key == null) {
0991: key = ((EntityCache) instanceCache)
0992: .createCacheKey(args[0]);
0993: }
0994:
0995: if (instanceCache.isActive(key)) {
0996: return factory.getEntityEJBObject(key);
0997: }
0998: }
0999: } else if (!metaData.getContainerConfiguration()
1000: .getSyncOnCommitOnly()) {
1001: EntityContainer.synchronizeEntitiesWithinTransaction(tx);
1002: }
1003:
1004: return getPersistenceManager().findEntity(method, args,
1005: instance, factory);
1006: }
1007:
1008: // Inner classes -------------------------------------------------
1009:
1010: /**
1011: * This is the last step before invocation - all interceptors are done
1012: */
1013: class ContainerInterceptor extends AbstractContainerInterceptor {
1014: public Object invokeHome(Invocation mi) throws Exception {
1015: // Invoke and handle exceptions
1016: Method miMethod = mi.getMethod();
1017: Method m = (Method) homeMapping.get(miMethod);
1018: if (m == null) {
1019: String msg = "Invalid invocation, check your deployment packaging"
1020: + ", method=" + miMethod;
1021: throw new EJBException(msg);
1022: }
1023:
1024: if (m.getDeclaringClass().equals(EntityContainer.class)) {
1025: try {
1026: return mi.performCall(EntityContainer.this , m,
1027: new Object[] { mi });
1028: } catch (Exception e) {
1029: rethrow(e);
1030: }
1031: } else // Home method
1032: {
1033: EnterpriseContext ctx = (EnterpriseContext) mi
1034: .getEnterpriseContext();
1035: try {
1036: AllowedOperationsAssociation
1037: .pushInMethodFlag(AllowedOperationsAssociation.IN_EJB_HOME);
1038: return mi.performCall(ctx.getInstance(), m, mi
1039: .getArguments());
1040: } catch (Exception e) {
1041: rethrow(e);
1042: } finally {
1043: AllowedOperationsAssociation.popInMethodFlag();
1044: }
1045: }
1046:
1047: // We will never get this far, but the compiler does not know that
1048: throw new org.jboss.util.UnreachableStatementException();
1049: }
1050:
1051: public Object invoke(Invocation mi) throws Exception {
1052: // Get method
1053: Method miMethod = mi.getMethod();
1054: Method m = (Method) beanMapping.get(miMethod);
1055: if (m == null) {
1056: String msg = "Invalid invocation, check your deployment packaging"
1057: + ", method=" + miMethod;
1058: throw new EJBException(msg);
1059: }
1060:
1061: // Select instance to invoke (container or bean)
1062: if (m.getDeclaringClass().equals(EntityContainer.class)) {
1063: // Invoke container
1064: try {
1065: return mi.performCall(EntityContainer.this , m,
1066: new Object[] { mi });
1067: } catch (Exception e) {
1068: rethrow(e);
1069: }
1070: } else {
1071: // Invoke bean instance
1072: try {
1073: EnterpriseContext ctx = (EnterpriseContext) mi
1074: .getEnterpriseContext();
1075: Object instance = ctx.getInstance();
1076:
1077: return mi.performCall(instance, m, mi
1078: .getArguments());
1079: } catch (Exception e) {
1080: rethrow(e);
1081: }
1082: }
1083:
1084: // We will never get this far, but the compiler does not know that
1085: throw new org.jboss.util.UnreachableStatementException();
1086: }
1087: }
1088: }
|