package com.sleepycat.je.txn; import java.nio.ByteBuffer; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import javax.transaction.xa.XAResource; import javax.transaction.xa.Xid; import com.sleepycat.je.Database; import com.sleepycat.je.DatabaseException; import com.sleepycat.je.DbInternal; import com.sleepycat.je.LockStats; import com.sleepycat.je.RunRecoveryException; import com.sleepycat.je.TransactionConfig; import com.sleepycat.je.dbi.CursorImpl; import com.sleepycat.je.dbi.DatabaseId; import com.sleepycat.je.dbi.DatabaseImpl; import com.sleepycat.je.dbi.EnvironmentImpl; import com.sleepycat.je.dbi.MemoryBudget; import com.sleepycat.je.log.LogManager; import com.sleepycat.je.log.LogReadable; import com.sleepycat.je.log.LogUtils; import com.sleepycat.je.log.LogWritable; import com.sleepycat.je.log.entry.LNLogEntry; import com.sleepycat.je.recovery.RecoveryManager; import com.sleepycat.je.tree.LN; import com.sleepycat.je.tree.TreeLocation; import com.sleepycat.je.utilint.DbLsn; import com.sleepycat.je.utilint.Tracer; import de.ovgu.cide.jakutil.*; /** * A Txn is one that's created by a call to Environment.txnBegin. This class * must support multithreaded use. */ public class Txn extends Locker implements LogWritable, LogReadable { public static final byte TXN_NOSYNC=0; public static final byte TXN_WRITE_NOSYNC=1; public static final byte TXN_SYNC=2; private static final String DEBUG_NAME=Txn.class.getName(); private byte txnState; private CursorImpl cursorSet; private static final byte USABLE=0; private static final byte CLOSED=1; private static final byte ONLY_ABORTABLE=2; private static final byte STATE_BITS=3; private static final byte IS_PREPARED=4; private static final byte XA_SUSPENDED=8; private Set readLocks; private Map writeInfo; private Map undoDatabases; private long lastLoggedLsn=DbLsn.NULL_LSN; private long firstLoggedLsn=DbLsn.NULL_LSN; private byte defaultFlushSyncBehavior; private boolean serializableIsolation; private boolean readCommittedIsolation; private int inMemorySize; public static int ACCUMULATED_LIMIT=10000; /** * Create a transaction from Environment.txnBegin. */ public Txn( EnvironmentImpl envImpl, TransactionConfig config) throws DatabaseException { super(envImpl,config.getReadUncommitted(),config.getNoWait()); init(envImpl,config); } public Txn( EnvironmentImpl envImpl, TransactionConfig config, long id) throws DatabaseException { super(envImpl,config.getReadUncommitted(),config.getNoWait()); init(envImpl,config); this.id=id; } private void init( EnvironmentImpl envImpl, TransactionConfig config) throws DatabaseException { serializableIsolation=config.getSerializableIsolation(); readCommittedIsolation=config.getReadCommitted(); if (config.getSync()) { defaultFlushSyncBehavior=TXN_SYNC; } else if (config.getWriteNoSync()) { defaultFlushSyncBehavior=TXN_WRITE_NOSYNC; } else if (config.getNoSync()) { defaultFlushSyncBehavior=TXN_NOSYNC; } else { defaultFlushSyncBehavior=TXN_SYNC; } lastLoggedLsn=DbLsn.NULL_LSN; firstLoggedLsn=DbLsn.NULL_LSN; txnState=USABLE; this.hook809(); this.envImpl.getTxnManager().registerTxn(this); } /** * Constructor for reading from log. */ public Txn(){ lastLoggedLsn=DbLsn.NULL_LSN; } /** * UserTxns get a new unique id for each instance. */ protected long generateId( TxnManager txnManager){ return txnManager.incTxnId(); } /** * Access to last LSN. */ long getLastLsn(){ return lastLoggedLsn; } public void setPrepared( boolean prepared){ if (prepared) { txnState|=IS_PREPARED; } else { txnState&=~IS_PREPARED; } } public void setSuspended( boolean suspended){ if (suspended) { txnState|=XA_SUSPENDED; } else { txnState&=~XA_SUSPENDED; } } public boolean isSuspended(){ return (txnState & XA_SUSPENDED) != 0; } /** * Gets a lock on this nodeId and, if it is a write lock, saves an abort * LSN. Caller will set the abortLsn later, after the write lock has been * obtained. * @see Locker#lockInternal * @Override */ LockResult lockInternal( long nodeId, LockType lockType, boolean noWait, DatabaseImpl database) throws DatabaseException { long timeout=0; boolean useNoWait=noWait || defaultNoWait; synchronized (this) { checkState(false); if (!useNoWait) { timeout=lockTimeOutMillis; } } LockGrantType grant=lockManager.lock(nodeId,this,lockType,timeout,useNoWait,database); WriteLockInfo info=null; if (writeInfo != null) { if (grant != LockGrantType.DENIED && lockType.isWriteLock()) { synchronized (this) { info=(WriteLockInfo)writeInfo.get(new Long(nodeId)); undoDatabases.put(database.getId(),database); } } } return new LockResult(grant,info); } public int prepare( Xid xid) throws DatabaseException { if ((txnState & IS_PREPARED) != 0) { throw new DatabaseException("prepare() has already been called for Transaction " + id + "."); } synchronized (this) { checkState(false); if (checkCursorsForClose()) { throw new DatabaseException("Transaction " + id + " prepare failed because there were open cursors."); } TxnPrepare prepareRecord=new TxnPrepare(id,xid); LogManager logManager=envImpl.getLogManager(); logManager.logForceFlush(prepareRecord,true); } setPrepared(true); return XAResource.XA_OK; } public void commit( Xid xid) throws DatabaseException { commit(TXN_SYNC); envImpl.getTxnManager().unRegisterXATxn(xid,true); return; } public void abort( Xid xid) throws DatabaseException { abort(true); envImpl.getTxnManager().unRegisterXATxn(xid,false); return; } /** * Call commit() with the default sync configuration property. */ public long commit() throws DatabaseException { return commit(defaultFlushSyncBehavior); } /** * Commit this transaction * 1. Releases read locks * 2. Writes a txn commit record into the log * 3. Flushes the log to disk. * 4. Add deleted LN info to IN compressor queue * 5. Release all write locks * If any step of this fails, we must convert this transaction to an abort. */ public long commit( byte flushSyncBehavior) throws DatabaseException { try { long commitLsn=DbLsn.NULL_LSN; synchronized (this) { checkState(false); if (checkCursorsForClose()) { throw new DatabaseException("Transaction " + id + " commit failed because there were open cursors."); } if (handleLockToHandleMap != null) { Iterator handleLockIter=handleLockToHandleMap.entrySet().iterator(); while (handleLockIter.hasNext()) { Map.Entry entry=(Map.Entry)handleLockIter.next(); transferHandleLockToHandleSet((Long)entry.getKey(),(Set)entry.getValue()); } } LogManager logManager=envImpl.getLogManager(); int numReadLocks=clearReadLocks(); int numWriteLocks=0; if (writeInfo != null) { numWriteLocks=writeInfo.size(); TxnCommit commitRecord=new TxnCommit(id,lastLoggedLsn); if (flushSyncBehavior == TXN_SYNC) { commitLsn=logManager.logForceFlush(commitRecord,true); } else if (flushSyncBehavior == TXN_WRITE_NOSYNC) { commitLsn=logManager.logForceFlush(commitRecord,false); } else { commitLsn=logManager.log(commitRecord); } this.hook806(); Set alreadyCountedLsnSet=new HashSet(); Iterator iter=writeInfo.values().iterator(); while (iter.hasNext()) { WriteLockInfo info=(WriteLockInfo)iter.next(); lockManager.release(info.lock,this); if (info.abortLsn != DbLsn.NULL_LSN && !info.abortKnownDeleted) { Long longLsn=new Long(info.abortLsn); if (!alreadyCountedLsnSet.contains(longLsn)) { logManager.countObsoleteNode(info.abortLsn,null); alreadyCountedLsnSet.add(longLsn); } } } writeInfo=null; this.hook803(); } traceCommit(numWriteLocks,numReadLocks); } this.hook805(); close(true); return commitLsn; } catch ( RunRecoveryException e) { throw e; } catch ( Throwable t) { try { abortInternal(flushSyncBehavior == TXN_SYNC,!(t instanceof DatabaseException)); this.hook800(t); } catch ( Throwable abortT2) { throw new DatabaseException("Failed while attempting to commit transaction " + id + ". The attempt to abort and clean up also failed. "+ "The original exception seen from commit = "+ t.getMessage()+ " The exception from the cleanup = "+ abortT2.getMessage(),t); } throw new DatabaseException("Failed while attempting to commit transaction " + id + ", aborted instead. Original exception = "+ t.getMessage(),t); } } /** * Abort this transaction. Steps are: * 1. Release LN read locks. * 2. Write a txn abort entry to the log. This is only for log * file cleaning optimization and there's no need to guarantee a * flush to disk. * 3. Find the last LN log entry written for this txn, and use that * to traverse the log looking for nodes to undo. For each node, * use the same undo logic as recovery to rollback the transaction. Note * that we walk the log in order to undo in reverse order of the * actual operations. For example, suppose the txn did this: * delete K1/D1 (in LN 10) * create K1/D1 (in LN 20) * If we process LN10 before LN 20, we'd inadvertently create a * duplicate tree of "K1", which would be fatal for the mapping tree. * 4. Release the write lock for this LN. */ public long abort( boolean forceFlush) throws DatabaseException { return abortInternal(forceFlush,true); } private long abortInternal( boolean forceFlush, boolean writeAbortRecord) throws DatabaseException { try { int numReadLocks; int numWriteLocks; long abortLsn; synchronized (this) { checkState(true); TxnAbort abortRecord=new TxnAbort(id,lastLoggedLsn); abortLsn=DbLsn.NULL_LSN; if (writeInfo != null) { if (writeAbortRecord) { if (forceFlush) { abortLsn=envImpl.getLogManager().logForceFlush(abortRecord,true); } else { abortLsn=envImpl.getLogManager().log(abortRecord); } } } undo(); numReadLocks=(readLocks == null) ? 0 : clearReadLocks(); this.hook808(); numWriteLocks=(writeInfo == null) ? 0 : clearWriteLocks(); this.hook804(); } this.hook807(); synchronized (this) { boolean openCursors=checkCursorsForClose(); this.hook799(numReadLocks,numWriteLocks,openCursors); if (openCursors) { throw new DatabaseException("Transaction " + id + " detected open cursors while aborting"); } if (handleToHandleLockMap != null) { Iterator handleIter=handleToHandleLockMap.keySet().iterator(); while (handleIter.hasNext()) { Database handle=(Database)handleIter.next(); DbInternal.dbInvalidate(handle); } } return abortLsn; } } finally { close(false); } } /** * Rollback the changes to this txn's write locked nodes. */ private void undo() throws DatabaseException { Long nodeId=null; long undoLsn=lastLoggedLsn; LogManager logManager=envImpl.getLogManager(); try { Set alreadyUndone=new HashSet(); TreeLocation location=new TreeLocation(); while (undoLsn != DbLsn.NULL_LSN) { LNLogEntry undoEntry=(LNLogEntry)logManager.getLogEntry(undoLsn); LN undoLN=undoEntry.getLN(); nodeId=new Long(undoLN.getNodeId()); if (!alreadyUndone.contains(nodeId)) { alreadyUndone.add(nodeId); DatabaseId dbId=undoEntry.getDbId(); DatabaseImpl db=(DatabaseImpl)undoDatabases.get(dbId); undoLN.postFetchInit(db,undoLsn); long abortLsn=undoEntry.getAbortLsn(); boolean abortKnownDeleted=undoEntry.getAbortKnownDeleted(); this.hook802(undoLsn,location,undoEntry,undoLN,db,abortLsn,abortKnownDeleted); if (!undoLN.isDeleted()) { logManager.countObsoleteNode(undoLsn,null); } } undoLsn=undoEntry.getUserTxn().getLastLsn(); } } catch ( RuntimeException e) { throw new DatabaseException("Txn undo for node=" + nodeId + " LSN="+ DbLsn.getNoFormatString(undoLsn),e); } catch ( DatabaseException e) { this.hook801(nodeId,undoLsn,e); throw e; } } private int clearWriteLocks() throws DatabaseException { int numWriteLocks=writeInfo.size(); Iterator iter=writeInfo.values().iterator(); while (iter.hasNext()) { WriteLockInfo info=(WriteLockInfo)iter.next(); lockManager.release(info.lock,this); } writeInfo=null; return numWriteLocks; } private int clearReadLocks() throws DatabaseException { int numReadLocks=0; if (readLocks != null) { numReadLocks=readLocks.size(); Iterator iter=readLocks.iterator(); while (iter.hasNext()) { Lock rLock=(Lock)iter.next(); lockManager.release(rLock,this); } readLocks=null; } return numReadLocks; } /** * Called by the recovery manager when logging a transaction aware object. * This method is synchronized by the caller, by being called within the * log latch. Record the last LSN for this transaction, to create the * transaction chain, and also record the LSN in the write info for abort * logic. */ public void addLogInfo( long lastLsn) throws DatabaseException { lastLoggedLsn=lastLsn; synchronized (this) { if (firstLoggedLsn == DbLsn.NULL_LSN) { firstLoggedLsn=lastLsn; } } } /** * @return first logged LSN, to aid recovery rollback. */ long getFirstActiveLsn() throws DatabaseException { synchronized (this) { return firstLoggedLsn; } } /** * Add lock to the appropriate queue. */ void addLock( Long nodeId, Lock lock, LockType type, LockGrantType grantStatus) throws DatabaseException { new Txn_addLock(this,nodeId,lock,type,grantStatus).execute(); } private void addReadLock( Lock lock){ int delta=0; if (readLocks == null) { readLocks=new HashSet(); delta=this.hook811(delta); } readLocks.add(lock); this.hook810(delta); } /** * Remove the lock from the set owned by this transaction. If specified to * LockManager.release, the lock manager will call this when its releasing * a lock. Usually done because the transaction doesn't need to really keep * the lock, i.e for a deleted record. */ void removeLock( long nodeId, Lock lock) throws DatabaseException { synchronized (this) { if ((readLocks != null) && readLocks.remove(lock)) { this.hook812(); } else if ((writeInfo != null) && (writeInfo.remove(new Long(nodeId)) != null)) { this.hook813(); } } } /** * A lock is being demoted. Move it from the write collection into the read * collection. */ void moveWriteToReadLock( long nodeId, Lock lock){ boolean found=false; synchronized (this) { if ((writeInfo != null) && (writeInfo.remove(new Long(nodeId)) != null)) { found=true; this.hook814(); } assert found : "Couldn't find lock for Node " + nodeId + " in writeInfo Map."; addReadLock(lock); } } /** * @return true if this transaction created this node. We know that this * is true if the node is write locked and has a null abort LSN. */ public boolean createdNode( long nodeId) throws DatabaseException { boolean created=false; synchronized (this) { if (writeInfo != null) { WriteLockInfo info=(WriteLockInfo)writeInfo.get(new Long(nodeId)); if (info != null) { created=info.createdThisTxn; } } } return created; } /** * @return the abortLsn for this node. */ public long getAbortLsn( long nodeId) throws DatabaseException { WriteLockInfo info=null; synchronized (this) { if (writeInfo != null) { info=(WriteLockInfo)writeInfo.get(new Long(nodeId)); } } if (info == null) { return DbLsn.NULL_LSN; } else { return info.abortLsn; } } /** * @return the WriteLockInfo for this node. */ public WriteLockInfo getWriteLockInfo( long nodeId) throws DatabaseException { WriteLockInfo info=WriteLockInfo.basicWriteLockInfo; synchronized (this) { if (writeInfo != null) { info=(WriteLockInfo)writeInfo.get(new Long(nodeId)); } } return info; } /** * Is always transactional. */ public boolean isTransactional(){ return true; } /** * Is serializable isolation if so configured. */ public boolean isSerializableIsolation(){ return serializableIsolation; } /** * Is read-committed isolation if so configured. */ public boolean isReadCommittedIsolation(){ return readCommittedIsolation; } /** * This is a transactional locker. */ public Txn getTxnLocker(){ return this; } /** * Returns 'this', since this locker holds no non-transactional locks. */ public Locker newNonTxnLocker() throws DatabaseException { return this; } /** * This locker holds no non-transactional locks. */ public void releaseNonTxnLocks() throws DatabaseException { } /** * Created transactions do nothing at the end of the operation. */ public void operationEnd() throws DatabaseException { } /** * Created transactions do nothing at the end of the operation. */ public void operationEnd( boolean operationOK) throws DatabaseException { } /** * Created transactions don't transfer locks until commit. */ public void setHandleLockOwner( boolean ignore, Database dbHandle, boolean dbIsClosing) throws DatabaseException { if (dbIsClosing) { Long handleLockId=(Long)handleToHandleLockMap.get(dbHandle); if (handleLockId != null) { Set dbHandleSet=(Set)handleLockToHandleMap.get(handleLockId); boolean removed=dbHandleSet.remove(dbHandle); assert removed : "Can't find " + dbHandle + " from dbHandleSet"; if (dbHandleSet.size() == 0) { Object foo=handleLockToHandleMap.remove(handleLockId); assert (foo != null) : "Can't find " + handleLockId + " from handleLockIdtoHandleMap."; } } unregisterHandle(dbHandle); } else { if (dbHandle != null) { DbInternal.dbSetHandleLocker(dbHandle,this); } } } /** * Cursors operating under this transaction are added to the collection. */ public void registerCursor( CursorImpl cursor) throws DatabaseException { synchronized (this) { cursor.setLockerNext(cursorSet); if (cursorSet != null) { cursorSet.setLockerPrev(cursor); } cursorSet=cursor; } } /** * Remove a cursor from the collection. */ public void unRegisterCursor( CursorImpl cursor) throws DatabaseException { synchronized (this) { CursorImpl prev=cursor.getLockerPrev(); CursorImpl next=cursor.getLockerNext(); if (prev == null) { cursorSet=next; } else { prev.setLockerNext(next); } if (next != null) { next.setLockerPrev(prev); } cursor.setLockerPrev(null); cursor.setLockerNext(null); } } /** * @return true if this txn is willing to give up the handle lock to * another txn before this txn ends. */ public boolean isHandleLockTransferrable(){ return false; } /** * Check if all cursors associated with the txn are closed. If not, those * open cursors will be forcibly closed. * @return true if open cursors exist */ private boolean checkCursorsForClose() throws DatabaseException { CursorImpl c=cursorSet; while (c != null) { if (!c.isClosed()) { return true; } c=c.getLockerNext(); } return false; } /** * Set the state of a transaction to ONLY_ABORTABLE. */ public void setOnlyAbortable(){ txnState&=~STATE_BITS; txnState|=ONLY_ABORTABLE; } /** * Get the state of a transaction's ONLY_ABORTABLE. */ public boolean getOnlyAbortable(){ return (txnState & ONLY_ABORTABLE) != 0; } /** * Throw an exception if the transaction is not open. * If calledByAbort is true, it means we're being called * from abort(). * Caller must invoke with "this" synchronized. */ protected void checkState( boolean calledByAbort) throws DatabaseException { boolean ok=false; boolean onlyAbortable=false; byte state=(byte)(txnState & STATE_BITS); ok=(state == USABLE); onlyAbortable=(state == ONLY_ABORTABLE); if (!calledByAbort && onlyAbortable) { throw new DatabaseException("Transaction " + id + " must be aborted."); } if (ok || (calledByAbort && onlyAbortable)) { return; } throw new DatabaseException("Transaction " + id + " has been closed."); } /** */ private void close( boolean isCommit) throws DatabaseException { synchronized (this) { txnState&=~STATE_BITS; txnState|=CLOSED; } envImpl.getTxnManager().unRegisterTxn(this,isCommit); } /** * @see LogWritable#getLogSize */ public int getLogSize(){ return LogUtils.LONG_BYTES + LogUtils.LONG_BYTES; } /** * @see LogWritable#writeToLog */ public void writeToLog( ByteBuffer logBuffer){ LogUtils.writeLong(logBuffer,id); LogUtils.writeLong(logBuffer,lastLoggedLsn); } /** * @see LogReadable#readFromLogIt's ok for FindBugs to whine about id not being synchronized. */ public void readFromLog( ByteBuffer logBuffer, byte entryTypeVersion){ id=LogUtils.readLong(logBuffer); lastLoggedLsn=LogUtils.readLong(logBuffer); } /** * @see LogReadable#dumpLog */ public void dumpLog( StringBuffer sb, boolean verbose){ sb.append("<txn id=\""); sb.append(super.toString()); sb.append("\">"); sb.append(DbLsn.toString(lastLoggedLsn)); sb.append("</txn>"); } /** * @see LogReadable#getTransactionId */ public long getTransactionId(){ return getId(); } /** * @see LogReadable#logEntryIsTransactional */ public boolean logEntryIsTransactional(){ return true; } /** * Transfer a single handle lock to the set of corresponding handles at * commit time. */ private void transferHandleLockToHandleSet( Long handleLockId, Set dbHandleSet) throws DatabaseException { int numHandles=dbHandleSet.size(); Database[] dbHandles=new Database[numHandles]; dbHandles=(Database[])dbHandleSet.toArray(dbHandles); Locker[] destTxns=new Locker[numHandles]; for (int i=0; i < numHandles; i++) { destTxns[i]=new BasicLocker(envImpl); } long nodeId=handleLockId.longValue(); lockManager.transferMultiple(nodeId,this,destTxns); for (int i=0; i < numHandles; i++) { destTxns[i].addToHandleMaps(handleLockId,dbHandles[i]); DbInternal.dbSetHandleLocker(dbHandles[i],destTxns[i]); } } /** * Send trace messages to the java.util.logger. Don't rely on the logger * alone to conditionalize whether we send this message, we don't even want * to construct the message if the level is not enabled. The string * construction can be numerous enough to show up on a performance profile. */ private void traceCommit( int numWriteLocks, int numReadLocks){ new Txn_traceCommit(this,numWriteLocks,numReadLocks).execute(); } int getInMemorySize(){ return inMemorySize; } /** * Store information about a DatabaseImpl that will have to be * purged at transaction commit or abort. This handles cleanup after * operations like Environment.truncateDatabase, * Environment.removeDatabase. Cleanup like this is done outside the * usual transaction commit or node undo processing, because * the mapping tree is always AutoTxn'ed to avoid deadlock and is * essentially non-transactional */ private static class DatabaseCleanupInfo { DatabaseImpl dbImpl; boolean deleteAtCommit; DatabaseCleanupInfo( DatabaseImpl dbImpl, boolean deleteAtCommit){ this.dbImpl=dbImpl; this.deleteAtCommit=deleteAtCommit; } } @MethodObject static class Txn_addLock { Txn_addLock( Txn _this, Long nodeId, Lock lock, LockType type, LockGrantType grantStatus){ this._this=_this; this.nodeId=nodeId; this.lock=lock; this.type=type; this.grantStatus=grantStatus; } void execute() throws DatabaseException { synchronized (_this) { this.hook815(); if (type.isWriteLock()) { if (_this.writeInfo == null) { _this.writeInfo=new HashMap(); _this.undoDatabases=new HashMap(); this.hook818(); } _this.writeInfo.put(nodeId,new WriteLockInfo(lock)); this.hook817(); if ((grantStatus == LockGrantType.PROMOTION) || (grantStatus == LockGrantType.WAIT_PROMOTION)) { _this.readLocks.remove(lock); this.hook819(); } this.hook816(); } else { _this.addReadLock(lock); } } } protected Txn _this; protected Long nodeId; protected Lock lock; protected LockType type; protected LockGrantType grantStatus; protected int delta; protected void hook815() throws DatabaseException { } protected void hook816() throws DatabaseException { } protected void hook817() throws DatabaseException { } protected void hook818() throws DatabaseException { } protected void hook819() throws DatabaseException { } } @MethodObject static class Txn_traceCommit { Txn_traceCommit( Txn _this, int numWriteLocks, int numReadLocks){ this._this=_this; this.numWriteLocks=numWriteLocks; this.numReadLocks=numReadLocks; } void execute(){ } protected Txn _this; protected int numWriteLocks; protected int numReadLocks; protected Logger logger; protected StringBuffer sb; } protected void hook799( int numReadLocks, int numWriteLocks, boolean openCursors) throws DatabaseException { } protected void hook800( Throwable t) throws DatabaseException, Throwable { } protected void hook801( Long nodeId, long undoLsn, DatabaseException e) throws DatabaseException { } protected void hook802( long undoLsn, TreeLocation location, LNLogEntry undoEntry, LN undoLN, DatabaseImpl db, long abortLsn, boolean abortKnownDeleted) throws DatabaseException, RuntimeException { RecoveryManager.undo(Level.FINER,db,location,undoLN,undoEntry.getKey(),undoEntry.getDupKey(),undoLsn,abortLsn,abortKnownDeleted,null,false); } protected void hook803() throws DatabaseException, RunRecoveryException, Throwable { } protected void hook804() throws DatabaseException { } protected void hook805() throws DatabaseException, RunRecoveryException, Throwable { } protected void hook806() throws DatabaseException, RunRecoveryException, Throwable { } protected void hook807() throws DatabaseException { } protected void hook808() throws DatabaseException { } protected void hook809() throws DatabaseException { } protected void hook810( int delta){ } protected int hook811( int delta){ return delta; } protected void hook812() throws DatabaseException { } protected void hook813() throws DatabaseException { } protected void hook814(){ } }