001: /*-
002: * See the file LICENSE for redistribution information.
003: *
004: * Copyright (c) 2002,2008 Oracle. All rights reserved.
005: *
006: * $Id: FileEdgeCaseTest.java,v 1.2.2.3 2008/01/07 15:14:29 cwl Exp $
007: */
008:
009: package com.sleepycat.je.log;
010:
011: import java.io.File;
012: import java.io.IOException;
013: import java.io.RandomAccessFile;
014:
015: import junit.framework.TestCase;
016:
017: import com.sleepycat.je.DatabaseException;
018: import com.sleepycat.je.DbInternal;
019: import com.sleepycat.je.Environment;
020: import com.sleepycat.je.EnvironmentConfig;
021: import com.sleepycat.je.dbi.EnvironmentImpl;
022: import com.sleepycat.je.recovery.NoRootException;
023: import com.sleepycat.je.util.TestUtils;
024:
025: public class FileEdgeCaseTest extends TestCase {
026:
027: private File envHome;
028: private Environment env;
029: private String firstFile;
030:
031: public FileEdgeCaseTest() {
032: envHome = new File(System.getProperty(TestUtils.DEST_DIR));
033: }
034:
035: public void setUp() throws IOException {
036:
037: TestUtils.removeLogFiles("Setup", envHome, false);
038: }
039:
040: public void tearDown() throws Exception {
041:
042: /*
043: * Close down environments in case the unit test failed so that the log
044: * files can be removed.
045: */
046: try {
047: if (env != null) {
048: env.close();
049: env = null;
050: }
051: } catch (DatabaseException e) {
052: e.printStackTrace();
053: // ok, the test closed it
054: }
055: TestUtils.removeLogFiles("TearDown", envHome, false);
056: }
057:
058: /**
059: * SR #15133
060: * Create a JE environment with a single log file and a checksum
061: * exception in the second entry in the log file.
062: *
063: * When an application attempts to open this JE environment, JE truncates
064: * the log file at the point before the bad checksum, because it assumes
065: * that bad entries at the end of the log are the result of incompletely
066: * flushed writes from the last environment use. However, the truncated
067: * log doesn't have a valid environment root, so JE complains and asks the
068: * application to move aside the existing log file (via the exception
069: * message). The resulting environment has a single log file, with
070: * a single valid entry, which is the file header.
071: *
072: * Any subsequent attempts to open the environment should also fail at the
073: * same point. In the error case reported by this SR, we didn't handle this
074: * single log file/single file header case right, and subsequent opens
075: * first truncated before the file header, leaving a 0 length log, and
076: * then proceeded to write error trace messages into the log. This
077: * resulted in a log file with no file header, (but with trace messages)
078: * and any following opens got unpredictable errors like
079: * ClassCastExceptions and BufferUnderflows.
080: *
081: * The correct situation is to continue to get the same exception.
082: */
083: public void testPostChecksumError() throws IOException,
084: DatabaseException {
085:
086: EnvironmentConfig config = new EnvironmentConfig();
087: config.setAllowCreate(true);
088: env = new Environment(envHome, config);
089:
090: EnvironmentImpl envImpl = DbInternal.envGetEnvironmentImpl(env);
091: FileManager fm = envImpl.getFileManager();
092: firstFile = fm.getFullFileName(0, FileManager.JE_SUFFIX);
093:
094: env.close();
095: env = null;
096:
097: /* Intentionally corrupt the second entry. */
098: corruptSecondEntry();
099:
100: /*
101: * Next attempt to open the environment should fail with a
102: * NoRootException
103: */
104: try {
105: env = new Environment(envHome, config);
106: } catch (NoRootException expected) {
107: }
108:
109: /*
110: * Next attempt to open the environment should fail with a
111: * NoRootException
112: */
113: try {
114: env = new Environment(envHome, config);
115: } catch (NoRootException expected) {
116: }
117: }
118:
119: /**
120: * Write junk into the second log entry, after the file header.
121: */
122: private void corruptSecondEntry() throws IOException {
123:
124: RandomAccessFile file = new RandomAccessFile(firstFile,
125: FileManager.FileMode.READWRITE_MODE.getModeValue());
126:
127: try {
128: byte[] junk = new byte[20];
129: file.seek(FileManager.firstLogEntryOffset());
130: file.write(junk);
131: } catch (Exception e) {
132: e.printStackTrace();
133: } finally {
134: file.close();
135: }
136: }
137: }
|