001: /*
002:
003: Derby - Class org.apache.derby.impl.store.raw.xact.TransactionTableEntry
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.impl.store.raw.xact;
023:
024: import org.apache.derby.iapi.services.sanity.SanityManager;
025: import org.apache.derby.iapi.services.io.Formatable;
026: import org.apache.derby.iapi.services.io.FormatIdUtil;
027: import org.apache.derby.iapi.services.io.StoredFormatIds;
028:
029: import org.apache.derby.iapi.error.StandardException;
030: import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
031: import org.apache.derby.iapi.sql.conn.StatementContext;
032: import org.apache.derby.iapi.store.access.TransactionInfo;
033: import org.apache.derby.iapi.store.raw.GlobalTransactionId;
034: import org.apache.derby.iapi.store.raw.xact.TransactionId;
035: import org.apache.derby.iapi.store.raw.log.LogInstant;
036:
037: import java.io.ObjectOutput;
038: import java.io.ObjectInput;
039: import java.io.IOException;
040:
041: /**
042: Transaction table entry is used to store all relavent information of a
043: transaction into the transaction table for the use of checkpoint, recovery,
044: Transaction management during Quiesce state, and for dumping transaction table. Only works
045: with the following classes: TransactionTable, XactFactory, Xact
046: <BR>
047: During run time, whenever any transaction is started, it is put into the
048: transaction table. Whenever any transaction is closed, it is removed from
049: the transaction table.
050:
051:
052: */
053:
054: public class TransactionTableEntry implements Formatable,
055: TransactionInfo, Cloneable {
056: // These fields are only populated if this TTE has been read in from the
057: // log. Otherwise, they are gotten from the transaction object myxact.
058: private TransactionId xid;
059: private GlobalTransactionId gid;
060: private LogInstant firstLog;
061: private LogInstant lastLog;
062:
063: // this field is always present - it is 0 for read only transaction, this
064: // is a copy of the status from the Xact (the copy is necessary as during
065: // recovery the Xact is shared by all transaction table entries during
066: // redo and undo).
067: private int transactionStatus;
068:
069: // fields useful for returning transaction information if read from
070: // transaction log during recovery
071: private transient Xact myxact;
072: private transient boolean update;
073: private transient boolean recovery; // is this a transaction read
074: // from the log during recovery?
075: private transient boolean needExclusion; // in a quiesce state , this
076: // transaction needs to be
077: // barred from activation
078: // during quiesce state
079:
080: private boolean isClone; // am I a clone made for the
081: // TransactionVTI?
082:
083: private transient LanguageConnectionContext lcc;
084:
085: /* package */
086: // entry attribute
087: static final int UPDATE = 0x1;
088: static final int RECOVERY = 0x2;
089: static final int EXCLUDE = 0x4;
090:
091: TransactionTableEntry(Xact xact, TransactionId tid, int status,
092: int attribute) {
093: myxact = xact;
094: xid = tid;
095: transactionStatus = status;
096:
097: update = (attribute & UPDATE) != 0;
098: needExclusion = (attribute & EXCLUDE) != 0;
099: recovery = (attribute & RECOVERY) != 0;
100:
101: if (SanityManager.DEBUG) {
102: SanityManager.ASSERT(tid != null, "tid is null");
103: if (update && xact.getFirstLogInstant() == null) {
104: SanityManager
105: .THROWASSERT("update transaction has firstLog = null");
106: }
107:
108: /*
109: if (!update && xact.getFirstLogInstant() != null)
110: {
111: SanityManager.THROWASSERT(
112: "read only transaction has firstLog = " +
113: xact.getFirstLogInstant());
114: }
115: */
116: }
117:
118: // Normally, we don't need to remember the gid, firstLog and lastLog
119: // because myxact will have the same information. However, in
120: // recovery, there is only one transaction taking on different identity
121: // as the log is replayed. Then each transaction table entry has keep
122: // its own identity and not rely on myxact. These recovery
123: // transactions are materialized in the transaction table via a
124: // readObject in the checkpoint log record, or are added by
125: // addUpdateTransaction when the log is scanned.
126: if (recovery) {
127: // make a copy of everything
128: if (SanityManager.DEBUG) {
129: SanityManager.ASSERT(update, "recovery but not update");
130:
131: if (tid != xact.getId()) {
132: SanityManager
133: .THROWASSERT("adding a update transaction during recovery "
134: + " but the tids doesn't match"
135: + tid + " " + xact.getId());
136: }
137: }
138:
139: gid = xact.getGlobalId();
140: firstLog = xact.getFirstLogInstant();
141: lastLog = xact.getLastLogInstant();
142: }
143: }
144:
145: /*
146: * Formatable methods
147: */
148: public TransactionTableEntry() {
149: }
150:
151: public void writeExternal(ObjectOutput out) throws IOException {
152: if (SanityManager.DEBUG) {
153: SanityManager.ASSERT(!recovery,
154: "writing out a recovery transaction");
155: SanityManager.ASSERT(update,
156: "writing out read only transaction");
157: SanityManager.ASSERT(myxact.getFirstLogInstant() != null,
158: "myxact.getFirstLogInstant is null");
159: SanityManager.ASSERT(!isClone, "cannot write out a clone");
160: }
161:
162: // Why is is safe to access first and last log instant in myxact while
163: // this is happening? Because we only writes out update transaction
164: // during run time. When a read only transactions becomes an update
165: // transaction , or when an update transaction commits, the beginXact
166: // and endXact log record's doMe method will try to change the
167: // transaction table entry's state to updat and non-update
168: // respectively. That change needs to go thru the transaction table
169: // which is mutually exclusive to writing out the transaction table.
170: // Since we are only looking at update transactions and it is "stuck"
171: // in update state in the middle of a TransactionTable.writeExternal
172: // call, all the fields we access in myxact is stable (actually the xid
173: // is also stable but we already have it).
174: //
175: out.writeObject(xid);
176: out.writeObject(myxact.getGlobalId());
177: out.writeObject(myxact.getFirstLogInstant());
178: out.writeObject(myxact.getLastLogInstant());
179: out.writeInt(transactionStatus);
180: }
181:
182: public void readExternal(ObjectInput in)
183: throws ClassNotFoundException, IOException {
184: // the only time a transaction table entry is written out is to the
185: // log, so this must be read in during recovery.
186: if (SanityManager.DEBUG)
187: SanityManager.ASSERT(!isClone, "cannot write out a clone");
188:
189: xid = (TransactionId) in.readObject();
190: gid = (GlobalTransactionId) in.readObject();
191: firstLog = (LogInstant) in.readObject();
192: lastLog = (LogInstant) in.readObject();
193: transactionStatus = in.readInt();
194: update = true;
195: recovery = true;
196: needExclusion = true;
197:
198: if (SanityManager.DEBUG) {
199: SanityManager.ASSERT(xid != null,
200: "read in transaction table entry with null id");
201: SanityManager.ASSERT(firstLog != null,
202: "read in transaction table entry with firstLog");
203: }
204:
205: }
206:
207: // set my transaction instance variable for a recovery transaction
208: void setXact(Xact xact) {
209: /*
210: RESOLVE (mikem) - prepared transactions now call setXact() when they are
211: not in recovery.
212: if (SanityManager.DEBUG)
213: {
214: SanityManager.ASSERT(recovery,
215: "setting non-recovery transaction table entry xact");
216: SanityManager.ASSERT(!isClone, "cannot setXact with a clone");
217: }
218: */
219: myxact = xact;
220:
221: }
222:
223: /**
224: Return my format identifier.
225: */
226: public int getTypeFormatId() {
227: return StoredFormatIds.RAW_STORE_TRANSACTION_TABLE_ENTRY;
228: }
229:
230: public String toString() {
231: if (SanityManager.DEBUG) {
232: StringBuffer str = new StringBuffer(500).append("Xid=")
233: .append(getXid()).append(" gid=").append(getGid())
234: .append(" firstLog=").append(getFirstLog()).append(
235: " lastLog=").append(getLastLog()).append(
236: " transactionStatus=").append(
237: transactionStatus).append(" myxact=")
238: .append(myxact).append(" update=").append(update)
239: .append(" recovery=").append(recovery).append(
240: " prepare=").append(isPrepared()).append(
241: " needExclusion=").append(needExclusion)
242: .append("\n");
243: return str.toString();
244: } else
245: return null;
246: }
247:
248: void updateTransactionStatus(Xact xact, int status, int attribute) {
249: if (SanityManager.DEBUG) {
250: SanityManager.ASSERT(myxact == xact,
251: "update transaction status for wrong xact");
252: SanityManager.ASSERT(!isClone, "cannot change a clone");
253: }
254:
255: this .update = (attribute & UPDATE) != 0;
256: }
257:
258: void removeUpdateTransaction() {
259: if (SanityManager.DEBUG)
260: SanityManager.ASSERT(!isClone, "cannot change a clone");
261:
262: this .update = false;
263: transactionStatus = 0;
264:
265: }
266:
267: void unsetRecoveryStatus() {
268: if (SanityManager.DEBUG)
269: SanityManager.ASSERT(!isClone, "cannot change a clone");
270:
271: // RESOLVE (mikem) - this is kind of ugly. move to a better place?
272:
273: firstLog = null;
274:
275: this .recovery = false;
276: }
277:
278: void prepareTransaction() {
279: if (SanityManager.DEBUG)
280: SanityManager.ASSERT(!isClone, "cannot change a clone");
281:
282: transactionStatus |= Xact.END_PREPARED;
283: }
284:
285: /**************************************************************************
286: * get instance variables
287: **************************************************************************
288: */
289:
290: TransactionId getXid() {
291: if (SanityManager.DEBUG) {
292: SanityManager.ASSERT(xid != null, "TTE with null xid");
293: SanityManager.ASSERT(!isClone,
294: "cannot call method with a clone");
295: }
296:
297: return xid;
298: }
299:
300: public final GlobalTransactionId getGid() {
301: if (SanityManager.DEBUG)
302: SanityManager.ASSERT(!isClone,
303: "cannot call method with a clone");
304:
305: if (gid != null)
306: return gid;
307:
308: if (myxact != null)
309: return myxact.getGlobalId();
310:
311: return null;
312: }
313:
314: LogInstant getFirstLog() {
315: if (SanityManager.DEBUG) {
316: SanityManager.ASSERT(!isClone,
317: "cannot call method with a clone");
318:
319: if (recovery) {
320: SanityManager.ASSERT(firstLog != null,
321: "a recovery transaction with a null firstLog");
322: } else {
323: SanityManager.ASSERT(firstLog == null,
324: "a normal transaction with a non-null firstLog"
325: + "myxact.getFirstLogInstant() = "
326: + myxact.getFirstLogInstant());
327: }
328: }
329:
330: if (firstLog != null)
331: return firstLog;
332:
333: if (myxact != null)
334: return myxact.getFirstLogInstant();
335:
336: return null;
337: }
338:
339: LogInstant getLastLog() {
340: if (SanityManager.DEBUG)
341: SanityManager.ASSERT(!isClone,
342: "cannot call method with a clone");
343:
344: if (lastLog != null)
345: return lastLog;
346:
347: if (myxact != null)
348: return myxact.getLastLogInstant();
349:
350: return null;
351: }
352:
353: public final Xact getXact() {
354: if (SanityManager.DEBUG)
355: SanityManager.ASSERT(!isClone,
356: "cannot call method with a clone");
357:
358: return myxact;
359: }
360:
361: int getTransactionStatus() {
362: if (SanityManager.DEBUG)
363: SanityManager.ASSERT(!isClone,
364: "cannot call method with a clone");
365:
366: return transactionStatus;
367: }
368:
369: boolean isUpdate() {
370: if (SanityManager.DEBUG)
371: SanityManager.ASSERT(!isClone,
372: "cannot call method with a clone");
373:
374: return update;
375: }
376:
377: boolean isRecovery() {
378: if (SanityManager.DEBUG)
379: SanityManager.ASSERT(!isClone,
380: "cannot call method with a clone");
381:
382: return recovery;
383: }
384:
385: boolean isPrepared() {
386: if (SanityManager.DEBUG)
387: SanityManager.ASSERT(!isClone,
388: "cannot call method with a clone");
389:
390: return ((transactionStatus & Xact.END_PREPARED) != 0);
391: }
392:
393: public boolean needExclusion() {
394: if (SanityManager.DEBUG)
395: SanityManager.ASSERT(!isClone,
396: "cannot call method with a clone");
397:
398: return needExclusion;
399: }
400:
401: /**
402: Methods of TransactionInfo
403: */
404: public String getTransactionIdString() {
405: if (SanityManager.DEBUG) {
406: SanityManager.ASSERT(!recovery,
407: "trying to display recovery transaction");
408: SanityManager.ASSERT(myxact != null, "my xact is null");
409: SanityManager.ASSERT(isClone,
410: "Should only call method on a clone");
411: }
412:
413: TransactionId t = myxact.getIdNoCheck();
414: return (t == null) ? "CLOSED" : t.toString();
415: }
416:
417: public String getGlobalTransactionIdString() {
418: if (SanityManager.DEBUG) {
419: SanityManager.ASSERT(!recovery,
420: "trying to display recovery transaction");
421: SanityManager.ASSERT(myxact != null, "my xact is null");
422: SanityManager.ASSERT(isClone,
423: "Should only call method on a clone");
424: }
425:
426: GlobalTransactionId gid = myxact.getGlobalId();
427: return (gid == null) ? null : gid.toString();
428: }
429:
430: public String getUsernameString() {
431: if (SanityManager.DEBUG)
432: SanityManager.ASSERT(isClone,
433: "Should only call method on a clone");
434:
435: getlcc();
436: return (lcc == null) ? null : lcc.getAuthorizationId();
437: }
438:
439: public String getTransactionTypeString() {
440: if (SanityManager.DEBUG)
441: SanityManager.ASSERT(isClone,
442: "Should only call method on a clone");
443:
444: if (myxact == null)
445: return null;
446: else if (myxact.getTransName() != null)
447: return myxact.getTransName();
448: else
449: return myxact.getContextId();
450: }
451:
452: public String getTransactionStatusString() {
453: if (SanityManager.DEBUG)
454: SanityManager.ASSERT(isClone,
455: "Should only call method on a clone");
456:
457: return (myxact == null) ? null : myxact.getState();
458: }
459:
460: public String getStatementTextString() {
461: if (SanityManager.DEBUG)
462: SanityManager.ASSERT(isClone,
463: "Should only call method on a clone");
464:
465: getlcc();
466: if (lcc != null) {
467: StatementContext sc = lcc.getStatementContext();
468: if (sc != null)
469: return sc.getStatementText();
470: }
471: return null;
472:
473: }
474:
475: public String getFirstLogInstantString() {
476: if (SanityManager.DEBUG)
477: SanityManager.ASSERT(isClone,
478: "Should only call method on a clone");
479:
480: LogInstant logInstant = (myxact == null) ? null : myxact
481: .getFirstLogInstant();
482:
483: return (logInstant == null) ? null : logInstant.toString();
484:
485: }
486:
487: private void getlcc() {
488: if (SanityManager.DEBUG)
489: SanityManager.ASSERT(isClone,
490: "Should only call method on a clone");
491:
492: if (lcc == null && myxact != null && myxact.xc != null) {
493: XactContext xc = myxact.xc;
494:
495: lcc = (LanguageConnectionContext) xc.getContextManager()
496: .getContext(LanguageConnectionContext.CONTEXT_ID);
497: }
498: }
499:
500: /**
501: Cloneable
502: */
503: protected Object clone() {
504: try {
505: Object c = super .clone();
506: ((TransactionTableEntry) c).isClone = true;
507:
508: return c;
509: } catch (CloneNotSupportedException e) {
510: // this should not happen, we are cloneable
511: if (SanityManager.DEBUG) {
512: SanityManager
513: .THROWASSERT("TransactionTableEntry cloneable but throws CloneNotSupportedException "
514: + e);
515: }
516: return null;
517: }
518: }
519: }
|