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();
      SecurityActions.setContextClassLoader(container.getClassLoader());
      container.pushENC();
      ctx.hasTxSynchronization(false);
      ctx.setTransaction(null);
      try {
        try {
          // If rolled back -> invalidate instance
          if (status != Status.STATUS_ROLLEDBACK) {
            switch (commitOption) {
                // Keep instance cached after tx commit
              case ConfigurationMetaData.A_COMMIT_OPTION:
                throw new IllegalStateException(
                    "Commit option A not allowed with this Interceptor");
                // Keep instance active, but invalidate state
              case ConfigurationMetaData.B_COMMIT_OPTION:
                break;
                // Invalidate everything AND Passivate instance
              case ConfigurationMetaData.C_COMMIT_OPTION:
                break;
              case ConfigurationMetaData.D_COMMIT_OPTION:
                throw new IllegalStateException(
                    "Commit option D not allowed with this Interceptor");
            }
          }
          try {
            if (ctx.getId() != null) container.getPersistenceManager().passivateEntity(ctx);
          } catch (Exception ignored) {
          }
          container.getInstancePool().free(ctx);
        } finally {
          if (trace) log.trace("afterCompletion, clear tx for ctx=" + ctx + ", tx=" + tx);
        }
      } // synchronized(lock)
      finally {
        container.popENC();
        SecurityActions.setContextClassLoader(oldCl);
      }
    }
  /** Register a transaction synchronization callback with a context. */
  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);
    }
  }
    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);
        }
      }
    }