docs: @Override
      public final synchronized void beforeCompletion()
                      throws RemoteException
      {
          final boolean isTraceOn = TraceComponent.isAnyTracingEnabled();
          if (isTraceOn && tc.isEntryEnabled())
              Tr.entry(tc, "beforeCompletion: " + StateStrs[state]);
  
          if (removed) {
              if (isTraceOn && tc.isEntryEnabled())
                  Tr.exit(tc, "beforeCompletion: removed");
              return;
          }
  
          switch (state)
          {
          // These are the "normal" commit points
              case TX_METHOD_READY:
  
                  setState(COMMITTING_OUTSIDE_METHOD);
  
                  // Session Synchronization BeforeCompletion needs to be called if either
                  // the annotation (or XML) was specified or the bean implemented
                  // the interface and a global transaction is active.        F743-25855
                  if (ivBeforeCompletion != null || sessionSync != null)
                  {
                      ContainerTx tx = container.getActiveContainerTx();
  
                      // SessionSynchronization beforeCompletion is only called for
                      // global transactions that are NOT being rolledback... and
                      // it is skipped if a remove method (@Remove) was called.    390657
                      if ((tx != null) &&
                          (tx.isTransactionGlobal()) &&
                          (!tx.getRollbackOnly()) &&
                          (tx.ivRemoveBeanO != this)) // 390657
                      {
                          EJBThreadData threadData = EJSContainer.getThreadData();
  
                          threadData.pushContexts(this);
                          try
                          {
                              if (ivBeforeCompletion != null) {
                                  invokeSessionSynchMethod(ivBeforeCompletion, null);
                              } else {
                                  sessionSync.beforeCompletion();
                              }
                          } finally
                          {
                              threadData.popContexts();
                          }
                      }
                  }
  
                  break;
  
              // Could happen during async rollback (i.e. a second thread is
              // rolling back the tran, as the current thread is committing it.
              case IN_METHOD:
              case METHOD_READY:
              case AFTER_COMPLETION: // d159152
              case DESTROYED:
                  if (isTraceOn && tc.isEntryEnabled())
                      Tr.exit(tc, "beforeCompletion: asynch rollback: " + getStateName(state));
                  return;
  
              default:
  
                  throw new InvalidBeanOStateException(getStateName(state),
                                  "TX_METHOD_READY | TX_IN_METHOD | " +
                                                  "METHOD_READY | DESTROYED");
  
          }
  
          if (isTraceOn && tc.isEntryEnabled())
              Tr.exit(tc, "beforeCompletion: " + getStateName(state));
      }