private void reportProblem(Exception e) {

    StringBuilder sb = new StringBuilder();
    sb.append("Halted log file reading at file 0x")
        .append(Long.toHexString(window.currentFileNum()))
        .append(" offset 0x")
        .append(Long.toHexString(nextEntryOffset))
        .append(" offset(decimal)=")
        .append(nextEntryOffset)
        .append(" prev=0x")
        .append(Long.toHexString(currentEntryPrevOffset));

    if (currentEntryHeader != null) {
      LogEntryType problemType = LogEntryType.findType(currentEntryHeader.getType());
      sb.append(":\nentry=")
          .append(problemType)
          .append("type=")
          .append(currentEntryHeader.getType())
          .append(",version=")
          .append(currentEntryHeader.getVersion())
          .append(")\nprev=0x")
          .append(Long.toHexString(currentEntryPrevOffset))
          .append("\nsize=")
          .append(currentEntryHeader.getItemSize())
          .append("\nNext entry should be at 0x")
          .append(
              Long.toHexString(
                  nextEntryOffset
                      + currentEntryHeader.getSize()
                      + currentEntryHeader.getItemSize()));
    }

    LoggerUtils.traceAndLogException(envImpl, "FileReader", "readNextEntry", sb.toString(), e);
  }
 /** TBW */
 protected void handleGapInBackwardsScan(long prevFileNum) {
   throw new EnvironmentFailureException(
       envImpl,
       EnvironmentFailureReason.LOG_INTEGRITY,
       "Cannot read backward over cleaned file"
           + " from "
           + window.currentFileNum()
           + " to "
           + prevFileNum);
 }
  /**
   * Ensure that the next target is in the window. The default behavior is that the next target is
   * the next, following entry, so we can assume that it's in the window. All we have to do is to
   * check if we've gone past the specified end point.
   *
   * @throws DatabaseException
   * @throws FileNotFoundException
   * @throws ChecksumException
   */
  protected void setForwardPosition()
      throws EOFException, DatabaseException, ChecksumException, FileNotFoundException {

    if (finishLsn != DbLsn.NULL_LSN) {
      /* The next log entry has passed the end LSN. */
      long nextLsn = DbLsn.makeLsn(window.currentFileNum(), nextEntryOffset);
      if (DbLsn.compareTo(nextLsn, finishLsn) >= 0) {
        throw new EOFException();
      }
    }
  }
  /**
   * Add the entry bytes to the checksum and check the value. This method must be called with the
   * buffer positioned at the start of the entry.
   */
  private void validateChecksum(ByteBuffer dataBuffer, boolean isChecksumTarget)
      throws ChecksumException {

    if (!doChecksumOnRead) {
      return;
    }

    if (!isChecksumTarget) {
      return;
    }

    cksumValidator.update(dataBuffer, currentEntryHeader.getItemSize());
    cksumValidator.validate(
        currentEntryHeader.getChecksum(), window.currentFileNum(), currentEntryOffset);
  }
  /**
   * Ensure that the next target is in the window. The default behavior is that the next target is
   * the next previous entry.
   *
   * @throws DatabaseException
   */
  protected void setBackwardPosition()
      throws ChecksumException, FileNotFoundException, EOFException, DatabaseException {

    /*
     * currentEntryPrevOffset is the entry before the current entry.
     * currentEntryOffset is the entry we just read (or the end of the
     * file if we're starting out.
     */
    if ((currentEntryPrevOffset != 0) && window.containsOffset(currentEntryPrevOffset)) {

      /* The next log entry has passed the start LSN. */
      long nextLsn = DbLsn.makeLsn(window.currentFileNum(), currentEntryPrevOffset);
      if (finishLsn != DbLsn.NULL_LSN) {
        if (DbLsn.compareTo(nextLsn, finishLsn) == -1) {
          throw new EOFException(
              "finish="
                  + DbLsn.getNoFormatString(finishLsn)
                  + "next="
                  + DbLsn.getNoFormatString(nextLsn));
        }
      }

      /* This log entry starts in this buffer, just reposition. */
      window.positionBuffer(currentEntryPrevOffset);
    } else {

      /*
       * The start of the log entry is not in this read buffer so
       * we must fill the buffer again.
       *
       * 1) The target log entry is in a different file from the
       * current window's file. Move the window to the previous
       * file and start the read from the target LSN.
       *
       * 2) The target log entry is the same file but the log entry
       * is larger than the read chunk size. Start the next read
       * buffer from the target LSN. It's going to take multiple
       * reads to get the log entry, and we might as well get as
       * much as possible.
       *
       * 3) In the same file, and the log entry fits within one
       * read buffer. Try to position the next buffer chunk so the
       * target entry is held within the buffer, all the way at the
       * end. That way, since we're reading backwards, there will be
       * more buffered data available for following reads.
       */
      long nextFile;
      long nextWindowStart;
      long nextTarget;

      if (currentEntryPrevOffset == 0) {
        /* Case 1: Go to another file. */
        currentEntryPrevOffset = fileManager.getFileHeaderPrevOffset(window.currentFileNum());

        Long prevFileNum = fileManager.getFollowingFileNum(window.currentFileNum(), false);
        if (prevFileNum == null) {
          throw new EOFException("No file following " + window.currentFileNum());
        }

        /*
         *  Check finishLSN  before proceeding, in case we should stop
         *  the search before attempting to set the file reader to a
         *  position in the previous file. In  [#22407] we threw a
         *  spurious EFE complaining that we cannot read backwards over
         *  a cleaned file because the previous file had  been cleaned
         *  away.
         */
        if (finishLsn != DbLsn.NULL_LSN && prevFileNum < DbLsn.getFileNumber(finishLsn)) {
          throw new EOFException(
              "finish="
                  + DbLsn.getNoFormatString(finishLsn)
                  + " nextFile=0x"
                  + Long.toHexString(prevFileNum));
        }

        if (window.currentFileNum() - prevFileNum.longValue() != 1) {
          handleGapInBackwardsScan(prevFileNum);
        }

        nextFile = prevFileNum;
        nextWindowStart = currentEntryPrevOffset;
        nextTarget = currentEntryPrevOffset;
      } else if ((currentEntryOffset - currentEntryPrevOffset) > window.capacity()) {

        /*
         * Case 2: The entry is in the same file, but is bigger
         * than one buffer. Position it at the front of the buffer.
         */
        nextFile = window.currentFileNum();
        nextWindowStart = currentEntryPrevOffset;
        nextTarget = currentEntryPrevOffset;
      } else {

        /*
         * Case 3: In same file, but not in this buffer. The target
         * entry will fit in one buffer.
         */
        nextFile = window.currentFileNum();
        long newPosition = currentEntryOffset - window.capacity();
        nextWindowStart = (newPosition < 0) ? 0 : newPosition;
        nextTarget = currentEntryPrevOffset;
      }

      /* The next log entry has passed the start LSN. */
      long nextLsn = DbLsn.makeLsn(nextFile, currentEntryPrevOffset);
      if (finishLsn != DbLsn.NULL_LSN) {
        if (DbLsn.compareTo(nextLsn, finishLsn) == -1) {
          throw new EOFException(
              "finish="
                  + DbLsn.getNoFormatString(finishLsn)
                  + " next="
                  + DbLsn.getNoFormatString(nextLsn));
        }
      }

      window.slideAndFill(nextFile, nextWindowStart, nextTarget, forward);
    }

    /* The current entry will start at this offset. */
    currentEntryOffset = currentEntryPrevOffset;
  }
 /** Get LSN of the last entry read. */
 public long getLastLsn() {
   return DbLsn.makeLsn(window.currentFileNum(), currentEntryOffset);
 }