/** * Tries to passivate the instance. If the instance is in use and passivateAfterCommit parameter * is true then the instance will passivated after the transaction commits. Otherwise, the * instance will be passivated later according to the container's commit option and max age. */ protected void tryToPassivate(EnterpriseContext ctx, boolean passivateAfterCommit) { Object id = ctx.getId(); if (id == null) return; BeanLock lock = getContainer().getLockManager().getLock(id); boolean lockedBean = false; try { /* If this is a BeanLockExt only attempt the lock as the call to remove is going to have to acquire the cache lock, but this may already be held since this method is called by passivation policies without the cache lock. This can lead to a deadlock as in the case of a size based eviction during a cache get attempts to lock the bean that has been locked by an age based background thread as seen in bug 987389 on sourceforge. */ if (lock instanceof BeanLockExt) { BeanLockExt lock2 = (BeanLockExt) lock; lockedBean = lock2.attemptSync(); if (lockedBean == false) { unableToPassivateDueToCtxLock(ctx, passivateAfterCommit); return; } } else { // Use the blocking sync lock.sync(); lockedBean = true; } if (canPassivate(ctx)) { try { remove(id); passivate(ctx); freeContext(ctx); } catch (Exception ignored) { log.warn("failed to passivate, id=" + id, ignored); } } else { // Touch the entry to make it MRU synchronized (getCacheLock()) { getCache().get(id); } unableToPassivateDueToCtxLock(ctx, passivateAfterCommit); } } finally { if (lockedBean) lock.releaseSync(); getContainer().getLockManager().removeLockRef(id); } }
public void afterCompletion(int status) { boolean trace = log.isTraceEnabled(); // This is an independent point of entry. We need to make sure the // thread is associated with the right context class loader ClassLoader oldCl = SecurityActions.getContextClassLoader(); boolean setCl = !oldCl.equals(container.getClassLoader()); if (setCl) { SecurityActions.setContextClassLoader(container.getClassLoader()); } container.pushENC(); int commitOption = ctx.isPassivateAfterCommit() ? ConfigurationMetaData.C_COMMIT_OPTION : EntitySynchronizationInterceptor.this.commitOption; lock.sync(); // The context is no longer synchronized on the TX ctx.hasTxSynchronization(false); ctx.setTxAssociation(GlobalTxEntityMap.NONE); ctx.setTransaction(null); try { try { // If rolled back -> invalidate instance if (status == Status.STATUS_ROLLEDBACK) { // remove from the cache container.getInstanceCache().remove(ctx.getCacheKey()); } else { switch (commitOption) { // Keep instance cached after tx commit case ConfigurationMetaData.A_COMMIT_OPTION: case ConfigurationMetaData.D_COMMIT_OPTION: // The state is still valid (only point of access is us) ctx.setValid(true); break; // 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 { // We weren't removed, passivate // Here we own the lock, so we don't try to passivate // we just passivate if (ctx.getId() != null) { container.getInstanceCache().remove(ctx.getId()); container.getPersistenceManager().passivateEntity(ctx); } // If we get this far, we return to the pool container.getInstancePool().free(ctx); } catch (Exception e) { log.debug("Exception releasing context", e); } break; } } } finally { if (trace) log.trace("afterCompletion, clear tx for ctx=" + ctx + ", tx=" + tx); lock.endTransaction(tx); if (trace) log.trace("afterCompletion, sent notify on TxLock for ctx=" + ctx); } } // synchronized(lock) finally { lock.releaseSync(); container.getLockManager().removeLockRef(lock.getId()); container.popENC(); if (setCl) { SecurityActions.setContextClassLoader(oldCl); } } }