Adding JMCR-Stable version
[Benchmarks_CSolver.git] / JMCR-Stable / real-world application / derby-10.3.2.1 / java / engine / org / apache / derby / impl / store / raw / log / Scan.java
diff --git a/JMCR-Stable/real-world application/derby-10.3.2.1/java/engine/org/apache/derby/impl/store/raw/log/Scan.java b/JMCR-Stable/real-world application/derby-10.3.2.1/java/engine/org/apache/derby/impl/store/raw/log/Scan.java
new file mode 100644 (file)
index 0000000..61cfd07
--- /dev/null
@@ -0,0 +1,1264 @@
+/*\r
+\r
+   Derby - Class org.apache.derby.impl.store.raw.log.Scan\r
+\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to you under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+      http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+\r
+ */\r
+\r
+package org.apache.derby.impl.store.raw.log;\r
+\r
+import org.apache.derby.iapi.reference.SQLState;\r
+\r
+import org.apache.derby.iapi.services.io.ArrayInputStream;\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+\r
+import org.apache.derby.iapi.store.raw.log.LogInstant;\r
+\r
+import org.apache.derby.iapi.store.raw.xact.TransactionId;\r
+\r
+import org.apache.derby.impl.store.raw.log.LogCounter;\r
+import org.apache.derby.impl.store.raw.log.LogRecord;\r
+import org.apache.derby.impl.store.raw.log.StreamLogScan;\r
+\r
+import org.apache.derby.io.StorageRandomAccessFile;\r
+\r
+import java.io.IOException;\r
+import org.apache.derby.iapi.store.raw.Loggable;\r
+\r
+/**\r
+\r
+               Scan the the log which is implemented by a series of log files.n\r
+               This log scan knows how to move across log file if it is positioned at\r
+               the boundary of a log file and needs to getNextRecord.\r
+\r
+       <PRE>\r
+       4 bytes - length of user data, i.e. N\r
+       8 bytes - long representing log instant\r
+       N bytes of supplied data\r
+       4 bytes - length of user data, i.e. N\r
+       </PRE>\r
+\r
+*/\r
+\r
+public class Scan implements StreamLogScan {\r
+\r
+       // value for scanDirection\r
+       public static final byte FORWARD = 1;\r
+       public static final byte BACKWARD = 2;\r
+       public static final byte BACKWARD_FROM_LOG_END = 4;\r
+\r
+       private StorageRandomAccessFile scan;           // an output stream to the log file\r
+       private LogToFile logFactory;           // log factory knows how to to skip\r
+                                                                               // from log file to log file\r
+\r
+       private long currentLogFileNumber;      // the log file the scan is currently on\r
+\r
+       private long currentLogFileLength;      // the size of the current log file\r
+                                                                               // used only for FORWARD scan to determine when\r
+                                                                               // to switch the next log file\r
+\r
+       private long knownGoodLogEnd; // For FORWARD scan only\r
+                                                               // during recovery, we need to determine the end\r
+                                                               // of the log.  Everytime a complete log record\r
+                                                               // is read in, knownGoodLogEnd is set to the\r
+                                                               // log instant of the next log record if it is\r
+                                                               // on the same log file.\r
+                                                               // \r
+                                                               // only valid afer a successfull getNextRecord\r
+                                                               // on a FOWARD scan. \r
+\r
+\r
+       private long currentInstant;            // the log instant the scan is\r
+                                                                               // currently on - only valid after a\r
+                                                                               // successful getNextRecord\r
+\r
+       private long stopAt;                            // scan until we find a log record whose \r
+                                                                               // log instance < stopAt if we scan BACKWARD\r
+                                                                               // log instance > stopAt if we scan FORWARD\r
+                                                                               // log instance >= stopAt if we scan FORWARD_FLUSHED\r
+\r
+\r
+       private byte scanDirection;             // BACKWARD or FORWARD\r
+\r
+       private boolean fuzzyLogEnd = false;   //get sets to true during forward scan\r
+                                             //for recovery, if there were\r
+                                             //partial writes at the end of the log before crash;\r
+                                             //during forward scan for recovery.\r
+\r
+\r
+       /**\r
+           For backward scan, we expect a scan positioned at the end of the next log record.\r
+               For forward scan, we expect a scan positioned at the beginning of the next log record.\r
+\r
+               For forward flushed scan, we expect stopAt to be the instant for the\r
+                  first not-flushed log record. Like any forward scan, we expect a scan\r
+                  positioned at the beginning of the next log record.\r
+\r
+               @exception StandardException Standard Derby error policy\r
+               @exception IOException cannot access the log at the new position.\r
+       */\r
+       public Scan(LogToFile logFactory, long startAt, LogInstant stopAt, byte direction)\r
+                throws IOException, StandardException\r
+       {\r
+               if (SanityManager.DEBUG)\r
+                       SanityManager.ASSERT(startAt != LogCounter.INVALID_LOG_INSTANT, \r
+                                                                "cannot start scan on an invalid log instant");\r
+\r
+               this.logFactory = logFactory;\r
+               currentLogFileNumber = LogCounter.getLogFileNumber(startAt);\r
+               currentLogFileLength = -1;\r
+               knownGoodLogEnd = LogCounter.INVALID_LOG_INSTANT;// set at getNextRecord for FORWARD scan\r
+               currentInstant = LogCounter.INVALID_LOG_INSTANT; // set at getNextRecord\r
+               if (stopAt != null)\r
+                       this.stopAt = ((LogCounter) stopAt).getValueAsLong();\r
+               else\r
+                       this.stopAt = LogCounter.INVALID_LOG_INSTANT;\r
+\r
+               switch(direction)\r
+               {\r
+               case FORWARD:\r
+                       scan =  logFactory.getLogFileAtPosition(startAt);\r
+                       scanDirection = FORWARD;\r
+\r
+                       if (SanityManager.DEBUG)\r
+                               if (scan == null)\r
+                                       SanityManager.THROWASSERT(\r
+                                               "scan null at " + LogCounter.toDebugString(startAt));\r
+\r
+                       // NOTE: just get the length of the file without syncing.\r
+                       // this only works because the only place forward scan is used\r
+                       // right now is on recovery redo and nothing is being added to \r
+                       // the current log file.  When the forward scan is used for some\r
+                       // other purpose, need to sync access to the end of the log\r
+                       currentLogFileLength = scan.length();\r
+                       break;\r
+\r
+               case BACKWARD:\r
+                       // startAt is at the front of the log record, for backward\r
+                       // scan we need to be positioned at the end of the log record\r
+                       scan =  logFactory.getLogFileAtPosition(startAt);\r
+                       int logsize = scan.readInt();\r
+\r
+                       // skip forward over the log record and all the overhead, but remember\r
+                       // we just read an int off the overhead\r
+                       scan.seek(scan.getFilePointer() + logsize + LogToFile.LOG_RECORD_OVERHEAD - 4);\r
+                       scanDirection = BACKWARD;\r
+                       break;\r
+\r
+               case BACKWARD_FROM_LOG_END:\r
+                       // startAt is at the end of the log, no need to skip the log record\r
+                       scan =  logFactory.getLogFileAtPosition(startAt);\r
+                       scanDirection = BACKWARD;\r
+                       break;\r
+\r
+               }\r
+       }\r
+\r
+       /*\r
+       ** Methods of StreamLogScan\r
+       */\r
+\r
+       /**\r
+               Read the next log record.\r
+               Switching log to a previous log file if necessary, \r
+               Resize the input stream byte array if necessary.  \r
+               @see StreamLogScan#getNextRecord\r
+\r
+               @return the next LogRecord, or null if the end of the\r
+               scan has been reached.\r
+\r
+               @exception StandardException Standard Derby error policy\r
+       */\r
+       public LogRecord getNextRecord(ArrayInputStream input, \r
+                                                        TransactionId tranId, \r
+                                                        int groupmask)\r
+                throws StandardException\r
+       {\r
+               if (scan == null)\r
+                       return null;\r
+\r
+               if (SanityManager.DEBUG)\r
+                       SanityManager.ASSERT(scanDirection != 0, "scan has been secretly closed!");\r
+\r
+               LogRecord lr = null;\r
+               try\r
+               {\r
+                       if (scanDirection == BACKWARD)\r
+                               lr = getNextRecordBackward(input, tranId, groupmask);\r
+                       else if (scanDirection == FORWARD)\r
+                               lr = getNextRecordForward(input, tranId, groupmask);\r
+\r
+                       return lr;\r
+\r
+               }\r
+               catch (IOException ioe)\r
+               {\r
+                       if (SanityManager.DEBUG)\r
+                               ioe.printStackTrace();\r
+\r
+                       throw logFactory.markCorrupt(\r
+                StandardException.newException(SQLState.LOG_CORRUPTED, ioe));\r
+               }\r
+               catch (ClassNotFoundException cnfe)\r
+               {\r
+                       if (SanityManager.DEBUG)\r
+                               cnfe.printStackTrace();\r
+\r
+                       throw logFactory.markCorrupt(\r
+                StandardException.newException(SQLState.LOG_CORRUPTED, cnfe));\r
+               }\r
+               finally\r
+               {\r
+                       if (lr == null)\r
+                               close();                // no more log record, close the scan\r
+               }\r
+\r
+       }\r
+\r
+       /**\r
+               Read the previous log record.\r
+               Switching log to a previous log file if necessary, \r
+               Resize the input stream byte array if necessary.  \r
+               @see StreamLogScan#getNextRecord\r
+\r
+               Side effects include: \r
+                               on a successful read, setting currentInstant.\r
+                               on a log file switch, setting currentLogFileNumber.\r
+\r
+               @return the previous LogRecord, or null if the end of the\r
+               scan has been reached.\r
+       */\r
+       private LogRecord getNextRecordBackward(ArrayInputStream input, \r
+                                                                         TransactionId tranId,  \r
+                                                                         int groupmask) \r
+                throws StandardException, IOException, ClassNotFoundException\r
+       {\r
+               if (SanityManager.DEBUG)\r
+                       SanityManager.ASSERT(scanDirection == BACKWARD, "can only called by backward scan");\r
+\r
+               // scan is positioned just past the last byte of the record, or\r
+               // right at the beginning of the file (end of the file header)\r
+               // may need to switch log file\r
+\r
+               boolean candidate;\r
+               // if we have filtering, peek at the group and/or the transaction id,\r
+               // do them in one read rather than 2 reads.\r
+               int peekAmount = LogRecord.formatOverhead() + LogRecord.maxGroupStoredSize();\r
+               if (tranId != null)\r
+                       peekAmount += LogRecord.maxTransactionIdStoredSize(tranId);\r
+\r
+               int readAmount;                 // the number of bytes actually read\r
+\r
+               LogRecord lr;\r
+               long curpos = scan.getFilePointer();\r
+\r
+               do\r
+               {\r
+                       // this log record is a candidate unless proven otherwise\r
+                       candidate = true; \r
+                       lr = null;\r
+                       readAmount = -1;\r
+\r
+                       if (curpos == LogToFile.LOG_FILE_HEADER_SIZE)\r
+                       {\r
+                               // don't go thru the trouble of switching log file if we\r
+                               // will have gone past stopAt\r
+                               if (stopAt != LogCounter.INVALID_LOG_INSTANT &&\r
+                                       LogCounter.getLogFileNumber(stopAt) == currentLogFileNumber)\r
+                               {\r
+                                       if (SanityManager.DEBUG)\r
+                    {\r
+                        if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))\r
+                        {\r
+                            SanityManager.DEBUG(LogToFile.DBG_FLAG, \r
+                                "stopping at " + currentLogFileNumber);\r
+                        }\r
+                    }\r
+\r
+                                       return null;  // no more log record\r
+                               }\r
+                               \r
+                               // figure out where the last log record is in the previous\r
+                               // log file\r
+                               scan.seek(LogToFile.LOG_FILE_HEADER_PREVIOUS_LOG_INSTANT_OFFSET);\r
+                               long previousLogInstant = scan.readLong();\r
+                               scan.close();\r
+\r
+                               if (SanityManager.DEBUG)\r
+                               {\r
+                                       SanityManager.ASSERT(previousLogInstant != LogCounter.INVALID_LOG_INSTANT,\r
+                                                                        "scanning backward beyond the first log file");\r
+                                       if (currentLogFileNumber != \r
+                                                       LogCounter.getLogFileNumber(previousLogInstant) + 1)\r
+                                               SanityManager.THROWASSERT(\r
+                                               "scanning backward but get incorrect log file number " + \r
+                                                "expected " + (currentLogFileNumber -1) + \r
+                                                "get " +\r
+                                                LogCounter.getLogFileNumber(previousLogInstant));\r
+\r
+                                       SanityManager.ASSERT(LogCounter.getLogFilePosition(previousLogInstant) > \r
+                                                                        LogToFile.LOG_FILE_HEADER_SIZE,\r
+                                                                        "scanning backward encounter completely empty log file");\r
+\r
+                                       SanityManager.DEBUG(LogToFile.DBG_FLAG, \r
+                                                                       "scanning backwards from log file " +\r
+                                                                       currentLogFileNumber + ", switch to (" + \r
+                                                                       LogCounter.getLogFileNumber(previousLogInstant) + "," +\r
+                                                                       LogCounter.getLogFilePosition(previousLogInstant) + ")"\r
+                                                                       );\r
+                               }\r
+\r
+                               // log file switch, set this.currentLogFileNumber\r
+                               currentLogFileNumber = LogCounter.getLogFileNumber(previousLogInstant);\r
+\r
+                               scan = logFactory.getLogFileAtPosition(previousLogInstant);\r
+\r
+                               // scan is located right past the last byte of the last log\r
+                               // record in the previous log file.  currentLogFileNumber is\r
+                               // set.  We asserted that the scan is not located right at the\r
+                               // end of the file header, in other words, there is at least\r
+                               // one log record in this log file.\r
+                               curpos = scan.getFilePointer();\r
+\r
+                               // if the log file happens to be empty skip and proceed. \r
+                               // ideally this case should never occur because log switch is\r
+                               // not suppose to happen on an empty log file. \r
+                               // But it is safer to put following check incase if it ever\r
+                               // happens to avoid any recovery issues. \r
+                               if (curpos == LogToFile.LOG_FILE_HEADER_SIZE)\r
+                                       continue;\r
+                       }\r
+\r
+                       scan.seek(curpos - 4);\r
+                       int recordLength = scan.readInt(); // get the length after the log record\r
+\r
+                       // calculate where this log record started.\r
+                       // include the eight bytes for the long log instant at the front\r
+                       // the four bytes of length in the front and the four bytes we just read\r
+                       long recordStartPosition = curpos - recordLength -\r
+                               LogToFile.LOG_RECORD_OVERHEAD; \r
+\r
+                       if (SanityManager.DEBUG)\r
+                       {\r
+                               if (recordStartPosition < LogToFile.LOG_FILE_HEADER_SIZE)\r
+                                       SanityManager.THROWASSERT(\r
+                                                                "next position " + recordStartPosition +\r
+                                                                " recordLength " + recordLength + \r
+                                                                " current file position " + scan.getFilePointer());\r
+\r
+                               scan.seek(recordStartPosition);\r
+\r
+                               // read the length before the log record and check it against the\r
+                               // length after the log record\r
+                               int checkLength = scan.readInt();\r
+\r
+                               if (checkLength != recordLength)\r
+                               {\r
+                                       long inst = LogCounter.makeLogInstantAsLong(currentLogFileNumber, recordStartPosition);\r
+\r
+                                       throw logFactory.markCorrupt(\r
+                        StandardException.newException(\r
+                            SQLState.LOG_RECORD_CORRUPTED, \r
+                            new Long(checkLength),\r
+                            new Long(recordLength),\r
+                            new Long(inst),\r
+                            new Long(currentLogFileNumber)));\r
+                               }\r
+                       }\r
+                       else\r
+                       {\r
+                               // skip over the length in insane\r
+                               scan.seek(recordStartPosition+4);\r
+                       }\r
+\r
+                       // scan is positioned just before the log instant\r
+                       // read the current log instant - this is the currentInstant if we have not\r
+                       // exceeded the scan limit\r
+                       currentInstant = scan.readLong();\r
+\r
+                       if (SanityManager.DEBUG)\r
+                       {\r
+                               // sanity check the current instant against the scan position\r
+                               if (LogCounter.getLogFileNumber(currentInstant) !=\r
+                                       currentLogFileNumber ||\r
+                                       LogCounter.getLogFilePosition(currentInstant) !=\r
+                                       recordStartPosition)\r
+                                       SanityManager.THROWASSERT(\r
+                                                                "Wrong LogInstant on log record " +\r
+                                                               LogCounter.toDebugString(currentInstant) + \r
+                                                                " version real position (" +\r
+                                                                currentLogFileNumber + "," +\r
+                                                                recordStartPosition + ")");\r
+                       }\r
+\r
+\r
+                       // if stopAt == INVALID_LOG_INSTANT, no stop instant, read till\r
+                       // nothing more can be read.  Else check scan limit\r
+                       if (currentInstant < stopAt && stopAt != LogCounter.INVALID_LOG_INSTANT)\r
+                       {\r
+                               currentInstant = LogCounter.INVALID_LOG_INSTANT;\r
+                               return null;    // we went past the stopAt\r
+                       }\r
+\r
+\r
+                       byte[] data = input.getData();\r
+\r
+                       if (data.length < recordLength)\r
+                       {\r
+                               // make a new array of sufficient size and reset the arrary\r
+                               // in the input stream\r
+                               data = new byte[recordLength];\r
+                               input.setData(data);\r
+                       }\r
+\r
+                       // If the log is encrypted, we must do the filtering after reading\r
+                       // and decrypting the record.\r
+                       if (logFactory.databaseEncrypted())\r
+                       {\r
+                               scan.readFully(data, 0, recordLength);\r
+                               int len = logFactory.decrypt(data, 0, recordLength, data, 0);\r
+                               if (SanityManager.DEBUG)\r
+                                       SanityManager.ASSERT(len == recordLength);\r
+                               input.setLimit(0, recordLength);\r
+                       }\r
+                       else // no need to decrypt, only get the group and tid if we filter \r
+                       {\r
+                               if (groupmask == 0 && tranId == null)\r
+                               {\r
+                                       // no filter, get the whole thing\r
+                                       scan.readFully(data, 0, recordLength);\r
+                                       input.setLimit(0, recordLength);\r
+                               }\r
+                               else\r
+                               {\r
+                                       // Read only enough so that group and the tran id is in\r
+                                       // the data buffer.  Group is stored as compressed int\r
+                                       // and tran id is stored as who knows what.  read min\r
+                                       // of peekAmount or recordLength\r
+                                       readAmount = (recordLength > peekAmount) ?\r
+                                               peekAmount : recordLength; \r
+\r
+                                       // in the data buffer, we now have enough to peek\r
+                                       scan.readFully(data, 0, readAmount);\r
+                                       input.setLimit(0, readAmount);\r
+                               }\r
+                       }\r
+\r
+                       lr = (LogRecord) input.readObject();\r
+\r
+                       // skip the checksum log records, there is no need to look at them \r
+                       // during backward scans. They are used only in forwardscan during recovery. \r
+                       if(lr.isChecksum())\r
+                       {\r
+                               candidate = false; \r
+                       }else if (groupmask != 0 || tranId != null)\r
+                       {\r
+\r
+                               // skip the checksum log records  \r
+                               if(lr.isChecksum())\r
+                                       candidate = false; \r
+\r
+                               if (candidate && groupmask != 0 && (groupmask & lr.group()) == 0)\r
+                                       candidate = false; // no match, throw this log record out \r
+\r
+                               if (candidate && tranId != null)\r
+                               {\r
+                                       TransactionId tid = lr.getTransactionId();\r
+                                       if (!tid.equals(tranId)) // nomatch\r
+                                               candidate = false; // throw this log record out\r
+                               }\r
+\r
+                               // if this log record is not filtered out, we need to read\r
+                               // in the rest of the log record to the input buffer.\r
+                               // Except if it is an encrypted database, in which case the\r
+                               // entire log record have already be read in for\r
+                               // decryption.\r
+                               if (candidate && !logFactory.databaseEncrypted())\r
+                               {\r
+                                       // read the rest of the log into the buffer\r
+                                       if (SanityManager.DEBUG)\r
+                                               SanityManager.ASSERT(readAmount > 0);\r
+\r
+                                       if (readAmount < recordLength)\r
+                                       {\r
+                                               // Need to remember where we are because the log\r
+                                               // record may have read part of it off the input\r
+                                               // stream already and that position is lost when we\r
+                                               // set limit again.\r
+                                               int inputPosition = input.getPosition();\r
+\r
+                                               scan.readFully(data, readAmount,\r
+                                                                          recordLength-readAmount); \r
+\r
+                                               input.setLimit(0, recordLength);\r
+                                               input.setPosition(inputPosition);\r
+                                       }\r
+                               }\r
+                       }\r
+\r
+                       // go back to the start of the log record so that the next time\r
+                       // this method is called, it is positioned right past the last byte\r
+                       // of the record.\r
+                       curpos = recordStartPosition;\r
+                       scan.seek(curpos);\r
+\r
+               } while (candidate == false);\r
+\r
+               return lr;\r
+\r
+       }\r
+\r
+       /**\r
+               Read the next log record.\r
+               Switching log to a previous log file if necessary, \r
+               Resize the input stream byte array if necessary.  \r
+               @see StreamLogScan#getNextRecord\r
+\r
+               Side effects include: \r
+                               on a successful read, setting currentInstant, knownGoodLogEnd\r
+                               on a log file switch, setting currentLogFileNumber, currentLogFileLength.\r
+                               on detecting a fuzzy log end that needs clearing, it will call\r
+                               logFactory to clear the fuzzy log end.\r
+\r
+               @return the next LogRecord, or null if the end of the\r
+               scan has been reached.\r
+       */\r
+       private LogRecord getNextRecordForward(ArrayInputStream input, \r
+                                                                        TransactionId tranId,  \r
+                                                                        int groupmask)\r
+                throws StandardException,  IOException, ClassNotFoundException\r
+       {\r
+               if (SanityManager.DEBUG)\r
+                       SanityManager.ASSERT(scanDirection == FORWARD, "can only called by forward scan");\r
+\r
+               // NOTE:\r
+               //\r
+               // if forward scan, scan is positioned at the first byte of the\r
+               // next record, or the end of file - note the the 'end of file'\r
+               // is defined at the time the scan is initialized.  If we are\r
+               // on the current log file, it may well have grown by now...\r
+               //\r
+               // This is not a problem in reality because the only forward\r
+               // scan on the log now is recovery redo and the log does not\r
+               // grow.  If in the future, a foward scan of the log is used\r
+               // for some other reasons, need to keep this in mind.\r
+               //\r
+\r
+               // first we need to make sure the entire log record is on the\r
+               // log, or else this is a fuzzy log end.\r
+\r
+               // RESOLVE: can get this from knownGoodLogEnd if this is not the first\r
+               // time getNext is called.  Probably just as fast to call\r
+               // scan.getFilePointer though...\r
+               long recordStartPosition = scan.getFilePointer();\r
+\r
+               boolean candidate;\r
+\r
+               // if we have filtering, peek at the group and/or the transaction id,\r
+               // do them in one read rather than 2 reads.\r
+               int peekAmount = LogRecord.formatOverhead() + LogRecord.maxGroupStoredSize();\r
+               if (tranId != null)\r
+                       peekAmount += LogRecord.maxTransactionIdStoredSize(tranId);\r
+\r
+               int readAmount;                 // the number of bytes actually read\r
+\r
+               LogRecord lr;\r
+\r
+               do\r
+               {\r
+                       // this log record is a candidate unless proven otherwise\r
+                       candidate = true;\r
+                       lr = null;\r
+                       readAmount = -1;\r
+\r
+                       // if we are not right at the end but this position + 4 is at\r
+                       // or exceeds the end, we know we don't have a complete log\r
+                       // record.  This is the log file and chalk it up as the fuzzy\r
+                       // end.\r
+                       if (recordStartPosition + 4 > currentLogFileLength)\r
+                       {\r
+                               // since there is no end of log file marker, we are at the\r
+                               // end of the log.\r
+                               if (SanityManager.DEBUG)\r
+                {\r
+                    if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))\r
+                    {\r
+                        SanityManager.DEBUG(LogToFile.DBG_FLAG, \r
+                            "detected fuzzy log end on log file " + \r
+                                currentLogFileNumber + \r
+                            " record start position " + recordStartPosition + \r
+                            " file length " + currentLogFileLength);\r
+                    }\r
+                }\r
+                               \r
+                               //if  recordStartPosition == currentLogFileLength\r
+                               //there is NO fuzz, it just a properly ended log \r
+                               //without the end marker. \r
+                               if(recordStartPosition != currentLogFileLength)\r
+                                       fuzzyLogEnd = true ;\r
+\r
+                               // don't bother to write the end of log file marker because\r
+                               // if it is not overwritten by the next log record then\r
+                               // the next time the database is recovered it will come\r
+                               // back right here\r
+                               return null;\r
+                       }\r
+\r
+                       // read in the length before the log record\r
+                       int recordLength = scan.readInt();\r
+\r
+                       while (recordLength == 0 || recordStartPosition + recordLength +\r
+                                  LogToFile.LOG_RECORD_OVERHEAD > currentLogFileLength) \r
+                       {\r
+                               // if recordLength is zero or the log record goes beyond the\r
+                               // current file, then we have detected the end of a log file.\r
+                               //\r
+                               // If recordLength == 0 then we know that this log file has either\r
+                               // been properly switched or it had a 1/2 written log record which \r
+                               // was subsequently cleared by clearFuzzyEnd.\r
+                               //\r
+                               // If recordLength != 0 but log record goes beyond the current log\r
+                               // file, we have detected a fuzzy end.  This is the last log file\r
+                               // since we will clear it by clearFuzzyEnd.\r
+\r
+                               if (recordLength != 0) // this is a fuzzy log end\r
+                               {\r
+                                       if (SanityManager.DEBUG)\r
+                    {\r
+                        if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))\r
+                        {\r
+                            SanityManager.DEBUG(\r
+                                LogToFile.DBG_FLAG, \r
+                                "detected fuzzy log end on log file " + \r
+                                    currentLogFileNumber + \r
+                                " record start position " + \r
+                                    recordStartPosition + \r
+                                " file length " + currentLogFileLength + \r
+                                                               " recordLength=" + recordLength );\r
+                        }\r
+                    }\r
+\r
+                                       fuzzyLogEnd = true;\r
+                                       scan.close();\r
+                                       scan = null;\r
+\r
+                                       return null;\r
+                               }\r
+\r
+                               // recordLength == 0\r
+\r
+                               if (SanityManager.DEBUG) \r
+                {\r
+                    if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))\r
+                    {\r
+                        if (recordStartPosition + 4 == currentLogFileLength)\r
+                        {\r
+                            SanityManager.DEBUG(LogToFile.DBG_FLAG, \r
+                                "detected proper log end on log file " + \r
+                                currentLogFileNumber);\r
+                        }\r
+                        else\r
+                        {\r
+                            SanityManager.DEBUG(LogToFile.DBG_FLAG, \r
+                                    "detected zapped log end on log file " + \r
+                                        currentLogFileNumber +\r
+                                    " end marker at " + \r
+                                        recordStartPosition + \r
+                                    " real end at " + currentLogFileLength);\r
+                        }\r
+                    }\r
+                               }\r
+                               \r
+                               // don't go thru the trouble of switching log file if we\r
+                               // have will have gone past stopAt if we want to stop here\r
+                               if (stopAt != LogCounter.INVALID_LOG_INSTANT &&\r
+                                       LogCounter.getLogFileNumber(stopAt) == currentLogFileNumber)\r
+                               {\r
+                                       return null;\r
+                               }\r
+\r
+                               //\r
+                               // we have a log end marker and we don't want to stop yet, switch\r
+                               // log file\r
+                               //\r
+                               scan.close();\r
+\r
+                               // set this.currentLogFileNumber\r
+                               scan = logFactory.getLogFileAtBeginning(++currentLogFileNumber);\r
+                               if (scan == null) // we have seen the last log file\r
+                               {\r
+                                       return null;\r
+                               }\r
+\r
+                               // scan is position just past the log header\r
+                               recordStartPosition = scan.getFilePointer();\r
+\r
+                // Verify that the header of the new log file refers\r
+                // to the end of the log record of the previous file\r
+                // (Rest of header has been verified by getLogFileAtBeginning)\r
+                               scan.seek(LogToFile\r
+                          .LOG_FILE_HEADER_PREVIOUS_LOG_INSTANT_OFFSET);\r
+                long previousLogInstant = scan.readLong();\r
+                if (previousLogInstant != knownGoodLogEnd) {\r
+                    // If there is a mismatch, something is wrong and\r
+                    // we return null to stop the scan.  The same\r
+                    // behavior occurs when getLogFileAtBeginning\r
+                    // detects an error in the other fields of the header.\r
+                    if (SanityManager.DEBUG) {\r
+                        if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG)) {\r
+                            SanityManager.DEBUG(LogToFile.DBG_FLAG, \r
+                                                "log file " \r
+                                                + currentLogFileNumber  \r
+                                                + ": previous log record: "\r
+                                                + previousLogInstant\r
+                                                + " known previous log record: "\r
+                                                + knownGoodLogEnd);\r
+                        }\r
+                    }\r
+                    return null;\r
+                               }\r
+\r
+\r
+                               scan.seek(recordStartPosition);\r
+\r
+                               if (SanityManager.DEBUG) \r
+                {\r
+                    if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))\r
+                    {\r
+                        SanityManager.DEBUG(LogToFile.DBG_FLAG, \r
+                            "switched to next log file " + \r
+                            currentLogFileNumber);\r
+                    }\r
+                }\r
+\r
+                // Advance knownGoodLogEnd to make sure that if this\r
+                // log file is the last log file and empty, logging\r
+                // continues in this file, not the old file.\r
+                knownGoodLogEnd = LogCounter.makeLogInstantAsLong\r
+                    (currentLogFileNumber, recordStartPosition);\r
+\r
+                               // set this.currentLogFileLength\r
+                               currentLogFileLength = scan.length();\r
+\r
+                               if (recordStartPosition+4 >= currentLogFileLength) // empty log file\r
+                               {\r
+                                       if (SanityManager.DEBUG)\r
+                    {\r
+                        if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))\r
+                        {\r
+                            SanityManager.DEBUG(LogToFile.DBG_FLAG, \r
+                                "log file " + currentLogFileNumber + \r
+                                " is empty");\r
+                        }\r
+                    }\r
+\r
+                                       return null;\r
+                               }\r
+\r
+                               // we have successfully switched to the next log file.\r
+                               // scan is positioned just before the next log record\r
+                               // see if this one is written in entirety\r
+                               recordLength = scan.readInt();\r
+                       }\r
+\r
+                       // we know the entire log record is on this log file\r
+\r
+                       // read the current log instant\r
+                       currentInstant = scan.readLong();\r
+\r
+                       /*check if the current instant happens is less than the last one. \r
+                        *This can happen if system crashed before writing the log instant\r
+                        *completely. If the instant is partially written it will be less\r
+                        *than the last one and should be the last record that was suppose to\r
+                        *get written. Currentlt preallocated files are filled with zeros,\r
+                        *this should hold good.\r
+                        *Note: In case of Non-preallocated files earlier check with log\r
+                        * file lengths should have found the end. But in prellocated files, log file\r
+                        *length is not sufficiant to find the log end. This check \r
+                        *is must to find the end in preallocated log files. \r
+                        */\r
+                       if(currentInstant < knownGoodLogEnd)\r
+                       {\r
+                               fuzzyLogEnd = true ;\r
+                               return null;\r
+                       }\r
+\r
+                       // sanity check it \r
+                       if (SanityManager.DEBUG)\r
+                       {\r
+                               if (LogCounter.getLogFileNumber(currentInstant) !=\r
+                                       currentLogFileNumber ||\r
+                                       LogCounter.getLogFilePosition(currentInstant) !=\r
+                                       recordStartPosition)\r
+                                       SanityManager.THROWASSERT(\r
+                                                         "Wrong LogInstant on log record " +\r
+                                                               LogCounter.toDebugString(currentInstant) + \r
+                                                                " version real position (" +\r
+                                                                currentLogFileNumber + "," +\r
+                                                                recordStartPosition + ")");\r
+                       }\r
+\r
+\r
+                       // if stopAt == INVALID_LOG_INSTANT, no stop instant, read till\r
+                       // nothing more can be read.  Else check scan limit\r
+                       if (stopAt != LogCounter.INVALID_LOG_INSTANT && currentInstant > stopAt)\r
+                       {\r
+                               currentInstant = LogCounter.INVALID_LOG_INSTANT;\r
+                               return null;                    // we went past the stopAt\r
+                       }\r
+\r
+                       // read in the log record\r
+                       byte[] data = input.getData();\r
+\r
+                       if (data.length < recordLength)\r
+                       {\r
+                               // make a new array of sufficient size and reset the arrary\r
+                               // in the input stream\r
+                               data = new byte[recordLength];\r
+                               input.setData(data);\r
+                       }\r
+\r
+                       // If the log is encrypted, we must do the filtering after\r
+                       // reading and decryptiong the record.\r
+\r
+                       if (logFactory.databaseEncrypted())\r
+                       {\r
+                               scan.readFully(data, 0, recordLength);\r
+                               int len = logFactory.decrypt(data, 0, recordLength, data, 0);\r
+                               if (SanityManager.DEBUG)\r
+                                       SanityManager.ASSERT(len == recordLength);\r
+\r
+                               input.setLimit(0, len);\r
+                       }\r
+                       else // no need to decrypt, only get the group and tid if we filter \r
+                       {\r
+                               if (groupmask == 0 && tranId == null)\r
+                               {\r
+                                       // no filter, get the whole thing\r
+                                       scan.readFully(data, 0, recordLength);\r
+                                       input.setLimit(0, recordLength);\r
+                               }\r
+                               else\r
+                               {\r
+                                       // Read only enough so that group and the tran id is in\r
+                                       // the data buffer.  Group is stored as compressed int\r
+                                       // and tran id is stored as who knows what.  read min\r
+                                       // of peekAmount or recordLength\r
+                                       readAmount = (recordLength > peekAmount) ?\r
+                                               peekAmount : recordLength; \r
+\r
+                                       // in the data buffer, we now have enough to peek\r
+                                       scan.readFully(data, 0, readAmount);\r
+                                       input.setLimit(0, readAmount);\r
+                               }\r
+                       }\r
+\r
+                       lr = (LogRecord) input.readObject();\r
+                       if (groupmask != 0 || tranId != null)\r
+                       {\r
+                               if (groupmask != 0 && (groupmask & lr.group()) == 0)\r
+                                       candidate = false; // no match, throw this log record out \r
+\r
+                               if (candidate && tranId != null)\r
+                               {\r
+                                       TransactionId tid = lr.getTransactionId();\r
+                                       if (!tid.equals(tranId)) // nomatch\r
+                                               candidate = false; // throw this log record out\r
+                               }\r
+\r
+                               // if this log record is not filtered out, we need to read\r
+                               // in the rest of the log record to the input buffer.\r
+                               // Except if it is an encrypted database, in which case the\r
+                               // entire log record have already be read in for\r
+                               // decryption.\r
+                               if (candidate && !logFactory.databaseEncrypted())\r
+                               {\r
+                                       // read the rest of the log into the buffer\r
+                                       if (SanityManager.DEBUG)\r
+                                               SanityManager.ASSERT(readAmount > 0);\r
+\r
+                                       if (readAmount < recordLength)\r
+                                       {\r
+                                               // Need to remember where we are because the log\r
+                                               // record may have read part of it off the input\r
+                                               // stream already and that position is lost when we\r
+                                               // set limit again.\r
+                                               int inputPosition = input.getPosition();\r
+\r
+                                               scan.readFully(data, readAmount,\r
+                                                                          recordLength-readAmount); \r
+\r
+                                               input.setLimit(0, recordLength);\r
+                                               input.setPosition(inputPosition);\r
+                                       }\r
+                               }\r
+                       }\r
+\r
+                       /*check if the logrecord length written before and after the \r
+                        *log record are equal, if not the end of of the log is reached.\r
+                        *This can happen if system crashed before writing the length field \r
+                        *in the end of the records completely. If the length is partially\r
+                        *written or not written at all  it will not match with length written \r
+                        *in the beginning of the log record. Currentlt preallocated files\r
+                        *are filled with zeros, log record length can never be zero; \r
+                        *if the lengths are not matching, end of the properly written log\r
+                        *is reached.\r
+                        *Note: In case of Non-preallocated files earlier fuzzy case check with log\r
+                        * file lengths should have found the end. But in prellocated files, log file\r
+                        *length is not sufficiant to find the log end. This check \r
+                        *is must to find the end in preallocated log files. \r
+                        */\r
+                       // read the length after the log record and check it against the\r
+                       // length before the log record, make sure we go to the correct\r
+                       // place for skipped log record.\r
+                       if (!candidate)\r
+                               scan.seek(recordStartPosition - 4);\r
+                       int checkLength = scan.readInt();\r
+                       if (checkLength != recordLength && checkLength < recordLength)\r
+                       {\r
+\r
+\r
+                               //lengh written in the end of the log record should be always\r
+                               //less then the length written in the beginning if the log\r
+                               //record was half written before the crash.\r
+                               if(checkLength < recordLength)\r
+                               {\r
+                                       fuzzyLogEnd = true ;\r
+                                       return null;\r
+                               }else\r
+                               {\r
+                               \r
+                                       //If checklength > recordLength then it can be not be a partial write\r
+                                       //probablly it is corrupted for some reason , this should never\r
+                                       //happen throw error in debug mode. In non debug case , let's\r
+                                       //hope it's only is wrong and system can proceed. \r
+                                               \r
+                                       if (SanityManager.DEBUG)\r
+                                       {       \r
+                                               throw logFactory.markCorrupt\r
+                                               (StandardException.newException(\r
+                                                       SQLState.LOG_RECORD_CORRUPTED, \r
+                            new Long(checkLength),\r
+                            new Long(recordLength),\r
+                            new Long(currentInstant),\r
+                            new Long(currentLogFileNumber)));\r
+\r
+                                       }\r
+                                       \r
+                                       //In non debug case, do nothing , let's hope it's only\r
+                                       //length part that is incorrect and system can proceed. \r
+                               }\r
+\r
+                       }\r
+\r
+                       // next record start position is right after this record\r
+                       recordStartPosition += recordLength + LogToFile.LOG_RECORD_OVERHEAD;\r
+                       knownGoodLogEnd = LogCounter.makeLogInstantAsLong\r
+                                                               (currentLogFileNumber, recordStartPosition);\r
+\r
+\r
+                       if (SanityManager.DEBUG)\r
+                       {\r
+                               if (recordStartPosition != scan.getFilePointer())\r
+                                       SanityManager.THROWASSERT(\r
+                                                                        "calculated end " + recordStartPosition + \r
+                                                                        " != real end " + scan.getFilePointer());\r
+                       }\r
+                       else\r
+                       {\r
+                               // seek to the start of the next log record\r
+                               scan.seek(recordStartPosition);\r
+                       }\r
+\r
+                       // the scan is now positioned just past this log record and right\r
+                       // at the beginning of the next log record\r
+\r
+\r
+                       /** if the current log record is a checksum log record then\r
+                        * using the information available in this record validate\r
+                        * that data in the log file by matching the checksum in \r
+                        * checksum log record and by recalculating the checksum for the \r
+                        * specified length of the data in the log file. cheksum values\r
+                        * should match unless the right was incomplete before the crash.\r
+                        */\r
+                       if(lr.isChecksum())\r
+                       {\r
+                               // checksum log record should not be returned to the logger recovery redo\r
+                               // routines, it is just used to identify the incomplete log writes.\r
+\r
+                               candidate = false;\r
+                               Loggable op = lr.getLoggable(); \r
+                               if (SanityManager.DEBUG)\r
+                {\r
+                    if (SanityManager.DEBUG_ON(LogToFile.DUMP_LOG_ONLY) ||\r
+                        SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))\r
+\r
+                                               SanityManager.DEBUG(LogToFile.DBG_FLAG, \r
+                                                                                       "scanned " + "Null" + " : " + op + \r
+                                                                                       " instant = " + \r
+                                                                                       LogCounter.toDebugString(currentInstant) + \r
+                                                                                       " logEnd = " +  LogCounter.toDebugString(knownGoodLogEnd));\r
+                               }\r
+\r
+                               ChecksumOperation clop = (ChecksumOperation) op;\r
+                               int ckDataLength =  clop.getDataLength(); \r
+                               // resize the buffer to be size of checksum data length if required.\r
+                               if (data.length < ckDataLength)\r
+                               {\r
+                                       // make a new array of sufficient size and reset the arrary\r
+                                       // in the input stream\r
+                                       data = new byte[ckDataLength];\r
+                                       input.setData(data);\r
+                                       input.setLimit(0, ckDataLength);\r
+                               }\r
+                               \r
+                               boolean validChecksum = false;\r
+                               // check if the expected number of bytes by the checksum log\r
+                               // record actually exist in the file and then verify if checksum\r
+                               // is valid to identify any incomplete out of order writes.\r
+                               if((recordStartPosition + ckDataLength) <= currentLogFileLength)\r
+                               {\r
+                                       // read the data into the buffer\r
+                                       scan.readFully(data, 0, ckDataLength);\r
+                                       // verify the checksum \r
+                                       if(clop.isChecksumValid(data, 0 , ckDataLength))\r
+                                               validChecksum = true;\r
+                               }\r
+\r
+\r
+                               if(!validChecksum)\r
+                               {\r
+                                       // declare that the end of the transaction log is fuzzy, checksum is invalid\r
+                                       // only when the writes are incomplete; this can happen\r
+                                       // only when writes at the end of the log were partially\r
+                                       // written before the crash. \r
+\r
+                                       if (SanityManager.DEBUG)\r
+                    {\r
+                        if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))\r
+                        {\r
+                            SanityManager.DEBUG(\r
+                                LogToFile.DBG_FLAG, \r
+                                "detected fuzzy log end on log file while doing checksum checks " + \r
+                                                               currentLogFileNumber + \r
+                                " checksum record start position " + recordStartPosition + \r
+                                " file length " + currentLogFileLength + \r
+                                                               " checksumDataLength=" + ckDataLength);\r
+                        }\r
+                                               \r
+                                       }\r
+                                       \r
+                                       fuzzyLogEnd = true;\r
+                                       scan.close();\r
+                                       scan = null;\r
+                                       return null;\r
+                               }\r
+\r
+                               // reset the scan to the start of the next log record\r
+                               scan.seek(recordStartPosition);\r
+                       }\r
+\r
+\r
+               } while (candidate == false) ;\r
+\r
+               return lr;\r
+       }\r
+\r
+\r
+       /**\r
+               Reset the scan to the given LogInstant.\r
+\r
+               @param instant the position to reset to\r
+               @exception IOException scan cannot access the log at the new position.\r
+               @exception StandardException standard Derby error policy\r
+       */\r
+\r
+       public void resetPosition(LogInstant instant) \r
+                throws IOException, StandardException\r
+       {\r
+               if (SanityManager.DEBUG)\r
+                       SanityManager.ASSERT(instant != null);\r
+\r
+               long instant_long = ((LogCounter)instant).getValueAsLong();\r
+\r
+               if ((instant_long == LogCounter.INVALID_LOG_INSTANT) ||\r
+                       (stopAt != LogCounter.INVALID_LOG_INSTANT &&\r
+                        (scanDirection == FORWARD && instant_long > stopAt) ||\r
+                        (scanDirection == FORWARD && instant_long < stopAt)))\r
+               {\r
+                       close();\r
+\r
+                       throw StandardException.newException(\r
+                    SQLState.LOG_RESET_BEYOND_SCAN_LIMIT, \r
+                    instant, new LogCounter(stopAt));\r
+               }\r
+               else\r
+               {\r
+                       long fnum = ((LogCounter)instant).getLogFileNumber();\r
+\r
+                       if (fnum != currentLogFileNumber)\r
+                       {\r
+                               if (SanityManager.DEBUG) \r
+                {\r
+                    if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))\r
+                    {\r
+                        SanityManager.DEBUG(LogToFile.DBG_FLAG, \r
+                                                                               "Scan " + scanDirection +\r
+                                                                               " resetting to " + instant + \r
+                                                                               " need to switch log from " + \r
+                                                                               currentLogFileNumber + " to " + fnum);\r
+                    }\r
+                               }\r
+\r
+                               scan.close();\r
+                               scan = logFactory.getLogFileAtPosition(instant_long);\r
+\r
+                               currentLogFileNumber= fnum;\r
+\r
+                               if (scanDirection == FORWARD)\r
+                               {\r
+                                       // NOTE: \r
+                                       //\r
+                                       // just get the length of the file without syncing.\r
+                                       // this only works because the only place forward scan is used\r
+                                       // right now is on recovery redo and nothing is being added to \r
+                                       // the current log file.  When the forward scan is used for some\r
+                                       // other purpose, need to sync access to the end of the log\r
+                                       //\r
+                                       currentLogFileLength = scan.length();\r
+                               }\r
+                       }\r
+                       else\r
+\r
+                       {\r
+                               long fpos = ((LogCounter)instant).getLogFilePosition();\r
+                               scan.seek(fpos);\r
+\r
+                               //\r
+                               //RESOLVE: Can this be optimized? Does it belong here.\r
+                               currentLogFileLength = scan.length();\r
+\r
+                               if (SanityManager.DEBUG)\r
+                {\r
+                    if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))\r
+                    {\r
+                        SanityManager.DEBUG(LogToFile.DBG_FLAG, \r
+                                                                               "Scan reset to " + instant);\r
+                    }\r
+                               }\r
+                       }\r
+\r
+\r
+                       currentInstant = instant_long;\r
+\r
+                       //scan is being reset, it is possibly that, scan is doing a random \r
+                       //access of the log file. set the knownGoodLogEnd to  the instant\r
+                       //scan  is being reset to.\r
+                       //Note: reset gets called with undo forward scan for CLR processing during \r
+                       //recovery, if this value is not reset checks to find the end of log \r
+                       //getNextRecordForward() will fail because undoscan scans log file\r
+                       //back & forth to redo CLR's.\r
+                       knownGoodLogEnd = currentInstant;\r
+\r
+                       if (SanityManager.DEBUG)\r
+            {\r
+                if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))\r
+                {\r
+                    SanityManager.DEBUG(LogToFile.DBG_FLAG, \r
+                        "Scan.getInstant reset to " + currentInstant + \r
+                        LogCounter.toDebugString(currentInstant));\r
+                }\r
+                       }\r
+               }\r
+       }\r
+\r
+       /**\r
+               Return the log instant (as an integer) the scan is currently on - this is the log\r
+               instant of the log record that was returned by getNextRecord.\r
+       */\r
+       public long getInstant()\r
+       {\r
+               return currentInstant;\r
+       }\r
+\r
+       /**\r
+               Return the log instant at the end of the log record on the current\r
+               LogFile in the form of a log instant.\r
+        After the scan has been closed, the end of the last log record will be\r
+        returned except when the scan ended in an empty log file.  In that\r
+        case, the start of this empty log file will be returned.  (This is\r
+        done to make sure new log records are inserted into the newest log\r
+        file.)\r
+       */\r
+       public long getLogRecordEnd()\r
+       {\r
+               return knownGoodLogEnd;\r
+       }\r
+\r
+       /**\r
+          returns true if there is partially writen log records before the crash \r
+          in the last log file. Partiall wrires are identified during forward \r
+          redo scans for log recovery.\r
+       */\r
+       public boolean isLogEndFuzzy()\r
+       {\r
+               return fuzzyLogEnd;\r
+       }\r
+\r
+       /**\r
+               Return the log instant the scan is currently on - this is the log\r
+               instant of the log record that was returned by getNextRecord.\r
+       */\r
+       public LogInstant getLogInstant()\r
+       {\r
+               if (currentInstant == LogCounter.INVALID_LOG_INSTANT)\r
+                       return null;\r
+               else\r
+                       return new LogCounter(currentInstant);\r
+       }\r
+\r
+       /**\r
+               Close the scan.\r
+       */\r
+       public void close()\r
+       {\r
+               if (scan != null)\r
+               {\r
+                       try \r
+                       {\r
+                               scan.close();\r
+                       }\r
+                       catch (IOException ioe)\r
+                       {}\r
+\r
+                       scan = null;\r
+               }\r
+\r
+               logFactory = null;\r
+               currentLogFileNumber = -1;\r
+               currentLogFileLength = -1;\r
+        // Do not reset knownGoodLogEnd, it needs to be available after the\r
+        // scan has closed.\r
+               currentInstant = LogCounter.INVALID_LOG_INSTANT;\r
+               stopAt = LogCounter.INVALID_LOG_INSTANT;\r
+               scanDirection = 0;\r
+       }\r
+\r
+}\r