001: /*
002:
003: Derby - Class org.apache.derby.impl.services.locks.Timeout
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.services.locks;
023:
024: import org.apache.derby.impl.services.locks.TableNameInfo;
025:
026: import org.apache.derby.iapi.services.context.ContextService;
027:
028: import org.apache.derby.iapi.reference.SQLState;
029:
030: import org.apache.derby.iapi.services.locks.Latch;
031: import org.apache.derby.iapi.services.locks.Lockable;
032: import org.apache.derby.iapi.services.locks.VirtualLockTable;
033: import org.apache.derby.iapi.services.sanity.SanityManager;
034:
035: import org.apache.derby.iapi.error.StandardException;
036: import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
037: import org.apache.derby.iapi.store.access.TransactionController;
038:
039: import org.apache.derby.iapi.util.CheapDateFormatter;
040:
041: import java.util.Enumeration;
042: import java.util.Hashtable;
043:
044: /**
045: * Code to support Timeout error output.
046: * @author gavin
047: */
048:
049: public final class Timeout {
050: public static final int TABLE_AND_ROWLOCK = VirtualLockTable.TABLE_AND_ROWLOCK;
051: public static final int ALL = VirtualLockTable.ALL;
052:
053: public static final String newline = "\n";
054: //FIXME: The newline might not be truely platform independent.
055: // We do not want to use a system call because of security reasons.
056: // LINE_SEPARATOR returns ^M for some reason, not ^M<nl>.
057: //public static final String newline = String.valueOf( (char)(new Byte(Character.LINE_SEPARATOR)).intValue() );
058: //public static final String newline = System.getProperty( "line.separator" );
059:
060: private TransactionController tc;
061: private TableNameInfo tabInfo;
062:
063: /* the current Latch to extract info out of */
064: private Latch currentLock;
065: /* the current row output of the lockTable */
066: private char[] outputRow;
067: /* the entire lockTable as a buffer */
068: private StringBuffer sb;
069: /* the hashtable information of the current lock */
070: private Hashtable currentRow;
071: /* the time when the exception was thrown */
072: private final long currentTime;
073: /* the snapshot of the lockTable that timeout */
074: private final Enumeration lockTable;
075:
076: // column1: XID varchar(10) not null
077: // column2: TYPE varchar(13) not null
078: // column3: MODE varchar(4) not null
079: // column4: LOCKCOUNT varchar(9) not null
080: // column5: LOCKNAME varchar(80) not null
081: // column6: STATE varchar(5) not null
082: // column7: TABLETYPE varchar(38) not null / LOCKOBJ varchar(38)
083: // column8: INDEXNAME varchar(50) nullable as String "NULL" / CONTAINER_ID / MODE (latch only) varchar(50)
084: // column9: TABLENAME varchar(38) not null / CONGLOM_ID varchar(38)
085: // Total length of this string is 10+1+13+1+6+1+9+1+80+1+5+1+38+1+48+1+38=256
086: private final static String[] column = new String[9];
087: private final static int LENGTHOFTABLE;
088: static {
089: column[0] = "XID ";
090: column[1] = "TYPE ";
091: column[2] = "MODE";
092: column[3] = "LOCKCOUNT";
093: column[4] = "LOCKNAME ";
094: column[5] = "STATE";
095: column[6] = "TABLETYPE / LOCKOBJ ";
096: column[7] = "INDEXNAME / CONTAINER_ID / (MODE for LATCH only) ";
097: column[8] = "TABLENAME / CONGLOM_ID ";
098:
099: int length = 0;
100: for (int i = 0; i < column.length; i++) {
101: length += column[i].length();
102: }
103: length += column.length; // for the separator
104: if (SanityManager.DEBUG) { // 256 is a good number, can be expanded or contracted if necessary
105: SanityManager.ASSERT(length == 256,
106: "TIMEOUT_MONITOR: length of the row is not 256");
107: }
108: LENGTHOFTABLE = length;
109: }
110: private final static char LINE = '-';
111: private final static char SEPARATOR = '|';
112:
113: /**
114: * Constructor
115: * @param myTimeoutLock The Latch that the timeout happened on
116: * @param myLockTable
117: * @param time The time when the lockTable was cloned.
118: */
119: private Timeout(Latch myTimeoutLock, Enumeration myLockTable,
120: long time) {
121: currentLock = myTimeoutLock;
122: lockTable = myLockTable;
123: currentTime = time;
124:
125: if (SanityManager.DEBUG) {
126: SanityManager
127: .ASSERT(currentTime > 0,
128: "TIMEOUT_MONITOR: currentTime is not set correctly");
129: }
130: }
131:
132: /**
133: * createException creates a StandardException based on:
134: * currentLock
135: * a snapshot of the lockTable
136: * @return StandardException The exception with the lockTable snapshot in it
137: */
138: private StandardException createException() {
139: try {
140: buildLockTableString();
141: } catch (StandardException se) {
142: return se;
143: }
144:
145: StandardException se = StandardException.newException(
146: SQLState.LOCK_TIMEOUT_LOG, sb.toString());
147: se.setReport(StandardException.REPORT_ALWAYS);
148: return se;
149: }
150:
151: /**
152: * buildLockTableString creates a LockTable info String
153: */
154: private String buildLockTableString() throws StandardException {
155: sb = new StringBuffer(8192);
156: outputRow = new char[LENGTHOFTABLE];
157: int i; // counter
158:
159: // need language here to print out tablenames
160: LanguageConnectionContext lcc = (LanguageConnectionContext) ContextService
161: .getContext(LanguageConnectionContext.CONTEXT_ID);
162: if (lcc != null)
163: tc = lcc.getTransactionExecute();
164:
165: try {
166: tabInfo = new TableNameInfo(lcc, true);
167: } catch (Exception se) { //just don't do anything
168: }
169:
170: sb.append(newline);
171: sb.append(CheapDateFormatter.formatDate(currentTime));
172: sb.append(newline);
173: for (i = 0; i < column.length; i++) {
174: sb.append(column[i]);
175: sb.append(SEPARATOR);
176: }
177: sb.append(newline);
178:
179: for (i = 0; i < LENGTHOFTABLE; i++)
180: sb.append(LINE);
181:
182: sb.append(newline);
183:
184: // get the timeout lock info
185: if (currentLock != null) {
186: dumpLock();
187: if (timeoutInfoHash()) {
188: sb.append("*** The following row is the victim ***");
189: sb.append(newline);
190: sb.append(outputRow);
191: sb.append(newline);
192: sb.append("*** The above row is the victim ***");
193: sb.append(newline);
194: } else {
195: sb
196: .append("*** A victim was chosen, but it cannot be printed because the lockable object, "
197: + currentLock
198: + ", does not want to participate ***");
199: sb.append(newline);
200: }
201: }
202:
203: // get lock info from the rest of the table
204: if (lockTable != null) {
205: while (lockTable.hasMoreElements()) {
206: currentLock = (Latch) lockTable.nextElement();
207: dumpLock();
208: if (timeoutInfoHash()) {
209: sb.append(outputRow);
210: sb.append(newline);
211: } else {
212: sb
213: .append("*** A latch/lock, "
214: + currentLock
215: + ", exist in the lockTable that cannot be printed ***");
216: sb.append(newline);
217: }
218: }
219: for (i = 0; i < LENGTHOFTABLE; i++)
220: sb.append(LINE);
221:
222: sb.append(newline);
223: }
224: return sb.toString();
225: }
226:
227: /**
228: * The static entry way to get the LockTable in the system.
229: * @param timeoutLock The Latch that the timeout happened on
230: * @param table The lockTable
231: * @param time The time when the lockTable was cloned
232: * @return StandardException The exception with the lockTable snapshot in it
233: */
234: static StandardException buildException(Latch timeoutLock,
235: Enumeration table, long time) {
236: Timeout myTimeout = new Timeout(timeoutLock, table, time);
237: return myTimeout.createException();
238: }
239:
240: /*
241: * A static entry way to get the LockTable in the system.
242: * For track 3311
243: */
244: public static String buildString(Enumeration table, long time)
245: throws StandardException {
246: Timeout myTimeout = new Timeout(null, table, time);
247: return myTimeout.buildLockTableString();
248: }
249:
250: /**
251: * dumpLock puts information about currentLock into currentRow for output later.
252: * @throws StandardException
253: */
254: private void dumpLock() throws StandardException {
255: Hashtable attributes = new Hashtable(17);
256: Object lock_type = currentLock.getQualifier();
257:
258: // want containerId, segmentId, pageNum, recId from locktable
259: Lockable lockable = currentLock.getLockable();
260:
261: // See if the lockable object wants to participate
262: if (!lockable.lockAttributes(ALL, attributes)) {
263: currentRow = null;
264: return;
265: }
266:
267: // if it does, the lockable object must have filled in the following
268: // fields
269: if (SanityManager.DEBUG) {
270: SanityManager
271: .ASSERT(
272: attributes.get(VirtualLockTable.LOCKNAME) != null,
273: "lock table can only represent locks that have a LOCKNAME");
274: SanityManager
275: .ASSERT(
276: attributes.get(VirtualLockTable.LOCKTYPE) != null,
277: "lock table can only represent locks that have a LOCKTYPE");
278: if (attributes.get(VirtualLockTable.CONTAINERID) == null
279: && attributes.get(VirtualLockTable.CONGLOMID) == null)
280: SanityManager
281: .THROWASSERT("lock table can only represent locks that are associated with a container or conglomerate");
282: }
283:
284: Long conglomId = (Long) attributes
285: .get(VirtualLockTable.CONGLOMID);
286:
287: if (conglomId == null) {
288: if (attributes.get(VirtualLockTable.CONTAINERID) != null
289: && tc != null) {
290: Long value = (Long) attributes
291: .get(VirtualLockTable.CONTAINERID);
292: conglomId = new Long(tc
293: .findConglomid(value.longValue()));
294: attributes.put(VirtualLockTable.CONGLOMID, conglomId);
295: }
296: }
297:
298: Long containerId = (Long) attributes
299: .get(VirtualLockTable.CONTAINERID);
300:
301: if (containerId == null) {
302: if (conglomId != null && tc != null) {
303: try {
304: containerId = new Long(tc.findContainerid(conglomId
305: .longValue()));
306: attributes.put(VirtualLockTable.CONTAINERID,
307: containerId);
308: } catch (Exception e) {
309: // just don't do anything
310: }
311: }
312: }
313:
314: attributes.put(VirtualLockTable.LOCKOBJ, currentLock);
315: attributes.put(VirtualLockTable.XACTID, currentLock
316: .getCompatabilitySpace().toString());
317: attributes.put(VirtualLockTable.LOCKMODE, lock_type.toString());
318: attributes.put(VirtualLockTable.LOCKCOUNT, Integer
319: .toString(currentLock.getCount()));
320: attributes.put(VirtualLockTable.STATE,
321: (currentLock.getCount() != 0) ? "GRANT" : "WAIT");
322:
323: if (tabInfo != null && conglomId != null) {
324: try {
325: String tableName = tabInfo.getTableName(conglomId);
326: attributes.put(VirtualLockTable.TABLENAME, tableName);
327: } catch (NullPointerException e) {
328: attributes.put(VirtualLockTable.TABLENAME, conglomId);
329: }
330:
331: try {
332: String indexName = tabInfo.getIndexName(conglomId);
333: if (indexName != null)
334: attributes.put(VirtualLockTable.INDEXNAME,
335: indexName);
336: else {
337: if (attributes.get(VirtualLockTable.LOCKTYPE)
338: .equals("LATCH")) { // because MODE field is way to short to display this,
339: // just put it in the indexname field for LATCH only.
340: attributes
341: .put(
342: VirtualLockTable.INDEXNAME,
343: attributes
344: .get(VirtualLockTable.LOCKMODE));
345: } else
346: attributes.put(VirtualLockTable.INDEXNAME,
347: "NULL");
348: }
349: } catch (Exception e) { // we are here because tabInfo.indexCache is null
350: if (VirtualLockTable.CONTAINERID != null)
351: attributes.put(VirtualLockTable.INDEXNAME,
352: VirtualLockTable.CONTAINERID);
353: else
354: attributes.put(VirtualLockTable.INDEXNAME, "NULL");
355: }
356:
357: String tableType = tabInfo.getTableType(conglomId);
358: attributes.put(VirtualLockTable.TABLETYPE, tableType);
359: } else {
360: if (conglomId != null)
361: attributes.put(VirtualLockTable.TABLENAME,
362: VirtualLockTable.CONGLOMID);
363: else
364: attributes.put(VirtualLockTable.TABLENAME, "NULL");
365:
366: if (VirtualLockTable.CONTAINERID != null)
367: attributes.put(VirtualLockTable.INDEXNAME,
368: VirtualLockTable.CONTAINERID);
369: else
370: attributes.put(VirtualLockTable.INDEXNAME, "NULL");
371:
372: attributes.put(VirtualLockTable.TABLETYPE, currentLock
373: .toString());
374: }
375: currentRow = attributes;
376: }
377:
378: /**
379: * cpArray helps built the output string (outputRow).
380: * @param toCp the String to be copied into outputRow
381: * @param start the start place
382: * @param end the end place
383: */
384: private void cpArray(String toCp, int start, int end) { // build a field in the output string
385: int i = 0;
386: int totalAllowWrite = end - start;
387:
388: if (toCp != null) {
389: for (; i < toCp.length(); i++) {
390: if ((totalAllowWrite - i) == 0)
391: break;
392:
393: outputRow[i + start] = toCp.charAt(i);
394: }
395: }
396: for (; i + start != end; i++)
397: outputRow[i + start] = ' ';
398:
399: outputRow[end] = SEPARATOR;
400: }
401:
402: /**
403: * Copies the needed information from currentRow into the StringBuffer for output
404: * @return true if successful
405: */
406: private boolean timeoutInfoHash() {
407: if (currentRow == null)
408: return false;
409:
410: String[] myData = new String[9];
411: myData[0] = VirtualLockTable.XACTID;
412: myData[1] = VirtualLockTable.LOCKTYPE;
413: myData[2] = VirtualLockTable.LOCKMODE;
414: myData[3] = VirtualLockTable.LOCKCOUNT;
415: myData[4] = VirtualLockTable.LOCKNAME;
416: myData[5] = VirtualLockTable.STATE;
417: myData[6] = VirtualLockTable.TABLETYPE;
418: myData[7] = VirtualLockTable.INDEXNAME;
419: myData[8] = VirtualLockTable.TABLENAME;
420:
421: int currentLength = 0;
422: for (int i = 0; i < myData.length; i++) {
423: cpArray(currentRow.get(myData[i]).toString(),
424: currentLength, currentLength + column[i].length());
425: // the next beginning position
426: currentLength = currentLength + column[i].length() + 1;
427: }
428: return true;
429: }
430: }
|