docs: public void startModule(EJBModuleMetaDataImpl mmd) throws RuntimeError {
          final boolean isTraceOn = TraceComponent.isAnyTracingEnabled();
          if (isTraceOn && tc.isEntryEnabled())
              Tr.entry(tc, "startModule", mmd.getJ2EEName());
  
          String jarName = null; //294477
          String appJarName = null;
          RuntimeError error = null;
          Object origCL = ThreadContextAccessor.UNCHANGED;
          NameSpaceBinder<?> binder = null;
          boolean postInvokeNeeded = false;
          try {
              // Fire the ModuleMetaData event to the listeners.
              // TODO - why is this here rather than createMetaData?
              if (isTraceOn && tc.isDebugEnabled()) //203449
                  Tr.debug(tc, "Fire Module Metadata created event to listeners for module: " +
                               mmd.getJ2EEName());
              mmd.ivMetaDataDestroyRequired = true; //d505055
              fireMetaDataCreated(mmd);
  
              ModuleInitData mid = mmd.ivInitData; // F743-36113
              mmd.ivInitData = null;
  
              jarName = mmd.getName(); //294477
  
              EJBModuleConfigData moduleConfig = createEJBModuleConfigData(mid);
              preInvokeStartModule(mmd, moduleConfig);
  
              // 621157 - Past this point, ensure we notify the container and
              // collaborators that the module has started, even if an exception
              // occurs.  Otherwise, collaborators get confused when we notify
              // them that a module is stopping without first notifying them that
              // the module has started.
              postInvokeNeeded = true;
  
              // For EJB 2.1 and earlier modules, the Scheduler is only
              // 'started' if EJBDeploy flagged the module as containing
              // Timer objects. For EJB 3.0 and later modules, there is no
              // EJBDeploy, so the Scheduler is started if the customer has
              // configured to use a Timer database other than the default,
              // or if the default database exists (i.e. it might have timers).
              // Otherwise, for EJB 3.0 and later, the Scheduler will be
              // created and started on first use by a Timer bean.      d438133
              if (mid.ivHasTimers == null) {
                  initializeTimerService(true);
              } else if (mid.ivHasTimers) {
                  initializeTimerService(false);
              }
  
              // Create the module namespace binder.                         F69147.2
              binder = createNameSpaceBinder(mmd);
              binder.beginBind();
  
              boolean hasEJB = false;
              for (BeanInitData bid : mid.ivBeans) {
                  // Create the BeanMetaData if it hasn't already been created.
                  BeanMetaData bmd = mmd.ivBeanMetaDatas.get(bid.ivName);
                  if (bmd == null) {
                      bmd = createBeanMetaData(bid, mmd);
                  }
  
                  hasEJB |= bmd.type != InternalConstants.TYPE_MANAGED_BEAN;
  
                  // F743-4950 - If this EJB is a Singleton Session bean, then
                  // add it to the application metadata to finish its processing
                  // when the application finishes its startup processing.
                  if (bmd.isSingletonSessionBean()) {
                      mmd.getEJBApplicationMetaData().addSingleton(bmd, bid.ivStartup, bid.ivDependsOn);
                  }
              }
  
              if (!hasEJB && !mmd.ivManagedBeansOnly) // F743-36113
              {
                  // Error - EJB modules must have at least one bean configured.  Stop application from starting.
                  Tr.error(tc, "NO_BEANS_IN_MODULE_CNTR9269W", jarName);
                  throw new EJBConfigurationException("The " + jarName +
                                                      " Enterprise JavaBeans (EJB) module does not have any enterprise beans configured.");
              }
  
              validateMergedXML(mid); // d680497.1
  
              ivEJBMDOrchestrator.processEJBJarBindings(mid, mmd); // F743-36290.1
  
              Collection<BeanMetaData> bmds = mmd.ivBeanMetaDatas.values();
  
              // d664917.2 - Process all BeanMetaData.  Note that metadata
              // processing must be done using the runtime class loader.
              for (BeanMetaData bmd : bmds) {
                  if (isTraceOn && tc.isDebugEnabled()) {
                      bmd.dump();
                  }
                  validateMergedXML(bmd); // d680497
  
                  // Notify PM that the bean has been installed.          // RTC112791
                  beanInstall(bmd);
  
                  if (!bmd.fullyInitialized) // F91481
                  {
                      initializeBMD(bmd);
                  }
              }
  
              // Switch over from the runtime classloader to the application classloader so that
              // the classes loaded during install will be accessible during the execution of the application.
              origCL = svThreadContextAccessor.pushContextClassLoader(mid.getContextClassLoader()); // F85059
  
              // Start all EJBs unless they're deferred.
              for (BeanMetaData bmd : bmds) {
                  if (!bmd.ivDeferEJBInitialization) {
                      fireMetaDataCreatedAndStartBean(bmd); // d648522, d739043
                  }
              }
  
              // All EJBs have either been started, or their metadata has been
              // sufficiently processed to allow deferred initialization, so make
              // the EJB visible.
              for (BeanMetaData bmd : bmds) {
                  // Add the EJB to HomeOfHomes (remote, serialized refs, etc.).
                  addHome(bmd);
  
                  // Bind non-MDB into JNDI.
                  if (bmd.type != InternalConstants.TYPE_MESSAGE_DRIVEN) {
                      try {
                          bindInterfaces(binder, bmd);
                      } catch (Exception e) {
                          FFDCFilter.processException(e, CLASS_NAME + ".install", "950", this);
                          if (isTraceOn && tc.isDebugEnabled())
                              Tr.debug(tc, "startModule: exception", e);
                          error = new RuntimeError(e); // 119723 set warning to last exception
                      }
                  }
              }
  
              // d608829.1 - If a bean failed to start, then there's no need to
              // finish starting the module.
              if (error == null) {
                  // If any Remote bindings were made (component or business) then an
                  // EJBFactory also needs to be bound to support ejb-link / auto-link
                  // from the client.                                             d440604
                  binder.bindEJBFactory(); // F69147.2
  
                  startMDBs(mid, mmd);
  
                  // d604213 - Modules can be started after the application is running,
                  // so we must create automatic timers at module start and not at
                  // application start.
                  if (mmd.ivAutomaticTimerBeans != null) {
                      // The EJB TimerService does not run in the z/OS CRA.
                      if (!EJSPlatformHelper.isZOSCRA()) {
                          int numPersistentCreated = 0;
                          int numNonPersistentCreated = 0;
                          if (mmd.ivHasNonPersistentAutomaticTimers)
                              numNonPersistentCreated = createNonPersistentAutomaticTimers(mmd.ivJ2EEName.getApplication(), mmd.getName(), mmd.ivAutomaticTimerBeans);
                          if (mmd.ivHasPersistentAutomaticTimers)
                              numPersistentCreated = createPersistentAutomaticTimers(mmd.ivJ2EEName.getApplication(), mmd.getName(), mmd.ivAutomaticTimerBeans);
  
                          Tr.info(tc, "AUTOMATIC_TIMER_CREATION_CNTR0219I",
                                  new Object[] { numPersistentCreated, numNonPersistentCreated, mmd.getName() });
                      }
  
                      mmd.ivAutomaticTimerBeans = null;
                  }
  
                  if (!mmd.ivManagedBeansOnly) {
                      registerMBeans(mid, mmd); //198685
                  }
              }
  
              ivEJBMDOrchestrator.processGeneralizations(moduleConfig, mmd); // F743-21131
  
              postInvokeNeeded = false; // d621157
              postInvokeStartModule(mmd, appJarName); // d621157
  
              mid.unload();
          }
          //d607801: removed catch block
          catch (Throwable t) {
              FFDCFilter.processException(t, CLASS_NAME + ".install", "982", this);
              error = new RuntimeError(t); // 119723
          } finally {
              // The following code was reordered so that the context classloader       @MD20022A
              // is reset after the call to executeBatchedOperation.  On 390,           @MD20022A
              // executeBatchedOperation drives a remote operation to the control       @MD20022A
              // region.  It returns IORs for the bound homes.  For each IOR,           @MD20022A
              // the base JDK ORB calls lookupLocalObject (which 390 overrides)         @MD20022A
              // to determine if the object is local and if so to obtain a stub         @MD20022A
              // for it.  SOV defect 68226 validates that the stub returned by          @MD20022A
              // lookupLocalObject is compatible with the active classloaders.          @MD20022A
              // If the context classloader has been reset, this validation fails       @MD20022A
              // and the JDK ORB tries to load a new compatible stub.  This won't       @MD20022A
              // be able to find the stub class from the EJB application either         @MD20022A
              // and it will end up creating and caching an instance of                 @MD20022A
              // org.omg.stub.javax.ejb._EJBHome_Stub.  In order to avoid this,         @MD20022A
              // the context classloader is reset after executeBatchedOperation.        @MD20022A
  
              if (postInvokeNeeded) { // d621157
                  try {
                      postInvokeStartModule(mmd, appJarName);
                  } catch (Throwable t) {
                      FFDCFilter.processException(t, CLASS_NAME + ".startModule", "761", this);
                  }
              }
  
              if (binder != null) {
                  try {
                      binder.end(); // F69147.2
                  } catch (Throwable t) {
                      if (error == null) {
                          error = new RuntimeError(t);
                      }
                  }
              }
  
              svThreadContextAccessor.popContextClassLoader(origCL); // F85059
  
              if (error != null) {
                  if (isTraceOn && tc.isDebugEnabled())
                      Tr.debug(tc, "startModule: " + error);
  
                  try {
                      mmd.getEJBApplicationMetaData().stoppingModule(mmd); // F743-26072
                      uninstall(mmd, true); // d127220 //d130898
                  } catch (Throwable t) {
                      FFDCFilter.processException(t, CLASS_NAME + ".startModule", "980", this);
                  }
  
                  if (isTraceOn && tc.isEntryEnabled())
                      Tr.exit(tc, "startModule: " + error);
                  throw error; // 118362
              }
  
              if (isTraceOn && tc.isEntryEnabled())
                  Tr.exit(tc, "startModule");
          }
      }