/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2002-2005
* Sleepycat Software. All rights reserved.
*
* $Id: DatabaseImpl.java,v 1.132 2005/09/28 01:43:44 mark Exp $
*/
package com.sleepycat.je.dbi;
import java.io.PrintStream;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import com.sleepycat.je.BtreeStats;
import com.sleepycat.je.Cursor;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.DatabaseStats;
import com.sleepycat.je.DbInternal;
import com.sleepycat.je.LockMode;
import com.sleepycat.je.OperationStatus;
import com.sleepycat.je.SecondaryDatabase;
import com.sleepycat.je.StatsConfig;
import com.sleepycat.je.VerifyConfig;
import com.sleepycat.je.cleaner.TrackedFileSummary;
import com.sleepycat.je.cleaner.UtilizationTracker;
import com.sleepycat.je.config.EnvironmentParams;
import com.sleepycat.je.latch.Latch;
import com.sleepycat.je.log.LogException;
import com.sleepycat.je.log.LogReadable;
import com.sleepycat.je.log.LogUtils;
import com.sleepycat.je.log.LogWritable;
import com.sleepycat.je.tree.BIN;
import com.sleepycat.je.tree.DBIN;
import com.sleepycat.je.tree.DIN;
import com.sleepycat.je.tree.DupCountLN;
import com.sleepycat.je.tree.IN;
import com.sleepycat.je.tree.Node;
import com.sleepycat.je.tree.Tree;
import com.sleepycat.je.tree.TreeUtils;
import com.sleepycat.je.tree.TreeWalkerStatsAccumulator;
import com.sleepycat.je.txn.Locker;
import com.sleepycat.je.txn.ThreadLocker;
import com.sleepycat.je.utilint.CmdUtil;
import com.sleepycat.je.utilint.DbLsn;
/**
* The underlying object for a given database.
*/
public class DatabaseImpl
implements LogWritable, LogReadable, Cloneable {
private DatabaseId id; // unique id
private Tree tree;
private EnvironmentImpl envImpl; // Tree operations find the env this way
private boolean duplicatesAllowed; // duplicates allowed
private boolean transactional;// All open handles are transactional
private Set referringHandles; // Set of open Database handles
private boolean isDeleted; // DatabaseImpl has been deleted--do not use.
private TrackedFileSummary[] deletedTrackingInfo; // Used during delete
private BtreeStats stats; // most recent btree stats w/ !DB_FAST_STAT
private long eofNodeId; // Logical EOF node for range locking
/*
* The user defined Btree and duplicate comparison functions, if specified.
*/
private Comparator btreeComparator = null;
private Comparator duplicateComparator = null;
private String btreeComparatorName = "";
private String duplicateComparatorName = "";
/*
* Cache some configuration values.
*/
private int binDeltaPercent;
private int binMaxDeltas;
private int maxMainTreeEntriesPerNode;
private int maxDupTreeEntriesPerNode;
/*
* The debugDatabaseName is used for error messages only, to avoid
* accessing the db mapping tree in error situations. Currently it's not
* guaranteed to be transactionally correct, nor is it updated by rename.
*/
private String debugDatabaseName;
/**
* Create a database object for a new database.
*/
public DatabaseImpl(String dbName,
DatabaseId id,
EnvironmentImpl envImpl,
DatabaseConfig dbConfig)
throws DatabaseException {
this.id = id;
this.envImpl = envImpl;
this.btreeComparator = dbConfig.getBtreeComparator();
this.duplicateComparator = dbConfig.getDuplicateComparator();
duplicatesAllowed = dbConfig.getSortedDuplicates();
transactional = dbConfig.getTransactional();
maxMainTreeEntriesPerNode = dbConfig.getNodeMaxEntries();
maxDupTreeEntriesPerNode = dbConfig.getNodeMaxDupTreeEntries();
initDefaultSettings();
isDeleted = false;
/*
* The tree needs the env, make sure we assign it before
* allocating the tree.
*/
tree = new Tree(this);
referringHandles = Collections.synchronizedSet(new HashSet());
eofNodeId = Node.getNextNodeId();
/* For error messages only. */
debugDatabaseName = dbName;
}
/**
* Create an empty database object for initialization from the log. Note
* that the rest of the initialization comes from readFromLog(), except
* for the debugDatabaseName, which is set by the caller.
*/
public DatabaseImpl()
throws DatabaseException {
id = new DatabaseId();
envImpl = null;
isDeleted = false;
tree = new Tree();
referringHandles = Collections.synchronizedSet(new HashSet());
/* initDefaultSettings is called after envImpl is set. */
eofNodeId = Node.getNextNodeId();
}
public void setDebugDatabaseName(String debugName) {
debugDatabaseName = debugName;
}
public String getDebugName() {
return debugDatabaseName;
}
/**
* Initialize configuration settings when creating a new instance or after
* reading an instance from the log. The envImpl field must be set before
* calling this method.
*/
private void initDefaultSettings()
throws DatabaseException {
DbConfigManager configMgr = envImpl.getConfigManager();
binDeltaPercent =
configMgr.getInt(EnvironmentParams.BIN_DELTA_PERCENT);
binMaxDeltas =
configMgr.getInt(EnvironmentParams.BIN_MAX_DELTAS);
if (maxMainTreeEntriesPerNode == 0) {
maxMainTreeEntriesPerNode =
configMgr.getInt(EnvironmentParams.NODE_MAX);
}
if (maxDupTreeEntriesPerNode == 0) {
maxDupTreeEntriesPerNode =
configMgr.getInt(EnvironmentParams.NODE_MAX_DUPTREE);
}
}
/**
* Clone. For now just pass off to the super class for a field-by-field
* copy.
*/
public Object clone()
throws CloneNotSupportedException {
return super.clone();
}
/**
* @return the database tree.
*/
public Tree getTree() {
return tree;
}
void setTree(Tree tree) {
this.tree = tree;
}
/**
* @return the database id.
*/
public DatabaseId getId() {
return id;
}
void setId(DatabaseId id) {
this.id = id;
}
public long getEofNodeId() {
return eofNodeId;
}
/**
* @return true if this database is transactional.
*/
public boolean isTransactional() {
return transactional;
}
/**
* Sets the transactional property for the first opened handle.
*/
public void setTransactional(boolean transactional) {
this.transactional = transactional;
}
/**
* @return true if duplicates are allowed in this database.
*/
public boolean getSortedDuplicates() {
return duplicatesAllowed;
}
public int getNodeMaxEntries() {
return maxMainTreeEntriesPerNode;
}
public int getNodeMaxDupTreeEntries() {
return maxDupTreeEntriesPerNode;
}
/**
* Set the duplicate comparison function for this database.
*
* @param duplicateComparator - The Duplicate Comparison function.
*/
public void setDuplicateComparator(Comparator duplicateComparator) {
this.duplicateComparator = duplicateComparator;
}
/**
* Set the btree comparison function for this database.
*
* @param btreeComparator - The btree Comparison function.
*/
public void setBtreeComparator(Comparator btreeComparator) {
this.btreeComparator = btreeComparator;
}
/**
* @return the btree Comparator object.
*/
public Comparator getBtreeComparator() {
return btreeComparator;
}
/**
* @return the duplicate Comparator object.
*/
public Comparator getDuplicateComparator() {
return duplicateComparator;
}
/**
* Set the db environment during recovery, after instantiating the database
* from the log
*/
public void setEnvironmentImpl(EnvironmentImpl envImpl)
throws DatabaseException {
this.envImpl = envImpl;
initDefaultSettings();
tree.setDatabase(this);
}
/**
* @return the database environment.
*/
public EnvironmentImpl getDbEnvironment() {
return envImpl;
}
/**
* Returns whether one or more handles are open.
*/
public boolean hasOpenHandles() {
return referringHandles.size() > 0;
}
/**
* Add a referring handle
*/
public void addReferringHandle(Database db) {
referringHandles.add(db);
}
/**
* Decrement the reference count.
*/
public void removeReferringHandle(Database db) {
referringHandles.remove(db);
}
/**
* @return the referring handle count.
*/
synchronized int getReferringHandleCount() {
return referringHandles.size();
}
/**
* For this secondary database return the primary that it is associated
* with, or null if not associated with any primary. Note that not all
* handles need be associated with a primary.
*/
public Database findPrimaryDatabase()
throws DatabaseException {
for (Iterator i = referringHandles.iterator(); i.hasNext();) {
Object obj = i.next();
if (obj instanceof SecondaryDatabase) {
return ((SecondaryDatabase) obj).getPrimaryDatabase();
}
}
return null;
}
public String getName()
throws DatabaseException {
return envImpl.getDbMapTree().getDbName(id);
}
public boolean getIsDeleted() {
return isDeleted;
}
/**
* Purge a DatabaseImpl and corresponding MapLN in the db mapping tree.
* Purging consists of removing all related INs from the db mapping tree
* and deleting the related MapLN.
* Used at the a transaction end in these cases:
* - purge the deleted database after a commit of
* Environment.removeDatabase
* - purge the deleted database after a commit of
* Environment.truncateDatabase
* - purge the newly created database after an abort of
* Environment.truncateDatabase
*/
public void deleteAndReleaseINs()
throws DatabaseException {
this.isDeleted = true;
envImpl.getInMemoryINs().clearDb(this);
/*
* Count obsolete nodes for a deleted database at transaction commit
* time. Write out the modified file summaries for recovery.
*/
envImpl.getUtilizationProfile().countAndLogSummaries
(deletedTrackingInfo);
/* Clear tracking info. */
deletedTrackingInfo = null;
/* Delete this database from the mapping tree. */
envImpl.getDbMapTree().deleteMapLN(id);
}
public void checkIsDeleted(String operation)
throws DatabaseException {
if (isDeleted) {
throw new DatabaseException
("Attempt to " + operation + " a deleted database");
}
}
/**
* Called when this database is truncated or removed to record the number
* of obsolete nodes that should be counted for the deleted tree. We save
* the number of obsolete nodes here, but the counting in the utilization
* profile does not actually occur until deleteAndReleaseINs() is called
* when the transaction is committed.
*/
public int recordObsoleteNodes()
throws DatabaseException {
/*
* Needs improvement: This is very inefficient since we load every
* database record and copy its data, and then discard the data.
* Perhaps internal cursor methods should allow passing null for the
* DatabaseEntry parameters, since parameter checking is done by the
* public API.
*/
UtilizationTracker tracker = new UtilizationTracker(envImpl);
ObsoleteINCounter inCounter = new ObsoleteINCounter(tracker);
Locker locker = new ThreadLocker(envImpl);
Cursor cursor = null;
int count = 0;
CursorImpl impl = null;
try {
EnvironmentImpl.incThreadLocalReferenceCount();
cursor = DbInternal.newCursor(this, locker, null);
/* Setup accumulator to count obsolete INs. */
impl = DbInternal.getCursorImpl(cursor);
tree.setTreeStatsAccumulator(inCounter);
impl.setTreeStatsAccumulator(inCounter);
DatabaseEntry foundData = new DatabaseEntry();
DatabaseEntry key = new DatabaseEntry();
OperationStatus status = DbInternal.position
(cursor, key, foundData, LockMode.READ_UNCOMMITTED, true);
while (status == OperationStatus.SUCCESS) {
count++;
impl = DbInternal.getCursorImpl(cursor);
long lsn = impl.getBIN().getLsn(impl.getIndex());
/*
* Use inexact counting to avoid the memory overhead of
* tracking LSNs for every record in an obsolete database.
*/
tracker.countObsoleteNodeInexact(lsn, null);
status = DbInternal.retrieveNext
(cursor, key, foundData, LockMode.READ_UNCOMMITTED,
GetMode.NEXT);
}
} finally {
if (impl != null) {
impl.setTreeStatsAccumulator(null);
}
tree.setTreeStatsAccumulator(null);
EnvironmentImpl.decThreadLocalReferenceCount();
if (cursor != null) {
cursor.close();
}
}
deletedTrackingInfo = tracker.getTrackedFiles();
return count;
}
/**
* Tree walker callback implementation that counts all INs as obsolete.
*/
private static class ObsoleteINCounter
implements TreeWalkerStatsAccumulator {
private UtilizationTracker tracker;
ObsoleteINCounter(UtilizationTracker tracker) {
this.tracker = tracker;
}
private void countIN(IN node) {
long lsn = node.getLastFullVersion();
if (lsn != DbLsn.NULL_LSN) {
/*
* Use inexact counting to avoid the memory overhead of
* tracking LSNs for every record in an obsolete database.
*/
tracker.countObsoleteNodeInexact(lsn, node.getLogType());
}
}
public void processIN(IN node, Long nid, int level) {
countIN(node);
}
public void processBIN(BIN node, Long nid, int level) {
countIN(node);
}
public void processDIN(DIN node, Long nid, int level) {
countIN(node);
}
public void processDBIN(DBIN node, Long nid, int level) {
countIN(node);
}
public void processDupCountLN(DupCountLN node, Long nid) {
}
public void incrementLNCount() {
}
public void incrementDeletedLNCount() {
}
}
public DatabaseStats stat(StatsConfig config)
throws DatabaseException {
if (stats == null) {
/*
* Called first time w/ FAST_STATS so just give them an
* empty one.
*/
stats = new BtreeStats();
}
if (!config.getFast()) {
if (tree == null) {
return new BtreeStats();
}
PrintStream out = config.getShowProgressStream();
if (out == null) {
out = System.err;
}
StatsAccumulator statsAcc =
new StatsAccumulator(out,
config.getShowProgressInterval(),
getEmptyStats());
walkDatabaseTree(statsAcc, out, true);
statsAcc.copyToStats(stats);
}
return stats;
}
/*
* @param config verify configuration
* @param emptyStats empty database stats, to be filled by this method
* @return true if the verify saw no errors.
*/
public boolean verify(VerifyConfig config, DatabaseStats emptyStats)
throws DatabaseException {
if (tree == null) {
return true;
}
PrintStream out = config.getShowProgressStream();
if (out == null) {
out = System.err;
}
StatsAccumulator statsAcc =
new StatsAccumulator(out,
config.getShowProgressInterval(),
emptyStats) {
void verifyNode(Node node) {
try {
node.verify(null);
} catch (DatabaseException INE) {
progressStream.println(INE);
}
}
};
boolean ok = walkDatabaseTree(statsAcc, out, config.getPrintInfo());
statsAcc.copyToStats(emptyStats);
return ok;
}
/* @return the right kind of stats object for this database. */
public DatabaseStats getEmptyStats() {
return new BtreeStats();
}
/*
* @return true if no errors.
*/
private boolean walkDatabaseTree(TreeWalkerStatsAccumulator statsAcc,
PrintStream out,
boolean verbose)
throws DatabaseException {
boolean ok = true;
Locker locker = new ThreadLocker(envImpl);
Cursor cursor = null;
CursorImpl impl = null;
try {
EnvironmentImpl.incThreadLocalReferenceCount();
cursor = DbInternal.newCursor(this, locker, null);
impl = DbInternal.getCursorImpl(cursor);
tree.setTreeStatsAccumulator(statsAcc);
/*
* This will only be used on the first call for the position()
* call.
*/
impl.setTreeStatsAccumulator(statsAcc);
DatabaseEntry foundData = new DatabaseEntry();
DatabaseEntry key = new DatabaseEntry();
OperationStatus status = DbInternal.position
(cursor, key, foundData, LockMode.READ_UNCOMMITTED, true);
int errorCount = 0;
int nonReportedErrors = 0;
boolean continueAfterError = false;
while (status == OperationStatus.SUCCESS) {
try {
status = DbInternal.retrieveNext
(cursor, key, foundData, LockMode.READ_UNCOMMITTED,
GetMode.NEXT);
} catch (DatabaseException DBE) {
ok = false;
if (DbInternal.advanceCursor(cursor, key, foundData)) {
if (verbose) {
out.println("Error encountered (continuing):");
out.println(DBE);
printErrorRecord(out, key, foundData);
}
} else {
throw DBE;
}
}
}
} finally {
if (impl != null) {
impl.setTreeStatsAccumulator(null);
}
tree.setTreeStatsAccumulator(null);
EnvironmentImpl.decThreadLocalReferenceCount();
if (cursor != null) {
cursor.close();
}
}
return ok;
}
/**
* Prints the key and data, if available, for a BIN entry that could not be
* read/verified. Uses the same format as DbDump and prints both the hex
* and printable versions of the entries.
*/
private void printErrorRecord(PrintStream out,
DatabaseEntry key,
DatabaseEntry data) {
byte[] bytes = key.getData();
StringBuffer sb = new StringBuffer("Error Key ");
if (bytes == null) {
sb.append("UNKNOWN");
} else {
CmdUtil.formatEntry(sb, bytes, false);
sb.append(' ');
CmdUtil.formatEntry(sb, bytes, true);
}
out.println(sb);
bytes = data.getData();
sb = new StringBuffer("Error Data ");
if (bytes == null) {
sb.append("UNKNOWN");
} else {
CmdUtil.formatEntry(sb, bytes, false);
sb.append(' ');
CmdUtil.formatEntry(sb, bytes, true);
}
out.println(sb);
}
static class StatsAccumulator implements TreeWalkerStatsAccumulator {
private Set inNodeIdsSeen = new HashSet();
private Set binNodeIdsSeen = new HashSet();
private Set dinNodeIdsSeen = new HashSet();
private Set dbinNodeIdsSeen = new HashSet();
private Set dupCountLNsSeen = new HashSet();
private long[] insSeenByLevel = null;
private long[] binsSeenByLevel = null;
private long[] dinsSeenByLevel = null;
private long[] dbinsSeenByLevel = null;
private long lnCount = 0;
private long deletedLNCount = 0;
private int mainTreeMaxDepth = 0;
private int duplicateTreeMaxDepth = 0;
private DatabaseStats useStats;
PrintStream progressStream;
int progressInterval;
/* The max levels we ever expect to see in a tree. */
private static final int MAX_LEVELS = 100;
StatsAccumulator(PrintStream progressStream,
int progressInterval,
DatabaseStats useStats) {
this.progressStream = progressStream;
this.progressInterval = progressInterval;
insSeenByLevel = new long[MAX_LEVELS];
binsSeenByLevel = new long[MAX_LEVELS];
dinsSeenByLevel = new long[MAX_LEVELS];
dbinsSeenByLevel = new long[MAX_LEVELS];
this.useStats = useStats;
}
void verifyNode(Node node) {
}
public void processIN(IN node, Long nid, int level) {
if (inNodeIdsSeen.add(nid)) {
tallyLevel(level, insSeenByLevel);
verifyNode(node);
}
}
public void processBIN(BIN node, Long nid, int level) {
if (binNodeIdsSeen.add(nid)) {
tallyLevel(level, binsSeenByLevel);
verifyNode(node);
}
}
public void processDIN(DIN node, Long nid, int level) {
if (dinNodeIdsSeen.add(nid)) {
tallyLevel(level, dinsSeenByLevel);
verifyNode(node);
}
}
public void processDBIN(DBIN node, Long nid, int level) {
if (dbinNodeIdsSeen.add(nid)) {
tallyLevel(level, dbinsSeenByLevel);
verifyNode(node);
}
}
public void processDupCountLN(DupCountLN node, Long nid) {
dupCountLNsSeen.add(nid);
verifyNode(node);
}
private void tallyLevel(int levelArg, long[] nodesSeenByLevel) {
int level = levelArg;
if (level >= IN.DBMAP_LEVEL) {
return;
}
if (level >= IN.MAIN_LEVEL) {
level &= ~IN.MAIN_LEVEL;
if (level > mainTreeMaxDepth) {
mainTreeMaxDepth = level;
}
} else {
if (level > duplicateTreeMaxDepth) {
duplicateTreeMaxDepth = level;
}
}
nodesSeenByLevel[level]++;
}
public void incrementLNCount() {
lnCount++;
if (progressInterval != 0) {
if ((lnCount % progressInterval) == 0) {
copyToStats(useStats);
progressStream.println(useStats);
}
}
}
public void incrementDeletedLNCount() {
deletedLNCount++;
}
Set getINNodeIdsSeen() {
return inNodeIdsSeen;
}
Set getBINNodeIdsSeen() {
return binNodeIdsSeen;
}
Set getDINNodeIdsSeen() {
return dinNodeIdsSeen;
}
Set getDBINNodeIdsSeen() {
return dbinNodeIdsSeen;
}
long[] getINsByLevel() {
return insSeenByLevel;
}
long[] getBINsByLevel() {
return binsSeenByLevel;
}
long[] getDINsByLevel() {
return dinsSeenByLevel;
}
long[] getDBINsByLevel() {
return dbinsSeenByLevel;
}
long getLNCount() {
return lnCount;
}
Set getDupCountLNCount() {
return dupCountLNsSeen;
}
long getDeletedLNCount() {
return deletedLNCount;
}
int getMainTreeMaxDepth() {
return mainTreeMaxDepth;
}
int getDuplicateTreeMaxDepth() {
return duplicateTreeMaxDepth;
}
private void copyToStats(DatabaseStats stats) {
BtreeStats bStats = (BtreeStats) stats;
bStats.setInternalNodeCount(getINNodeIdsSeen().size());
bStats.setBottomInternalNodeCount
(getBINNodeIdsSeen().size());
bStats.setDuplicateInternalNodeCount
(getDINNodeIdsSeen().size());
bStats.setDuplicateBottomInternalNodeCount
(getDBINNodeIdsSeen().size());
bStats.setLeafNodeCount(getLNCount());
bStats.setDeletedLeafNodeCount(getDeletedLNCount());
bStats.setDupCountLeafNodeCount
(getDupCountLNCount().size());
bStats.setMainTreeMaxDepth(getMainTreeMaxDepth());
bStats.setDuplicateTreeMaxDepth(getDuplicateTreeMaxDepth());
bStats.setINsByLevel(getINsByLevel());
bStats.setBINsByLevel(getBINsByLevel());
bStats.setDINsByLevel(getDINsByLevel());
bStats.setDBINsByLevel(getDBINsByLevel());
}
}
/**
* Preload the cache, using up to maxBytes bytes or maxMillsecs msec.
*/
public void preload(long maxBytes, long maxMillisecs)
throws DatabaseException {
long targetTime = Long.MAX_VALUE;
if (maxMillisecs > 0) {
targetTime = System.currentTimeMillis() + maxMillisecs;
}
IN next = tree.getFirstNode();
if (next == null) {
return;
}
if (maxBytes == 0) {
maxBytes = envImpl.getMemoryBudget().getCacheBudget();
}
try {
while (true) {
if (System.currentTimeMillis() > targetTime) {
break;
}
next = tree.getNextBin((BIN) next, null);
if (next == null) {
break;
}
if (envImpl.getMemoryBudget().getCacheMemoryUsage() >
maxBytes) {
break;
}
}
} finally {
if (next != null) {
next.releaseLatch();
}
}
assert Latch.countLatchesHeld() == 0;
}
/*
* Dumping
*/
public String dumpString(int nSpaces) {
StringBuffer sb = new StringBuffer();
sb.append(TreeUtils.indent(nSpaces));
sb.append("<database id=\"" );
sb.append(id.toString());
sb.append("\"");
if (btreeComparator != null) {
sb.append(" btc=\"");
sb.append(serializeComparator(btreeComparator));
sb.append("\"");
}
if (duplicateComparator != null) {
sb.append(" dupc=\"");
sb.append(serializeComparator(duplicateComparator));
sb.append("\"");
}
sb.append("/>");
return sb.toString();
}
/*
* Logging support
*/
/**
* @see LogWritable#getLogSize
*/
public int getLogSize() {
return
id.getLogSize() +
tree.getLogSize() +
LogUtils.getBooleanLogSize() +
LogUtils.getStringLogSize(serializeComparator(btreeComparator)) +
LogUtils.getStringLogSize
(serializeComparator(duplicateComparator)) +
(LogUtils.getIntLogSize() * 2);
}
/**
* @see LogWritable#writeToLog
*/
public void writeToLog(ByteBuffer logBuffer) {
id.writeToLog(logBuffer);
tree.writeToLog(logBuffer);
LogUtils.writeBoolean(logBuffer, duplicatesAllowed);
LogUtils.writeString(logBuffer,
serializeComparator(btreeComparator));
LogUtils.writeString(logBuffer,
serializeComparator(duplicateComparator));
LogUtils.writeInt(logBuffer, maxMainTreeEntriesPerNode);
LogUtils.writeInt(logBuffer, maxDupTreeEntriesPerNode);
}
/**
* @see LogReadable#readFromLog
*/
public void readFromLog(ByteBuffer itemBuffer, byte entryTypeVersion)
throws LogException {
id.readFromLog(itemBuffer, entryTypeVersion);
tree.readFromLog(itemBuffer, entryTypeVersion);
duplicatesAllowed = LogUtils.readBoolean(itemBuffer);
btreeComparatorName = LogUtils.readString(itemBuffer);
duplicateComparatorName = LogUtils.readString(itemBuffer);
try {
if (!EnvironmentImpl.getNoComparators()) {
/*
* Don't instantiate if comparators are unnecessary
* (DbPrintLog).
*/
if (btreeComparatorName.length() != 0) {
Class btreeComparatorClass =
Class.forName(btreeComparatorName);
btreeComparator =
instantiateComparator(btreeComparatorClass, "Btree");
}
if (duplicateComparatorName.length() != 0) {
Class duplicateComparatorClass =
Class.forName(duplicateComparatorName);
duplicateComparator =
instantiateComparator(duplicateComparatorClass,
"Duplicate");
}
}
} catch (ClassNotFoundException CNFE) {
throw new LogException("couldn't instantiate class comparator",
CNFE);
}
if (entryTypeVersion >= 1) {
maxMainTreeEntriesPerNode = LogUtils.readInt(itemBuffer);
maxDupTreeEntriesPerNode = LogUtils.readInt(itemBuffer);
}
}
/**
* @see LogReadable#dumpLog
*/
public void dumpLog(StringBuffer sb, boolean verbose) {
sb.append("<database>");
id.dumpLog(sb, verbose);
tree.dumpLog(sb, verbose);
sb.append("<dupsort v=\"").append(duplicatesAllowed);
sb.append("\"/>");
sb.append("<btcf name=\"");
sb.append(btreeComparatorName);
sb.append("\"/>");
sb.append("<dupcf name=\"");
sb.append(duplicateComparatorName);
sb.append("\"/>");
sb.append("</database>");
}
/**
* @see LogReadable#logEntryIsTransactional
*/
public boolean logEntryIsTransactional() {
return false;
}
/**
* @see LogReadable#getTransactionId
*/
public long getTransactionId() {
return 0;
}
/**
* Used both to write to the log and to validate a comparator when set in
* DatabaseConfig.
*/
public static String serializeComparator(Comparator comparator) {
if (comparator != null) {
return comparator.getClass().getName();
} else {
return "";
}
}
/**
* Used both to read from the log and to validate a comparator when set in
* DatabaseConfig.
*/
public static Comparator instantiateComparator(Class comparator,
String comparatorType)
throws LogException {
if (comparator == null) {
return null;
}
Comparator ret = null;
try {
return (Comparator) comparator.newInstance();
} catch (InstantiationException IE) {
throw new LogException
("Exception while trying to load " + comparatorType +
" Comparator class: " + IE);
} catch (IllegalAccessException IAE) {
throw new LogException
("Exception while trying to load " + comparatorType +
" Comparator class: " + IAE);
}
}
public int getBinDeltaPercent() {
return binDeltaPercent;
}
public int getBinMaxDeltas() {
return binMaxDeltas;
}
}