/* * 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.plugins; import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException; import java.rmi.RemoteException; import java.util.Collection; import java.util.HashMap; import javax.ejb.EntityBean; import javax.ejb.RemoveException; import javax.ejb.EJBException; import org.jboss.ejb.Container; import org.jboss.ejb.EntityContainer; import org.jboss.ejb.EntityPersistenceManager; import org.jboss.ejb.EntityEnterpriseContext; import org.jboss.ejb.EntityCache; import org.jboss.ejb.EntityPersistenceStore; import org.jboss.ejb.AllowedOperationsAssociation; import org.jboss.ejb.GenericEntityObjectFactory; import org.jboss.metadata.ConfigurationMetaData; /** * The CMP Persistence Manager implements the semantics of the CMP * EJB 1.1 call back specification. * <p/> * This Manager works with a "EntityPersistenceStore" that takes care of the * physical storing of instances (JAWS, JDBC O/R, FILE, Object). * * @author <a href="mailto:marc.fleury@telkel.com">Marc Fleury</a> * @author <a href="mailto:danch@nvisia.com">Dan Christopherson</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:andreas.schaefer@madplanet.com">Andreas Schaefer</a> * @author <a href="mailto:dain@daingroup.com">Dain Sundstrom</a> * @author <a href="mailto:alex@jboss.org">Alex Loubyansky</a> * @version $Revision: 81030 $ */ public class CMPPersistenceManager implements EntityPersistenceManager { // Constants ----------------------------------------------------- // Attributes ---------------------------------------------------- EntityContainer con; // Physical persistence implementation EntityPersistenceStore store; HashMap createMethods = new HashMap(); HashMap postCreateMethods = new HashMap(); private boolean insertAfterEjbPostCreate; private boolean ejbStoreForClean; // Static -------------------------------------------------------- // Constructors -------------------------------------------------- // Public -------------------------------------------------------- public void setContainer(Container c) { con = (EntityContainer) c; if(store != null) { store.setContainer(c); } if(con != null) { ConfigurationMetaData configuration = con.getBeanMetaData().getContainerConfiguration(); ejbStoreForClean = configuration.isEjbStoreForClean(); } } /** * Gets the entity persistence store. */ public EntityPersistenceStore getPersistenceStore() { return store; } public void setPersistenceStore(EntityPersistenceStore store) { this.store = store; //Give it the container if(con != null) store.setContainer(con); } public void create() throws Exception { if(con.getHomeClass() != null) { Method[] methods = con.getHomeClass().getMethods(); createMethodCache(methods); } if(con.getLocalHomeClass() != null) { Method[] methods = con.getLocalHomeClass().getMethods(); createMethodCache(methods); } insertAfterEjbPostCreate = con.getBeanMetaData().getContainerConfiguration().isInsertAfterEjbPostCreate(); store.create(); } /** * Returns a new instance of the bean class or a subclass of the bean class. * * @return the new instance */ public Object createBeanClassInstance() throws Exception { return store.createBeanClassInstance(); } private void createMethodCache(Method[] methods) throws NoSuchMethodException { // Create cache of create methods Class beanClass = con.getBeanClass(); for(int i = 0; i < methods.length; i++) { String name = methods[i].getName(); if(name.startsWith("create")) { Class[] types = methods[i].getParameterTypes(); try { String nameSuffix = name.substring(0, 1).toUpperCase() + name.substring(1); Method beanMethod = beanClass.getMethod("ejb" + nameSuffix, types); createMethods.put(methods[i], beanMethod); beanMethod = beanClass.getMethod("ejbPost" + nameSuffix, types); postCreateMethods.put(methods[i], beanMethod); } catch(NoSuchMethodException nsme) { throw new NoSuchMethodException("Can't find ejb[Post]Create in " + beanClass.getName()); } } } } public void start() throws Exception { store.start(); } public void stop() { store.stop(); } public void destroy() { store.destroy(); } public void createEntity(Method m, Object[] args, EntityEnterpriseContext ctx) throws Exception { // Deligate initialization of bean to persistence store store.initEntity(ctx); // Call ejbCreate on the target bean try { AllowedOperationsAssociation.pushInMethodFlag(IN_EJB_CREATE); Method createMethod = (Method) createMethods.get(m); createMethod.invoke(ctx.getInstance(), args); } catch(IllegalAccessException e) { // Throw this as a bean exception...(?) throw new EJBException(e); } catch(InvocationTargetException ite) { Throwable e = ite.getTargetException(); if(e instanceof EJBException) { // Rethrow exception throw (EJBException) e; } else if(e instanceof RuntimeException) { // Wrap runtime exceptions throw new EJBException((Exception) e); } else if(e instanceof Exception) { // Remote, Create, or custom app. exception throw (Exception) e; } else { throw (Error) e; } } finally { AllowedOperationsAssociation.popInMethodFlag(); } // if insertAfterEjbPostCreate == true, this will INSERT entity // otherwise, primary key is extracted from the context and returned Object id; try { AllowedOperationsAssociation.pushInMethodFlag(IN_EJB_CREATE); id = store.createEntity(m, args, ctx); } finally { AllowedOperationsAssociation.popInMethodFlag(); } // Set the key on the target context ctx.setId(id); // Create a new CacheKey Object cacheKey = ((EntityCache) con.getInstanceCache()).createCacheKey(id); // Give it to the context ctx.setCacheKey(cacheKey); } public void postCreateEntity(Method m, Object[] args, EntityEnterpriseContext ctx) throws Exception { // this call should go first as it sets up relationships // for fk fields mapped to pk fields store.postCreateEntity(m, args, ctx); try { AllowedOperationsAssociation.pushInMethodFlag(IN_EJB_POST_CREATE); Method postCreateMethod = (Method) postCreateMethods.get(m); postCreateMethod.invoke(ctx.getInstance(), args); if(insertAfterEjbPostCreate) { store.createEntity(m, args, ctx); } } catch(IllegalAccessException e) { // Throw this as a bean exception...(?) throw new EJBException(e); } catch(InvocationTargetException ite) { Throwable e = ite.getTargetException(); if(e instanceof EJBException) { // Rethrow exception throw (EJBException) e; } else if(e instanceof RuntimeException) { // Wrap runtime exceptions throw new EJBException((Exception) e); } else if(e instanceof Exception) { // Remote, Create, or custom app. exception throw (Exception) e; } else { throw (Error) e; } } finally { AllowedOperationsAssociation.popInMethodFlag(); } } public Object findEntity(Method finderMethod, Object[] args, EntityEnterpriseContext ctx, GenericEntityObjectFactory factory) throws Exception { try { AllowedOperationsAssociation.pushInMethodFlag(IN_EJB_FIND); return store.findEntity(finderMethod, args, ctx, factory); } finally { AllowedOperationsAssociation.popInMethodFlag(); } } /** * find multiple entities */ public Collection findEntities(Method finderMethod, Object[] args, EntityEnterpriseContext ctx, GenericEntityObjectFactory factory) throws Exception { try { // return the finderResults so that the invoker layer can extend this back // giving the client an OO 'cursor' AllowedOperationsAssociation.pushInMethodFlag(IN_EJB_FIND); return store.findEntities(finderMethod, args, ctx, factory); } finally { AllowedOperationsAssociation.popInMethodFlag(); } } /* * activateEntity(EnterpriseContext ctx) * * The method calls the target beans for spec compliant callbacks. * Since these are pure EJB calls it is not obvious that the store should * expose the interfaces. In case of jaws however we found that store specific * contexts could be set in the activateEntity calls and hence a propagation of * the call made sense. The persistence store is called for "extension" purposes. * * @see activateEntity on EntityPersistenceStore.java */ public void activateEntity(EntityEnterpriseContext ctx) throws RemoteException { // Create a new CacheKey Object id = ctx.getId(); Object cacheKey = ((EntityCache) con.getInstanceCache()).createCacheKey(id); // Give it to the context ctx.setCacheKey(cacheKey); try { AllowedOperationsAssociation.pushInMethodFlag(IN_EJB_ACTIVATE); EntityBean eb = (EntityBean) ctx.getInstance(); eb.ejbActivate(); } catch(Exception e) { if(e instanceof RemoteException) { // Rethrow exception throw (RemoteException) e; } else if(e instanceof EJBException) { // Rethrow exception throw (EJBException) e; } else { // Wrap runtime exceptions throw new EJBException((Exception) e); } } finally { AllowedOperationsAssociation.popInMethodFlag(); } // The implementation of the call can be left absolutely empty, the // propagation of the call is just a notification for stores that would // need to know that an instance is being activated store.activateEntity(ctx); } public void loadEntity(EntityEnterpriseContext ctx) throws RemoteException { //long lStart = System.currentTimeMillis(); // Have the store load the fields of the instance store.loadEntity(ctx); //mLoad.add( System.currentTimeMillis() - lStart ); invokeLoad(ctx); } public boolean isStoreRequired(EntityEnterpriseContext ctx) throws Exception { return store.isStoreRequired(ctx); } public boolean isModified(EntityEnterpriseContext ctx) throws Exception { return store.isModified(ctx); } public void storeEntity(EntityEnterpriseContext ctx) throws RemoteException { AllowedOperationsAssociation.pushInMethodFlag(IN_EJB_STORE); try { store.storeEntity(ctx); } finally { AllowedOperationsAssociation.popInMethodFlag(); } } public void invokeEjbStore(EntityEnterpriseContext ctx) throws RemoteException { AllowedOperationsAssociation.pushInMethodFlag(IN_EJB_STORE); try { // if call-ejb-store-for-clean=true then invoke ejbStore first (the last chance to modify the instance) if(ejbStoreForClean) { ejbStore(ctx); } else { // else check whether the instance is dirty and invoke ejbStore only if it is really dirty boolean modified = false; try { modified = isStoreRequired(ctx); } catch(Exception e) { throwRemoteException(e); } if(modified) { ejbStore(ctx); } } } finally { AllowedOperationsAssociation.popInMethodFlag(); } } public void passivateEntity(EntityEnterpriseContext ctx) throws RemoteException { try { AllowedOperationsAssociation.pushInMethodFlag(IN_EJB_PASSIVATE); EntityBean eb = (EntityBean) ctx.getInstance(); eb.ejbPassivate(); } catch(Exception e) { throwRemoteException(e); } finally { AllowedOperationsAssociation.popInMethodFlag(); } store.passivateEntity(ctx); ctx.setEJBObject(null); ctx.setEJBLocalObject(null); } public void removeEntity(EntityEnterpriseContext ctx) throws RemoteException, RemoveException { try { AllowedOperationsAssociation.pushInMethodFlag(IN_EJB_REMOVE); EntityBean eb = (EntityBean) ctx.getInstance(); eb.ejbRemove(); } catch(Exception e) { if(e instanceof RemoveException) { // Rethrow exception throw (RemoveException) e; } else { throwRemoteException(e); } } finally { AllowedOperationsAssociation.popInMethodFlag(); } store.removeEntity(ctx); } protected void invokeLoad(EntityEnterpriseContext ctx) throws RemoteException { try { AllowedOperationsAssociation.pushInMethodFlag(IN_EJB_LOAD); EntityBean eb = (EntityBean) ctx.getInstance(); eb.ejbLoad(); } catch(Exception e) { throwRemoteException(e); } finally { AllowedOperationsAssociation.popInMethodFlag(); } } // Private private void ejbStore(EntityEnterpriseContext ctx) throws RemoteException { try { EntityBean eb = (EntityBean) ctx.getInstance(); eb.ejbStore(); } catch(Exception e) { throwRemoteException(e); } } private void throwRemoteException(Exception e) throws RemoteException { if(e instanceof RemoteException) { // Rethrow exception throw (RemoteException) e; } else if(e instanceof EJBException) { // Rethrow exception throw (EJBException) e; } else { // Wrap runtime exceptions throw new EJBException((Exception) e); } } }