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