gt:     protected void hook150() throws DatabaseException {
context: package com.sleepycat.je.cleaner;
  import java.io.IOException;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.Iterator;
  import java.util.Map;
  import java.util.Set;
  import java.util.SortedMap;
  import java.util.TreeMap;
  import java.util.logging.Level;
  import com.sleepycat.je.DatabaseException;
  import com.sleepycat.je.dbi.DatabaseId;
  import com.sleepycat.je.dbi.DatabaseImpl;
  import com.sleepycat.je.dbi.DbTree;
  import com.sleepycat.je.dbi.EnvironmentImpl;
  import com.sleepycat.je.dbi.MemoryBudget;
  import com.sleepycat.je.log.CleanerFileReader;
  import com.sleepycat.je.tree.BIN;
  import com.sleepycat.je.tree.ChildReference;
  import com.sleepycat.je.tree.DIN;
  import com.sleepycat.je.tree.IN;
  import com.sleepycat.je.tree.LN;
  import com.sleepycat.je.tree.SearchResult;
  import com.sleepycat.je.tree.Tree;
  import com.sleepycat.je.tree.TreeLocation;
  import com.sleepycat.je.tree.WithRootLatched;
  import com.sleepycat.je.txn.BasicLocker;
  import com.sleepycat.je.txn.LockGrantType;
  import com.sleepycat.je.txn.LockResult;
  import com.sleepycat.je.txn.LockType;
  import com.sleepycat.je.utilint.DaemonThread;
  import com.sleepycat.je.utilint.DbLsn;
  import com.sleepycat.je.utilint.Tracer;
  import de.ovgu.cide.jakutil.*;
  /** 
   * Reads all entries in a log file and either determines them to be obsolete or
   * marks them for migration. LNs are marked for migration by setting the BIN
   * entry MIGRATE flag. INs are marked for migration by setting the dirty flag.
   * May be invoked explicitly by calling doClean, or woken up if used as a daemon
   * thread.
   */
  class FileProcessor extends DaemonThread {
    /** 
   * The number of LN log entries after we process pending LNs. If we do this
   * too seldom, the pending LN queue may grow large, and it isn't budgeted
   * memory. If we process it too often, we will repeatedly request a
   * non-blocking lock for the same locked node.
   */
    private static final int PROCESS_PENDING_EVERY_N_LNS=100;
    /** 
   * Whether to prohibit BINDeltas for a BIN that is fetched by the cleaner.
   * The theory is that when fetching a BIN during cleaning we normally expect
   * that the BIN will be evicted soon, and a delta during checkpoint would be
   * wasted. However, this does not take into account use of the BIN by the
   * application after fetching; the BIN could become hot and then deltas may
   * be profitable. To be safe we currently allow deltas when fetching.
   */
    private static final boolean PROHIBIT_DELTAS_WHEN_FETCHING=false;
    private static final boolean DEBUG_TRACING=false;
    private EnvironmentImpl env;
    private Cleaner cleaner;
    private FileSelector fileSelector;
    private UtilizationProfile profile;
    FileProcessor(  String name,  EnvironmentImpl env,  Cleaner cleaner,  UtilizationProfile profile,  FileSelector fileSelector){
      super(0,name,env);
      this.env=env;
      this.cleaner=cleaner;
      this.fileSelector=fileSelector;
      this.profile=profile;
    }
    public void clearEnv(){
      env=null;
      cleaner=null;
      fileSelector=null;
      profile=null;
    }
    /** 
   * Return the number of retries when a deadlock exception occurs.
   */
    protected int nDeadlockRetries() throws DatabaseException {
      return cleaner.nDeadlockRetries;
    }
    /** 
   * Cleaner doesn't have a work queue so just throw an exception if it's ever
   * called.
   */
    public void addToQueue(  Object o) throws DatabaseException {
      throw new DatabaseException("Cleaner.addToQueue should never be called.");
    }
    /** 
   * Activates the cleaner. Is normally called when je.cleaner.byteInterval
   * bytes are written to the log.
   */
    public void onWakeup() throws DatabaseException {
      doClean(true,true,false);
    }
    /** 
   * Cleans selected files and returns the number of files cleaned. May be
   * called by the daemon thread or programatically.
   * @param invokedFromDaemoncurrently has no effect.
   * @param cleanMultipleFilesis true to clean until we're under budget, or false to clean
   * at most one file.
   * @param forceCleaningis true to clean even if we're not under the utilization
   * threshold.
   * @return the number of files cleaned, not including files cleaned
   * unsuccessfully.
   */
    public synchronized int doClean(  boolean invokedFromDaemon,  boolean cleanMultipleFiles,  boolean forceCleaning) throws DatabaseException {
      if (env.isClosed()) {
        return 0;
      }
      int nOriginalLogFiles=profile.getNumberOfFiles();
      int nFilesCleaned=0;
      while (true) {
        if (nFilesCleaned >= nOriginalLogFiles) {
          break;
        }
        if (env.isClosing()) {
          break;
        }
        cleaner.processPending();
        cleaner.deleteSafeToDeleteFiles();
        boolean needLowUtilizationSet=cleaner.clusterResident || cleaner.clusterAll;
        Long fileNum=fileSelector.selectFileForCleaning(profile,forceCleaning,needLowUtilizationSet,cleaner.maxBatchFiles);
        cleaner.updateReadOnlyFileCollections();
        if (fileNum == null) {
          break;
        }
        this.hook138();
        boolean finished=false;
        long fileNumValue=fileNum.longValue();
        int runId=++cleaner.nCleanerRuns;
        try {
          String traceMsg="CleanerRun " + runId + " on file 0x"+ Long.toHexString(fileNumValue);
          traceMsg=this.hook139(traceMsg);
          this.hook121(traceMsg);
          if (DEBUG_TRACING) {
            System.out.println("\n" + traceMsg);
          }
          if (processFile(fileNum)) {
            fileSelector.addCleanedFile(fileNum);
            nFilesCleaned+=1;
            this.hook140();
            finished=true;
          }
        }
   catch (      IOException IOE) {
          this.hook122(IOE);
          throw new DatabaseException(IOE);
        }
   finally {
          if (!finished) {
            fileSelector.putBackFileForCleaning(fileNum);
          }
          String traceMsg="CleanerRun " + runId + " on file 0x"+ Long.toHexString(fileNumValue)+ " invokedFromDaemon="+ invokedFromDaemon+ " finished="+ finished;
          traceMsg=this.hook141(traceMsg);
          this.hook123(traceMsg);
          if (DEBUG_TRACING) {
            System.out.println("\n" + traceMsg);
          }
        }
        if (!cleanMultipleFiles) {
          break;
        }
      }
      return nFilesCleaned;
    }
    /** 
   * Process all log entries in the given file.
   * Note that we check for obsolete entries using the active TFS
   * (TrackedFileSummary) for a file while it is being processed, and we
   * prohibit flushing (eviction) of that offset information until file
   * processing is complete. An entry could become obsolete because: 1- normal
   * application activity deletes or updates the entry, 2- proactive migration
   * migrates the entry before we process it, or 3- if trackDetail is false.
   * However, checking the TFS is expensive if it has many entries, because we
   * perform a linear search. There is a tradeoff between the cost of the TFS
   * lookup and its benefit, which is to avoid a tree search if the entry is
   * obsolete. Note that many more lookups for non-obsolete entries than
   * obsolete entries will typically be done. In spite of that we check the
   * tracked summary to avoid the situation where eviction does proactive
   * migration, and evicts a BIN that is very soon afterward fetched during
   * cleaning.
   * @return false if we aborted file processing because the environment is
   * being closed.
   */
    private boolean processFile(  Long fileNum) throws DatabaseException, IOException {
      return new FileProcessor_processFile(this,fileNum).execute();
    }
    /** 
   * Processes the first LN in the look ahead cache and removes it from the
   * cache. While the BIN is latched, look through the BIN for other LNs in
   * the cache; if any match, process them to avoid a tree search later.
   * @param info
   * @param offset
   */
    private void processLN(  Long fileNum,  TreeLocation location,  Long offset,  LNInfo info,  Object lookAheadCachep,  Map dbCache) throws DatabaseException {
      new FileProcessor_processLN(this,fileNum,location,offset,info,lookAheadCachep,dbCache).execute();
    }
    /** 
   * Processes an LN that was found in the tree. Lock the LN's node ID and
   * then set the entry's MIGRATE flag if the LSN of the LN log entry is the
   * active LSN in the tree.
   * @param infoidentifies the LN log entry.
   * @param logLsnis the LSN of the log entry.
   * @param treeLsnis the LSN found in the tree.
   * @param binis the BIN found in the tree; is latched on method entry and
   * exit.
   * @param indexis the BIN index found in the tree.
   * @param parentDINis non-null for a DupCountLN only; if non-null, is latched on
   * method entry and exit.
   */
    private void processFoundLN(  LNInfo info,  long logLsn,  long treeLsn,  BIN bin,  int index,  DIN parentDIN) throws DatabaseException {
      LN ln=info.getLN();
      byte[] key=info.getKey();
      byte[] dupKey=info.getDupKey();
      DatabaseImpl db=bin.getDatabase();
      boolean isDupCountLN=parentDIN != null;
      boolean obsolete=false;
      boolean migrated=false;
      boolean lockDenied=false;
      boolean completed=false;
      long nodeId=ln.getNodeId();
      BasicLocker locker=null;
      try {
        Tree tree=db.getTree();
        assert tree != null;
        if (treeLsn != logLsn) {
          locker=new BasicLocker(env);
          LockResult lockRet=locker.nonBlockingLock(nodeId,LockType.READ,db);
          if (lockRet.getLockGrant() == LockGrantType.DENIED) {
            this.hook142();
            lockDenied=true;
          }
   else {
            this.hook143();
            obsolete=true;
          }
        }
        if (!obsolete && !lockDenied) {
          if (isDupCountLN) {
            ChildReference dclRef=parentDIN.getDupCountLNRef();
            dclRef.setMigrate(true);
            parentDIN.setDirty(true);
            if (treeLsn == logLsn && dclRef.getTarget() == null) {
              ln.postFetchInit(db,logLsn);
              parentDIN.updateDupCountLN(ln);
            }
          }
   else {
            bin.setMigrate(index,true);
            bin.setDirty(true);
            if (treeLsn == logLsn && bin.getTarget(index) == null) {
              ln.postFetchInit(db,logLsn);
              bin.updateEntry(index,ln);
            }
            if (PROHIBIT_DELTAS_WHEN_FETCHING && bin.getGeneration() == 0) {
              bin.setProhibitNextDelta();
            }
            bin.setGeneration();
          }
          this.hook144();
          migrated=true;
        }
        completed=true;
      }
    finally {
        if (locker != null) {
          locker.operationEnd();
        }
        if (completed && lockDenied) {
          fileSelector.addPendingLN(ln,db.getId(),key,dupKey);
        }
        this.hook124(logLsn,ln,obsolete,migrated,completed);
      }
    }
    /** 
   * If an IN is still in use in the in-memory tree, dirty it. The checkpoint
   * invoked at the end of the cleaning run will end up rewriting it.
   */
    private void processIN(  IN inClone,  DatabaseImpl db,  long lsn) throws DatabaseException {
      try {
        boolean obsolete=false;
        boolean dirtied=false;
        boolean completed=false;
        this.hook125(inClone,db,lsn,obsolete,dirtied,completed);
      }
   catch (    ReturnVoid r) {
        return;
      }
    }
    /** 
   * Given a clone of an IN that has been taken out of the log, try to find it
   * in the tree and verify that it is the current one in the log. Returns the
   * node in the tree if it is found and it is current re: LSN's. Otherwise
   * returns null if the clone is not found in the tree or it's not the latest
   * version. Caller is responsible for unlatching the returned IN.
   */
    private IN findINInTree(  Tree tree,  DatabaseImpl db,  IN inClone,  long lsn) throws DatabaseException {
      try {
        if (inClone.isDbRoot()) {
          IN rootIN=isRoot(tree,db,inClone,lsn);
          if (rootIN == null) {
            return null;
          }
   else {
            return rootIN;
          }
        }
        inClone.latch(Cleaner.UPDATE_GENERATION);
        SearchResult result=null;
        this.hook134(tree,db,inClone,lsn,result);
        throw ReturnHack.returnObject;
      }
   catch (    ReturnObject r) {
        return (IN)r.value;
      }
    }
  private static class RootDoWork implements WithRootLatched {
      private DatabaseImpl db;
      private IN inClone;
      private long lsn;
      RootDoWork(    DatabaseImpl db,    IN inClone,    long lsn){
        this.db=db;
        this.inClone=inClone;
        this.lsn=lsn;
      }
      public IN doWork(    ChildReference root) throws DatabaseException {
        if (root == null || root.fetchTarget(db,null).getNodeId() != inClone.getNodeId()) {
          return null;
        }
        if (DbLsn.compareTo(root.getLsn(),lsn) <= 0) {
          IN rootIN=(IN)root.fetchTarget(db,null);
          rootIN.latch(Cleaner.UPDATE_GENERATION);
          return rootIN;
        }
   else {
          return null;
        }
      }
    }
    /** 
   * Check if the cloned IN is the same node as the root in tree. Return the
   * real root if it is, null otherwise. If non-null is returned, the returned
   * IN (the root) is latched -- caller is responsible for unlatching it.
   */
    private IN isRoot(  Tree tree,  DatabaseImpl db,  IN inClone,  long lsn) throws DatabaseException {
      RootDoWork rdw=new RootDoWork(db,inClone,lsn);
      return tree.withRootLatchedShared(rdw);
    }
    /** 
   * XXX: Was this intended to override Thread.toString()? If so it no longer
   * does, because we separated Thread from DaemonThread.
   */
    public String toString(){
      StringBuffer sb=new StringBuffer();
      sb.append("<Cleaner name=\"").append(name).append("\"/>");
      return sb.toString();
    }
  @MethodObject static class FileProcessor_processFile {
      FileProcessor_processFile(    FileProcessor _this,    Long fileNum){
        this._this=_this;
        this.fileNum=fileNum;
      }
      boolean execute() throws DatabaseException, IOException {
        obsoleteOffsets=new PackedOffsets();
        tfs=_this.profile.getObsoleteDetail(fileNum,obsoleteOffsets,true);
        obsoleteIter=obsoleteOffsets.iterator();
        nextObsolete=-1;
        readBufferSize=_this.cleaner.readBufferSize;
        this.hook128();
        this.hook161();
        this.hook119();
        this.hook127();
        this.hook154();
        dbCache=new HashMap();
        try {
          reader=new CleanerFileReader(_this.env,readBufferSize,DbLsn.NULL_LSN,fileNum);
          this.hook137();
          dbMapTree=_this.env.getDbMapTree();
          location=new TreeLocation();
          nProcessedLNs=0;
          while (reader.readNextEntry()) {
            this.hook146();
            lsn=reader.getLastLsn();
            fileOffset=DbLsn.getFileOffset(lsn);
            isLN=reader.isLN();
            isIN=reader.isIN();
            isRoot=reader.isRoot();
            isObsolete=false;
            if (_this.env.isClosing()) {
              return false;
            }
            while (nextObsolete < fileOffset && obsoleteIter.hasNext()) {
              nextObsolete=obsoleteIter.next();
            }
            if (nextObsolete == fileOffset) {
              isObsolete=true;
            }
            if (!isObsolete && !isLN && !isIN&& !isRoot) {
              isObsolete=true;
            }
            if (!isObsolete && isLN && reader.getLN().isDeleted()) {
              isObsolete=true;
            }
            if (!isObsolete && tfs != null && tfs.containsObsoleteOffset(fileOffset)) {
              isObsolete=true;
            }
            if (isObsolete) {
              this.hook147();
              this.hook156();
              continue;
            }
            this.hook120();
            if (isLN) {
              targetLN=reader.getLN();
              dbId2=reader.getDatabaseId();
              key=reader.getKey();
              dupKey=reader.getDupTreeKey();
              aLsn=new Long(DbLsn.getFileOffset(lsn));
              aLninfo=new LNInfo(targetLN,dbId2,key,dupKey);
              this.hook130();
              nProcessedLNs+=1;
              if (nProcessedLNs % _this.PROCESS_PENDING_EVERY_N_LNS == 0) {
                _this.cleaner.processPending();
              }
            }
   else           if (isIN) {
              targetIN=reader.getIN();
              dbId3=reader.getDatabaseId();
              db3=dbMapTree.getDb(dbId3,_this.cleaner.lockTimeout,dbCache);
              targetIN.setDatabase(db3);
              _this.processIN(targetIN,db3,lsn);
            }
   else           if (isRoot) {
              _this.env.rewriteMapTreeRoot(lsn);
            }
   else {
              assert false;
            }
          }
          this.hook129();
          this.hook155();
          this.hook145();
        }
    finally {
          this.hook162();
          if (tfs != null) {
            tfs.setAllowFlush(true);
          }
        }
        return true;
      }
      protected FileProcessor _this;
      protected Long fileNum;
      protected PackedOffsets obsoleteOffsets;
      protected TrackedFileSummary tfs;
      protected PackedOffsets.Iterator obsoleteIter;
      protected long nextObsolete;
      protected int readBufferSize;
      protected int lookAheadCacheSize;
      protected int adjustMem;
      protected MemoryBudget budget;
      protected LookAheadCache lookAheadCache;
      protected Set checkPendingDbSet;
      protected Map dbCache;
      protected CleanerFileReader reader;
      protected DbTree dbMapTree;
      protected TreeLocation location;
      protected int nProcessedLNs;
      protected long lsn;
      protected long fileOffset;
      protected boolean isLN;
      protected boolean isIN;
      protected boolean isRoot;
      protected boolean isObsolete;
      protected DatabaseId dbId1;
      protected LN targetLN;
      protected DatabaseId dbId2;
      protected byte[] key;
      protected byte[] dupKey;
      protected Long aLsn;
      protected LNInfo aLninfo;
      protected Object p;
      protected IN targetIN;
      protected DatabaseId dbId3;
      protected DatabaseImpl db3;
      protected DatabaseId dbId;
      protected DatabaseImpl db;
      protected void hook119() throws DatabaseException, IOException {
      }
      protected void hook120() throws DatabaseException, IOException {
      }
      protected void hook127() throws DatabaseException, IOException {
      }
      protected void hook128() throws DatabaseException, IOException {
      }
      protected void hook129() throws DatabaseException, IOException {
      }
      protected void hook130() throws DatabaseException, IOException {
        p=null;
        this.hook131();
        _this.processLN(fileNum,location,aLsn,aLninfo,p,dbCache);
      }
      protected void hook131() throws DatabaseException, IOException {
      }
      protected void hook137() throws DatabaseException, IOException {
      }
      protected void hook145() throws DatabaseException, IOException {
      }
      protected void hook146() throws DatabaseException, IOException {
      }
      protected void hook147() throws DatabaseException, IOException {
      }
      protected void hook154() throws DatabaseException, IOException {
      }
      protected void hook155() throws DatabaseException, IOException {
      }
      protected void hook156() throws DatabaseException, IOException {
      }
      protected void hook161() throws DatabaseException, IOException {
      }
      protected void hook162() throws DatabaseException, IOException {
      }
    }
  @MethodObject static class FileProcessor_processLN {
      FileProcessor_processLN(    FileProcessor _this,    Long fileNum,    TreeLocation location,    Long offset,    LNInfo info,    Object lookAheadCachep,    Map dbCache){
        this._this=_this;
        this.fileNum=fileNum;
        this.location=location;
        this.offset=offset;
        this.info=info;
        this.lookAheadCachep=lookAheadCachep;
        this.dbCache=dbCache;
      }
      void execute() throws DatabaseException {
        this.hook132();
        ln=info.getLN();
        key=info.getKey();
        dupKey=info.getDupKey();
        logLsn=DbLsn.makeLsn(fileNum.longValue(),offset.longValue());
        db=_this.env.getDbMapTree().getDb(info.getDbId(),_this.cleaner.lockTimeout,dbCache);
        processedHere=true;
        obsolete=false;
        completed=false;
        bin=null;
        parentDIN=null;
        try {
          b=db == null;
          this.hook157();
          if (b) {
            this.hook158();
            this.hook148();
            obsolete=true;
            completed=true;
            return;
          }
          tree=db.getTree();
          assert tree != null;
          parentFound=tree.getParentBINForChildLN(location,key,dupKey,ln,false,true,false,Cleaner.UPDATE_GENERATION);
          bin=location.bin;
          index=location.index;
          if (!parentFound) {
            this.hook149();
            obsolete=true;
            completed=true;
            return;
          }
          if (bin.isEntryKnownDeleted(index)) {
            this.hook150();
            obsolete=true;
            completed=true;
            return;
          }
          isDupCountLN=ln.containsDuplicates();
  {
          }
          if (isDupCountLN) {
            parentDIN=(DIN)bin.fetchTarget(index);
            parentDIN.latch(Cleaner.UPDATE_GENERATION);
            dclRef=parentDIN.getDupCountLNRef();
            treeLsn=dclRef.getLsn();
          }
   else {
            treeLsn=bin.getLsn(index);
          }
          processedHere=false;
          _this.processFoundLN(info,logLsn,treeLsn,bin,index,parentDIN);
          completed=true;
          this.hook133();
          return;
        }
    finally {
          this.hook135();
          this.hook126();
        }
      }
      protected FileProcessor _this;
      protected Long fileNum;
      protected TreeLocation location;
      protected Long offset;
      protected LNInfo info;
      protected Object lookAheadCachep;
      protected Map dbCache;
      protected LookAheadCache lookAheadCache;
      protected LN ln;
      protected byte[] key;
      protected byte[] dupKey;
      protected long logLsn;
      protected DatabaseImpl db;
      protected boolean processedHere;
      protected boolean obsolete;
      protected boolean completed;
      protected BIN bin;
      protected DIN parentDIN;
      protected boolean b;
      protected Tree tree;
      protected boolean parentFound;
      protected int index;
      protected boolean isDupCountLN;
      protected long treeLsn;
      protected ChildReference dclRef;
      protected long lsn;
      protected Long myOffset;
      protected LNInfo myInfo;
      protected void hook126() throws DatabaseException {
      }
      protected void hook132() throws DatabaseException {
      }
      protected void hook133() throws DatabaseException {
      }
      protected void hook135() throws DatabaseException {
      }
      protected void hook148() throws DatabaseException {
      }
      protected void hook149() throws DatabaseException {
      }
  