package org.jboss.ejb.plugins.cmp.jdbc;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.HashSet;
import java.util.List;
import java.util.Iterator;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBException;
import javax.ejb.FinderException;
import javax.ejb.RemoveException;
import javax.transaction.Status;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import org.jboss.deployment.DeploymentException;
import org.jboss.ejb.Container;
import org.jboss.ejb.EjbModule;
import org.jboss.ejb.EntityContainer;
import org.jboss.ejb.EntityEnterpriseContext;
import org.jboss.ejb.GenericEntityObjectFactory;
import org.jboss.ejb.plugins.cmp.ejbql.Catalog;
import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCCMPFieldBridge;
import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCCMRFieldBridge;
import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCEntityBridge;
import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCAbstractEntityBridge;
import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCApplicationMetaData;
import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCEntityMetaData;
import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCXmlFileLoader;
import org.jboss.logging.Logger;
import org.jboss.metadata.ApplicationMetaData;
import org.jboss.tm.TransactionLocal;
public final class JDBCStoreManager implements JDBCEntityPersistenceStore
{
private static final Object TX_DATA_KEY = "TX_DATA_KEY";
private static final String CATALOG = "CATALOG";
private static final String CREATED_MANAGERS = "CREATED_JDBCStoreManagers";
private static final String CMP_JDBC = "CMP-JDBC";
private EjbModule ejbModule;
private EntityContainer container;
private Logger log;
private JDBCEntityMetaData metaData;
private JDBCEntityBridge entityBridge;
private JDBCTypeFactory typeFactory;
private JDBCQueryManager queryManager;
private JDBCCommandFactory commandFactory;
private ReadAheadCache readAheadCache;
private JDBCInitCommand initCommand;
private JDBCStartCommand startCommand;
private JDBCStopCommand stopCommand;
private JDBCDestroyCommand destroyCommand;
private JDBCCreateBeanClassInstanceCommand createBeanClassInstanceCommand;
private JDBCInitEntityCommand initEntityCommand;
private JDBCFindEntityCommand findEntityCommand;
private JDBCFindEntitiesCommand findEntitiesCommand;
private JDBCCreateCommand createEntityCommand;
private JDBCPostCreateEntityCommand postCreateEntityCommand;
private JDBCRemoveEntityCommand removeEntityCommand;
private JDBCLoadEntityCommand loadEntityCommand;
private JDBCIsModifiedCommand isModifiedCommand;
private JDBCStoreEntityCommand storeEntityCommand;
private JDBCActivateEntityCommand activateEntityCommand;
private JDBCPassivateEntityCommand passivateEntityCommand;
private JDBCLoadRelationCommand loadRelationCommand;
private JDBCDeleteRelationsCommand deleteRelationsCommand;
private JDBCInsertRelationsCommand insertRelationsCommand;
private TransactionManager tm;
private TransactionLocal txDataMap;
private TransactionLocal cascadeDeleteSet = new TransactionLocal()
{
protected Object initialValue()
{
return new CascadeDeleteRegistry();
}
};
public EntityContainer getContainer()
{
return container;
}
public void setContainer(Container container)
{
this.container = (EntityContainer)container;
if(container != null)
{
ejbModule = container.getEjbModule();
log = Logger.getLogger(
this.getClass().getName() +
"." +
container.getBeanMetaData().getEjbName());
}
else
{
ejbModule = null;
}
}
public JDBCAbstractEntityBridge getEntityBridge()
{
return entityBridge;
}
public JDBCTypeFactory getJDBCTypeFactory()
{
return typeFactory;
}
public JDBCEntityMetaData getMetaData()
{
return metaData;
}
public JDBCQueryManager getQueryManager()
{
return queryManager;
}
public JDBCCommandFactory getCommandFactory()
{
return commandFactory;
}
public ReadAheadCache getReadAheadCache()
{
return readAheadCache;
}
public Map getApplicationDataMap()
{
return ejbModule.getModuleDataMap();
}
public Object getApplicationData(Object key)
{
return ejbModule.getModuleData(key);
}
public void putApplicationData(Object key, Object value)
{
ejbModule.putModuleData(key, value);
}
private Map getApplicationTxDataMap()
{
try
{
Transaction tx = tm.getTransaction();
if(tx == null)
{
return null;
}
Map txMap = (Map)txDataMap.get(tx);
if(txMap == null)
{
int status = tx.getStatus();
if(status == Status.STATUS_ACTIVE || status == Status.STATUS_PREPARING)
{
txMap = new HashMap();
txDataMap.set(tx, txMap);
}
}
return txMap;
}
catch(EJBException e)
{
throw e;
}
catch(Exception e)
{
throw new EJBException("Error getting application tx data map.", e);
}
}
public void scheduleCascadeDelete(List pks)
{
CascadeDeleteRegistry registry = (CascadeDeleteRegistry)cascadeDeleteSet.get();
registry.scheduleAll(pks);
}
public boolean unscheduledCascadeDelete(Object pk)
{
CascadeDeleteRegistry registry = (CascadeDeleteRegistry)cascadeDeleteSet.get();
return registry.unschedule(pk);
}
public Object getApplicationTxData(Object key)
{
Map map = getApplicationTxDataMap();
if(map != null)
{
return map.get(key);
}
return null;
}
public void putApplicationTxData(Object key, Object value)
{
Map map = getApplicationTxDataMap();
if(map != null)
{
map.put(key, value);
}
}
private Map getEntityTxDataMap()
{
Map entityTxDataMap = (Map)getApplicationTxData(this);
if(entityTxDataMap == null)
{
entityTxDataMap = new HashMap();
putApplicationTxData(this, entityTxDataMap);
}
return entityTxDataMap;
}
public Object getEntityTxData(Object key)
{
return getEntityTxDataMap().get(key);
}
public void putEntityTxData(Object key, Object value)
{
getEntityTxDataMap().put(key, value);
}
public void removeEntityTxData(Object key)
{
getEntityTxDataMap().remove(key);
}
public Catalog getCatalog()
{
return (Catalog)getApplicationData(CATALOG);
}
private void initApplicationDataMap()
{
Map moduleData = ejbModule.getModuleDataMap();
synchronized(moduleData)
{
txDataMap = (TransactionLocal)moduleData.get(TX_DATA_KEY);
if(txDataMap == null)
{
txDataMap = new TransactionLocal();
moduleData.put(TX_DATA_KEY, txDataMap);
}
}
}
public void create() throws Exception
{
HashMap managersMap = (HashMap)getApplicationData(CREATED_MANAGERS);
if(managersMap == null)
{
managersMap = new HashMap();
putApplicationData(CREATED_MANAGERS, managersMap);
}
managersMap.put(container.getBeanMetaData().getEjbName(), this);
}
public void start() throws Exception
{
initStoreManager();
Catalog catalog = getCatalog();
HashMap managersMap = (HashMap)getApplicationData(CREATED_MANAGERS);
if(catalog.getEntityCount() == managersMap.size()
&& catalog.getEJBNames().equals(managersMap.keySet()))
{
ArrayList managers = new ArrayList(managersMap.values());
for(int i = 0; i < managers.size(); ++i)
{
JDBCStoreManager manager = (JDBCStoreManager)managers.get(i);
manager.resolveRelationships();
}
for(int i = 0; i < managers.size(); ++i)
{
JDBCStoreManager manager = (JDBCStoreManager)managers.get(i);
manager.startStoreManager();
}
for(int i = 0; i < managers.size(); ++i)
{
JDBCStoreManager manager = (JDBCStoreManager)managers.get(i);
manager.startCommand.addForeignKeyConstraints();
}
}
}
private void initStoreManager() throws Exception
{
if(log.isDebugEnabled())
log.debug("Initializing CMP plugin for " + container.getBeanMetaData().getEjbName());
tm = container.getTransactionManager();
initApplicationDataMap();
metaData = loadJDBCEntityMetaData();
typeFactory = new JDBCTypeFactory(
metaData.getTypeMapping(),
metaData.getJDBCApplication().getValueClasses(),
metaData.getJDBCApplication().getUserTypeMappings()
);
entityBridge = new JDBCEntityBridge(metaData, this);
entityBridge.init();
Catalog catalog = getCatalog();
if(catalog == null)
{
catalog = new Catalog();
putApplicationData(CATALOG, catalog);
}
catalog.addEntity(entityBridge);
readAheadCache = new ReadAheadCache(this);
readAheadCache.create();
commandFactory = new JDBCCommandFactory(this);
initCommand = commandFactory.createInitCommand();
initCommand.execute();
}
private void resolveRelationships() throws Exception
{
entityBridge.resolveRelationships();
}
private void startStoreManager() throws Exception
{
entityBridge.start();
startCommand = commandFactory.createStartCommand();
stopCommand = commandFactory.createStopCommand();
destroyCommand = commandFactory.createDestroyCommand();
initEntityCommand = commandFactory.createInitEntityCommand();
createBeanClassInstanceCommand = commandFactory.createCreateBeanClassInstanceCommand();
findEntityCommand = commandFactory.createFindEntityCommand();
findEntitiesCommand = commandFactory.createFindEntitiesCommand();
createEntityCommand = commandFactory.createCreateEntityCommand();
postCreateEntityCommand = commandFactory.createPostCreateEntityCommand();
removeEntityCommand = commandFactory.createRemoveEntityCommand();
loadEntityCommand = commandFactory.createLoadEntityCommand();
isModifiedCommand = commandFactory.createIsModifiedCommand();
storeEntityCommand = commandFactory.createStoreEntityCommand();
activateEntityCommand = commandFactory.createActivateEntityCommand();
passivateEntityCommand = commandFactory.createPassivateEntityCommand();
loadRelationCommand = commandFactory.createLoadRelationCommand();
deleteRelationsCommand = commandFactory.createDeleteRelationsCommand();
insertRelationsCommand = commandFactory.createInsertRelationsCommand();
queryManager = new JDBCQueryManager(this);
startCommand.execute();
queryManager.start();
readAheadCache.start();
}
public void stop()
{
if(stopCommand != null)
{
Map managersMap = (HashMap)getApplicationData(CREATED_MANAGERS);
while(!managersMap.isEmpty())
{
int stoppedInIteration = 0;
for(Iterator i = managersMap.values().iterator(); i.hasNext();)
{
JDBCStoreManager manager = (JDBCStoreManager)i.next();
if(manager.stopCommand.execute())
{
i.remove();
++stoppedInIteration;
}
}
if(stoppedInIteration == 0)
{
break;
}
}
}
readAheadCache.stop();
}
public void destroy()
{
if(destroyCommand != null)
{
destroyCommand.execute();
}
if(readAheadCache != null)
{
readAheadCache.destroy();
}
readAheadCache = null;
if(queryManager != null)
{
queryManager.clear();
}
queryManager = null;
if(createBeanClassInstanceCommand != null)
{
createBeanClassInstanceCommand.destroy();
} }
public Object createBeanClassInstance() throws Exception
{
if(createBeanClassInstanceCommand == null)
throw new IllegalStateException("createBeanClassInstanceCommand == null");
return createBeanClassInstanceCommand.execute();
}
public void initEntity(EntityEnterpriseContext ctx)
{
initEntityCommand.execute(ctx);
}
public Object createEntity(Method createMethod, Object[] args, EntityEnterpriseContext ctx)
throws CreateException
{
Object pk = createEntityCommand.execute(createMethod, args, ctx);
if(pk == null)
throw new CreateException("Primary key for created instance is null.");
return pk;
}
public Object postCreateEntity(Method createMethod, Object[] args, EntityEnterpriseContext ctx)
{
return postCreateEntityCommand.execute(createMethod, args, ctx);
}
public Object findEntity(Method finderMethod,
Object[] args,
EntityEnterpriseContext ctx,
GenericEntityObjectFactory factory)
throws FinderException
{
return findEntityCommand.execute(finderMethod, args, ctx, factory);
}
public Collection findEntities(Method finderMethod,
Object[] args,
EntityEnterpriseContext ctx,
GenericEntityObjectFactory factory)
throws FinderException
{
return findEntitiesCommand.execute(finderMethod, args, ctx, factory);
}
public void activateEntity(EntityEnterpriseContext ctx)
{
activateEntityCommand.execute(ctx);
}
public void loadEntity(EntityEnterpriseContext ctx)
{
loadEntity(ctx, true);
}
public boolean loadEntity(EntityEnterpriseContext ctx, boolean failIfNotFound)
{
if(!ctx.isValid())
{
if(log.isTraceEnabled())
{
log.trace("RESET PERSISTENCE CONTEXT: id=" + ctx.getId());
}
entityBridge.resetPersistenceContext(ctx);
}
JDBCEntityBridge.setCreated(ctx);
return loadEntityCommand.execute(ctx, failIfNotFound);
}
public void loadField(JDBCCMPFieldBridge field, EntityEnterpriseContext ctx)
{
loadEntityCommand.execute(field, ctx);
}
public boolean isStoreRequired(EntityEnterpriseContext ctx)
{
return isModifiedCommand.execute(ctx);
}
public boolean isModified(EntityEnterpriseContext ctx)
{
return entityBridge.isModified(ctx);
}
public void storeEntity(EntityEnterpriseContext ctx)
{
storeEntityCommand.execute(ctx);
synchronizeRelationData();
}
private void synchronizeRelationData()
{
final JDBCCMRFieldBridge[] cmrFields = (JDBCCMRFieldBridge[]) entityBridge.getCMRFields();
for(int i = 0; i < cmrFields.length; ++i)
{
final JDBCCMRFieldBridge.RelationDataManager relationManager = cmrFields[i].getRelationDataManager();
if(relationManager.isDirty())
{
final RelationData relationData = relationManager.getRelationData();
deleteRelations(relationData);
insertRelations(relationData);
relationData.addedRelations.clear();
relationData.removedRelations.clear();
relationData.notRelatedPairs.clear();
}
}
}
public void passivateEntity(EntityEnterpriseContext ctx)
{
passivateEntityCommand.execute(ctx);
}
public void removeEntity(EntityEnterpriseContext ctx) throws RemoveException, RemoteException
{
removeEntityCommand.execute(ctx);
}
public Collection loadRelation(JDBCCMRFieldBridge cmrField, Object pk)
{
return loadRelationCommand.execute(cmrField, pk);
}
private void deleteRelations(RelationData relationData)
{
deleteRelationsCommand.execute(relationData);
}
private void insertRelations(RelationData relationData)
{
insertRelationsCommand.execute(relationData);
}
private JDBCEntityMetaData loadJDBCEntityMetaData()
throws DeploymentException
{
ApplicationMetaData amd = container.getBeanMetaData().getApplicationMetaData();
JDBCApplicationMetaData jamd = (JDBCApplicationMetaData)amd.getPluginData(CMP_JDBC);
if(jamd == null)
{
JDBCXmlFileLoader jfl = new JDBCXmlFileLoader(
amd,
container.getClassLoader(),
container.getLocalClassLoader(),
log);
jamd = jfl.load();
amd.addPluginData(CMP_JDBC, jamd);
}
String ejbName = container.getBeanMetaData().getEjbName();
JDBCEntityMetaData metadata = jamd.getBeanByEjbName(ejbName);
if(metadata == null)
{
throw new DeploymentException("No metadata found for bean " + ejbName);
}
return metadata;
}
private final class CascadeDeleteRegistry
{
private Set scheduled;
public void scheduleAll(List pks)
{
if(scheduled == null)
{
scheduled = new HashSet();
}
scheduled.addAll(pks);
}
public boolean unschedule(Object pk)
{
return scheduled.remove(pk);
}
}
}