| Method from org.jboss.ejb.plugins.EntitySynchronizationInterceptor Detail: |
public void create() throws Exception {
try
{
ConfigurationMetaData configuration = container.getBeanMetaData().getContainerConfiguration();
commitOption = configuration.getCommitOption();
optionDRefreshRate = configuration.getOptionDRefreshRate();
}
catch(Exception e)
{
log.warn(e.getMessage());
}
}
|
protected Synchronization createSynchronization(Transaction tx,
EntityEnterpriseContext ctx) {
return new InstanceSynchronization(tx, ctx);
}
|
public Container getContainer() {
return container;
}
|
public Object invoke(Invocation mi) throws Exception {
// We are going to work with the context a lot
EntityEnterpriseContext ctx = (EntityEnterpriseContext)mi.getEnterpriseContext();
// The Tx coming as part of the Method Invocation
Transaction tx = mi.getTransaction();
if(log.isTraceEnabled())
log.trace("invoke called for ctx " + ctx + ", tx=" + tx);
if(!ctx.isValid())
{
container.getPersistenceManager().loadEntity(ctx);
ctx.setValid(true);
}
// mark the context as read only if this is a readonly method and the context
// was not already readonly
boolean didSetReadOnly = false;
if(!ctx.isReadOnly() &&
(container.isReadOnly() ||
container.getBeanMetaData().isMethodReadOnly(mi.getMethod())))
{
ctx.setReadOnly(true);
didSetReadOnly = true;
}
// So we can go on with the invocation
// Invocation with a running Transaction
try
{
if(tx != null && tx.getStatus() != Status.STATUS_NO_TRANSACTION)
{
// readonly does not synchronize, lock or belong with transaction.
boolean isReadOnly = container.isReadOnly();
if(isReadOnly == false)
{
Method method = mi.getMethod();
if(method != null)
isReadOnly = container.getBeanMetaData().isMethodReadOnly(method.getName());
}
try
{
if(isReadOnly == false)
{
// register the wrapper with the transaction monitor (but only
// register once). The transaction demarcation will trigger the
// storage operations
register(ctx, tx);
}
//Invoke down the chain
Object retVal = getNext().invoke(mi);
// Register again as a finder in the middle of a method
// will de-register this entity, and then the rest of the method can
// change fields which will never be stored
if(isReadOnly == false)
{
// register the wrapper with the transaction monitor (but only
// register once). The transaction demarcation will trigger the
// storage operations
register(ctx, tx);
}
// return the return value
return retVal;
}
finally
{
// We were read-only and the context wasn't already synchronized, tidyup the cache
if(isReadOnly && ctx.hasTxSynchronization() == false)
{
switch(commitOption)
{
// Keep instance active, but invalidate state
case ConfigurationMetaData.B_COMMIT_OPTION:
// Invalidate state (there might be other points of entry)
ctx.setValid(false);
break;
// Invalidate everything AND Passivate instance
case ConfigurationMetaData.C_COMMIT_OPTION:
try
{
// FIXME: We cannot passivate here, because previous
// interceptors work with the context, in particular
// the re-entrance interceptor is doing lock counting
// Just remove it from the cache
if(ctx.getId() != null)
container.getInstanceCache().remove(ctx.getId());
}
catch(Exception e)
{
log.debug("Exception releasing context", e);
}
break;
}
}
}
}
else
{
// No tx
try
{
Object result = getNext().invoke(mi);
// Store after each invocation -- not on exception though, or removal
// And skip reads too ("get" methods)
if(ctx.getId() != null && !container.isReadOnly())
{
container.invokeEjbStore(ctx);
container.storeEntity(ctx);
}
return result;
}
catch(Exception e)
{
// Exception - force reload on next call
ctx.setValid(false);
throw e;
}
finally
{
switch(commitOption)
{
// Keep instance active, but invalidate state
case ConfigurationMetaData.B_COMMIT_OPTION:
// Invalidate state (there might be other points of entry)
ctx.setValid(false);
break;
// Invalidate everything AND Passivate instance
case ConfigurationMetaData.C_COMMIT_OPTION:
try
{
// Do not call release if getId() is null. This means that
// the entity has been removed from cache.
// release will schedule a passivation and this removed ctx
// could be put back into the cache!
// This is necessary because we have no lock, we
// don't want to return an instance to the pool that is
// being used
if(ctx.getId() != null)
container.getInstanceCache().remove(ctx.getId());
}
catch(Exception e)
{
log.debug("Exception releasing context", e);
}
break;
}
}
}
}
finally
{
// if we marked the context as read only we need to reset it
if(didSetReadOnly)
{
ctx.setReadOnly(false);
}
}
}
|
public Object invokeHome(Invocation mi) throws Exception {
EntityEnterpriseContext ctx = (EntityEnterpriseContext)mi.getEnterpriseContext();
Transaction tx = mi.getTransaction();
Object rtn = getNext().invokeHome(mi);
// An anonymous context was sent in, so if it has an id it is a real instance now
if(ctx.getId() != null)
{
// it doesn't need to be read, but it might have been changed from the db already.
ctx.setValid(true);
if(tx != null)
{
BeanLock lock = container.getLockManager().getLock(ctx.getCacheKey());
try
{
lock.schedule(mi);
register(ctx, tx); // Set tx
lock.endInvocation(mi);
}
finally
{
container.getLockManager().removeLockRef(lock.getId());
}
}
}
return rtn;
}
|
protected void register(EntityEnterpriseContext ctx,
Transaction tx) {
boolean trace = log.isTraceEnabled();
if(trace)
log.trace("register, ctx=" + ctx + ", tx=" + tx);
EntityContainer ctxContainer = null;
try
{
ctxContainer = (EntityContainer)ctx.getContainer();
if(!ctx.hasTxSynchronization())
{
// Create a new synchronization
Synchronization synch = createSynchronization(tx, ctx);
// We want to be notified when the transaction commits
tx.registerSynchronization(synch);
ctx.hasTxSynchronization(true);
}
//mark it dirty in global tx entity map if it is not read only
if(!ctxContainer.isReadOnly())
{
ctx.getTxAssociation().scheduleSync(tx, ctx);
}
}
catch(RollbackException e)
{
// The state in the instance is to be discarded, we force a reload of state
synchronized(ctx)
{
ctx.setValid(false);
ctx.hasTxSynchronization(false);
ctx.setTransaction(null);
ctx.setTxAssociation(GlobalTxEntityMap.NONE);
}
throw new EJBException(e);
}
catch(Throwable t)
{
// If anything goes wrong with the association remove the ctx-tx association
ctx.hasTxSynchronization(false);
ctx.setTxAssociation(GlobalTxEntityMap.NONE);
if(t instanceof RuntimeException)
throw (RuntimeException)t;
else if(t instanceof Error)
throw (Error)t;
else if(t instanceof Exception)
throw new EJBException((Exception)t);
else
throw new NestedRuntimeException(t);
}
}
Register a transaction synchronization callback with a context. |
public void setContainer(Container container) {
this.container = (EntityContainer) container;
}
|
public void start() {
try
{
//start up the validContexts thread if commit option D
if (commitOption == ConfigurationMetaData.D_COMMIT_OPTION)
{
vcr = new ValidContextsRefresher();
LRUEnterpriseContextCachePolicy.tasksTimer.schedule(vcr, optionDRefreshRate, optionDRefreshRate);
log.debug("Scheduled a cache flush every " + optionDRefreshRate/1000 + " seconds");
}
}
catch(Exception e)
{
vcr = null;
log.warn("problem scheduling valid contexts refresher", e);
}
}
|
public void stop() {
if (vcr != null)
{
TimerTask temp = vcr;
vcr = null;
temp.cancel();
}
}
|