KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2002,2006 Oracle. All rights reserved.
5  *
6  * $Id: LastFileReader.java,v 1.47 2006/11/27 18:38:25 linda Exp $
7  */

8
9 package com.sleepycat.je.log;
10
11 import java.io.File JavaDoc;
12 import java.io.IOException JavaDoc;
13 import java.nio.ByteBuffer JavaDoc;
14 import java.util.HashMap JavaDoc;
15 import java.util.HashSet JavaDoc;
16 import java.util.Map JavaDoc;
17 import java.util.Set JavaDoc;
18 import java.util.logging.Level JavaDoc;
19
20 import com.sleepycat.je.DatabaseException;
21 import com.sleepycat.je.dbi.EnvironmentImpl;
22 import com.sleepycat.je.utilint.DbLsn;
23 import com.sleepycat.je.utilint.Tracer;
24
25 /**
26  * LastFileReader traverses the last log file, doing checksums and looking for
27  * the end of the log. Different log types can be registered with it and it
28  * will remember the last occurrence of targetted entry types.
29  */

30 public class LastFileReader extends FileReader {
31
32     /* Log entry types to track. */
33     private Set JavaDoc trackableEntries;
34
35     private long nextUnprovenOffset;
36     private long lastValidOffset;
37     private LogEntryType entryType;
38
39     /*
40      * Last lsn seen for tracked types. Key = LogEntryType, data is the offset
41      * (Long).
42      */

43     private Map JavaDoc lastOffsetSeen;
44
45     /**
46      * This file reader is always positioned at the last file.
47      */

48     public LastFileReader(EnvironmentImpl env,
49                           int readBufferSize)
50         throws IOException JavaDoc, DatabaseException {
51
52         super(env, readBufferSize, true, DbLsn.NULL_LSN, new Long JavaDoc(-1),
53           DbLsn.NULL_LSN, DbLsn.NULL_LSN);
54
55         trackableEntries = new HashSet JavaDoc();
56         lastOffsetSeen = new HashMap JavaDoc();
57
58         lastValidOffset = 0;
59     anticipateChecksumErrors = true;
60         nextUnprovenOffset = nextEntryOffset;
61     }
62
63     /**
64      * Ctor which allows passing in the file number we want to read to the end
65      * of. This is used by the ScavengerFileReader when it encounters a bad
66      * log record in the middle of a file.
67      */

68     public LastFileReader(EnvironmentImpl env,
69                           int readBufferSize,
70               Long JavaDoc specificFileNumber)
71         throws IOException JavaDoc, DatabaseException {
72
73         super(env, readBufferSize, true, DbLsn.NULL_LSN,
74               specificFileNumber, DbLsn.NULL_LSN, DbLsn.NULL_LSN);
75
76         trackableEntries = new HashSet JavaDoc();
77         lastOffsetSeen = new HashMap JavaDoc();
78
79         lastValidOffset = 0;
80     anticipateChecksumErrors = true;
81         nextUnprovenOffset = nextEntryOffset;
82     }
83
84     /**
85      * Override so that we always start at the last file.
86      */

87     protected void initStartingPosition(long endOfFileLsn,
88                     Long JavaDoc singleFileNum)
89         throws IOException JavaDoc, DatabaseException {
90
91         eof = false;
92
93         /*
94          * Start at what seems like the last file. If it doesn't exist, we're
95          * done.
96          */

97         Long JavaDoc lastNum = ((singleFileNum != null) &&
98             (singleFileNum.longValue() >= 0)) ?
99         singleFileNum :
100         fileManager.getLastFileNum();
101         FileHandle fileHandle = null;
102         readBufferFileEnd = 0;
103
104         long fileLen = 0;
105         while ((fileHandle == null) && !eof) {
106             if (lastNum == null) {
107                 eof = true;
108             } else {
109                 try {
110                     readBufferFileNum = lastNum.longValue();
111                     fileHandle = fileManager.getFileHandle(readBufferFileNum);
112
113                     /*
114                      * Check the size of this file. If it opened successfully
115                      * but only held a header or is 0 length, backup to the
116                      * next "last" file unless this is the only file in the
117                      * log. Note that an incomplete header will end up throwing
118                      * a checksum exception, but a 0 length file will open
119                      * successfully in read only mode.
120                      */

121                     fileLen = fileHandle.getFile().length();
122                     if (fileLen <= FileManager.firstLogEntryOffset()) {
123                         lastNum = fileManager.getFollowingFileNum
124                 (lastNum.longValue(), false);
125                         if (lastNum != null) {
126                             fileHandle.release();
127                             fileHandle = null;
128                         }
129                     }
130                 } catch (DatabaseException e) {
131                     lastNum = attemptToMoveBadFile(e);
132                     fileHandle = null;
133                 } finally {
134                     if (fileHandle != null) {
135                         fileHandle.release();
136                     }
137                 }
138             }
139         }
140
141         nextEntryOffset = 0;
142     }
143
144     /**
145      * Something is wrong with this file. If there is no data in this file (the
146      * header is <= the file header size) then move this last file aside and
147      * search the next "last" file. If the last file does have data in it,
148      * throw an exception back to the application, since we're not sure what to
149      * do now.
150      */

151     private Long JavaDoc attemptToMoveBadFile(DatabaseException origException)
152         throws DatabaseException, IOException JavaDoc {
153
154         String JavaDoc fileName = fileManager.getFullFileNames(readBufferFileNum)[0];
155         File JavaDoc problemFile = new File JavaDoc(fileName);
156         Long JavaDoc lastNum = null;
157
158         if (problemFile.length() <= FileManager.firstLogEntryOffset()) {
159             fileManager.clear(); // close all existing files
160
/* Move this file aside. */
161             lastNum = fileManager.getFollowingFileNum(readBufferFileNum,
162                                                       false);
163             fileManager.renameFile(readBufferFileNum,
164                                    FileManager.BAD_SUFFIX);
165
166         } else {
167             /* There's data in this file, throw up to the app. */
168             throw origException;
169         }
170         return lastNum;
171     }
172
173     public void setEndOfFile()
174         throws IOException JavaDoc, DatabaseException {
175
176         fileManager.truncateLog(readBufferFileNum, nextUnprovenOffset);
177     }
178
179     /**
180      * @return The LSN to be used for the next log entry.
181      */

182     public long getEndOfLog() {
183         return DbLsn.makeLsn(readBufferFileNum, nextUnprovenOffset);
184     }
185
186     public long getLastValidLsn() {
187         return DbLsn.makeLsn(readBufferFileNum, lastValidOffset);
188     }
189
190     public long getPrevOffset() {
191         return lastValidOffset;
192     }
193
194     public LogEntryType getEntryType() {
195         return entryType;
196     }
197
198     /**
199      * Tell the reader that we are interested in these kind of entries.
200      */

201     public void setTargetType(LogEntryType type) {
202         trackableEntries.add(type);
203     }
204
205     /**
206      * @return The last LSN seen in the log for this kind of entry, or null.
207      */

208     public long getLastSeen(LogEntryType type) {
209         Long JavaDoc typeNumber =(Long JavaDoc) lastOffsetSeen.get(type);
210         if (typeNumber != null) {
211             return DbLsn.makeLsn(readBufferFileNum, typeNumber.longValue());
212         } else {
213             return DbLsn.NULL_LSN;
214         }
215     }
216
217     /**
218      * Validate the checksum on each entry, see if we should remember the LSN
219      * of this entry.
220      */

221     protected boolean processEntry(ByteBuffer JavaDoc entryBuffer) {
222
223         /* Skip over the data, we're not doing anything with it. */
224         entryBuffer.position(entryBuffer.position() + currentEntrySize);
225
226         /* If we're supposed to remember this lsn, record it. */
227         entryType = new LogEntryType(currentEntryTypeNum,
228                                      currentEntryTypeVersion);
229         if (trackableEntries.contains(entryType)) {
230             lastOffsetSeen.put(entryType, new Long JavaDoc(currentEntryOffset));
231         }
232
233         return true;
234     }
235
236     /**
237      * readNextEntry will stop at a bad entry.
238      * @return true if an element has been read.
239      */

240     public boolean readNextEntry()
241         throws DatabaseException, IOException JavaDoc {
242
243         boolean foundEntry = false;
244
245         try {
246
247             /*
248              * At this point,
249              * currentEntryOffset is the entry we just read.
250              * nextEntryOffset is the entry we're about to read.
251              * currentEntryPrevOffset is 2 entries ago.
252              * Note that readNextEntry() moves all the offset pointers up.
253              */

254
255             foundEntry = super.readNextEntry();
256
257
258             /*
259              * Note that initStartingPosition() makes sure that the file header
260              * entry is valid. So by the time we get to this method, we know
261              * we're at a file with a valid file header entry.
262              */

263             lastValidOffset = currentEntryOffset;
264             nextUnprovenOffset = nextEntryOffset;
265         } catch (DbChecksumException e) {
266             Tracer.trace(Level.INFO,
267                          env, "Found checksum exception while searching " +
268                          " for end of log. Last valid entry is at " +
269                          DbLsn.toString
270              (DbLsn.makeLsn(readBufferFileNum, lastValidOffset)) +
271                          " Bad entry is at " +
272                          DbLsn.makeLsn(readBufferFileNum, nextUnprovenOffset));
273         }
274         return foundEntry;
275     }
276 }
277
Popular Tags