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