private final OwnableReentrantLock lock = new OwnableReentrantLock();
@Override
public Object processInvocation(InterceptorContext context) throws Exception {
final EntityBeanComponent component = getComponent(context, EntityBeanComponent.class);
final EntityBeanComponentInstance instance = (EntityBeanComponentInstance) context.getPrivateData(ComponentInstance.class);
//we do not synchronize for instances that are not associated with an identity
if (instance.getPrimaryKey() == null) {
return context.proceed();
}
final TransactionSynchronizationRegistry transactionSynchronizationRegistry = component.getTransactionSynchronizationRegistry();
if (ROOT_LOGGER.isTraceEnabled()) {
ROOT_LOGGER.trace("Trying to acquire lock: " + lock + " for entity bean " + instance + " during invocation: " + context);
}
// we obtain a lock in this synchronization interceptor because the lock needs to be tied to the synchronization
// so that it can released on the tx synchronization callbacks
final Object lockOwner = getLockOwner(transactionSynchronizationRegistry);
lock.pushOwner(lockOwner);
try {
lock.lock();
boolean syncRegistered = false;
synchronized (lock) {
if (ROOT_LOGGER.isTraceEnabled()) {
ROOT_LOGGER.trace("Acquired lock: " + lock + " for entity bean instance: " + instance + " during invocation: " + context);
}
if (context.getPrivateData(InternalInvocationMarker.class) == null) {
if (instance.isRemoved() || instance.isDiscarded()) {
final Object primaryKey = context.getPrivateData(EntityBeanComponent.PRIMARY_KEY_CONTEXT_KEY);
component.getCache().release(instance, true);
lock.unlock();
throw MESSAGES.instanceWasRemoved(component.getComponentName(), primaryKey);
}
}
Object currentTransactionKey = null;
try {
// get the key to current transaction associated with this thread
currentTransactionKey = transactionSynchronizationRegistry.getTransactionKey();
if (!instance.isSynchronizeRegistered()) {
// if this entity instance is already associated with a different transaction, then it's an error
// if the thread is currently associated with a tx, then register a tx synchronization
if (currentTransactionKey != null) {
// register a tx synchronization for this entity instance
final Synchronization entitySynchronization = new EntityBeanSynchronization(instance, lockOwner);
transactionSynchronizationRegistry.registerInterposedSynchronization(entitySynchronization);
syncRegistered = true;
if (ROOT_LOGGER.isTraceEnabled()) {
ROOT_LOGGER.trace("Registered tx synchronization: " + entitySynchronization + " for tx: " + currentTransactionKey +
" associated with stateful component instance: " + instance);
}
}
instance.setSynchronizationRegistered(true);
}
// proceed with the invocation
return context.proceed();
} finally {
// if the current call did *not* register a tx SessionSynchronization, then we have to explicitly mark the
// entity instance as "no longer in use". If it registered a tx EntityBeanSynchronization, then releasing the lock is
// taken care off by a tx synchronization callbacks.
//if is important to note that we increase the lock count on every invocation
//which means we need to release it on every invocation except for the one where we actually registered the TX synchronization
if (currentTransactionKey == null) {
instance.store();
releaseInstance(instance, true);
} else if (!syncRegistered) {
component.getCache().release(instance, true);
lock.unlock();
}