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