package com.sleepycat.je.log;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.HashSet;
import java.util.Set;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.dbi.EnvironmentImpl;
import com.sleepycat.je.log.entry.LogEntry;
import com.sleepycat.je.utilint.DbLsn;
import de.ovgu.cide.jakutil.*;
/**
* A ScavengerFileReader reads the log backwards. If it encounters a checksum
* error, it goes to the start of that log file and reads forward until it
* encounters a checksum error. It then continues the reading backwards in the
* log.
* The caller may set "dumpCorruptedBounds" to true if information about the
* start and finish of the corrupted portion should be displayed on stderr.
* The caller is expected to implement processEntryCallback. This method is
* called once for each entry that the ScavengerFileReader finds in the log.
*/
abstract public class ScavengerFileReader extends FileReader {
private Set targetEntryTypes;
private int readBufferSize;
private boolean dumpCorruptedBounds;
/**
* Create this reader to start at a given LSN.
*/
public ScavengerFileReader( EnvironmentImpl env, int readBufferSize, long startLsn, long finishLsn, long endOfFileLsn) throws IOException, DatabaseException {
super(env,readBufferSize,false,startLsn,null,endOfFileLsn,finishLsn);
this.readBufferSize=readBufferSize;
anticipateChecksumErrors=true;
targetEntryTypes=new HashSet();
dumpCorruptedBounds=false;
}
/**
* Set to true if corrupted boundaries should be dumped to stderr.
*/
public void setDumpCorruptedBounds( boolean dumpCorruptedBounds){
this.dumpCorruptedBounds=dumpCorruptedBounds;
}
/**
* Tell the reader that we are interested in these kind of entries.
*/
public void setTargetType( LogEntryType type){
targetEntryTypes.add(new Byte(type.getTypeNum()));
}
protected boolean processEntry( ByteBuffer entryBuffer) throws DatabaseException {
LogEntryType lastEntryType=LogEntryType.findType(currentEntryTypeNum,currentEntryTypeVersion);
LogEntry entry=lastEntryType.getSharedLogEntry();
entry.readEntry(entryBuffer,currentEntrySize,currentEntryTypeVersion,true);
processEntryCallback(entry,lastEntryType);
return true;
}
abstract protected void processEntryCallback( LogEntry entry, LogEntryType entryType) throws DatabaseException ;
public boolean readNextEntry() throws DatabaseException, IOException {
long saveCurrentEntryOffset=currentEntryOffset;
try {
return super.readNextEntry();
}
catch ( DbChecksumException DCE) {
resyncReader(DbLsn.makeLsn(readBufferFileNum,saveCurrentEntryOffset),dumpCorruptedBounds);
return super.readNextEntry();
}
}
protected boolean resyncReader( long nextGoodRecordPostCorruption, boolean showCorruptedBounds) throws DatabaseException, IOException {
LastFileReader reader=null;
long tryReadBufferFileNum=DbLsn.getFileNumber(nextGoodRecordPostCorruption);
while (tryReadBufferFileNum >= 0) {
try {
reader=new LastFileReader(env,readBufferSize,new Long(tryReadBufferFileNum));
break;
}
catch ( DbChecksumException DCE) {
tryReadBufferFileNum--;
continue;
}
}
boolean switchedFiles=tryReadBufferFileNum != DbLsn.getFileNumber(nextGoodRecordPostCorruption);
if (!switchedFiles) {
while (reader.readNextEntry()) {
}
}
long lastUsedLsn=reader.getLastValidLsn();
long nextAvailableLsn=reader.getEndOfLog();
if (showCorruptedBounds) {
System.err.println("A checksum error was found in the log.");
System.err.println("Corruption begins at LSN:\n " + DbLsn.toString(nextAvailableLsn));
System.err.println("Last known good record before corruption is at LSN:\n " + DbLsn.toString(lastUsedLsn));
System.err.println("Next known good record after corruption is at LSN:\n " + DbLsn.toString(nextGoodRecordPostCorruption));
}
startLsn=lastUsedLsn;
initStartingPosition(nextAvailableLsn,null);
if (switchedFiles) {
currentEntryPrevOffset=0;
}
return true;
}
/**
* @return true if this reader should process this entry, or just skip
* over it.
*/
protected boolean isTargetEntry( byte logEntryTypeNumber, byte logEntryTypeVersion){
if (targetEntryTypes.size() == 0) {
return true;
}
else {
return targetEntryTypes.contains(new Byte(logEntryTypeNumber));
}
}
}