docs: protected void replayTranLog() throws Exception {
          if (tc.isEntryEnabled())
              Tr.entry(tc, "replayTranLog");
  
          final LogCursor recoverableUnits = _tranLog.recoverableUnits();
          LogCursor recoverableUnitSections = null;
          int recoveredServiceItems = 0;
          boolean shuttingDown = false;
          boolean recoveredTransactions = false;
  
          try {
              // The recovered transctions may include completed transactions.
              // These will be filtered out by the TransactionImpl.reconstruct call
              // We will force a checkpoint after this in case we previously crashed.
              while (recoverableUnits.hasNext() && !shuttingDown) {
                  final RecoverableUnit ru = (RecoverableUnit) recoverableUnits.next();
                  if (tc.isEventEnabled())
                      Tr.event(tc, "Replaying record " + ru.identity() + " from the transaction log");
  
                  recoverableUnitSections = ru.sections();
                  boolean tranRecord = false;
                  while (!tranRecord && 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 service data sections
                      if (tc.isEventEnabled())
                          Tr.event(tc, "Replaying section " + rusId, Util.toHexString(logData));
                      switch (rusId) {
                          case TransactionImpl.SERVER_DATA_SECTION:
                              if (tc.isDebugEnabled())
                                  Tr.debug(tc, "Server name data record");
                              if (_tranlogServerSection != null) {
                                  if (tc.isEventEnabled())
                                      Tr.event(tc, "Multiple log SERVER_DATA_SECTIONs found");
                                  Tr.warning(tc, "WTRN0019_LOGFILE_CORRUPTED", _tranLog.logProperties().logName());
                                  throw new IOException(_tranLog.logProperties().logName() + " corrupted");
                              }
                              if (_tranlogServiceData == null) {
                                  _tranlogServiceData = ru;
                              } else if (_tranlogServiceData != ru) {
                                  if (tc.isEventEnabled())
                                      Tr.event(tc, "Multiple log service data records found");
                                  Tr.warning(tc, "WTRN0019_LOGFILE_CORRUPTED", _tranLog.logProperties().logName());
                                  throw new IOException(_tranLog.logProperties().logName() + " corrupted");
                              }
                              _tranlogServerSection = rus;
                              recoveredServiceItems++;
                              if (logData != null && logData.length != 0) {
                                  final String recoveredServerName = new String(logData);
                                  if (_recoveredServerName == null) {
                                      _recoveredServerName = recoveredServerName;
                                      //
                                      // Check to see if the logged serverName is the same as this server
                                      // If it is different, we just output a message as we may be replaying another server log
                                      // on this server.  We leave the service data set to the original server name in case we
                                      // crash before recovery has completed.  We need to ensure we have a matching server name
                                      // with that from the partner log.  On shutdown, if there are no transactions running, we
                                      // clear out the transaction log, but leave the partner log and may update the server name
                                      // to this server in the partner log.  If there are transactions running, we never update
                                      // the server name in the log.
                                      //
                                      if (!_failureScopeController.serverName().equals(_recoveredServerName)) {
                                          Tr.warning(tc, "WTRN0020_RECOVERING_TRANSACTIONS", _recoveredServerName);
                                      }
                                  } else if (!_recoveredServerName.equals(recoveredServerName)) {
                                      Tr.error(tc, "WTRN0024_INCONSISTENT_LOGS");
                                      throw new IOException("Inconsistent Transaction and XA Resource recovery logs");
                                  }
                              }
                              break;
                          case TransactionImpl.APPLID_DATA_SECTION:
                              if (tc.isDebugEnabled())
                                  Tr.debug(tc, "Applid data record");
                              if (_tranlogApplIdSection != null) {
                                  if (tc.isEventEnabled())
                                      Tr.event(tc, "Multiple log APPLID_DATA_SECTIONs found");
                                  Tr.warning(tc, "WTRN0019_LOGFILE_CORRUPTED", _tranLog.logProperties().logName());
                                  throw new IOException(_tranLog.logProperties().logName() + " corrupted");
                              }
                              if (_tranlogServiceData == null) {
                                  _tranlogServiceData = ru;
                              } else if (_tranlogServiceData != ru) {
                                  if (tc.isEventEnabled())
                                      Tr.event(tc, "Multiple log service data records found");
                                  Tr.warning(tc, "WTRN0019_LOGFILE_CORRUPTED", _tranLog.logProperties().logName());
                                  throw new IOException(_tranLog.logProperties().logName() + " corrupted");
                              }
                              _tranlogApplIdSection = rus;
                              recoveredServiceItems++;
                              if (logData != null) {
                                  if (_recoveredApplId == null) {
                                      _recoveredApplId = logData;
                                  } else if (!Util.equal(_recoveredApplId, logData)) {
                                      Tr.error(tc, "WTRN0024_INCONSISTENT_LOGS");
                                      throw new IOException("Inconsistent Transaction and XA Resource recovery logs");
                                  }
                              }
                              break;
                          case TransactionImpl.EPOCH_DATA_SECTION:
                              if (tc.isDebugEnabled())
                                  Tr.debug(tc, "Epoch data record");
                              if (_tranlogEpochSection != null) {
                                  if (tc.isEventEnabled())
                                      Tr.event(tc, "Multiple log EPOCH_DATA_SECTIONs found");
                                  Tr.warning(tc, "WTRN0019_LOGFILE_CORRUPTED", _tranLog.logProperties().logName());
                                  throw new IOException(_tranLog.logProperties().logName() + " corrupted");
                              }
                              if (_tranlogServiceData == null) {
                                  _tranlogServiceData = ru;
                              } else if (_tranlogServiceData != ru) {
                                  if (tc.isEventEnabled())
                                      Tr.event(tc, "Multiple log service data records found");
                                  Tr.warning(tc, "WTRN0019_LOGFILE_CORRUPTED", _tranLog.logProperties().logName());
                                  throw new IOException(_tranLog.logProperties().logName() + " corrupted");
                              }
                              _tranlogEpochSection = rus;
                              recoveredServiceItems++;
                              if (logData != null && logData.length > 3) {
                                  final int recoveredEpoch = Util.getIntFromBytes(logData, 0, 4);
                                  if (tc.isDebugEnabled())
                                      Tr.debug(tc, "Recovered epoch: " + recoveredEpoch);
                                  // If epoch isnt set or tranlog epoch is larger than partner, take the larger.
                                  // These can get out of step if one crashes during a restart.
                                  if (recoveredEpoch > _recoveredEpoch) {
                                      _recoveredEpoch = recoveredEpoch;
                                  }
                              }
                              break;
                          default:
                              tranRecord = true;
                              break;
                      }
                  }
                  recoverableUnitSections.close();
                  recoverableUnitSections = null; // reset in case of throw/catch
  
                  if (tranRecord) {
                      recoveredTransactions = handleTranRecord(ru, recoveredTransactions, recoverableUnits);
                  }
  
                  // Check to see if shutdown has begun before proceeding. If it has, no
                  // further action can be taken.
                  shuttingDown = shutdownInProgress();
              }
  
              // Only bother to check that the retrieved information is valid if we are not
              // trying to stop processing the partner log.
              if (!shuttingDown) {
                  if (recoveredTransactions) {
                      // We have at least recovered a transaction
                      // Check we have all of the service data
                      if (recoveredServiceItems != TRANSACTION_SERVICE_ITEMS) {
                          if (tc.isEventEnabled())
                              Tr.event(tc, "Recoverable log records found without service data records");
                          Tr.warning(tc, "WTRN0019_LOGFILE_CORRUPTED", _tranLog.logProperties().logName());
                          throw new IOException(_tranLog.logProperties().logName() + " corrupted");
                      }
                  } else if (recoveredServiceItems != 0 && recoveredServiceItems != TRANSACTION_SERVICE_ITEMS) {
                      if (tc.isEventEnabled())
                          Tr.event(tc, "Only a subset of service data records recovered");
                      Tr.warning(tc, "WTRN0019_LOGFILE_CORRUPTED", _tranLog.logProperties().logName());
                      throw new IOException(_tranLog.logProperties().logName() + " corrupted");
                  }
              }
          } catch (Throwable exc) {
              FFDCFilter.processException(exc, "com.ibm.tx.jta.impl.RecoveryManager.replayTranLog", "512", this);
              if (recoverableUnitSections != null)
                  recoverableUnitSections.close();
              Tr.error(tc, "WTRN0025_TRAN_RECOVERY_FAILED", exc);
              SystemException se = new SystemException(exc.toString());
              if (tc.isEntryEnabled())
                  Tr.exit(tc, "replayTranLog", se);
              throw se;
          } finally {
              recoverableUnits.close();
          }
  
          if (tc.isEntryEnabled())
              Tr.exit(tc, "replayTranLog");
      }