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);
  }
  /**
   * 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);
  }
  /** @return true if the current entry is part of replication stream. */
  public boolean entryIsReplicated() {

    if (currentEntryHeader == null) {
      throw EnvironmentFailureException.unexpectedState(
          "entryIsReplicated should not be used before reader is " + "initialized");
    }
    return currentEntryHeader.getReplicated();
  }
  /**
   * Read the basic log entry header, leaving the buffer mark at the beginning of the checksummed
   * header data.
   */
  private void readBasicHeader(ByteBuffer dataBuffer) throws ChecksumException, DatabaseException {

    /* Read the header for this entry. */
    currentEntryHeader = new LogEntryHeader(dataBuffer, window.logVersion);

    /*
     * currentEntryPrevOffset is a separate field, and is not obtained
     * directly from the currentEntryHeader, because it is initialized and
     * used before any log entry was read.
     */
    currentEntryPrevOffset = currentEntryHeader.getPrevOffset();
  }
  private void startChecksum(ByteBuffer dataBuffer, boolean isChecksumTarget)
      throws ChecksumException {

    if (!doChecksumOnRead) {
      return;
    }

    if (!isChecksumTarget) {
      return;
    }

    /* Clear out any previous data. */
    cksumValidator.reset();

    int originalPosition = threadSafeBufferPosition(dataBuffer);
    if (currentEntryHeader.isInvisible()) {

      /*
       * Turn off invisibility so that the checksum will succeed. When
       * entries are made invisible, the checksum is not adjusted. Note
       * that the dataBuffer can leave the invisible bit transformed,
       * because the header has already been initialized, and this data
       * will never be read again.
       */
      LogEntryHeader.turnOffInvisible(
          dataBuffer, originalPosition - LogEntryHeader.MIN_HEADER_SIZE);
    }

    /* Position the buffer at the start of the data, after the checksum. */
    int headerSizeMinusChecksum = currentEntryHeader.getInvariantSizeMinusChecksum();
    int entryTypeStart = originalPosition - headerSizeMinusChecksum;
    threadSafeBufferPosition(dataBuffer, entryTypeStart);

    /* Load the validate with the header bytes. */
    cksumValidator.update(dataBuffer, headerSizeMinusChecksum);

    /* Move the data buffer back to the original position. */
    threadSafeBufferPosition(dataBuffer, originalPosition);
  }
 /**
  * May be called by processEntry when it determines that the entry does not need to be
  * read/de-serialized.
  */
 protected void skipEntry(ByteBuffer entryBuffer) {
   threadSafeBufferPosition(
       entryBuffer, threadSafeBufferPosition(entryBuffer) + currentEntryHeader.getItemSize());
 }
  /**
   * Variant of readNextEntry that throws FileNotFoundException and ChecksumException, rather than
   * wrapping them in an EnvironmentFailureException and invalidating the enviornment. This allows
   * users of this class (see cleaner.FileProcessor), and subclasses that override readNextEntry
   * (see ScavengerFileReader and LastFileReader), to handle these exceptions specially.
   */
  public final boolean readNextEntryAllowExceptions()
      throws FileNotFoundException, ChecksumException {

    boolean foundEntry = false;
    long savedCurrentEntryOffset = currentEntryOffset;
    long savedNextEntryOffset = nextEntryOffset;

    try {
      while ((!eof) && (!foundEntry)) {

        /* Read the invariant portion of the next header. */
        getLogEntryInReadBuffer();
        ByteBuffer dataBuffer = readData(LogEntryHeader.MIN_HEADER_SIZE, true); // collectData

        readBasicHeader(dataBuffer);

        boolean isTarget;
        boolean isChecksumTarget;
        boolean collectData;

        if (currentEntryHeader.isVariableLength()) {

          /*
           * For all variable length entries, init the checksum w/the
           * invariant portion of the header, before we know whether
           * the entry is a target for this reader.  This has
           * to be done before we read the variable portion of the
           * header, because readData() only guarantees that it
           * returns a dataBuffer that contains the next bytes that
           * are needed, and has no guarantee that it holds any bytes
           * that were previously read. The act of calling
           * readData() to obtain the optional portion may reset the
           * dataBuffer, and nudge the invariant part of the header
           * out of the buffer returned by readData()
           */
          startChecksum(dataBuffer);

          int optionalPortionLen = currentEntryHeader.getVariablePortionSize();

          /* Load the optional part of the header into a buffer. */
          dataBuffer = readData(optionalPortionLen, true);

          /*
           * Add to checksum while the buffer is positioned at
           * the start of the new bytes.
           */
          addToChecksum(dataBuffer, optionalPortionLen);

          /* Now read the optional bytes. */
          currentEntryHeader.readVariablePortion(dataBuffer);
        }

        /*
         * We've read the header of the next logrec. Move up our
         * offsets if we're moving forward. If we're moving
         * backwards, we set our offset before we read the header,
         * because we knew where the entry started.
         */
        if (forward) {
          currentEntryOffset = nextEntryOffset;
          nextEntryOffset +=
              currentEntryHeader.getSize()
                  + // header size
                  currentEntryHeader.getItemSize(); // item size
        }

        try {
          isTarget = isTargetEntry();

          isChecksumTarget = (isTarget || alwaysValidateChecksum);

          if (!currentEntryHeader.isVariableLength()) {
            startChecksum(dataBuffer, isChecksumTarget);
          }

          collectData = (isChecksumTarget && doChecksumOnRead) || isTarget;

          /*
           * Read in the body of the next entry. Note that even if
           * this isn't a targeted entry, we have to move the buffer
           * position along.
           */
          dataBuffer = readData(currentEntryHeader.getItemSize(), collectData);
        } catch (Throwable e) {
          if (forward) {
            currentEntryOffset = savedCurrentEntryOffset;
            nextEntryOffset = savedNextEntryOffset;
          }
          throw e;
        }

        /* Validate the log entry checksum. */
        validateChecksum(dataBuffer, isChecksumTarget);

        if (isTarget) {

          /*
           * For a target entry, call the subclass reader's
           * processEntry method to do whatever we need with the
           * entry.  It returns true if this entry is one that should
           * be returned.  Note that some entries, although targeted
           * and read, are not returned.
           */
          if (processEntry(dataBuffer)) {
            foundEntry = true;
            nRead++;
          }
        } else if (collectData) {

          /*
           * For a non-target entry that was validated, the buffer is
           * positioned at the start of the entry; skip over it.
           */
          skipEntry(dataBuffer);
        }
      }
    } catch (EOFException e) {
      eof = true;
    } catch (DatabaseException e) {
      eof = true;
      /* Report on error. */
      reportProblem(e);
      throw e;
    }

    return foundEntry;
  }
 /** Returns the total size (including header) of the last entry read. */
 public int getLastEntrySize() {
   return currentEntryHeader.getEntrySize();
 }