001: /*-
002: * See the file LICENSE for redistribution information.
003: *
004: * Copyright (c) 2002,2008 Oracle. All rights reserved.
005: *
006: * $Id: LNLogEntry.java,v 1.39.2.4 2008/01/07 15:14:13 cwl Exp $
007: */
008:
009: package com.sleepycat.je.log.entry;
010:
011: import java.nio.ByteBuffer;
012:
013: import com.sleepycat.je.DatabaseException;
014: import com.sleepycat.je.dbi.DatabaseId;
015: import com.sleepycat.je.log.LogEntryHeader;
016: import com.sleepycat.je.log.LogEntryType;
017: import com.sleepycat.je.log.LogUtils;
018: import com.sleepycat.je.tree.Key;
019: import com.sleepycat.je.tree.LN;
020: import com.sleepycat.je.txn.Txn;
021: import com.sleepycat.je.utilint.DbLsn;
022:
023: /**
024: * LNLogEntry embodies all LN transactional log entries.
025: * On disk, an LN log entry contains:
026: * <pre>
027: * ln
028: * databaseid
029: * key
030: * abortLsn -- if transactional
031: * abortKnownDeleted -- if transactional
032: * txn -- if transactional
033: * </pre>
034: */
035: public class LNLogEntry extends BaseEntry implements LogEntry,
036: NodeLogEntry {
037: private static final byte ABORT_KNOWN_DELETED_MASK = (byte) 1;
038:
039: /*
040: * Persistent fields in an LN entry
041: */
042: private LN ln;
043: private DatabaseId dbId;
044: private byte[] key;
045: private long abortLsn = DbLsn.NULL_LSN;
046: private boolean abortKnownDeleted;
047: private Txn txn; // conditional
048:
049: /*
050: * Transient fields used by the entry.
051: *
052: * Save the node id when we read the log entry from disk. Do so explicitly
053: * instead of merely returning ln.getNodeId(), because we don't always
054: * instantiate the LN.
055: */
056: private long nodeId;
057:
058: /* Constructor to read an entry. */
059: public LNLogEntry(Class LNClass) {
060: super (LNClass);
061: }
062:
063: /* Constructor to write an entry. */
064: public LNLogEntry(LogEntryType entryType, LN ln, DatabaseId dbId,
065: byte[] key, long abortLsn, boolean abortKnownDeleted,
066: Txn txn) {
067: setLogType(entryType);
068: this .ln = ln;
069: this .dbId = dbId;
070: this .key = key;
071: this .abortLsn = abortLsn;
072: this .abortKnownDeleted = abortKnownDeleted;
073: this .txn = txn;
074: this .nodeId = ln.getNodeId();
075:
076: /* A txn should only be provided for transactional entry types */
077: assert (entryType.isTransactional() == (txn != null));
078: }
079:
080: /**
081: * @see LogEntry#readEntry
082: */
083: public void readEntry(LogEntryHeader header,
084: ByteBuffer entryBuffer, boolean readFullItem)
085: throws DatabaseException {
086:
087: try {
088: if (readFullItem) {
089:
090: /* Read LN and get node ID. */
091: ln = (LN) logClass.newInstance();
092: ln.readFromLog(entryBuffer, header.getVersion());
093: nodeId = ln.getNodeId();
094:
095: /* DatabaseImpl Id */
096: dbId = new DatabaseId();
097: dbId.readFromLog(entryBuffer, header.getVersion());
098:
099: /* Key */
100: key = LogUtils.readByteArray(entryBuffer);
101:
102: if (entryType.isTransactional()) {
103:
104: /*
105: * AbortLsn. If it was a marker LSN that was used to fill
106: * in a create, mark it null.
107: */
108: abortLsn = LogUtils.readLong(entryBuffer);
109: if (DbLsn.getFileNumber(abortLsn) == DbLsn
110: .getFileNumber(DbLsn.NULL_LSN)) {
111: abortLsn = DbLsn.NULL_LSN;
112: }
113:
114: abortKnownDeleted = ((entryBuffer.get() & ABORT_KNOWN_DELETED_MASK) != 0) ? true
115: : false;
116:
117: /* Locker */
118: txn = new Txn();
119: txn.readFromLog(entryBuffer, header.getVersion());
120:
121: }
122: } else {
123:
124: /*
125: * Read node ID and then set buffer position to end. This takes
126: * advantage of the fact that the node id is in a known spot,
127: * at the beginning of the ln. We currently do not support
128: * getting the db and txn ID in this mode, and we may want to
129: * change the log format to do that efficiently.
130: */
131: int currentPosition = entryBuffer.position();
132: int endPosition = currentPosition
133: + header.getItemSize();
134: nodeId = LogUtils.readLong(entryBuffer);
135: entryBuffer.position(endPosition);
136: ln = null;
137: }
138: } catch (IllegalAccessException e) {
139: throw new DatabaseException(e);
140: } catch (InstantiationException e) {
141: throw new DatabaseException(e);
142: }
143: }
144:
145: /**
146: * @see LogEntry#dumpEntry
147: */
148: public StringBuffer dumpEntry(StringBuffer sb, boolean verbose) {
149: ln.dumpLog(sb, verbose);
150: dbId.dumpLog(sb, verbose);
151: sb.append(Key.dumpString(key, 0));
152: if (entryType.isTransactional()) {
153: if (abortLsn != DbLsn.NULL_LSN) {
154: sb.append(DbLsn.toString(abortLsn));
155: }
156: sb.append("<knownDeleted val=\"");
157: sb.append(abortKnownDeleted ? "true" : "false");
158: sb.append("\"/>");
159: txn.dumpLog(sb, verbose);
160: }
161: return sb;
162: }
163:
164: /**
165: * @see LogEntry#getMainItem
166: */
167: public Object getMainItem() {
168: return ln;
169: }
170:
171: /**
172: * @see LogEntry#clone
173: */
174: public Object clone() throws CloneNotSupportedException {
175: return super .clone();
176: }
177:
178: /**
179: * @see LogEntry#getTransactionId
180: */
181: public long getTransactionId() {
182: if (entryType.isTransactional()) {
183: return txn.getId();
184: } else {
185: return 0;
186: }
187: }
188:
189: /**
190: * @see NodeLogEntry#getNodeId
191: */
192: public long getNodeId() {
193: return nodeId;
194: }
195:
196: /*
197: * Writing support
198: */
199:
200: /**
201: */
202: public int getSize() {
203: int size = ln.getLogSize() + dbId.getLogSize()
204: + LogUtils.getByteArrayLogSize(key);
205: if (entryType.isTransactional()) {
206: size += LogUtils.getLongLogSize();
207: size++; // abortKnownDeleted
208: size += txn.getLogSize();
209: }
210: return size;
211: }
212:
213: public void setLastLoggedSize(int size) {
214: ln.setLastLoggedSize(size);
215: }
216:
217: /**
218: * @see LogEntry#writeEntry
219: */
220: public void writeEntry(LogEntryHeader header, ByteBuffer destBuffer) {
221: ln.writeToLog(destBuffer);
222: dbId.writeToLog(destBuffer);
223: LogUtils.writeByteArray(destBuffer, key);
224:
225: if (entryType.isTransactional()) {
226: LogUtils.writeLong(destBuffer, abortLsn);
227: byte aKD = 0;
228: if (abortKnownDeleted) {
229: aKD |= ABORT_KNOWN_DELETED_MASK;
230: }
231: destBuffer.put(aKD);
232: txn.writeToLog(destBuffer);
233: }
234: }
235:
236: /**
237: * Returns true for a deleted LN to count it immediately as obsolete.
238: * @see LogEntry#countAsObsoleteWhenLogged
239: */
240: public boolean countAsObsoleteWhenLogged() {
241: return ln.isDeleted();
242: }
243:
244: /**
245: * For LN entries, we need to record the latest LSN for that node with the
246: * owning transaction, within the protection of the log latch. This is a
247: * callback for the log manager to do that recording.
248: *
249: * @see LogEntry#postLogWork
250: */
251: public void postLogWork(long justLoggedLsn)
252: throws DatabaseException {
253:
254: if (entryType.isTransactional()) {
255: txn.addLogInfo(justLoggedLsn);
256: }
257: }
258:
259: /*
260: * Accessors
261: */
262: public LN getLN() {
263: return ln;
264: }
265:
266: public DatabaseId getDbId() {
267: return dbId;
268: }
269:
270: public byte[] getKey() {
271: return key;
272: }
273:
274: public byte[] getDupKey() {
275: if (ln.isDeleted()) {
276: return null;
277: } else {
278: return ln.getData();
279: }
280: }
281:
282: public long getAbortLsn() {
283: return abortLsn;
284: }
285:
286: public boolean getAbortKnownDeleted() {
287: return abortKnownDeleted;
288: }
289:
290: public Long getTxnId() {
291: if (entryType.isTransactional()) {
292: return new Long(txn.getId());
293: } else {
294: return null;
295: }
296: }
297:
298: public Txn getUserTxn() {
299: if (entryType.isTransactional()) {
300: return txn;
301: } else {
302: return null;
303: }
304: }
305: }
|