KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sleepycat > je > log > FileReader


1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2002,2006 Oracle. All rights reserved.
5  *
6  * $Id: FileReader.java,v 1.98 2006/11/03 03:07:50 mark Exp $
7  */

8
9 package com.sleepycat.je.log;
10
11 import java.io.IOException JavaDoc;
12 import java.nio.Buffer JavaDoc;
13 import java.nio.ByteBuffer JavaDoc;
14
15 import com.sleepycat.je.DatabaseException;
16 import com.sleepycat.je.config.EnvironmentParams;
17 import com.sleepycat.je.dbi.DbConfigManager;
18 import com.sleepycat.je.dbi.EnvironmentImpl;
19 import com.sleepycat.je.utilint.DbLsn;
20 import com.sleepycat.je.utilint.Tracer;
21
22 /**
23  * A FileReader is an abstract class that traverses the log files, reading in
24  * chunks of the file at a time. Concrete subclasses perform a particular
25  * action to each entry.
26  */

27 public abstract class FileReader {
28
29     protected EnvironmentImpl env;
30     protected FileManager fileManager;
31
32     /* Buffering reads */
33     private ByteBuffer JavaDoc readBuffer; // buffer for reading from the file
34
private ByteBuffer JavaDoc saveBuffer; // for piecing together data
35
private int maxReadBufferSize; // read buffer can't grow larger than this
36

37     /* Managing the buffer reads */
38     private boolean singleFile; // if true, do not read across files
39
protected boolean eof; // true if at end of the log.
40
// XXX, use exception instead of status?
41
private boolean forward; // if true, we're reading forward
42

43     /*
44      * ReadBufferFileNum, readBufferFileStart and readBufferFileEnd indicate
45      * how the read buffer maps to the file. For example, if the read buffer
46      * size is 100 and the read buffer was filled from file 9, starting at byte
47      * 100, then
48      * readBufferFileNum = 9
49      * readBufferFileStart = 100
50      * readBufferFileEnd = 200
51      */

52     protected long readBufferFileNum; // file number we're pointing to
53
protected long readBufferFileStart;// file position that maps to buf start
54
protected long readBufferFileEnd; // file position that maps to buf end
55

56     /* stats */
57     private int nRead; // num entries we've seen
58

59     /*
60      * The number of times we've tried to read in a log entry that was too
61      * large for the read buffer.
62      */

63     private long nRepeatIteratorReads;
64
65     /* Number of reads since the last time getAndResetNReads was called. */
66     private int nReadOperations;
67                                  
68     /* Info about the last entry seen. */
69     protected byte currentEntryTypeNum;
70     protected byte currentEntryTypeVersion;
71     protected long currentEntryPrevOffset;
72     protected int currentEntrySize;
73     protected long currentEntryChecksum;
74
75     /*
76      * nextEntryOffset is used to set the currentEntryOffset after we've read
77      * an entry.
78      */

79     protected long currentEntryOffset;
80     protected long nextEntryOffset;
81     protected long startLsn; // We start reading from this LSN.
82
private long finishLsn; // If going backwards, read up to this LSN.
83

84     /* For checking checksum on the read. */
85     protected ChecksumValidator cksumValidator;
86     private boolean doValidateChecksum; // Validate checksums
87
private boolean alwaysValidateChecksum; // Validate for all entry types
88

89     /* True if this is the scavenger and we are expecting checksum issues. */
90     protected boolean anticipateChecksumErrors;
91
92     /**
93      * A FileReader just needs to know what size chunks to read in.
94      * @param endOfFileLsn indicates the end of the log file
95      */

96     public FileReader(EnvironmentImpl env,
97                       int readBufferSize,
98                       boolean forward,
99                       long startLsn,
100                       Long JavaDoc singleFileNumber,
101                       long endOfFileLsn,
102                       long finishLsn)
103         throws IOException JavaDoc, DatabaseException {
104
105         this.env = env;
106         this.fileManager = env.getFileManager();
107         this.doValidateChecksum = env.getLogManager().getChecksumOnRead();
108
109         /* Allocate a read buffer. */
110         this.singleFile = (singleFileNumber != null);
111         this.forward = forward;
112
113         readBuffer = ByteBuffer.allocate(readBufferSize);
114         threadSafeBufferFlip(readBuffer);
115         saveBuffer = ByteBuffer.allocate(readBufferSize);
116
117         DbConfigManager configManager = env.getConfigManager();
118         maxReadBufferSize =
119         configManager.getInt(EnvironmentParams. LOG_ITERATOR_MAX_SIZE);
120
121         /* Determine the starting position. */
122         this.startLsn = startLsn;
123         this.finishLsn = finishLsn;
124         initStartingPosition(endOfFileLsn, singleFileNumber);
125
126         /* stats */
127         nRead = 0;
128         if (doValidateChecksum) {
129             cksumValidator = new ChecksumValidator();
130         }
131     anticipateChecksumErrors = false;
132     }
133
134     /**
135      * Helper for determining the starting position and opening up a file at
136      * the desired location.
137      */

138     protected void initStartingPosition(long endOfFileLsn,
139                     Long JavaDoc ignoreSingleFileNumber)
140         throws IOException JavaDoc, DatabaseException {
141
142         eof = false;
143         if (forward) {
144
145             /*
146              * Start off at the startLsn. If that's null, start at the
147              * beginning of the log. If there are no log files, set eof.
148              */

149             if (startLsn != DbLsn.NULL_LSN) {
150                 readBufferFileNum = DbLsn.getFileNumber(startLsn);
151                 readBufferFileEnd = DbLsn.getFileOffset(startLsn);
152             } else {
153                 Long JavaDoc firstNum = fileManager.getFirstFileNum();
154                 if (firstNum == null) {
155                     eof = true;
156                 } else {
157                     readBufferFileNum = firstNum.longValue();
158                     readBufferFileEnd = 0;
159                 }
160             }
161
162             /*
163              * After we read the first entry, the currentEntry will point here.
164              */

165             nextEntryOffset = readBufferFileEnd;
166         } else {
167
168             /*
169              * Make the read buffer look like it's positioned off the end of
170              * the file. Initialize the first LSN we want to read. When
171              * traversing the log backwards, we always start at the very end.
172              */

173             assert startLsn != DbLsn.NULL_LSN;
174             readBufferFileNum = DbLsn.getFileNumber(endOfFileLsn);
175             readBufferFileStart = DbLsn.getFileOffset(endOfFileLsn);
176             readBufferFileEnd = readBufferFileStart;
177
178             /*
179              * currentEntryPrevOffset points to the entry we want to start out
180              * reading when going backwards. If it's 0, the entry we want to
181              * read is in a different file.
182              */

183             if (DbLsn.getFileNumber(startLsn) ==
184         DbLsn.getFileNumber(endOfFileLsn)) {
185                 currentEntryPrevOffset = DbLsn.getFileOffset(startLsn);
186             } else {
187                 currentEntryPrevOffset = 0;
188             }
189             currentEntryOffset = DbLsn.getFileOffset(endOfFileLsn);
190         }
191     }
192
193     /**
194      * Whether to always validate the checksum, even for non-target entries.
195      */

196     public void setAlwaysValidateChecksum(boolean validate) {
197         alwaysValidateChecksum = validate;
198     }
199
200     /**
201      * @return the number of entries processed by this reader.
202      */

203     public int getNumRead() {
204         return nRead;
205     }
206
207     public long getNRepeatIteratorReads() {
208         return nRepeatIteratorReads;
209     }
210
211     /**
212      * Get LSN of the last entry read.
213      */

214     public long getLastLsn() {
215         return DbLsn.makeLsn(readBufferFileNum, currentEntryOffset);
216     }
217
218     /**
219      * Returns the total size (including header) of the last entry read.
220      */

221     public int getLastEntrySize() {
222         return LogManager.HEADER_BYTES + currentEntrySize;
223     }
224
225     /**
226      * readNextEntry scans the log files until either it's reached the end of
227      * the log or has hit an invalid portion. It then returns false.
228      *
229      * @return true if an element has been read
230      */

231     public boolean readNextEntry()
232         throws DatabaseException, IOException JavaDoc {
233
234         boolean foundEntry = false;
235         try {
236             while ((!eof) && (!foundEntry)) {
237
238                 /* Read the next header. */
239                 getLogEntryInReadBuffer();
240                 ByteBuffer JavaDoc dataBuffer =
241                     readData(LogManager.HEADER_BYTES, true);
242
243                 readHeader(dataBuffer);
244
245                 boolean isTargetEntry = isTargetEntry(currentEntryTypeNum,
246                                                       currentEntryTypeVersion);
247                 boolean doValidate = doValidateChecksum &&
248                     (isTargetEntry || alwaysValidateChecksum);
249                 boolean collectData = doValidate || isTargetEntry;
250
251                 /* Initialize the checksum with the header. */
252                 if (doValidate) {
253                     startChecksum(dataBuffer);
254                 }
255
256                 /*
257                  * Read in the body of the next entry. Note that even if this
258                  * isn't a targetted entry, we have to move the buffer position
259                  * along.
260                  */

261                 dataBuffer = readData(currentEntrySize, collectData);
262
263                 /*
264                  * We've read an entry. Move up our offsets if we're moving
265                  * forward. If we're moving backwards, we set our offset before
266                  * we read the header, because we knew where the entry started.
267                  */

268                 if (forward) {
269                     currentEntryOffset = nextEntryOffset;
270                     nextEntryOffset +=
271                         LogManager.HEADER_BYTES + currentEntrySize;
272                 }
273
274                 /* Validate the log entry checksum. */
275                 if (doValidate) {
276                     validateChecksum(dataBuffer);
277                 }
278
279                 if (isTargetEntry) {
280
281                     /*
282                      * For a target entry, call the subclass reader's
283                      * processEntry method to do whatever we need with the
284                      * entry. It returns true if this entry is one that should
285                      * be returned. Note that some entries, although targetted
286                      * and read, are not returned.
287                      */

288                     if (processEntry(dataBuffer)) {
289                         foundEntry = true;
290                         nRead++;
291                     }
292                 } else if (collectData) {
293
294                     /*
295                      * For a non-target entry that was validated, the buffer is
296                      * positioned at the start of the entry; skip over it.
297                      */

298                     threadSafeBufferPosition
299                         (dataBuffer,
300                          threadSafeBufferPosition(dataBuffer) +
301                          currentEntrySize);
302                 }
303             }
304         } catch (EOFException e) {
305             eof = true;
306         } catch (DatabaseException e) {
307             eof = true;
308             /* Report on error. */
309             LogEntryType problemType =
310                 LogEntryType.findType(currentEntryTypeNum,
311                       currentEntryTypeVersion);
312             Tracer.trace(env, "FileReader", "readNextEntry",
313              "Halted log file reading at file 0x" +
314                          Long.toHexString(readBufferFileNum) +
315                          " offset 0x" +
316                          Long.toHexString(nextEntryOffset) +
317                          " offset(decimal)=" + nextEntryOffset +
318                          ":\nentry="+ problemType +
319                          "(typeNum=" + currentEntryTypeNum +
320                          ",version=" + currentEntryTypeVersion +
321                          ")\nprev=0x" +
322                          Long.toHexString(currentEntryPrevOffset) +
323                          "\nsize=" + currentEntrySize +
324                          "\nNext entry should be at 0x" +
325                          Long.toHexString((nextEntryOffset +
326                                            LogManager.HEADER_BYTES +
327                                            currentEntrySize)) +
328                          "\n:", e);
329             throw e;
330         }
331         return foundEntry;
332     }
333
334     protected boolean resyncReader(long nextGoodRecordPostCorruption,
335                    boolean dumpCorruptedBounds)
336     throws DatabaseException, IOException JavaDoc {
337
338     /* Resync not allowed for straight FileReader runs. */
339     return false;
340     }
341
342     /**
343      * Make sure that the start of the target log entry is in the header. This
344      * is a no-op if we're reading forwards
345      */

346     private void getLogEntryInReadBuffer()
347         throws IOException JavaDoc, DatabaseException, EOFException {
348
349         /*
350          * If we're going forward, because we read every byte sequentially,
351          * we're always sure the read buffer is positioned at the right spot.
352          * If we go backwards, we need to jump the buffer position.
353          */

354         if (!forward) {
355
356             /*
357              * currentEntryPrevOffset is the entry before the current entry.
358              * currentEntryOffset is the entry we just read (or the end of the
359              * file if we're starting out.
360              */

361             if ((currentEntryPrevOffset != 0) &&
362                 (currentEntryPrevOffset >= readBufferFileStart)) {
363
364                 /* The next log entry has passed the start LSN. */
365                 long nextLsn = DbLsn.makeLsn(readBufferFileNum,
366                          currentEntryPrevOffset);
367                 if (finishLsn != DbLsn.NULL_LSN) {
368                     if (DbLsn.compareTo(nextLsn, finishLsn) == -1) {
369                         throw new EOFException();
370                     }
371                 }
372
373                 /* This log entry starts in this buffer, just reposition. */
374         threadSafeBufferPosition(readBuffer,
375                      (int) (currentEntryPrevOffset -
376                         readBufferFileStart));
377             } else {
378
379         /*
380          * If the start of the log entry is not in this read buffer,
381          * fill the buffer again. If the target log entry is in a
382          * different file from the current read buffer file, just start
383          * the read from the target LSN. If the target log entry is the
384          * same file but the log entry is larger than the read chunk
385          * size, also start the next read buffer from the target
386          * LSN. Otherwise, try to position the next buffer chunk so the
387          * target entry is held within the buffer, all the way at the
388          * end.
389          */

390                 if (currentEntryPrevOffset == 0) {
391                     /* Go to another file. */
392                     currentEntryPrevOffset =
393                         fileManager.getFileHeaderPrevOffset(readBufferFileNum);
394                     Long JavaDoc prevFileNum =
395                         fileManager.getFollowingFileNum(readBufferFileNum,
396                                                         false);
397                     if (prevFileNum == null) {
398                         throw new EOFException();
399                     }
400                     if (readBufferFileNum - prevFileNum.longValue() != 1) {
401
402             if (!resyncReader(DbLsn.makeLsn
403                       (prevFileNum.longValue(),
404                        DbLsn.MAX_FILE_OFFSET),
405                       false)) {
406
407                 throw new DatabaseException
408                 ("Cannot read backward over cleaned file" +
409                  " from " + readBufferFileNum +
410                  " to " + prevFileNum);
411             }
412             }
413                     readBufferFileNum = prevFileNum.longValue();
414                     readBufferFileStart = currentEntryPrevOffset;
415                 } else if ((currentEntryOffset - currentEntryPrevOffset) >
416                            readBuffer.capacity()) {
417
418                     /*
419              * The entry is in the same file, but is bigger than one
420              * buffer.
421              */

422                     readBufferFileStart = currentEntryPrevOffset;
423                 } else {
424
425                     /* In same file, but not in this buffer. */
426                     long newPosition = currentEntryOffset -
427                         readBuffer.capacity();
428                     readBufferFileStart = (newPosition < 0) ? 0 : newPosition;
429                 }
430
431                 /* The next log entry has passed the start LSN. */
432                 long nextLsn = DbLsn.makeLsn(readBufferFileNum,
433                          currentEntryPrevOffset);
434                 if (finishLsn != DbLsn.NULL_LSN) {
435                     if (DbLsn.compareTo(nextLsn, finishLsn) == -1) {
436                         throw new EOFException();
437                     }
438                 }
439
440                 /*
441                  * Now that we've set readBufferFileNum and
442                  * readBufferFileStart, do the read.
443                  */

444                 FileHandle fileHandle =
445                     fileManager.getFileHandle(readBufferFileNum);
446                 try {
447                     readBuffer.clear();
448                     fileManager.readFromFile(fileHandle.getFile(), readBuffer,
449                                              readBufferFileStart);
450                     nReadOperations += 1;
451
452             assert EnvironmentImpl.maybeForceYield();
453                 } finally {
454                     fileHandle.release();
455                 }
456                 readBufferFileEnd = readBufferFileStart +
457                     threadSafeBufferPosition(readBuffer);
458                 threadSafeBufferFlip(readBuffer);
459         threadSafeBufferPosition(readBuffer,
460                      (int) (currentEntryPrevOffset -
461                         readBufferFileStart));
462             }
463             
464             /* The current entry will start at this offset. */
465             currentEntryOffset = currentEntryPrevOffset;
466         } else {
467
468         /*
469          * Going forward, and an end point has been specified. Check if
470          * we've gone past.
471          */

472         if (finishLsn != DbLsn.NULL_LSN) {
473         /* The next log entry has passed the end LSN. */
474         long nextLsn = DbLsn.makeLsn(readBufferFileNum,
475                          nextEntryOffset);
476         if (DbLsn.compareTo(nextLsn, finishLsn) >= 0) {
477             throw new EOFException();
478         }
479         }
480     }
481     }
482
483     /**
484      * Read the log entry header, leaving the buffer mark at the beginning of
485      * the checksummed header data.
486      */

487     private void readHeader(ByteBuffer JavaDoc dataBuffer)
488         throws DatabaseException {
489
490         /* Get the checksum for this log entry. */
491         currentEntryChecksum = LogUtils.getUnsignedInt(dataBuffer);
492         dataBuffer.mark();
493
494         /* Read the log entry header. */
495         currentEntryTypeNum = dataBuffer.get();
496
497         /*
498          * Always validate the entry type, since this check is cheap. Throw a
499          * DbChecksumException so that LastFileReader and others will recognize
500          * this as data corruption.
501          */

502         if (!LogEntryType.isValidType(currentEntryTypeNum))
503             throw new DbChecksumException
504         ((anticipateChecksumErrors ? null : env),
505                  "FileReader read invalid log entry type: " +
506                  currentEntryTypeNum);
507
508         currentEntryTypeVersion = dataBuffer.get();
509         currentEntryPrevOffset = LogUtils.getUnsignedInt(dataBuffer);
510         currentEntrySize = LogUtils.readInt(dataBuffer);
511     }
512
513     /**
514      * Reset the checksum and add the header bytes. This method must be called
515      * with the entry header data at the buffer mark.
516      */

517     private void startChecksum(ByteBuffer JavaDoc dataBuffer)
518         throws DatabaseException {
519
520         /* Move back up to the beginning of the cksum covered header. */
521         cksumValidator.reset();
522         int entryStart = threadSafeBufferPosition(dataBuffer);
523         dataBuffer.reset();
524         cksumValidator.update(env, dataBuffer,
525                               LogManager.HEADER_CONTENT_BYTES,
526                               anticipateChecksumErrors);
527         
528         /* Move the data buffer back to where the log entry starts. */
529         threadSafeBufferPosition(dataBuffer, entryStart);
530     }
531
532     /**
533      * Add the entry bytes to the checksum and check the value. This method
534      * must be called with the buffer positioned at the start of the entry.
535      */

536     private void validateChecksum(ByteBuffer JavaDoc entryBuffer)
537         throws DatabaseException {
538
539         cksumValidator.update(env, entryBuffer, currentEntrySize,
540                   anticipateChecksumErrors);
541         cksumValidator.validate(env, currentEntryChecksum,
542                 readBufferFileNum, currentEntryOffset,
543                 anticipateChecksumErrors);
544     }
545
546     /**
547      * Try to read a specified number of bytes.
548      * @param amountToRead is the number of bytes we need
549      * @param collectData is true if we need to actually look at the data.
550      * If false, we know we're skipping this entry, and all we need to
551      * do is to count until we get to the right spot.
552      * @return a byte buffer positioned at the head of the desired portion,
553      * or null if we reached eof.
554      */

555     private ByteBuffer JavaDoc readData(int amountToRead, boolean collectData)
556         throws IOException JavaDoc, DatabaseException, EOFException {
557
558         int alreadyRead = 0;
559         ByteBuffer JavaDoc completeBuffer = null;
560         saveBuffer.clear();
561
562         while ((alreadyRead < amountToRead) && !eof) {
563             
564             int bytesNeeded = amountToRead - alreadyRead;
565             if (readBuffer.hasRemaining()) {
566
567                 /* There's data in the read buffer, process it. */
568                 if (collectData) {
569                     /*
570                      * Save data in a buffer for processing.
571                      */

572                     if ((alreadyRead > 0) ||
573                         (readBuffer.remaining() < bytesNeeded)) {
574
575                         /* We need to piece an entry together. */
576
577                         copyToSaveBuffer(bytesNeeded);
578                         alreadyRead = threadSafeBufferPosition(saveBuffer);
579                         completeBuffer = saveBuffer;
580                     } else {
581
582                         /* A complete entry is available in this buffer. */
583
584                         completeBuffer = readBuffer;
585                         alreadyRead = amountToRead;
586                     }
587                 } else {
588                     /*
589                      * No need to save data, just move buffer positions.
590                      */

591                     int positionIncrement =
592                         (readBuffer.remaining() > bytesNeeded) ?
593                         bytesNeeded : readBuffer.remaining();
594
595                     alreadyRead += positionIncrement;
596             threadSafeBufferPosition
597             (readBuffer,
598              threadSafeBufferPosition(readBuffer) +
599              positionIncrement);
600                     completeBuffer = readBuffer;
601                 }
602             } else {
603                 /*
604                  * Look for more data.
605                  */

606                 fillReadBuffer(bytesNeeded);
607             }
608         }
609
610         /* Flip the save buffer just in case we've been accumulating in it. */
611         threadSafeBufferFlip(saveBuffer);
612
613         return completeBuffer;
614     }
615
616     /**
617      * Change the read buffer size if we start hitting large log
618      * entries so we don't get into an expensive cycle of multiple reads
619      * and piecing together of log entries.
620      */

621     private void adjustReadBufferSize(int amountToRead) {
622         int readBufferSize = readBuffer.capacity();
623         /* We need to read something larger than the current buffer size. */
624         if (amountToRead > readBufferSize) {
625             /* We're not at the max yet. */
626             if (readBufferSize < maxReadBufferSize) {
627
628                 /*
629                  * Make the buffer the minimum of amountToRead or a
630                  * maxReadBufferSize.
631                  */

632                 if (amountToRead < maxReadBufferSize) {
633                     readBufferSize = amountToRead;
634                     /* Make it a modulo of 1K */
635                     int remainder = readBufferSize % 1024;
636                     readBufferSize += 1024 - remainder;
637                     readBufferSize = Math.min(readBufferSize,
638                                       maxReadBufferSize);
639                 } else {
640                     readBufferSize = maxReadBufferSize;
641                 }
642                 readBuffer = ByteBuffer.allocate(readBufferSize);
643             }
644             
645             if (amountToRead > readBuffer.capacity()) {
646                 nRepeatIteratorReads++;
647             }
648         }
649     }
650
651     /**
652      * Copy the required number of bytes into the save buffer.
653      */

654     private void copyToSaveBuffer(int bytesNeeded) {
655         /* How much can we get from this current read buffer? */
656         int bytesFromThisBuffer;
657
658         if (bytesNeeded <= readBuffer.remaining()) {
659             bytesFromThisBuffer = bytesNeeded;
660         } else {
661             bytesFromThisBuffer = readBuffer.remaining();
662         }
663                 
664         /* Gather it all into this save buffer. */
665         ByteBuffer JavaDoc temp;
666
667         /* Make sure the save buffer is big enough. */
668         if (saveBuffer.capacity() - threadSafeBufferPosition(saveBuffer) <
669             bytesFromThisBuffer) {
670             /* Grow the save buffer. */
671             temp = ByteBuffer.allocate(saveBuffer.capacity() +
672                                           bytesFromThisBuffer);
673             threadSafeBufferFlip(saveBuffer);
674             temp.put(saveBuffer);
675             saveBuffer = temp;
676         }
677
678         /*
679          * Bulk copy only the required section from the read buffer into the
680          * save buffer. We need from readBuffer.position() to
681          * readBuffer.position() + bytesFromThisBuffer
682          */

683         temp = readBuffer.slice();
684         temp.limit(bytesFromThisBuffer);
685         saveBuffer.put(temp);
686     threadSafeBufferPosition(readBuffer,
687                  threadSafeBufferPosition(readBuffer) +
688                  bytesFromThisBuffer);
689     }
690
691     /**
692      * Fill up the read buffer with more data.
693      */

694     private void fillReadBuffer(int bytesNeeded)
695     throws DatabaseException, EOFException {
696
697         FileHandle fileHandle = null;
698         try {
699             adjustReadBufferSize(bytesNeeded);
700
701             /* Get a file handle to read in more log. */
702             fileHandle = fileManager.getFileHandle(readBufferFileNum);
703             boolean fileOk = false;
704
705             /*
706              * Check to see if we've come to the end of the file. If so, get
707              * the next file.
708              */

709             if (readBufferFileEnd < fileHandle.getFile().length()) {
710                 fileOk = true;
711             } else {
712                 /* This file is done -- can we read in the next file? */
713                 if (!singleFile) {
714                     Long JavaDoc nextFile =
715                         fileManager.getFollowingFileNum(readBufferFileNum,
716                                                         forward);
717                     if (nextFile != null) {
718                         readBufferFileNum = nextFile.longValue();
719                         fileHandle.release();
720                         fileHandle =
721                             fileManager.getFileHandle(readBufferFileNum);
722                         fileOk = true;
723                         readBufferFileEnd = 0;
724                         nextEntryOffset = 0;
725                     }
726                 }
727             }
728
729             if (fileOk) {
730                 readBuffer.clear();
731         fileManager.readFromFile(fileHandle.getFile(), readBuffer,
732                                          readBufferFileEnd);
733                 nReadOperations += 1;
734
735         assert EnvironmentImpl.maybeForceYield();
736
737                 readBufferFileStart = readBufferFileEnd;
738                 readBufferFileEnd =
739             readBufferFileStart + threadSafeBufferPosition(readBuffer);
740                 threadSafeBufferFlip(readBuffer);
741             } else {
742                 throw new EOFException();
743             }
744         } catch (IOException JavaDoc e) {
745             e.printStackTrace();
746             throw new DatabaseException
747         ("Problem in fillReadBuffer, readBufferFileNum = " +
748          readBufferFileNum + ": " + e.getMessage());
749
750         } finally {
751             if (fileHandle != null) {
752                 fileHandle.release();
753             }
754         }
755     }
756
757     /**
758      * Returns the number of reads since the last time this method was called.
759      */

760     public int getAndResetNReads() {
761         int tmp = nReadOperations;
762         nReadOperations = 0;
763         return tmp;
764     }
765
766     /**
767      * @return true if this reader should process this entry, or just
768      * skip over it.
769      */

770     protected boolean isTargetEntry(byte logEntryTypeNumber,
771                                     byte logEntryTypeVersion)
772         throws DatabaseException {
773
774         return true;
775     }
776
777     /**
778      * Each file reader implements this method to process the entry data.
779      * @param enteryBuffer contains the entry data and is positioned at the
780      * data
781      * @return true if this entry should be returned
782      */

783     protected abstract boolean processEntry(ByteBuffer JavaDoc entryBuffer)
784         throws DatabaseException;
785
786     private static class EOFException extends Exception JavaDoc {
787     }
788
789     /**
790      * Note that we catch Exception here because it is possible that another
791      * thread is modifying the state of buffer simultaneously. Specifically,
792      * this can happen if another thread is writing this log buffer out and it
793      * does (e.g.) a flip operation on it. The actual mark/pos of the buffer
794      * may be caught in an unpredictable state. We could add another latch to
795      * protect this buffer, but that's heavier weight than we need. So the
796      * easiest thing to do is to just retry the duplicate operation. See
797      * [#9822].
798      */

799     private Buffer JavaDoc threadSafeBufferFlip(ByteBuffer JavaDoc buffer) {
800     while (true) {
801         try {
802         return buffer.flip();
803         } catch (IllegalArgumentException JavaDoc IAE) {
804         continue;
805         }
806     }
807     }
808
809     private int threadSafeBufferPosition(ByteBuffer JavaDoc buffer) {
810     while (true) {
811         try {
812         return buffer.position();
813         } catch (IllegalArgumentException JavaDoc IAE) {
814         continue;
815         }
816     }
817     }
818
819     private Buffer JavaDoc threadSafeBufferPosition(ByteBuffer JavaDoc buffer,
820                         int newPosition) {
821     while (true) {
822         try {
823         return buffer.position(newPosition);
824         } catch (IllegalArgumentException JavaDoc IAE) {
825         continue;
826         }
827     }
828     }
829 }
830
Popular Tags