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