History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/rmi/log/ReliableLog.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/rmi/log/ReliableLog.java+827−0
@@ -0,0 +1,827 @@
1+/*
2+ * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+
26+package sun.rmi.log;
27+
28+import java.io.*;
29+import java.lang.reflect.Constructor;
30+import java.rmi.server.RMIClassLoader;
31+import java.security.AccessController;
32+import java.security.PrivilegedAction;
33+import sun.security.action.GetBooleanAction;
34+import sun.security.action.GetPropertyAction;
35+
36+/**
37+ * This class is a simple implementation of a reliable Log. The
38+ * client of a ReliableLog must provide a set of callbacks (via a
39+ * LogHandler) that enables a ReliableLog to read and write
40+ * checkpoints and log records. This implementation ensures that the
41+ * current value of the data stored (via a ReliableLog) is recoverable
42+ * after a system crash. <p>
43+ *
44+ * The secondary storage strategy is to record values in files using a
45+ * representation of the caller's choosing. Two sorts of files are
46+ * kept: snapshots and logs. At any instant, one snapshot is current.
47+ * The log consists of a sequence of updates that have occurred since
48+ * the current snapshot was taken. The current stable state is the
49+ * value of the snapshot, as modified by the sequence of updates in
50+ * the log. From time to time, the client of a ReliableLog instructs
51+ * the package to make a new snapshot and clear the log. A ReliableLog
52+ * arranges disk writes such that updates are stable (as long as the
53+ * changes are force-written to disk) and atomic : no update is lost,
54+ * and each update either is recorded completely in the log or not at
55+ * all. Making a new snapshot is also atomic. <p>
56+ *
57+ * Normal use for maintaining the recoverable store is as follows: The
58+ * client maintains the relevant data structure in virtual memory. As
59+ * updates happen to the structure, the client informs the ReliableLog
60+ * (all it "log") by calling log.update. Periodically, the client
61+ * calls log.snapshot to provide the current value of the data
62+ * structure. On restart, the client calls log.recover to obtain the
63+ * latest snapshot and the following sequences of updates; the client
64+ * applies the updates to the snapshot to obtain the state that
65+ * existed before the crash. <p>
66+ *
67+ * The current logfile format is: <ol>
68+ * <li> a format version number (two 4-octet integers, major and
69+ * minor), followed by
70+ * <li> a sequence of log records. Each log record contains, in
71+ * order, <ol>
72+ * <li> a 4-octet integer representing the length of the following log
73+ * data,
74+ * <li> the log data (variable length). </ol> </ol> <p>
75+ *
76+ * @see LogHandler
77+ *
78+ * @author Ann Wollrath
79+ *
80+ */
81+public class ReliableLog {
82+
83+ public final static int PreferredMajorVersion = 0;
84+ public final static int PreferredMinorVersion = 2;
85+
86+ // sun.rmi.log.debug=false
87+ private boolean Debug = false;
88+
89+ private static String snapshotPrefix = "Snapshot.";
90+ private static String logfilePrefix = "Logfile.";
91+ private static String versionFile = "Version_Number";
92+ private static String newVersionFile = "New_Version_Number";
93+ private static int intBytes = 4;
94+ private static long diskPageSize = 512;
95+
96+ private File dir; // base directory
97+ private int version = 0; // current snapshot and log version
98+ private String logName = null;
99+ private LogFile log = null;
100+ private long snapshotBytes = 0;
101+ private long logBytes = 0;
102+ private int logEntries = 0;
103+ private long lastSnapshot = 0;
104+ private long lastLog = 0;
105+ //private long padBoundary = intBytes;
106+ private LogHandler handler;
107+ private final byte[] intBuf = new byte[4];
108+
109+ // format version numbers read from/written to this.log
110+ private int majorFormatVersion = 0;
111+ private int minorFormatVersion = 0;
112+
113+
114+ /**
115+ * Constructor for the log file. If the system property
116+ * sun.rmi.log.class is non-null and the class specified by this
117+ * property a) can be loaded, b) is a subclass of LogFile, and c) has a
118+ * public two-arg constructor (String, String), ReliableLog uses the
119+ * constructor to construct the LogFile.
120+ **/
121+ private static final Constructor<? extends LogFile>
122+ logClassConstructor = getLogClassConstructor();
123+
124+ /**
125+ * Creates a ReliableLog to handle checkpoints and logging in a
126+ * stable storage directory.
127+ *
128+ * @param dirPath path to the stable storage directory
129+ * @param logCl the closure object containing callbacks for logging and
130+ * recovery
131+ * @param pad ignored
132+ * @exception IOException If a directory creation error has
133+ * occurred or if initialSnapshot callback raises an exception or
134+ * if an exception occurs during invocation of the handler's
135+ * snapshot method or if other IOException occurs.
136+ */
137+ public ReliableLog(String dirPath,
138+ LogHandler handler,
139+ boolean pad)
140+ throws IOException
141+ {
142+ super();
143+ this.Debug = AccessController.doPrivileged(
144+ new GetBooleanAction("sun.rmi.log.debug")).booleanValue();
145+ dir = new File(dirPath);
146+ if (!(dir.exists() && dir.isDirectory())) {
147+ // create directory
148+ if (!dir.mkdir()) {
149+ throw new IOException("could not create directory for log: " +
150+ dirPath);
151+ }
152+ }
153+ //padBoundary = (pad ? diskPageSize : intBytes);
154+ this.handler = handler;
155+ lastSnapshot = 0;
156+ lastLog = 0;
157+ getVersion();
158+ if (version == 0) {
159+ try {
160+ snapshot(handler.initialSnapshot());
161+ } catch (IOException e) {
162+ throw e;
163+ } catch (Exception e) {
164+ throw new IOException("initial snapshot failed with " +
165+ "exception: " + e);
166+ }
167+ }
168+ }
169+
170+ /**
171+ * Creates a ReliableLog to handle checkpoints and logging in a
172+ * stable storage directory.
173+ *
174+ * @param dirPath path to the stable storage directory
175+ * @param logCl the closure object containing callbacks for logging and
176+ * recovery
177+ * @exception IOException If a directory creation error has
178+ * occurred or if initialSnapshot callback raises an exception
179+ */
180+ public ReliableLog(String dirPath,
181+ LogHandler handler)
182+ throws IOException
183+ {
184+ this(dirPath, handler, false);
185+ }
186+
187+ /* public methods */
188+
189+ /**
190+ * Returns an object which is the value recorded in the current
191+ * snapshot. This snapshot is recovered by calling the client
192+ * supplied callback "recover" and then subsequently invoking
193+ * the "readUpdate" callback to apply any logged updates to the state.
194+ *
195+ * @exception IOException If recovery fails due to serious log
196+ * corruption, read update failure, or if an exception occurs
197+ * during the recover callback
198+ */
199+ public synchronized Object recover()
200+ throws IOException
201+ {
202+ if (Debug)
203+ System.err.println("log.debug: recover()");
204+
205+ if (version == 0)
206+ return null;
207+
208+ Object snapshot;
209+ String fname = versionName(snapshotPrefix);
210+ File snapshotFile = new File(fname);
211+ InputStream in =
212+ new BufferedInputStream(new FileInputStream(snapshotFile));
213+
214+ if (Debug)
215+ System.err.println("log.debug: recovering from " + fname);
216+
217+ try {
218+ try {
219+ snapshot = handler.recover(in);
220+
221+ } catch (IOException e) {
222+ throw e;
223+ } catch (Exception e) {
224+ if (Debug)
225+ System.err.println("log.debug: recovery failed: " + e);
226+ throw new IOException("log recover failed with " +
227+ "exception: " + e);
228+ }
229+ snapshotBytes = snapshotFile.length();
230+ } finally {
231+ in.close();
232+ }
233+
234+ return recoverUpdates(snapshot);
235+ }
236+
237+ /**
238+ * Records this update in the log file (does not force update to disk).
239+ * The update is recorded by calling the client's "writeUpdate" callback.
240+ * This method must not be called until this log's recover method has
241+ * been invoked (and completed).
242+ *
243+ * @param value the object representing the update
244+ * @exception IOException If an exception occurred during a
245+ * writeUpdate callback or if other I/O error has occurred.
246+ */
247+ public synchronized void update(Object value) throws IOException {
248+ update(value, true);
249+ }
250+
251+ /**
252+ * Records this update in the log file. The update is recorded by
253+ * calling the client's writeUpdate callback. This method must not be
254+ * called until this log's recover method has been invoked
255+ * (and completed).
256+ *
257+ * @param value the object representing the update
258+ * @param forceToDisk ignored; changes are always forced to disk
259+ * @exception IOException If force-write to log failed or an
260+ * exception occurred during the writeUpdate callback or if other
261+ * I/O error occurs while updating the log.
262+ */
263+ public synchronized void update(Object value, boolean forceToDisk)
264+ throws IOException
265+ {
266+ // avoid accessing a null log field.
267+ if (log == null) {
268+ throw new IOException("log is inaccessible, " +
269+ "it may have been corrupted or closed");
270+ }
271+
272+ /*
273+ * If the entry length field spans a sector boundary, write
274+ * the high order bit of the entry length, otherwise write zero for
275+ * the entry length.
276+ */
277+ long entryStart = log.getFilePointer();
278+ boolean spansBoundary = log.checkSpansBoundary(entryStart);
279+ writeInt(log, spansBoundary? 1<<31 : 0);
280+
281+ /*
282+ * Write update, and sync.
283+ */
284+ try {
285+ handler.writeUpdate(new LogOutputStream(log), value);
286+ } catch (IOException e) {
287+ throw e;
288+ } catch (Exception e) {
289+ throw (IOException)
290+ new IOException("write update failed").initCause(e);
291+ }
292+ log.sync();
293+
294+ long entryEnd = log.getFilePointer();
295+ int updateLen = (int) ((entryEnd - entryStart) - intBytes);
296+ log.seek(entryStart);
297+
298+ if (spansBoundary) {
299+ /*
300+ * If length field spans a sector boundary, then
301+ * the next two steps are required (see 4652922):
302+ *
303+ * 1) Write actual length with high order bit set; sync.
304+ * 2) Then clear high order bit of length; sync.
305+ */
306+ writeInt(log, updateLen | 1<<31);
307+ log.sync();
308+
309+ log.seek(entryStart);
310+ log.writeByte(updateLen >> 24);
311+ log.sync();
312+
313+ } else {
314+ /*
315+ * Write actual length; sync.
316+ */
317+ writeInt(log, updateLen);
318+ log.sync();
319+ }
320+
321+ log.seek(entryEnd);
322+ logBytes = entryEnd;
323+ lastLog = System.currentTimeMillis();
324+ logEntries++;
325+ }
326+
327+ /**
328+ * Returns the constructor for the log file if the system property
329+ * sun.rmi.log.class is non-null and the class specified by the
330+ * property a) can be loaded, b) is a subclass of LogFile, and c) has a
331+ * public two-arg constructor (String, String); otherwise returns null.
332+ **/
333+ private static Constructor<? extends LogFile>
334+ getLogClassConstructor() {
335+
336+ String logClassName = AccessController.doPrivileged(
337+ new GetPropertyAction("sun.rmi.log.class"));
338+ if (logClassName != null) {
339+ try {
340+ ClassLoader loader =
341+ AccessController.doPrivileged(
342+ new PrivilegedAction<ClassLoader>() {
343+ public ClassLoader run() {
344+ return ClassLoader.getSystemClassLoader();
345+ }
346+ });
347+ Class cl = loader.loadClass(logClassName);
348+ if (LogFile.class.isAssignableFrom(cl)) {
349+ return cl.getConstructor(String.class, String.class);
350+ }
351+ } catch (Exception e) {
352+ System.err.println("Exception occurred:");
353+ e.printStackTrace();
354+ }
355+ }
356+ return null;
357+ }
358+
359+ /**
360+ * Records this value as the current snapshot by invoking the client
361+ * supplied "snapshot" callback and then empties the log.
362+ *
363+ * @param value the object representing the new snapshot
364+ * @exception IOException If an exception occurred during the
365+ * snapshot callback or if other I/O error has occurred during the
366+ * snapshot process
367+ */
368+ public synchronized void snapshot(Object value)
369+ throws IOException
370+ {
371+ int oldVersion = version;
372+ incrVersion();
373+
374+ String fname = versionName(snapshotPrefix);
375+ File snapshotFile = new File(fname);
376+ FileOutputStream out = new FileOutputStream(snapshotFile);
377+ try {
378+ try {
379+ handler.snapshot(out, value);
380+ } catch (IOException e) {
381+ throw e;
382+ } catch (Exception e) {
383+ throw new IOException("snapshot failed", e);
384+ }
385+ lastSnapshot = System.currentTimeMillis();
386+ } finally {
387+ out.close();
388+ snapshotBytes = snapshotFile.length();
389+ }
390+
391+ openLogFile(true);
392+ writeVersionFile(true);
393+ commitToNewVersion();
394+ deleteSnapshot(oldVersion);
395+ deleteLogFile(oldVersion);
396+ }
397+
398+ /**
399+ * Close the stable storage directory in an orderly manner.
400+ *
401+ * @exception IOException If an I/O error occurs when the log is
402+ * closed
403+ */
404+ public synchronized void close() throws IOException {
405+ if (log == null) return;
406+ try {
407+ log.close();
408+ } finally {
409+ log = null;
410+ }
411+ }
412+
413+ /**
414+ * Returns the size of the snapshot file in bytes;
415+ */
416+ public long snapshotSize() {
417+ return snapshotBytes;
418+ }
419+
420+ /**
421+ * Returns the size of the log file in bytes;
422+ */
423+ public long logSize() {
424+ return logBytes;
425+ }
426+
427+ /* private methods */
428+
429+ /**
430+ * Write an int value in single write operation. This method
431+ * assumes that the caller is synchronized on the log file.
432+ *
433+ * @param out output stream
434+ * @param val int value
435+ * @throws IOException if any other I/O error occurs
436+ */
437+ private void writeInt(DataOutput out, int val)
438+ throws IOException
439+ {
440+ intBuf[0] = (byte) (val >> 24);
441+ intBuf[1] = (byte) (val >> 16);
442+ intBuf[2] = (byte) (val >> 8);
443+ intBuf[3] = (byte) val;
444+ out.write(intBuf);
445+ }
446+
447+ /**
448+ * Generates a filename prepended with the stable storage directory path.
449+ *
450+ * @param name the leaf name of the file
451+ */
452+ private String fName(String name) {
453+ return dir.getPath() + File.separator + name;
454+ }
455+
456+ /**
457+ * Generates a version 0 filename prepended with the stable storage
458+ * directory path
459+ *
460+ * @param name version file name
461+ */
462+ private String versionName(String name) {
463+ return versionName(name, 0);
464+ }
465+
466+ /**
467+ * Generates a version filename prepended with the stable storage
468+ * directory path with the version number as a suffix.
469+ *
470+ * @param name version file name
471+ * @thisversion a version number
472+ */
473+ private String versionName(String prefix, int ver) {
474+ ver = (ver == 0) ? version : ver;
475+ return fName(prefix) + String.valueOf(ver);
476+ }
477+
478+ /**
479+ * Increments the directory version number.
480+ */
481+ private void incrVersion() {
482+ do { version++; } while (version==0);
483+ }
484+
485+ /**
486+ * Delete a file.
487+ *
488+ * @param name the name of the file
489+ * @exception IOException If new version file couldn't be removed
490+ */
491+ private void deleteFile(String name) throws IOException {
492+
493+ File f = new File(name);
494+ if (!f.delete())
495+ throw new IOException("couldn't remove file: " + name);
496+ }
497+
498+ /**
499+ * Removes the new version number file.
500+ *
501+ * @exception IOException If an I/O error has occurred.
502+ */
503+ private void deleteNewVersionFile() throws IOException {
504+ deleteFile(fName(newVersionFile));
505+ }
506+
507+ /**
508+ * Removes the snapshot file.
509+ *
510+ * @param ver the version to remove
511+ * @exception IOException If an I/O error has occurred.
512+ */
513+ private void deleteSnapshot(int ver) throws IOException {
514+ if (ver == 0) return;
515+ deleteFile(versionName(snapshotPrefix, ver));
516+ }
517+
518+ /**
519+ * Removes the log file.
520+ *
521+ * @param ver the version to remove
522+ * @exception IOException If an I/O error has occurred.
523+ */
524+ private void deleteLogFile(int ver) throws IOException {
525+ if (ver == 0) return;
526+ deleteFile(versionName(logfilePrefix, ver));
527+ }
528+
529+ /**
530+ * Opens the log file in read/write mode. If file does not exist, it is
531+ * created.
532+ *
533+ * @param truncate if true and file exists, file is truncated to zero
534+ * length
535+ * @exception IOException If an I/O error has occurred.
536+ */
537+ private void openLogFile(boolean truncate) throws IOException {
538+ try {
539+ close();
540+ } catch (IOException e) { /* assume this is okay */
541+ }
542+
543+ logName = versionName(logfilePrefix);
544+
545+ try {
546+ log = (logClassConstructor == null ?
547+ new LogFile(logName, "rw") :
548+ logClassConstructor.newInstance(logName, "rw"));
549+ } catch (Exception e) {
550+ throw (IOException) new IOException(
551+ "unable to construct LogFile instance").initCause(e);
552+ }
553+
554+ if (truncate) {
555+ initializeLogFile();
556+ }
557+ }
558+
559+ /**
560+ * Creates a new log file, truncated and initialized with the format
561+ * version number preferred by this implementation.
562+ * <p>Environment: inited, synchronized
563+ * <p>Precondition: valid: log, log contains nothing useful
564+ * <p>Postcondition: if successful, log is initialised with the format
565+ * version number (Preferred{Major,Minor}Version), and logBytes is
566+ * set to the resulting size of the updatelog, and logEntries is set to
567+ * zero. Otherwise, log is in an indeterminate state, and logBytes
568+ * is unchanged, and logEntries is unchanged.
569+ *
570+ * @exception IOException If an I/O error has occurred.
571+ */
572+ private void initializeLogFile()
573+ throws IOException
574+ {
575+ log.setLength(0);
576+ majorFormatVersion = PreferredMajorVersion;
577+ writeInt(log, PreferredMajorVersion);
578+ minorFormatVersion = PreferredMinorVersion;
579+ writeInt(log, PreferredMinorVersion);
580+ logBytes = intBytes * 2;
581+ logEntries = 0;
582+ }
583+
584+
585+ /**
586+ * Writes out version number to file.
587+ *
588+ * @param newVersion if true, writes to a new version file
589+ * @exception IOException If an I/O error has occurred.
590+ */
591+ private void writeVersionFile(boolean newVersion) throws IOException {
592+ String name;
593+ if (newVersion) {
594+ name = newVersionFile;
595+ } else {
596+ name = versionFile;
597+ }
598+ DataOutputStream out =
599+ new DataOutputStream(new FileOutputStream(fName(name)));
600+ writeInt(out, version);
601+ out.close();
602+ }
603+
604+ /**
605+ * Creates the initial version file
606+ *
607+ * @exception IOException If an I/O error has occurred.
608+ */
609+ private void createFirstVersion() throws IOException {
610+ version = 0;
611+ writeVersionFile(false);
612+ }
613+
614+ /**
615+ * Commits (atomically) the new version.
616+ *
617+ * @exception IOException If an I/O error has occurred.
618+ */
619+ private void commitToNewVersion() throws IOException {
620+ writeVersionFile(false);
621+ deleteNewVersionFile();
622+ }
623+
624+ /**
625+ * Reads version number from a file.
626+ *
627+ * @param name the name of the version file
628+ * @return the version
629+ * @exception IOException If an I/O error has occurred.
630+ */
631+ private int readVersion(String name) throws IOException {
632+ DataInputStream in = new DataInputStream(new FileInputStream(name));
633+ try {
634+ return in.readInt();
635+ } finally {
636+ in.close();
637+ }
638+ }
639+
640+ /**
641+ * Sets the version. If version file does not exist, the initial
642+ * version file is created.
643+ *
644+ * @exception IOException If an I/O error has occurred.
645+ */
646+ private void getVersion() throws IOException {
647+ try {
648+ version = readVersion(fName(newVersionFile));
649+ commitToNewVersion();
650+ } catch (IOException e) {
651+ try {
652+ deleteNewVersionFile();
653+ }
654+ catch (IOException ex) {
655+ }
656+
657+ try {
658+ version = readVersion(fName(versionFile));
659+ }
660+ catch (IOException ex) {
661+ createFirstVersion();
662+ }
663+ }
664+ }
665+
666+ /**
667+ * Applies outstanding updates to the snapshot.
668+ *
669+ * @param state the most recent snapshot
670+ * @exception IOException If serious log corruption is detected or
671+ * if an exception occurred during a readUpdate callback or if
672+ * other I/O error has occurred.
673+ * @return the resulting state of the object after all updates
674+ */
675+ private Object recoverUpdates(Object state)
676+ throws IOException
677+ {
678+ logBytes = 0;
679+ logEntries = 0;
680+
681+ if (version == 0) return state;
682+
683+ String fname = versionName(logfilePrefix);
684+ InputStream in =
685+ new BufferedInputStream(new FileInputStream(fname));
686+ DataInputStream dataIn = new DataInputStream(in);
687+
688+ if (Debug)
689+ System.err.println("log.debug: reading updates from " + fname);
690+
691+ try {
692+ majorFormatVersion = dataIn.readInt(); logBytes += intBytes;
693+ minorFormatVersion = dataIn.readInt(); logBytes += intBytes;
694+ } catch (EOFException e) {
695+ /* This is a log which was corrupted and/or cleared (by
696+ * fsck or equivalent). This is not an error.
697+ */
698+ openLogFile(true); // create and truncate
699+ in = null;
700+ }
701+ /* A new major version number is a catastrophe (it means
702+ * that the file format is incompatible with older
703+ * clients, and we'll only be breaking things by trying to
704+ * use the log). A new minor version is no big deal for
705+ * upward compatibility.
706+ */
707+ if (majorFormatVersion != PreferredMajorVersion) {
708+ if (Debug) {
709+ System.err.println("log.debug: major version mismatch: " +
710+ majorFormatVersion + "." + minorFormatVersion);
711+ }
712+ throw new IOException("Log file " + logName + " has a " +
713+ "version " + majorFormatVersion +
714+ "." + minorFormatVersion +
715+ " format, and this implementation " +
716+ " understands only version " +
717+ PreferredMajorVersion + "." +
718+ PreferredMinorVersion);
719+ }
720+
721+ try {
722+ while (in != null) {
723+ int updateLen = 0;
724+
725+ try {
726+ updateLen = dataIn.readInt();
727+ } catch (EOFException e) {
728+ if (Debug)
729+ System.err.println("log.debug: log was sync'd cleanly");
730+ break;
731+ }
732+ if (updateLen <= 0) {/* crashed while writing last log entry */
733+ if (Debug) {
734+ System.err.println(
735+ "log.debug: last update incomplete, " +
736+ "updateLen = 0x" +
737+ Integer.toHexString(updateLen));
738+ }
739+ break;
740+ }
741+
742+ // this is a fragile use of available() which relies on the
743+ // twin facts that BufferedInputStream correctly consults
744+ // the underlying stream, and that FileInputStream returns
745+ // the number of bytes remaining in the file (via FIONREAD).
746+ if (in.available() < updateLen) {
747+ /* corrupted record at end of log (can happen since we
748+ * do only one fsync)
749+ */
750+ if (Debug)
751+ System.err.println("log.debug: log was truncated");
752+ break;
753+ }
754+
755+ if (Debug)
756+ System.err.println("log.debug: rdUpdate size " + updateLen);
757+ try {
758+ state = handler.readUpdate(new LogInputStream(in, updateLen),
759+ state);
760+ } catch (IOException e) {
761+ throw e;
762+ } catch (Exception e) {
763+ e.printStackTrace();
764+ throw new IOException("read update failed with " +
765+ "exception: " + e);
766+ }
767+ logBytes += (intBytes + updateLen);
768+ logEntries++;
769+ } /* while */
770+ } finally {
771+ if (in != null)
772+ in.close();
773+ }
774+
775+ if (Debug)
776+ System.err.println("log.debug: recovered updates: " + logEntries);
777+
778+ /* reopen log file at end */
779+ openLogFile(false);
780+
781+ // avoid accessing a null log field
782+ if (log == null) {
783+ throw new IOException("rmid's log is inaccessible, " +
784+ "it may have been corrupted or closed");
785+ }
786+
787+ log.seek(logBytes);
788+ log.setLength(logBytes);
789+
790+ return state;
791+ }
792+
793+ /**
794+ * ReliableLog's log file implementation. This implementation
795+ * is subclassable for testing purposes.
796+ */
797+ public static class LogFile extends RandomAccessFile {
798+
799+ private final FileDescriptor fd;
800+
801+ /**
802+ * Constructs a LogFile and initializes the file descriptor.
803+ **/
804+ public LogFile(String name, String mode)
805+ throws FileNotFoundException, IOException
806+ {
807+ super(name, mode);
808+ this.fd = getFD();
809+ }
810+
811+ /**
812+ * Invokes sync on the file descriptor for this log file.
813+ */
814+ protected void sync() throws IOException {
815+ fd.sync();
816+ }
817+
818+ /**
819+ * Returns true if writing 4 bytes starting at the specified file
820+ * position, would span a 512 byte sector boundary; otherwise returns
821+ * false.
822+ **/
823+ protected boolean checkSpansBoundary(long fp) {
824+ return fp % 512 > 508;
825+ }
826+ }
827+}