Journal.java
package emissary.output.roller.journal;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.annotation.Nullable;
/**
* Tracks written progress of a corresponding file. Some formats within the framework don't lend themselves to knowing
* length of output prior to writing as in many journaled formats. Our model allows us to track successful or complete
* writes to an output file using JournalEntry objects and is simplified further by using a JournaledChannel.
*
* @see JournalReader
* @see JournalWriter
*/
public final class Journal {
static final byte SEP = 0x00;
// 6 byte magic
static final byte[] MAGIC = "BGJRNL".getBytes();
static final byte CURRENT_VERSION = 1;
static final int ENTRY_LENGTH = 1024;
// eight bytes and a null separator
static final int NINE = 9;
public static final String EXT = ".bgjournal";
static final String DELEXT = ".deletemarker";
// Fields package protected for testing
// not final to release on close
byte version;
String key;
final Path journalPath;
ArrayList<JournalEntry> entries = new ArrayList<>();
public Journal(Path journalPath) {
this.journalPath = journalPath;
}
public List<JournalEntry> getEntries() {
return Collections.unmodifiableList(entries);
}
void setVersion(byte b) {
this.version = b;
}
public byte getVersion() {
return version;
}
void setKey(String key) {
this.key = key;
}
/**
* Unique identifier for this Journal, typically used to identify a target, finalized file name.
*
* @return The key, generally a target file, for this Journal
*/
public String getKey() {
return this.key;
}
public Path getJournalPath() {
return this.journalPath;
}
/**
* Last JournalEntry in the file, which generally represents that last successful written offset in the associated
* content file.
*
* @return Last entry that should correlate to the last good position in a file or null if no entries are present.
*/
@Nullable
public JournalEntry getLastEntry() {
return entries.isEmpty() ? null : entries.get(entries.size() - 1);
}
/**
* This method retrieves the last valid JournalEntry based on the length or position within the content file. It is
* possible, on some architectures, that upon a crash data may not have flushed to disk. In this case, we need to query
* the Journal to find the last good position based on the length of the file.
*
* @param channelSize The maximum position, generally the file size, to search for
* @return JournalEntry containing the last good offset less than or equal to channelSize
*/
@Nullable
public JournalEntry getLastValidEntry(long channelSize) {
if (channelSize < 0) {
throw new IllegalArgumentException("Channel Size must be 0 or larger");
}
for (int i = entries.size() - 1; i > 0; i--) {
JournalEntry je = entries.get(i);
if (channelSize >= je.getOffset()) {
return je;
}
}
return null;
}
@Override
public String toString() {
return "Journal{" + "version=" + version + ", key=" + key + ", journalPath=" + journalPath + ", entries=" + entries.size() + '}';
}
}