docs: public void postInvoke(EJSWrapperBase wrapper, int methodId,
                             EJSDeployedSupport s) throws RemoteException {
          // LIDB2775-23.1
          DispatchEventListenerCookie[] dispatchEventListenerCookies = null;// @MD16426A
          EJBMethodInfoImpl methodInfo = s.methodInfo; // d114406 d154342.4 d173022
          final boolean isTraceOn = TraceComponent.isAnyTracingEnabled(); // d532639.2
  
          // If this is a stateless SessionBean create, run only the bare minimum of
          // collaborators and other processing. This path is encapsulated in a separate
          // method for now, to avoid lots of "if" statements within this method.
          if (methodInfo.isStatelessSessionBean && methodInfo.isHomeCreate) {
              EjbPostInvokeForStatelessCreate(wrapper, methodId, s);
          } else {
              if (isTraceOn) {
                  if (TEEJBInvocationInfo.isTraceEnabled())
                      TEEJBInvocationInfo.tracePostInvokeBegins(s, wrapper); // d161864
  
                  if (tcClntInfo.isDebugEnabled())
                      Tr.debug(tcClntInfo, "postInvoke(" + methodInfo.getMethodName() + ")");
  
                  if (tc.isEntryEnabled())
                      Tr.entry(tc, "EJBpostInvoke(" + methodId + ":" +
                                   methodInfo.getMethodName() + ")");
              }
  
              EJBThreadData threadData = s.ivThreadData; // d646139.1
              BeanId beanId = wrapper.beanId;
              BeanO beanO = null;
              BeanMetaData bmd = null;//92702
  
              // UOWManager.runUnderUOW may have left cached currentTx invalid; reset
              if (s.resetCurrentTx) {
                  if (isTraceOn && tc.isDebugEnabled())
                      Tr.debug(tc, "s.currentTx may be invalid; reset");
                  s.currentTx = getCurrentContainerTx();
              }
  
              // LIDB2775-23.1
              // Do SMF after method recording - // MD16426A - begin
              if (isZOS) {
                  dispatchEventListenerCookies = s.ivDispatchEventListenerCookies;
                  if (dispatchEventListenerCookies != null) {
                      // notify event dispatch listeners -
                      // Note: It is important that the 'afterEjbMethod' notification
                      //       occur as the first event during postInvoke so that this
                      //       signal is delivered as close as possible to the actual return
                      //       from the bean method invocation.
                      this.ivDispatchEventListenerManager.callDispatchEventListeners(DispatchEventListener.AFTER_EJBMETHOD, dispatchEventListenerCookies, methodInfo);// @MD16426A
                  } // @MD16426A
              }
  
              try {
  
                  beanO = s.beanO;//d140003.19
  
                  if (s.exType != ExceptionType.NO_EXCEPTION) { // d113344  //d140003.19
                      //87918.8(2)
                      if (s.exType == ExceptionType.CHECKED_EXCEPTION) { // d113344 //d140003.19
                          if (isTraceOn && tc.isEventEnabled())
                              Tr.event(tc, "Bean method threw exception",
                                       s.getException());//d140003.19
                      } else if (s.exType == ExceptionType.UNCHECKED_EXCEPTION) {
                          if (isTraceOn && tc.isEventEnabled())
                              Tr.event(tc, "Bean method threw unchecked exception",
                                       s.getException()); //d140003.19
                          // d113344
                          if (beanO != null && s.preInvokeException == false) {
                              // This indicates unchecked exception came from the business
                              // method itself, so we want to discard the bean instance.
                              beanO.discard();
                          }
  
                          if (isTraceOn && tc.isEventEnabled())
                              Tr.event(tc, "Bean Discarded as non null");
  
                      }
                  }
  
                  //89188
                  // bmd = (BeanMetaData) internalBeanMetaDataStore.get(wrapper.beanId.getJ2EEName());//89554
                  // d110126
                  // Optimizations to preInvoke/postInvoke processing:
                  // Created specialized case for stateless SessionBean creation
                  // BeanMetaData no longer looked up via hashtable, is now stored in the EJSWrapper
                  // Removed extraneous call to TxCntl.getCurrentTransaction (dead code)
  
                  bmd = wrapper.bmd; // d110126
                  //end 89188
  
                  // Call postInvoke on all of the BeforeActivation Collaborators
                  // that were successfully preInvoked. Note that the order is the
                  // same as preInvoke. Note the ComponentMetaDataCollaborator is
                  // no longer in this list; see below.              d228192 LI3795-56
                  if (ivBeforeActivationCollaborators != null) {
                      for (int i = 0; i < s.ivBeforeActivationPreInvoked; i++) {
                          if (isTraceOn && tc.isDebugEnabled())
                              Tr.debug(tc, "postInvoke : Invoking BeforeActivationCollaborator.postInvoke method on: "
                                           + ivBeforeActivationCollaborators[i].getClass().getName());
  
                          Object cookie = s.ivBeforeActivationCookies == null ? null : s.ivBeforeActivationCookies[i]; // F61004.3
                          notifyPostInvoke(ivBeforeActivationCollaborators[i], s, cookie);
                      }
                  }
  
                  // Call postInvoke on all of the AfterActivation Collaborators
                  // that were successfully preInvoked. Note that the order is the
                  // same as preInvoke.                                        d228192
                  if (ivAfterActivationCollaborators != null) {
                      for (int i = 0; i < s.ivAfterActivationPreInvoked; i++) {
                          if (isTraceOn && tc.isDebugEnabled())
                              Tr.debug(tc, "postInvoke : Invoking AfterActivationCollaborator.postInvoke method on: "
                                           + ivAfterActivationCollaborators[i].getClass().getName());
  
                          Object cookie = s.ivAfterActivationCookies == null ? null : s.ivAfterActivationCookies[i]; // F61004.3
                          notifyPostInvoke(ivAfterActivationCollaborators[i], s, cookie);
                      }
                  }
  
                  if (beanO != null) {
                      beanO.postInvoke(methodId, s);
                  }
  
              } catch (RemoteException ex) {
                  FFDCFilter.processException(ex, CLASS_NAME + ".postInvoke", "2268", new Object[] { this, wrapper, Integer.valueOf(methodId), s });//123338
                  if (isTraceOn && tc.isEventEnabled())
                      Tr.event(tc, "postInvoke failed", ex);
                  throw ex;
              } catch (Throwable ex) {
                  FFDCFilter.processException(ex, CLASS_NAME + ".postInvoke", "2273", new Object[] { this, wrapper, Integer.valueOf(methodId), s });//123338
                  if (isTraceOn && tc.isEventEnabled())
                      Tr.event(tc, "postInvoke failed", ex);
  
                  s.setUncheckedException(ex); //d117817
              } finally {
  
                  // LIDB2775-23.1
                  if (isZOS && dispatchEventListenerCookies != null) {
                      // Before transaction context changes, do SMF end dispatch recording
                      this.ivDispatchEventListenerManager.callDispatchEventListeners(DispatchEventListener.END_DISPATCH, dispatchEventListenerCookies, methodInfo); // @MD16426A
                  }
  
                  final ContainerTx currentTx = s.currentTx;//d140003.19
  
                  try {
                      if (beanO != null) {
                          activator.postInvoke(currentTx, beanO);
                      }
  
                      if (currentTx != null) {
                          currentTx.postInvoke(s);
                      }
  
                      if (s.uowCtrlPreInvoked) {
                          if (s.began && currentTx != null && currentTx.ivPostInvokeContext == null) {
                              // Indicate that afterCompletion should call
                              // postInvokePopCallbackContexts.                 RTC107108
                              // Don't do see if this is a nested EJB call from
                              // beforeCompletion.                              RTC115108
                              currentTx.ivPostInvokeContext = s;
                          }
  
                          uowCtrl.postInvoke(beanId, s.uowCookie, s.exType, methodInfo); // d113344 //d140003.19
                      }
                      // Since 'Lightweight' methods may not be executing the
                      // Transaction Collaborator, the postInvoke processing must
                      // be done here.                                        LI3795-56
                      else if (methodInfo.isLightweightTxCapable && s.currentTx != null) {
                          if (s.isLightweight) {
                              if (s.exType == ExceptionType.UNCHECKED_EXCEPTION) {
                                  // In this scenario, there is always an inherited global
                                  // transaction, and the correct action for all Tx
                                  // Strategies is to mark the tx as rollbackonly and throw
                                  // the CSITransactionRolledbackException, forcing the
                                  // catch block below.
                                  if (isTraceOn && tc.isDebugEnabled())
                                      Tr.debug(tc, "Lightweight:handleException - rollback");
                                  uowCtrl.setRollbackOnly();
                                  throw new CSITransactionRolledbackException("Unexpected Exception from Lightweight EJB method");
                              }
                          } else if (beanO != null &&
                                     bmd.type == InternalConstants.TYPE_STATEFUL_SESSION) {
                              // Simulate transaction commit for the bean.  We do not
                              // need to call ContainerTx.delist because we avoided
                              // ContainerTx.enlist during activation.            F61004.1
                              beanO.beforeCompletion();
                              simulateCommitBean(beanO, currentTx);
                          }
                      }
  
                      // If a remove method was invoked, and the bean instance was
                      // NOT removed during afterCompletion, then the transaction
                      // did not commit.. and thus a RemoveException should be
                      // thrown.  Stateful beans may NOT be removed while still
                      // enlisted in a transaction.  The RemoveException will be
                      // wrapped in the appropriate exception, as it is generally
                      // not on the throws clause for @Remove methods.           390657
                      if (currentTx != null &&
                          currentTx.ivRemoveBeanO != null) {
                          // Per a SUN (CTS) clarification, not removing a SF bean
                          // in a transaction will only be enforced for Bean Managed
                          // beans, since the UserTx is 'sticky' and would be
                          // orphanded if the bean were removed.                 d451675
                          if (bmd.usesBeanManagedTx ||
                              bmd.usesBeanManagedAS) {
                              currentTx.ivRemoveBeanO = null;
                              throw new RemoveException("Cannot remove stateful session bean " +
                                                        "within a transaction.");
                          } else {
                              // For the CMT case, the bean will just be removed from
                              // the transaction and removed.. without calling the
                              // synchronization methods. The following will transition
                              // the bean to the correct state, and remove from the
                              // EJB Cache... deleting the bean.                  d451675
  
                              // d666718 - Delist the bean from the transaction.
                              // Otherwise, we will try to perform redundant processing
                              // on it later when the transaction actually completes.
                              try {
                                  currentTx.delist(beanO);
                              } catch (TransactionRolledbackException ex) {
                                  FFDCFilter.processException(ex, CLASS_NAME + ".postInvoke",
                                                              "4641", this);
                                  if (isTraceOn && tc.isEventEnabled())
                                      Tr.event(tc, "Exception thrown from ContainerTx.delist()",
                                               new Object[] { beanO, ex });
                              }
  
                              simulateCommitBean(beanO, currentTx);
                          }
                      }
  
                  } catch (CSITransactionRolledbackException ex) {
                      FFDCFilter.processException(ex, CLASS_NAME + ".postInvoke",
                                                  "2326", new Object[] { this, wrapper, Integer.valueOf(methodId), s });//123338
                      postInvokeRolledbackException(s, ex);
                  } catch (Throwable t) {
                      FFDCFilter.processException(t, CLASS_NAME + ".postInvoke",
                                                  "2366", this);
                      if (isTraceOn && tc.isDebugEnabled())
                          Tr.debug(tc, "postInvoke: Exception in finally clause. An unexpected case", t);
  
                      if (s.exType == ExceptionType.NO_EXCEPTION) { //PQ94124
                          // An unexpected exception has occured in post processing,
                          // that the application is unaware of.
                          // call setUnCheckedEx and throw an exception.
                          s.setUncheckedException(t); //d117817
                          // s.setUncheckedException throws either runtime (for local I/F) or
                          // remote exception.
                      }
                  } finally {
                      // Bean-specific contexts need to be present for beforeCompletion
                      // for JPA @PreUpdate, so we cannot pop the contexts prior to
                      // transaction completion, but ContainerTx.afterCompletion unpins
                      // beans from the current transaction, which would leave "stale"
                      // contexts in place for subsequent afterCompletion callbacks,
                      // so ContainerTx.afterCompletion pops contexts.  However, we
                      // need to pop contexts ourselves if there was no transaction, or
                      // the transaction wasn't committed (because this method didn't
                      // begin it or it was a sticky BMT), or the transaction was
                      // committed but ContainerTx.afterCompletion wasn't called due to
                      // some fatal error in transactions.                    RTC107108
                      if (currentTx == null) {
                          postInvokePopCallbackContexts(s);
                      } else if (!s.began || currentTx.ivPostInvokeContext != null) {
                          postInvokePopCallbackContexts(s);
  
                          // Reset the indicator so that sticky BMT doesn't pop the
                          // callback context in the middle of a bean method, but only
                          // do so if we set the method context above (don't do so if
                          // this is a nested EJB call from beforeCompletion). RTC115108
                          if (currentTx.ivPostInvokeContext == s) {
                              currentTx.ivPostInvokeContext = null;
                          }
                      }
  
                      // Pmi instrumentation for methods in beans
                      // Use pmiBean cached in the wrapper.                  d140003.33
                      // Insure this is done even if an exception is thrown.    d623908
                      EJBPMICollaborator pmiBean = wrapper.ivPmiBean; // d174057.2
                      if (pmiBean != null && s.pmiPreInvoked) {
                          pmiBean.methodPostInvoke(wrapper.beanId, methodInfo,
                                                   s.pmiCookie);
  
                          if (methodInfo.isHomeCreate()) {
                              // d647928 - SFSB and entity creation always goes through
                              // home wrappers.  The other bean types have code in
                              // EJSHome for create time.
                              if (bmd.isStatefulSessionBean() || bmd.isEntityBean()) {
                                  pmiBean.finalTime(EJBPMICollaborator.CREATE_RT, s.pmiCookie);
                              }
                          } else if (methodInfo.isComponentRemove()) {
                              // d647928 - SFSB and entity can both be removed via the
                              // remove method on the wrapper.  That method is a no-op
                              // for the other bean types.
                              if (bmd.isStatefulSessionBean() || bmd.isEntityBean()) {
                                  pmiBean.finalTime(EJBPMICollaborator.REMOVE_RT, s.pmiCookie);
                              }
                          }
                      }
  
                      // PQ74774 Begins
                      if (s.ivSecurityCollaborator != null) {
                          notifyPostInvoke(s.ivSecurityCollaborator, s, s.securityCookie);
                      }
                      // PQ74774 Ends
  
                      //92702 - BAAC collaborators must be called last of all
                      //the only example we know of is the JNS collaborator
                      if (ivBeforeActivationAfterCompletionCollaborators != null) {
                          // Call postInvoke on all of the BeforeActivationAfterCompletion
                          // Collaborators that were successfully preInvoked. Note that
                          // the order is the same as preInvoke.                    d228192
                          for (int i = 0; i < s.ivBeforeActivationAfterCompletionPreInvoked; i++) {
                              if (isTraceOn && tc.isDebugEnabled())
                                  Tr.debug(tc, "postInokve : Invoking BeforeActivationAfterCompletionCollaborator.postInvoke method on: "
                                               + ivBeforeActivationAfterCompletionCollaborators[i].getClass().getName());
  
                              Object cookie = s.ivBeforeActivationAfterCompletionCookies == null ? null : s.ivBeforeActivationAfterCompletionCookies[i]; // F61004.3
                              notifyPostInvoke(ivBeforeActivationAfterCompletionCollaborators[i], s, cookie);
                          }
                      }
  
                      // Inform EJB method callback that method has completed. This must be
                      // called after the transaction has completed.
                      if (s.ivEJBMethodCallback != null) {
                          // hmmmm, what if exception occurs?  Same problem as if beforeActivationAfterCompletion
                          // collaborators throw an exception prior to this change.
                          s.invocationCallbackPostInvoke(); // d194342.1.1
                      }
  
                      // drop the reference on the wrapper
                      // Not required if the wrapper has already been removed
                      if (s.unpinOnPostInvoke) //d140003.19
                          wrapperManager.postInvoke(wrapper);
  
                      // If preInvoke began the transaction, then the ContainerTx
                      // is no longer in use, and so may be cleared, so just call
                      // releaseResources() to clear all of the fields... making garbage
                      // collection eaiser, and allows things to be garbage collected
                      // even if some other object (like PM) holds a reference to the
                      // ContainerTx for awhile.                     d154342.10 d156606
                      // Note that 'began' for the ContainerTx is not valid at this
                      // point, since the postInvoke call on it restored the 'began'
                      // setting to the previous method state, so use the 'began' flag
                      // in EJSDeployedSupport (valid for current method).      d156688
                      if (s.currentTx != null && s.began) {
                          s.currentTx.releaseResources(); // d215317
                          s.currentTx = null;
                      }
  
                      // 112678.6 d115602-1
                      if (methodInfo.setClassLoader) {
                          EJBThreadData.svThreadContextAccessor.popContextClassLoaderForUnprivileged(s.oldClassLoader);
                      } else {
                          threadData.popORBWrapperClassLoader();
                      }
  
                      //92682
                      threadData.popMethodContext(); // d646139.1, RTC102449
  
                      // d161864 Begins
                      if (isTraceOn) {
                          if (tc.isEntryEnabled())
                              Tr.exit(tc, "EJBpostInvoke(" + methodId + ":" +
                                          methodInfo.getMethodName() + ")" +
                                          ((s.ivException != null) ? ("**** throws " + s.ivException) : ""));
                          if (TEEJBInvocationInfo.isTraceEnabled()) {
                              if (s.ivException == null) {
                                  TEEJBInvocationInfo.tracePostInvokeEnds(s, wrapper);
                              } else {
                                  TEEJBInvocationInfo.tracePostInvokeException(s, wrapper,
                                                                               s.ivException);
                              }
                          }
                      }
                      // d161864 Ends
                  } //end 92702
              }
          }
      }