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); }
/** * 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.getSize() + currentEntryHeader.getItemSize(); }