001: /*
002:
003: Derby - Class org.apache.derby.impl.drda.DssTrace
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: package org.apache.derby.impl.drda;
022:
023: // Generic process and error tracing encapsulation.
024: // This class also traces a DRDA communications buffer.
025: // The value of the hex bytes are traced along with
026: // the ascii and ebcdic translations.
027: public class DssTrace {
028: // This class was implemented using character arrays to translate bytes
029: // into ascii and ebcdic. The goal was to be able to quickly index into the
030: // arrays to find the characters. Char arrays instead of strings were used as
031: // much as possible in an attempt to help speed up performance.
032: private static final String LIST_SEPARATOR = " # ";
033:
034: // trace representation for a java null.
035: private static final String NULL_VALUE = "null";
036:
037: // An array of characters used to translate bytes to ascii.
038: // The position in the array corresponds to the hex value of the
039: // character
040: private static final char asciiChar[] = {
041: // 0 1 2 3 4 5 6 7 8 9 A B C D E F
042: '.',
043: '.',
044: '.',
045: '.',
046: '.',
047: '.',
048: '.',
049: '.',
050: '.',
051: '.',
052: '.',
053: '.',
054: '.',
055: '.',
056: '.',
057: '.', //0
058: '.',
059: '.',
060: '.',
061: '.',
062: '.',
063: '.',
064: '.',
065: '.',
066: '.',
067: '.',
068: '.',
069: '.',
070: '.',
071: '.',
072: '.',
073: '.', //1
074: ' ', '!',
075: '"',
076: '#',
077: '$',
078: '%',
079: '&',
080: '\'',
081: '(',
082: ')',
083: '*',
084: '+',
085: ',',
086: '-',
087: '.',
088: '/', //2
089: '0', '1', '2',
090: '3',
091: '4',
092: '5',
093: '6',
094: '7',
095: '8',
096: '9',
097: ':',
098: ';',
099: '<',
100: '=',
101: '>',
102: '?', //3
103: '@', 'A', 'B', 'C',
104: 'D',
105: 'E',
106: 'F',
107: 'G',
108: 'H',
109: 'I',
110: 'J',
111: 'K',
112: 'L',
113: 'M',
114: 'N',
115: 'O', //4
116: 'P', 'Q', 'R', 'S', 'T',
117: 'U',
118: 'V',
119: 'W',
120: 'X',
121: 'Y',
122: 'Z',
123: '[',
124: '\\',
125: ']',
126: '^',
127: '_', //5
128: '`', 'a', 'b', 'c', 'd', 'e',
129: 'f',
130: 'g',
131: 'h',
132: 'i',
133: 'j',
134: 'k',
135: 'l',
136: 'm',
137: 'n',
138: 'o', //6
139: 'p', 'q', 'r', 's', 't', 'u', 'v',
140: 'w',
141: 'x',
142: 'y',
143: 'z',
144: '{',
145: '|',
146: '}',
147: '~',
148: '.', //7
149: '.', '.', '.', '.', '.', '.', '.', '.',
150: '.',
151: '.',
152: '.',
153: '.',
154: '.',
155: '.',
156: '.',
157: '.', //8
158: '.', '.', '.', '.', '.', '.', '.', '.', '.',
159: '.',
160: '.',
161: '.',
162: '.',
163: '.',
164: '.',
165: '.', //9
166: '.', '.', '.', '.', '.', '.', '.', '.', '.', '.',
167: '.',
168: '.',
169: '.',
170: '.',
171: '.',
172: '.', //A
173: '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.',
174: '.',
175: '.',
176: '.',
177: '.',
178: '.', //B
179: '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.',
180: '.',
181: '.',
182: '.',
183: '.', //C
184: '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.',
185: '.', '.',
186: '.',
187: '.', //D
188: '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.',
189: '.', '.', '.',
190: '.', //E
191: '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.',
192: '.', '.', '.', '.' //F
193: };
194:
195: // This mapping table associates a codepoint to a String describing the codepoint.
196: // This is needed because the trace prints the
197: // first codepoint in send and receive buffers.
198: // This could be final but there is no need to create the mapping
199: // if tracing isn't used. So... this array will only be created when
200: // the com buffer trace is started. Note this ref is not protected
201: // by final and care must be taken if it's value needs to change.
202: private static CodePointNameTable codePointNameTable = null;
203:
204: // This column position header is used to mark offsets into the trace.
205: private static final String colPosHeader = " 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF 0123456789ABCDEF";
206:
207: // An array of characters used to translate bytes to ebcdic.
208: // The position in the array corresponds to the hex value of the
209: // character.
210: private static final char ebcdicChar[] = {
211: // 0 1 2 3 4 5 6 7 8 9 A B C D E F
212: '.',
213: '.',
214: '.',
215: '.',
216: '.',
217: '.',
218: '.',
219: '.',
220: '.',
221: '.',
222: '.',
223: '.',
224: '.',
225: '.',
226: '.',
227: '.', //0
228: '.',
229: '.',
230: '.',
231: '.',
232: '.',
233: '.',
234: '.',
235: '.',
236: '.',
237: '.',
238: '.',
239: '.',
240: '.',
241: '.',
242: '.',
243: '.', //1
244: '.', '.',
245: '.',
246: '.',
247: '.',
248: '.',
249: '.',
250: '.',
251: '.',
252: '.',
253: '.',
254: '.',
255: '.',
256: '.',
257: '.',
258: '.', //2
259: '.', '.', '.',
260: '.',
261: '.',
262: '.',
263: '.',
264: '.',
265: '.',
266: '.',
267: '.',
268: '.',
269: '.',
270: '.',
271: '.',
272: '.', //3
273: ' ', '.', '.', '.',
274: '.',
275: '.',
276: '.',
277: '.',
278: '.',
279: '.',
280: '.',
281: '.',
282: '<',
283: '(',
284: '+',
285: '|', //4
286: '&', '.', '.', '.', '.',
287: '.',
288: '.',
289: '.',
290: '.',
291: '.',
292: '!',
293: '$',
294: '*',
295: ')',
296: ';',
297: '.', //5
298: '-', '/', '.', '.', '.', '.',
299: '.',
300: '.',
301: '.',
302: '.',
303: '|',
304: ',',
305: '%',
306: '_',
307: '>',
308: '?', //6
309: '.', '.', '.', '.', '.', '.', '.',
310: '.',
311: '.',
312: '`',
313: ':',
314: '#',
315: '@',
316: '\'',
317: '=',
318: '"', //7
319: '.', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
320: 'h',
321: 'i',
322: '.',
323: '.',
324: '.',
325: '.',
326: '.',
327: '.', //8
328: '.', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
329: 'r',
330: '.',
331: '.',
332: '.',
333: '.',
334: '.',
335: '.', //9
336: '.', '~', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
337: '.',
338: '.',
339: '.',
340: '.',
341: '.',
342: '.', //A
343: '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.',
344: '.',
345: '.',
346: '.',
347: '.',
348: '.', //B
349: '{', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', '.', '.',
350: '.',
351: '.',
352: '.',
353: '.', //C
354: '}', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', '.', '.',
355: '.', '.',
356: '.',
357: '.', //D
358: '\\', '.', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '.',
359: '.', '.', '.', '.',
360: '.', //E
361: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', '.',
362: '.', '.', '.', '.' //F
363: };
364:
365: // An array of characters representing hex numbers.
366: private static final char hexDigit[] = { '0', '1', '2', '3', '4',
367: '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
368:
369: // A PrintWriter is used in printing the trace.
370: private java.io.PrintWriter comBufferWriter = null;
371:
372: // The receive header comes befor bytes which would be read from
373: // a Stream.
374: private static final String receiveHeader = " RECEIVE BUFFER: (ASCII) (EBCDIC)";
375:
376: // The send header comes before bytes which would be written to
377: // a Stream.
378: private static final String sendHeader = " SEND BUFFER: (ASCII) (EBCDIC)";
379:
380: // The space character is defined for convience.
381: private static final char spaceChar = ' ';
382:
383: // This boolean indicates if the trace is on.
384: // It has been declared private now but may be made public at
385: // a later time.
386: private boolean comBufferTraceOn = false;
387:
388: // The comBufferSync is an object used for serialization.
389: // This separate object is used because this trace code may
390: // get eventually placed into another class which performs
391: // method entry and exit tracing. Since each trace may be writing
392: // to different logs, separate objects will be used to perform the
393: // synchronization.
394: private Boolean comBufferSync = new Boolean(true);
395:
396: // The zero character is defined for convinience.
397: private static final char zeroChar = '0';
398:
399: // The recevie constant is used to indicate that the bytes were read to a Stream.
400: // It indicates to this class that a receive header should be used.
401: protected static final int TYPE_TRACE_RECEIVE = 2;
402:
403: // The send constant is used to indicate that the bytes were written to
404: // a Stream. It indicates to this class that a send header should be used.
405: protected static final int TYPE_TRACE_SEND = 1;
406:
407: // Query if trace is on.
408: // This is currently needed since the comBufferTrcOn flag is private.
409: protected boolean isComBufferTraceOn() {
410: // The trace flag indicates if tracing is on.
411: return comBufferTraceOn;
412: }
413:
414: // Start the communications buffer trace.
415: // The name of the file to place the trace is passed to this method.
416: // After calling this method, calls to isComBufferTraceOn() will return true.
417: protected void startComBufferTrace(String fileName) {
418: synchronized (comBufferSync) {
419: try {
420: // Only start the trace if it is off.
421: if (comBufferTraceOn == false) {
422: // The writer will be buffered for effeciency.
423: comBufferWriter = new java.io.PrintWriter(
424: new java.io.BufferedWriter(
425: new java.io.FileWriter(fileName),
426: 4096));
427: // Turn on the trace flag.
428: comBufferTraceOn = true;
429: // initialize the codepoint name table if it is null.
430: // this is done here so that the CodePointName objects
431: // aren't created if the trace isn't used (save some memory).
432: // this process should only be done once
433: // since after the table is created the ref will
434: // no longer be null.
435: if (DssTrace.codePointNameTable == null) {
436: codePointNameTable = new CodePointNameTable();
437: }
438: }
439: } catch (java.io.IOException e) {
440: // The IOException is currently ignored. Handling should be added.
441: }
442: }
443: }
444:
445: // Stop the communications buffer trace.
446: // The trace file is flushed and closed. After calling this method,
447: // calls to isComBufferTraceOn () will return false.
448: protected void stopComBufferTrace() {
449: synchronized (comBufferSync) {
450: // Only stop the trace if it is actually on.
451: if (comBufferTraceOn == true) {
452: // Turn of the trace flag.
453: comBufferTraceOn = false;
454: // Flush and close the writer used for tracing.
455: comBufferWriter.flush();
456: comBufferWriter.close();
457: }
458: }
459: }
460:
461: // Write the communication buffer data to the trace.
462: // The data is passed in via a byte array. The start and length of the data is given.
463: // The type is needed to indicate if the data is part of the send or receive buffer.
464: // The class name, method name, and trcPt number are also written to the trace.
465: // Not much checking is performed on the parameters. This is done to help performance.
466: protected void writeComBufferData(byte[] buff, int offset, int len,
467: int type, String className, String methodName, int trcPt) {
468: // why don't we synchronize the method!!!
469:
470: // Grab the lock to make sure another thread doesn't try to
471: // write data or close the writer.
472: synchronized (comBufferSync) {
473:
474: // Only take action if the trace is on.
475: if (comBufferTraceOn) {
476:
477: // Obtain an instance of the Calendar so a timestamp can be written.
478: // this call seems to slow things down a bit.
479: java.util.Calendar time = java.util.Calendar
480: .getInstance();
481:
482: // Print the timestamp, class name, method name, thread name, and tracepoint.
483: comBufferWriter.println(" ("
484: + time.get(java.util.Calendar.YEAR) + "."
485: + (time.get(java.util.Calendar.MONTH) + 1)
486: + "."
487: + time.get(java.util.Calendar.DAY_OF_MONTH)
488: + " "
489: + time.get(java.util.Calendar.HOUR_OF_DAY)
490: + ":" + time.get(java.util.Calendar.MINUTE)
491: + ":" + time.get(java.util.Calendar.SECOND)
492: + ") " + className + " " + methodName + " "
493: + Thread.currentThread().getName() + " "
494: + trcPt);
495:
496: // A newline is added for formatting.
497: comBufferWriter.println();
498:
499: // The data will only be written if there is a non-zero positive length.
500: if (len != 0) {
501: String codePointName = null;
502: // If the length <= 10, lookup the first codepoint so it's name can be printed???
503: if (len >= 10) {
504: // Get the int value of the two byte unsigned codepoint.
505: int codePoint = getCodePoint(buff, offset + 8);
506: codePointName = codePointNameTable
507: .lookup(codePoint);
508: }
509:
510: if (codePointName == null) {
511: // codePointName was still null so either < 10 bytes were given or
512: // the codepoint wasn't found in the table. Just print the plain send header.
513: comBufferWriter.println(getHeader(type));
514: } else {
515: // codePointName isn't null so the name of the codepoint will be printed.
516: printHeaderWithCodePointName(codePointName,
517: type);
518: }
519:
520: // Print the col position header in the trace.
521: comBufferWriter.println(colPosHeader);
522:
523: // A char array will be used to translate the bytes to their character
524: // representations along with ascii and ebcdic representations.
525: char trcDump[] = new char[77];
526:
527: // bCounter, aCounter, eCounter are offsets used to help position the characters
528: short bCounter = 7;
529: short aCounter = 43;
530: short eCounter = 61;
531:
532: // The lines will be counted starting at zero. This is hard coded since we are
533: // at the beginning.
534: trcDump[0] = DssTrace.zeroChar;
535: trcDump[1] = DssTrace.zeroChar;
536: trcDump[2] = DssTrace.zeroChar;
537: trcDump[3] = DssTrace.zeroChar;
538:
539: // The 0's are already in the trace so bump the line counter up a row.
540: int lineCounter = 0x10;
541:
542: // Make sure the character array has all blanks in it.
543: // Some of these blanks will be replaced later with values.
544: // The 0's were not wrote over.
545: for (int j = 4; j < 77; j++) {
546: trcDump[j] = DssTrace.spaceChar;
547: }
548:
549: // i will maintain the position in the byte array to be traced.
550: int i = 0;
551:
552: do {
553: // Get the unsigned value of the byte.
554: // int num = b[off++] & 0xff;
555: int num = (buff[offset] < 0) ? buff[offset] + 256
556: : buff[offset]; // jev
557: offset++;
558: i++;
559: // Place the characters representing the bytes in the array.
560: trcDump[bCounter++] = DssTrace.hexDigit[((num >>> 4) & 0xf)];
561: trcDump[bCounter++] = DssTrace.hexDigit[(num & 0xf)];
562:
563: // Place the ascii and ebcdc representations in the array.
564: trcDump[aCounter++] = DssTrace.asciiChar[num];
565: trcDump[eCounter++] = DssTrace.ebcdicChar[num];
566:
567: if (((i % 8) == 0)) {
568: if (((i % 16) == 0)) {
569: // Print the array each time 16 bytes are processed.
570: comBufferWriter.println(trcDump);
571: if (i != len) {
572: // Not yet at the end of the byte array.
573: if ((len - i) < 16) {
574: // This is the last line so blank it all out.
575: // This keeps the last line looking pretty in case
576: // < 16 bytes remain.
577: for (int j = 0; j < trcDump.length; j++) {
578: trcDump[j] = DssTrace.spaceChar;
579: }
580: }
581: // Reset the counters.
582: bCounter = 0;
583: aCounter = 43;
584: eCounter = 61;
585: // Reset the lineCounter if it starts to get too large.
586: if (lineCounter == 0xfff0) {
587: lineCounter = 0;
588: }
589: // Place the characters representing the line counter in the array.
590: trcDump[bCounter++] = DssTrace.hexDigit[((lineCounter >>> 12) & 0xf)];
591: trcDump[bCounter++] = DssTrace.hexDigit[((lineCounter >>> 8) & 0xf)];
592: trcDump[bCounter++] = DssTrace.hexDigit[((lineCounter >>> 4) & 0xf)];
593: trcDump[bCounter++] = DssTrace.hexDigit[(lineCounter & 0xf)];
594: bCounter += 3;
595: // Bump up the line counter.
596: lineCounter += 0x10;
597: }
598: } else {
599: // 8 bytes were processed so move the counter to adjust for
600: // spaces between the columns of bytes.
601: bCounter += 2;
602: }
603: }
604: // do this until we all the data has been traced.
605: } while (i < len);
606:
607: // print the last line and add some blank lines to make it easier to read.
608: if (len % 16 != 0) {
609: comBufferWriter.println(trcDump);
610: }
611: comBufferWriter.println();
612: comBufferWriter.println();
613: }
614: // Flush the writer.
615: comBufferWriter.flush();
616: }
617: }
618: }
619:
620: // Gets the int value of the two byte unsigned codepoint.
621: private static int getCodePoint(byte[] buff, int offset) {
622: return ((buff[offset++] & 0xff) << 8)
623: + ((buff[offset] & 0xff) << 0);
624: }
625:
626: private static String getHeader(int type) {
627: switch (type) {
628: case DssTrace.TYPE_TRACE_SEND:
629: return DssTrace.sendHeader;
630: case DssTrace.TYPE_TRACE_RECEIVE:
631: return DssTrace.receiveHeader;
632: default:
633: // throw new !!!
634: return null;
635: }
636: }
637:
638: private static int getStartPosition(int type) {
639: switch (type) {
640: case DssTrace.TYPE_TRACE_SEND:
641: return 20; // This is right after 'SEND BUFFER: '.
642: case DssTrace.TYPE_TRACE_RECEIVE:
643: return 23; // This is right after 'RECEIVE BUFFER: '.
644: default:
645: // throw new !!!
646: return 0;
647: }
648: }
649:
650: private void printHeaderWithCodePointName(String codePointName,
651: int type) {
652: // Create a char array so some of the characters
653: // can be replaced with the name of the codepoint.
654: char headerArray[] = DssTrace.getHeader(type).toCharArray();
655:
656: // At most, 16 character name will be used. This is so
657: // the headers on top of the ascii and ebcdic rows aren't shifted.
658: int replaceLen = (codePointName.length() < 17) ? codePointName
659: .length() : 16;
660:
661: int offset = getStartPosition(type);
662: for (int i = 0; i < replaceLen; i++) {
663: headerArray[offset++] = codePointName.charAt(i); // make sure charAt() starts at 0!!!
664: }
665: comBufferWriter.println(headerArray);
666: }
667:
668: }
|