/*
* 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.cmp.jdbc2;
import org.jboss.ejb.EntityEnterpriseContext;
import org.jboss.ejb.Container;
import org.jboss.ejb.EntityContainer;
import org.jboss.ejb.EjbModule;
import org.jboss.ejb.GenericEntityObjectFactory;
import org.jboss.ejb.plugins.cmp.jdbc.JDBCTypeFactory;
import org.jboss.ejb.plugins.cmp.jdbc.JDBCEntityPersistenceStore;
import org.jboss.ejb.plugins.cmp.jdbc.JDBCStartCommand;
import org.jboss.ejb.plugins.cmp.jdbc.JDBCStopCommand;
import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCAbstractEntityBridge;
import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCEntityMetaData;
import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCApplicationMetaData;
import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCXmlFileLoader;
import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCEntityCommandMetaData;
import org.jboss.ejb.plugins.cmp.ejbql.Catalog;
import org.jboss.ejb.plugins.cmp.jdbc2.bridge.JDBCEntityBridge2;
import org.jboss.ejb.plugins.cmp.jdbc2.schema.Schema;
import org.jboss.ejb.plugins.cmp.jdbc2.schema.EntityTable;
import org.jboss.logging.Logger;
import org.jboss.deployment.DeploymentException;
import org.jboss.metadata.ApplicationMetaData;
import org.jboss.tm.TransactionLocal;
import javax.ejb.DuplicateKeyException;
import javax.ejb.FinderException;
import javax.ejb.EJBException;
import javax.ejb.CreateException;
import javax.ejb.RemoveException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Iterator;
import java.sql.SQLException;
/**
* @author <a href="mailto:alex@jboss.org">Alexey Loubyansky</a>
* @version <tt>$Revision: 94413 $</tt>
*/
public class JDBCStoreManager2
implements JDBCEntityPersistenceStore
{
private static final String CATALOG = "CATALOG";
private static final String SCHEMA = "SCHEMA";
private static final String CREATED_MANAGERS = "CREATED_JDBCStoreManagers";
private static final String CMP_JDBC = "CMP-JDBC";
private EntityContainer container;
private EjbModule ejbModule;
private Logger log;
private JDBCEntityMetaData metaData;
private JDBCEntityBridge2 entityBridge;
private JDBCTypeFactory typeFactory;
private Schema schema;
private InstanceFactory instanceFactory;
private QueryFactory queryFactory;
private CreateCommand createCmd;
private JDBCStartCommand startCmd;
private JDBCStopCommand stop;
private final TransactionLocal cascadeDeleteRegistry = new TransactionLocal()
{
protected Object initialValue()
{
return new HashMap();
}
};
// Public
public Schema getSchema()
{
schema = (Schema)getApplicationData(SCHEMA);
if(schema == null)
{
schema = new Schema(container.getEjbModule().getServiceName().getCanonicalName());
putApplicationData(SCHEMA, schema);
}
return schema;
}
public Catalog getCatalog()
{
Catalog catalog = (Catalog)getApplicationData(CATALOG);
if(catalog == null)
{
catalog = new Catalog();
putApplicationData(CATALOG, catalog);
}
return catalog;
}
public QueryFactory getQueryFactory()
{
return queryFactory;
}
public boolean registerCascadeDelete(Object key, Object value)
{
Map map = (Map)cascadeDeleteRegistry.get();
return map.put(key, value) == null;
}
public boolean isCascadeDeleted(Object key)
{
Map map = (Map)cascadeDeleteRegistry.get();
return map.containsKey(key);
}
public void unregisterCascadeDelete(Object key)
{
Map map = (Map)cascadeDeleteRegistry.get();
map.remove(key);
}
// ContainerPlugin implementation
public void setContainer(Container con)
{
this.container = (EntityContainer)con;
if(container != null)
{
ejbModule = container.getEjbModule();
log = Logger.getLogger(this.getClass().getName() + "." + container.getBeanMetaData().getEjbName());
}
else
{
// undeploy
ejbModule = null;
}
}
// Service implementation
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();
HashMap managersMap = (HashMap)getApplicationData(CREATED_MANAGERS);
Catalog catalog = getCatalog();
if(catalog.getEntityCount() == managersMap.size() && catalog.getEJBNames().equals(managersMap.keySet()))
{
// Make a copy of the managers (for safty)
List managers = new ArrayList(managersMap.values());
// Start Phase 2: resolve relationships
for(int i = 0; i < managers.size(); ++i)
{
JDBCStoreManager2 manager = (JDBCStoreManager2)managers.get(i);
manager.resolveRelationships();
}
// Start Phase 3: init cmr loaders
for(int i = 0; i < managers.size(); ++i)
{
JDBCStoreManager2 manager = (JDBCStoreManager2)managers.get(i);
manager.startEntity();
}
// Start Phase 4: create tables and compile queries
for(int i = 0; i < managers.size(); ++i)
{
JDBCStoreManager2 manager = (JDBCStoreManager2)managers.get(i);
manager.startStoreManager();
}
// add foreign key constraints
for(int i = 0; i < managers.size(); ++i)
{
JDBCStoreManager2 manager = (JDBCStoreManager2)managers.get(i);
manager.startCmd.addForeignKeyConstraints();
}
}
}
public void stop()
{
if(stop != null)
{
Map managersMap = (HashMap)getApplicationData(CREATED_MANAGERS);
while(!managersMap.isEmpty())
{
int stoppedInIteration = 0;
for(Iterator i = managersMap.values().iterator(); i.hasNext();)
{
JDBCStoreManager2 manager = (JDBCStoreManager2)i.next();
if(manager.stop.execute())
{
i.remove();
try
{
manager.entityBridge.stop();
}
catch(Exception e)
{
log.error("Failed to stop entity bridge.", e);
}
++stoppedInIteration;
}
}
if(stoppedInIteration == 0)
{
break;
}
}
}
}
public void destroy()
{
//To change body of implemented methods use File | Settings | File Templates.
}
// JDBCEntityPersistenceStore implementation
public JDBCAbstractEntityBridge getEntityBridge()
{
return entityBridge;
}
public JDBCEntityMetaData getMetaData()
{
return metaData;
}
public JDBCTypeFactory getJDBCTypeFactory()
{
return typeFactory;
}
public EntityContainer getContainer()
{
return container;
}
public Object getApplicationData(Object key)
{
return ejbModule.getModuleData(key);
}
public void putApplicationData(Object key, Object value)
{
ejbModule.putModuleData(key, value);
}
// EntityPersistenceStore implementation
public Object createBeanClassInstance() throws Exception
{
return instanceFactory.newInstance();
}
public void initEntity(EntityEnterpriseContext ctx)
{
entityBridge.initPersistenceContext(ctx);
entityBridge.initInstance(ctx);
}
public Object createEntity(Method m, Object[] args, EntityEnterpriseContext ctx)
throws CreateException
{
/*
Object pk;
PersistentContext pctx = (PersistentContext) ctx.getPersistenceContext();
if(ctx.getId() == null)
{
pk = entityBridge.extractPrimaryKeyFromInstance(ctx);
if(pk == null)
{
throw new CreateException("Primary key for created instance is null.");
}
pctx.setPk(pk);
}
else
{
// insert-after-ejb-post-create
try
{
pctx.flush();
}
catch(SQLException e)
{
if("23000".equals(e.getSQLState()))
{
throw new DuplicateKeyException("Unique key violation or invalid foreign key value: pk=" + ctx.getId());
}
else
{
throw new CreateException("Failed to create instance: pk=" + ctx.getId() +
", state=" + e.getSQLState() +
", msg=" + e.getMessage());
}
}
pk = ctx.getId();
}
return pk;
*/
return createCmd.execute(m, args, ctx);
}
public Object postCreateEntity(Method m, Object[] args, EntityEnterpriseContext ctx) throws CreateException
{
return null;
}
public Object findEntity(Method finderMethod,
Object[] args,
EntityEnterpriseContext instance,
GenericEntityObjectFactory factory)
throws FinderException
{
QueryCommand query = queryFactory.getQueryCommand(finderMethod);
return query.fetchOne(schema, factory, args);
}
public Collection findEntities(Method finderMethod,
Object[] args,
EntityEnterpriseContext instance,
GenericEntityObjectFactory factory)
throws FinderException
{
QueryCommand query = queryFactory.getQueryCommand(finderMethod);
return query.fetchCollection(schema, factory, args);
}
public void activateEntity(EntityEnterpriseContext ctx)
{
entityBridge.initPersistenceContext(ctx);
}
public void loadEntity(EntityEnterpriseContext ctx)
{
try
{
EntityTable.Row row = entityBridge.getTable().loadRow(ctx.getId());
PersistentContext pctx = new PersistentContext(entityBridge, row);
ctx.setPersistenceContext(pctx);
}
catch(EJBException e)
{
log.error("Failed to load instance of " + entityBridge.getEntityName() + " with pk=" + ctx.getId(), e);
throw e;
}
catch(Exception e)
{
throw new EJBException(
"Failed to load instance of " + entityBridge.getEntityName() + " with pk=" + ctx.getId(), e
);
}
}
public boolean isStoreRequired(EntityEnterpriseContext instance)
{
return entityBridge.isStoreRequired(instance);
}
public boolean isModified(EntityEnterpriseContext instance) throws Exception
{
return entityBridge.isModified(instance);
}
public void storeEntity(EntityEnterpriseContext instance)
{
// scary?
}
public void passivateEntity(EntityEnterpriseContext ctx)
{
JDBCEntityBridge2.destroyPersistenceContext(ctx);
}
public void removeEntity(EntityEnterpriseContext ctx) throws RemoveException
{
entityBridge.remove(ctx);
PersistentContext pctx = (PersistentContext)ctx.getPersistenceContext();
pctx.remove();
}
// Private
protected void initStoreManager() throws Exception
{
if(log.isDebugEnabled())
{
log.debug("Initializing CMP plugin for " + container.getBeanMetaData().getEjbName());
}
metaData = loadJDBCEntityMetaData();
// setup the type factory, which is used to map java types to sql types.
typeFactory = new JDBCTypeFactory(metaData.getTypeMapping(),
metaData.getJDBCApplication().getValueClasses(),
metaData.getJDBCApplication().getUserTypeMappings()
);
entityBridge = new JDBCEntityBridge2(this, metaData);
entityBridge.init();
Catalog catalog = getCatalog();
catalog.addEntity(entityBridge);
stop = new JDBCStopCommand(this);
}
private void resolveRelationships() throws Exception
{
entityBridge.resolveRelationships();
}
protected void startStoreManager() throws Exception
{
queryFactory = new QueryFactory(entityBridge);
queryFactory.init();
instanceFactory = new InstanceFactory(this, entityBridge);
startCmd = new JDBCStartCommand(this);
startCmd.execute();
final JDBCEntityCommandMetaData entityCommand = getMetaData().getEntityCommand();
if(entityCommand == null || "default".equals(entityCommand.getCommandName()))
{
createCmd = new ApplicationPkCreateCommand();
}
else
{
final Class cmdClass = entityCommand.getCommandClass();
if(cmdClass == null)
{
throw new DeploymentException(
"entity-command class name is not specified for entity " + entityBridge.getEntityName()
);
}
try
{
createCmd = (CreateCommand)cmdClass.newInstance();
}
catch(ClassCastException cce)
{
throw new DeploymentException("Entity command " + cmdClass + " does not implement " + CreateCommand.class);
}
}
createCmd.init(this);
}
private void startEntity()
throws DeploymentException
{
entityBridge.start();
}
private JDBCEntityMetaData loadJDBCEntityMetaData()
throws DeploymentException
{
ApplicationMetaData amd = container.getBeanMetaData().getApplicationMetaData();
// Get JDBC MetaData
JDBCApplicationMetaData jamd = (JDBCApplicationMetaData)amd.getPluginData(CMP_JDBC);
if(jamd == null)
{
// we are the first cmp entity to need jbosscmp-jdbc.
// Load jbosscmp-jdbc.xml for the whole application
JDBCXmlFileLoader jfl = new JDBCXmlFileLoader(container, log);
jamd = jfl.load();
amd.addPluginData(CMP_JDBC, jamd);
}
// Get JDBC Bean MetaData
String ejbName = container.getBeanMetaData().getEjbName();
JDBCEntityMetaData metadata = jamd.getBeanByEjbName(ejbName);
if(metadata == null)
{
throw new DeploymentException("No metadata found for bean " + ejbName);
}
return metadata;
}
}