/*
* 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);
}
}
}