docs: protected int replayPartnerLog() throws Exception {
          if (tc.isEntryEnabled())
              Tr.entry(tc, "replayPartnerLog");
  
          int result = 0;
          int recoveredServiceItems = 0;
          int records = 0;
  
          //
          // Replay all of the records and build the PartnerLogData table.  One record should be the service data
          // and contains the Classpath, server state, server name, applId and epoch for the server.
          //
          LogCursor recoverableUnits = null;
          LogCursor recoverableUnitSections = null;
  
          // We have two logical views of the same log on z/OS.  On the first
          // restart the service data might be in the main log, while on
          // subsequent restarts it will be in the restart log.  We need to
          // iterate over both of them, just in case.
          final ArrayList<RecoveryLog> logs = new ArrayList<RecoveryLog>(2);
          if (_recoverXaLog != null) {
              logs.add(_recoverXaLog);
          }
          logs.add(_xaLog);
  
          final Iterator logIterator = logs.iterator();
  
          boolean shuttingDown = false;
  
          try {
              while (logIterator.hasNext() && !shuttingDown) {
                  final RecoveryLog currentLog = (RecoveryLog) logIterator.next();
                  recoverableUnits = currentLog.recoverableUnits();
  
                  while (recoverableUnits.hasNext() && !shuttingDown) {
                      records++;
                      final RecoverableUnit ru = (RecoverableUnit) recoverableUnits.next();
                      final long id = ru.identity();
                      if (tc.isEventEnabled())
                          Tr.event(tc, "Replaying record " + id + " from the partner log");
                      byte[] xaLogData = null;
                      int xaPriority = JTAResource.DEFAULT_COMMIT_PRIORITY;
  
                      recoverableUnitSections = ru.sections();
                      while (recoverableUnitSections.hasNext()) {
                          final RecoverableUnitSection rus = (RecoverableUnitSection) recoverableUnitSections.next();
                          final int rusId = rus.identity();
                          final byte[] logData = rus.lastData(); // Only ever have 1 data item per Partner log sections
                          if (tc.isEventEnabled())
                              Tr.event(tc, "Replaying section " + rusId, Util.toHexString(logData));
                          switch (rusId) {
                              //
                              // The XAResource and WSCoordinator sections exist in single recoverable units
                              // so they can be individually accessed by recoverable unit id from the tranlog
                              //
                              case TransactionImpl.XARESOURCEDATA_SECTION:
                                  if (tc.isDebugEnabled())
                                      Tr.debug(tc, "XA resources data record");
                                  if (logData != null) {
                                      // On z/OS, we cannot add entries from the restart
                                      // log to the table, because these entries are for
                                      // a previous epoch, and entries from this epoch
                                      // would be able to find and use them.
                                      if (currentLog != _recoverXaLog) /* @MD18134A */
                                      { /* @MD18134A */
                                          xaLogData = logData; // Save until we find an optional priority
                                          if (tc.isDebugEnabled())
                                              Tr.debug(tc, "XA resource", xaLogData);
                                      }
                                  }
                                  break;
                              case TransactionImpl.RESOURCE_PRIORITY_SECTION:
                                  if (tc.isDebugEnabled())
                                      Tr.debug(tc, "Resources priority record");
                                  if (logData != null && logData.length == 4) {
                                      xaPriority = Util.getIntFromBytes(logData, 0, 4);
                                      if (tc.isDebugEnabled())
                                          Tr.debug(tc, "Resources priority", xaPriority);
                                  } else {
                                      if (tc.isEventEnabled())
                                          Tr.event(tc, "Invalid RESOURCE_PRIORITY_SECTION found");
                                      throw new IOException(_xaLog.logProperties().logName() + " corrupted");
                                  }
                                  break;
                              case TransactionImpl.WSCOORDINATOR_SECTION:
                                  handleWSCSection(logData, id);
                                  break;
                              case TransactionImpl.JCAPROVIDER_SECTION:
                                  if (tc.isDebugEnabled())
                                      Tr.debug(tc, "JCA provider data record");
                                  if (logData != null) {
                                      final JCARecoveryData jcard = new JCARecoveryData(this, logData, id);
                                      getPartnerLogTable().addEntry(jcard);
                                  }
                                  break;
                              //
                              // The following sections should all exist in the same RecoverableUnit and only
                              // one of each should exist in the partner log.
                              //
                              case TransactionImpl.CLASSPATH_SECTION:
                                  handleClasspathSection(ru, logData, rus);
                                  break;
                              case TransactionImpl.SERVER_STATE_SECTION:
                                  if (tc.isDebugEnabled())
                                      Tr.debug(tc, "Server state data record");
                                  if (_stateSection != null) {
                                      if (tc.isEventEnabled())
                                          Tr.event(tc, "Multiple log SERVER_STATE_SECTIONs found");
                                      Tr.warning(tc, "WTRN0019_LOGFILE_CORRUPTED", _xaLog.logProperties().logName());
                                      throw new IOException(_xaLog.logProperties().logName() + " corrupted");
                                  }
                                  checkPartnerServiceData(ru);
                                  _stateSection = rus;
                                  recoveredServiceItems++;
                                  // Extract the server status indicator
                                  int flag = -1;
                                  if (logData != null && logData.length > 3)
                                      flag = Util.getIntFromBytes(logData, 0, 4);
  
                                  if (flag == STARTING) {
                                      if (tc.isEventEnabled())
                                          Tr.event(tc, "previous server may have crashed");
                                  } else if (flag == STOPPING) {
                                      if (tc.isEventEnabled())
                                          Tr.event(tc, "previous server closed down cleanly with transactions still running");
                                  } else {
                                      // If the log record data is invalid, then return immediately.
                                      if (tc.isEventEnabled())
                                          Tr.event(tc, "Invalid log record data in SERVER_STATE_SECTION");
                                      Tr.warning(tc, "WTRN0019_LOGFILE_CORRUPTED", _xaLog.logProperties().logName());
                                      throw new IOException(_xaLog.logProperties().logName() + " corrupted");
                                  }
                                  break;
                              case TransactionImpl.SERVER_DATA_SECTION:
                                  if (tc.isDebugEnabled())
                                      Tr.debug(tc, "Server name data record");
                                  if (_partnerServerSection != null) {
                                      if (tc.isEventEnabled())
                                          Tr.event(tc, "Multiple log SERVER_DATA_SECTIONs found");
                                      Tr.warning(tc, "WTRN0019_LOGFILE_CORRUPTED", _xaLog.logProperties().logName());
                                      throw new IOException(_xaLog.logProperties().logName() + " corrupted");
                                  }
                                  checkPartnerServiceData(ru);
                                  _partnerServerSection = rus;
                                  recoveredServiceItems++;
                                  if ((logData != null) && (logData.length != 0)) {
                                      _recoveredServerName = new String(logData);
                                      //
                                      // Check to see if the logged serverName is the same as this server
                                      //
                                      if (!_failureScopeController.serverName().equals(_recoveredServerName)) {
                                          Tr.warning(tc, "WTRN0020_RECOVERING_TRANSACTIONS", _recoveredServerName);
                                      }
                                  }
                                  break;
                              case TransactionImpl.APPLID_DATA_SECTION:
                                  if (tc.isDebugEnabled())
                                      Tr.debug(tc, "Applid data record");
                                  if (_partnerApplIdSection != null) {
                                      if (tc.isEventEnabled())
                                          Tr.event(tc, "Multiple log APPLID_DATA_SECTIONs found");
                                      Tr.warning(tc, "WTRN0019_LOGFILE_CORRUPTED", _xaLog.logProperties().logName());
                                      throw new IOException(_xaLog.logProperties().logName() + " corrupted");
                                  }
                                  checkPartnerServiceData(ru);
                                  _partnerApplIdSection = rus;
                                  recoveredServiceItems++;
                                  if (logData != null) {
                                      _recoveredApplId = logData;
                                  }
                                  break;
                              case TransactionImpl.EPOCH_DATA_SECTION:
                                  if (tc.isDebugEnabled())
                                      Tr.debug(tc, "Epoch data record");
                                  if (_partnerEpochSection != null) {
                                      if (tc.isEventEnabled())
                                          Tr.event(tc, "Multiple log EPOCH_DATA_SECTIONs found");
                                      Tr.warning(tc, "WTRN0019_LOGFILE_CORRUPTED", _xaLog.logProperties().logName());
                                      throw new IOException(_xaLog.logProperties().logName() + " corrupted");
                                  }
                                  checkPartnerServiceData(ru);
                                  _partnerEpochSection = rus;
                                  recoveredServiceItems++;
                                  if (logData != null && logData.length > 3) {
                                      _recoveredEpoch = Util.getIntFromBytes(logData, 0, 4);
                                      if (tc.isDebugEnabled())
                                          Tr.debug(tc, "Recovered epoch: " + _recoveredEpoch);
                                  }
                                  break;
                              case TransactionImpl.LOW_WATERMARK_SECTION:
                                  if (tc.isDebugEnabled())
                                      Tr.debug(tc, "Low Watermark section");
                                  if (_partnerLowWatermarkSection != null) {
                                      if (tc.isEventEnabled())
                                          Tr.event(tc, "Multiple log LOW_WATERMARKs found");
                                      Tr.warning(tc, "WTRN0019_LOGFILE_CORRUPTED", _xaLog.logProperties().logName());
                                      throw new IOException(_xaLog.logProperties().logName() + " corrupted");
                                  }
                                  checkPartnerServiceData(ru);
  
                                  _partnerLowWatermarkSection = rus;
                                  recoveredServiceItems++;
                                  // Extract the server status indicator
                                  if (logData != null && logData.length > 3)
                                      _partnerEntryLowWatermark = Util.getIntFromBytes(logData, 0, 4);
  
                                  else {
                                      if (tc.isEventEnabled())
                                          Tr.event(tc, "LowWatermark data is invalid");
                                      Tr.warning(tc, "WTRN0019_LOGFILE_CORRUPTED", _xaLog.logProperties().logName());
                                      throw new IOException(_xaLog.logProperties().logName() + " corrupted");
                                  }
  
                                  break;
                              case TransactionImpl.NEXT_ID_SECTION:
                                  if (tc.isDebugEnabled())
                                      Tr.debug(tc, "Next Id section");
                                  if (_partnerNextIdSection != null) {
                                      if (tc.isEventEnabled())
                                          Tr.event(tc, "Multiple log NEXT_IDs found");
                                      Tr.warning(tc, "WTRN0019_LOGFILE_CORRUPTED", _xaLog.logProperties().logName());
                                      throw new IOException(_xaLog.logProperties().logName() + " corrupted");
                                  }
                                  checkPartnerServiceData(ru);
  
                                  _partnerNextIdSection = rus;
                                  recoveredServiceItems++;
                                  // Extract the server status indicator
                                  if (logData != null && logData.length > 3)
                                      _partnerEntryNextId = Util.getIntFromBytes(logData, 0, 4);
                                  else {
                                      if (tc.isEventEnabled())
                                          Tr.event(tc, "NextId data is invalid");
                                      Tr.warning(tc, "WTRN0019_LOGFILE_CORRUPTED", _xaLog.logProperties().logName());
                                      throw new IOException(_xaLog.logProperties().logName() + " corrupted");
                                  }
  
                                  break;
                              default:
                                  if (tc.isEventEnabled())
                                      Tr.event(tc, "Invalid partner log data records found");
                                  Tr.warning(tc, "WTRN0019_LOGFILE_CORRUPTED", _xaLog.logProperties().logName());
                                  throw new IOException(_xaLog.logProperties().logName() + " corrupted");
                          }
                      }
                      recoverableUnitSections.close();
                      recoverableUnitSections = null;
  
                      if (handleXAResourceRecord(currentLog, xaLogData, id, xaPriority)) {
                          result++;
                      }
  
                      // Determine if shutdown processing started during replayPartnerLog processing.
                      // If it has, no further action can be taken.
                      if (shutdownInProgress()) {
                          shuttingDown = true;
                      }
                  }
                  recoverableUnits.close();
                  recoverableUnits = null;
              } /* @MD18134M */
          } catch (IOException ioe) {
              FFDCFilter.processException(ioe, "com.ibm.tx.jta.impl.RecoveryManager.replayPartnerLog", "1100", this);
              if (recoverableUnitSections != null)
                  recoverableUnitSections.close();
              if (recoverableUnits != null)
                  recoverableUnits.close();
              Tr.warning(tc, "WTRN0019_LOGFILE_CORRUPTED", _xaLog.logProperties().logName());
              if (tc.isEntryEnabled())
                  Tr.exit(tc, "replayPartnerLog", ioe);
              throw ioe;
          }
  
          // Only bother to check that the retrieved information is valid if we are not
          // trying to stop processing the partner log.
          if (!shuttingDown) {
              if (records > 1) {
                  // We have at least recovered a record for an XARM or partner system
                  // Check we have all of the service data
                  if (recoveredServiceItems != PARTNERLOG_SERVICE_ITEMS) {
                      if (tc.isEventEnabled())
                          Tr.event(tc, "Recoverable log records found without service data records. Got " + recoveredServiceItems + " expected " + PARTNERLOG_SERVICE_ITEMS);
                      IOException ioe = new IOException(_xaLog.logProperties().logName() + " corrupted");
                      FFDCFilter.processException(ioe, "com.ibm.tx.jta.impl.RecoveryManager.replayPartnerLog", "1118", this);
                      Tr.warning(tc, "WTRN0019_LOGFILE_CORRUPTED", _xaLog.logProperties().logName());
                      if (tc.isEntryEnabled())
                          Tr.exit(tc, "replayPartnerLog", ioe);
                      throw ioe;
                  }
              } else if (recoveredServiceItems != 0 && recoveredServiceItems != PARTNERLOG_SERVICE_ITEMS) {
                  if (tc.isEventEnabled())
                      Tr.event(tc, "Only a subset of service data records recovered");
                  IOException ioe = new IOException(_xaLog.logProperties().logName() + " corrupted");
                  FFDCFilter.processException(ioe, "com.ibm.tx.jta.impl.RecoveryManager.replayPartnerLog", "1124", this);
                  Tr.warning(tc, "WTRN0019_LOGFILE_CORRUPTED", _xaLog.logProperties().logName());
                  if (tc.isEntryEnabled())
                      Tr.exit(tc, "replayPartnerLog", ioe);
                  throw ioe;
              }
          }
  
          if (tc.isEntryEnabled())
              Tr.exit(tc, "replayPartnerLog", result);
          return result;
      }