package org.jboss.ejb;
import org.jboss.logging.Logger;
import org.jboss.tm.TransactionLocal;
import javax.ejb.EJBException;
import javax.transaction.RollbackException;
import javax.transaction.Status;
import javax.transaction.Synchronization;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import java.util.ArrayList;
import java.util.List;
public class GlobalTxEntityMap
{
private static final Logger log = Logger.getLogger(GlobalTxEntityMap.class);
private final TransactionLocal txSynch = new TransactionLocal();
public static interface TxAssociation
{
void scheduleSync(Transaction tx, EntityEnterpriseContext instance)
throws SystemException, RollbackException;
void synchronize(Thread thread, Transaction tx, EntityEnterpriseContext instance)
throws Exception;
void invokeEjbStore(Thread thread, EntityEnterpriseContext instance)
throws Exception;
}
public static final TxAssociation NONE = new TxAssociation()
{
public void scheduleSync(Transaction tx, EntityEnterpriseContext instance)
throws SystemException, RollbackException
{
EntityContainer.getGlobalTxEntityMap().associate(tx, instance);
instance.setTxAssociation(SYNC_SCHEDULED);
}
public void synchronize(Thread thread, Transaction tx, EntityEnterpriseContext instance)
{
throw new UnsupportedOperationException();
}
public void invokeEjbStore(Thread thread, EntityEnterpriseContext instance)
{
throw new UnsupportedOperationException();
}
};
public static final TxAssociation SYNC_SCHEDULED = new TxAssociation()
{
public void scheduleSync(Transaction tx, EntityEnterpriseContext instance)
{
}
public void invokeEjbStore(Thread thread, EntityEnterpriseContext instance) throws Exception
{
if(instance.getId() != null)
{
EntityContainer container = (EntityContainer) instance.getContainer();
SecurityActions.setContextClassLoader(thread, container.getClassLoader());
container.invokeEjbStore(instance);
}
}
public void synchronize(Thread thread, Transaction tx, EntityEnterpriseContext instance)
throws Exception
{
if(instance.getId() != null)
{
EntityContainer container = (EntityContainer) instance.getContainer();
SecurityActions.setContextClassLoader(thread, container.getClassLoader());
container.storeEntity(instance);
instance.setTxAssociation(SYNCHRONIZED);
}
}
};
public static final TxAssociation SYNCHRONIZED = new TxAssociation()
{
public void scheduleSync(Transaction tx, EntityEnterpriseContext instance)
{
instance.setTxAssociation(SYNC_SCHEDULED);
}
public void invokeEjbStore(Thread thread, EntityEnterpriseContext instance)
{
}
public void synchronize(Thread thread, Transaction tx, EntityEnterpriseContext instance)
{
}
};
public void synchronizeEntities(Transaction tx)
{
GlobalTxSynchronization globalSync = (GlobalTxSynchronization) txSynch.get(tx);
if(globalSync != null)
{
globalSync.synchronize();
}
}
private void associate(Transaction tx, EntityEnterpriseContext entity)
throws RollbackException, SystemException
{
GlobalTxSynchronization globalSync = (GlobalTxSynchronization) txSynch.get(tx);
if(globalSync == null)
{
globalSync = new GlobalTxSynchronization(tx);
txSynch.set(tx, globalSync);
tx.registerSynchronization(globalSync);
}
globalSync.associate(entity);
}
private class GlobalTxSynchronization implements Synchronization
{
private Transaction tx;
private List instances = new ArrayList();
private boolean synchronizing;
public GlobalTxSynchronization(Transaction tx)
{
this.tx = tx;
}
public void associate(EntityEnterpriseContext ctx)
{
instances.add(ctx);
}
public void synchronize()
{
if(synchronizing || instances.isEmpty())
{
return;
}
synchronizing = true;
Thread currentThread = Thread.currentThread();
ClassLoader oldCl = SecurityActions.getContextClassLoader();
EntityEnterpriseContext instance = null;
try
{
for(int i = 0; i < instances.size(); i++)
{
if(tx.getStatus() == Status.STATUS_MARKED_ROLLBACK)
{
return;
}
instance = (EntityEnterpriseContext) instances.get(i);
instance.getTxAssociation().invokeEjbStore(currentThread, instance);
}
for(int i = 0; i < instances.size(); i++)
{
if(tx.getStatus() == Status.STATUS_MARKED_ROLLBACK)
{
return;
}
instance = (EntityEnterpriseContext) instances.get(i);
instance.getTxAssociation().synchronize(currentThread, tx, instance);
}
}
catch(Exception causeByException)
{
try
{
tx.setRollbackOnly();
}
catch(Exception e)
{
log.warn("Exception while trying to rollback tx: " + tx, e);
}
if(causeByException instanceof EJBException)
{
throw (EJBException) causeByException;
}
throw new EJBException("Exception in store of entity:" +
((instance == null) ? "<null>" : instance.getId().toString()), causeByException);
}
finally
{
SecurityActions.setContextClassLoader(oldCl);
synchronizing = false;
}
}
public void beforeCompletion()
{
if(log.isTraceEnabled())
{
log.trace("beforeCompletion called for tx " + tx);
}
synchronize();
}
public void afterCompletion(int status)
{
}
}
}