001: /*-
002: * See the file LICENSE for redistribution information.
003: *
004: * Copyright (c) 2002,2008 Oracle. All rights reserved.
005: *
006: * $Id: LogEntryHeader.java,v 1.1.2.4 2008/01/07 15:14:13 cwl Exp $
007: */
008:
009: package com.sleepycat.je.log;
010:
011: import java.nio.ByteBuffer;
012: import java.util.zip.Checksum;
013:
014: import com.sleepycat.je.DatabaseException;
015: import com.sleepycat.je.dbi.EnvironmentImpl;
016: import com.sleepycat.je.log.entry.LogEntry;
017: import com.sleepycat.je.utilint.Adler32;
018: import com.sleepycat.je.utilint.VLSN;
019:
020: /**
021: * A LogEntryHeader embodies the header information at the beginning of each
022: * log entry file.
023: */
024: public class LogEntryHeader {
025:
026: /**
027: * Persistent fields. Layout on disk is
028: * checksum - 4 bytes
029: * entry type - 1 byte
030: * entry version and flags - 1 byte
031: * offset of previous log entry - 4 bytes
032: * item size (not counting header size) - 4 bytes
033: * vlsn (optional) - 16 bytes
034: */
035:
036: static final int MIN_HEADER_SIZE = 14;
037:
038: /* Only used for tests and asserts. */
039: static final int MAX_HEADER_SIZE = MIN_HEADER_SIZE + VLSN.LOG_SIZE;
040:
041: private static final int CHECKSUM_BYTES = 4;
042: private static final int ENTRYTYPE_OFFSET = 4;
043: private static final int PREV_OFFSET = 6;
044: private static final int ITEMSIZE_OFFSET = 10;
045: private static final int VLSN_OFFSET = 14;
046:
047: private long checksum; // stored in 4 bytes as an unsigned int
048: private byte entryType;
049: private byte entryVersion;
050: private long prevOffset;
051: private int itemSize;
052: private VLSN vlsn;
053:
054: /* Transient fields */
055: private boolean isProvisional;
056: private boolean replicate;
057:
058: /**
059: * For reading a log entry.
060: */
061: public LogEntryHeader(EnvironmentImpl envImpl,
062: ByteBuffer entryBuffer, boolean anticipateChecksumErrors)
063: throws DatabaseException {
064:
065: checksum = LogUtils.getUnsignedInt(entryBuffer);
066: entryType = entryBuffer.get();
067: if (!LogEntryType.isValidType(entryType))
068: throw new DbChecksumException(
069: (anticipateChecksumErrors ? null : envImpl),
070: "Read invalid log entry type: " + entryType);
071:
072: entryVersion = entryBuffer.get();
073: prevOffset = LogUtils.getUnsignedInt(entryBuffer);
074: itemSize = LogUtils.readInt(entryBuffer);
075:
076: isProvisional = LogEntryType.isEntryProvisional(entryVersion);
077: replicate = LogEntryType.isEntryReplicated(entryVersion);
078: }
079:
080: /**
081: * For writing a log header.
082: */
083: LogEntryHeader(LogEntry entry, boolean isProvisional,
084: boolean replicate) {
085:
086: LogEntryType logEntryType = entry.getLogType();
087: entryType = logEntryType.getTypeNum();
088: entryVersion = logEntryType.getVersion();
089: this .itemSize = entry.getSize();
090: this .isProvisional = isProvisional;
091: this .replicate = replicate;
092: }
093:
094: long getChecksum() {
095: return checksum;
096: }
097:
098: public byte getType() {
099: return entryType;
100: }
101:
102: public byte getVersion() {
103: return entryVersion;
104: }
105:
106: long getPrevOffset() {
107: return prevOffset;
108: }
109:
110: public int getItemSize() {
111: return itemSize;
112: }
113:
114: public boolean getReplicate() {
115: return replicate;
116: }
117:
118: int getVariablePortionSize() {
119: return VLSN.LOG_SIZE;
120: }
121:
122: /**
123: * @return number of bytes used to store this header
124: */
125: public int getSize() {
126: if (replicate) {
127: return MIN_HEADER_SIZE + VLSN.LOG_SIZE;
128: } else {
129: return MIN_HEADER_SIZE;
130: }
131: }
132:
133: /**
134: */
135: int getSizeMinusChecksum() {
136: return getSize() - CHECKSUM_BYTES;
137: }
138:
139: /**
140: * Assumes this is called directly after the constructor, and that the
141: * entryBuffer is positioned right before the VLSN.
142: */
143: void readVariablePortion(ByteBuffer entryBuffer)
144: throws LogException {
145: if (replicate) {
146: vlsn = new VLSN();
147: vlsn.readFromLog(entryBuffer, entryVersion);
148: }
149: }
150:
151: /**
152: * Serialize this object into the buffer and leave the buffer positioned in
153: * the right place to write the following item. The checksum, prevEntry,
154: * and vlsn values will filled in later on.
155: */
156: void writeToLog(ByteBuffer entryBuffer) {
157:
158: /* Skip over the checksum, proceed to the entry type. */
159: entryBuffer.position(ENTRYTYPE_OFFSET);
160: entryBuffer.put(entryType);
161:
162: /* version and flags */
163: if (isProvisional) {
164: entryVersion = LogEntryType
165: .setEntryProvisional(entryVersion);
166: }
167: if (replicate) {
168: entryVersion = LogEntryType
169: .setEntryReplicated(entryVersion);
170: }
171: entryBuffer.put(entryVersion);
172:
173: /*
174: * Leave room for the prev offset, which must be added under
175: * the log write latch. Proceed to write the item size.
176: */
177: entryBuffer.position(ITEMSIZE_OFFSET);
178: LogUtils.writeInt(entryBuffer, itemSize);
179:
180: /*
181: * Leave room for a VLSN if needed, must also be generated
182: * under the log write latch.
183: */
184: if (replicate) {
185: entryBuffer
186: .position(entryBuffer.position() + VLSN.LOG_SIZE);
187: }
188: }
189:
190: /**
191: * Add those parts of the header that must be calculated later.
192: * That's
193: * - the prev offset, which must be done within the log write latch to
194: * be sure what that lsn is
195: * - the VLSN, for the same reason
196: * - the checksum, which must be added last, after all other
197: * fields are marshalled.
198: */
199: ByteBuffer addPostMarshallingInfo(EnvironmentImpl envImpl,
200: ByteBuffer entryBuffer, long lastOffset) {
201:
202: /* Add the prev pointer */
203: entryBuffer.position(PREV_OFFSET);
204: LogUtils.writeUnsignedInt(entryBuffer, lastOffset);
205:
206: /* Add the optional VLSN */
207: if (replicate) {
208: entryBuffer.position(VLSN_OFFSET);
209: VLSN vlsn = envImpl.getReplicator().bumpVLSN();
210: vlsn.writeToLog(entryBuffer);
211: }
212:
213: /* Now calculate the checksum and write it into the buffer. */
214: Checksum checksum = Adler32.makeChecksum();
215: checksum.update(entryBuffer.array(), CHECKSUM_BYTES,
216: entryBuffer.limit() - CHECKSUM_BYTES);
217: entryBuffer.position(0);
218: LogUtils.writeUnsignedInt(entryBuffer, checksum.getValue());
219:
220: /* Leave this buffer ready for copying into another buffer. */
221: entryBuffer.position(0);
222:
223: return entryBuffer;
224: }
225:
226: /**
227: * @param sb destination string buffer
228: * @param verbose if true, dump the full, verbose version
229: */
230: public void dumpLog(StringBuffer sb, boolean verbose) {
231: }
232:
233: /**
234: * For use in special case where commits are transformed to aborts because
235: * of i/o errors during a logBuffer flush. See [11271].
236: * Assumes that the entryBuffer is positioned at the start of the item.
237: * Return with the entryBuffer positioned to the end of the log entry.
238: */
239: void convertCommitToAbort(ByteBuffer entryBuffer) {
240: assert (entryType == LogEntryType.LOG_TXN_COMMIT.getTypeNum());
241:
242: /* Remember the start of the entry item. */
243: int itemStart = entryBuffer.position();
244:
245: /* Back up to where the type is stored and change the type. */
246: int entryTypePosition = itemStart
247: - (getSize() - ENTRYTYPE_OFFSET);
248: entryBuffer.position(entryTypePosition);
249: entryBuffer.put(LogEntryType.LOG_TXN_ABORT.getTypeNum());
250:
251: /*
252: * Recalculate the checksum. This byte buffer could be large,
253: * so don't just turn the whole buffer into an array to pass
254: * into the checksum object.
255: */
256: Checksum checksum = Adler32.makeChecksum();
257: int checksumSize = itemSize + (getSize() - CHECKSUM_BYTES);
258: checksum.update(entryBuffer.array(), entryTypePosition,
259: checksumSize);
260: entryBuffer.position(itemStart - getSize());
261: LogUtils.writeUnsignedInt(entryBuffer, checksum.getValue());
262: }
263: }
|