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(); }