--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.drda.DssTrace\r
+\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to You under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+\r
+ */\r
+package org.apache.derby.impl.drda;\r
+\r
+import java.io.IOException;\r
+import java.io.PrintWriter;\r
+import java.security.AccessController;\r
+import java.security.PrivilegedActionException;\r
+import java.security.PrivilegedExceptionAction;\r
+\r
+\r
+// Generic process and error tracing encapsulation.\r
+// This class also traces a DRDA communications buffer.\r
+// The value of the hex bytes are traced along with\r
+// the ascii and ebcdic translations.\r
+public class DssTrace\r
+{\r
+ // This class was implemented using character arrays to translate bytes\r
+ // into ascii and ebcdic. The goal was to be able to quickly index into the\r
+ // arrays to find the characters. Char arrays instead of strings were used as\r
+ // much as possible in an attempt to help speed up performance.\r
+ private static final String LIST_SEPARATOR = " # ";\r
+\r
+ // trace representation for a java null.\r
+ private static final String NULL_VALUE = "null";\r
+\r
+ // An array of characters used to translate bytes to ascii.\r
+ // The position in the array corresponds to the hex value of the\r
+ // character\r
+ private static final char asciiChar [] = {\r
+ // 0 1 2 3 4 5 6 7 8 9 A B C D E F\r
+ '.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.', //0\r
+ '.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.', //1\r
+ ' ','!','"','#','$','%','&','\'','(',')','*','+',',','-','.','/', //2\r
+ '0','1','2','3','4','5','6','7','8','9',':',';','<','=','>','?', //3\r
+ '@','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O', //4\r
+ 'P','Q','R','S','T','U','V','W','X','Y','Z','[','\\',']','^','_', //5\r
+ '`','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o', //6\r
+ 'p','q','r','s','t','u','v','w','x','y','z','{','|','}','~','.', //7\r
+ '.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.', //8\r
+ '.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.', //9\r
+ '.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.', //A\r
+ '.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.', //B\r
+ '.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.', //C\r
+ '.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.', //D\r
+ '.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.', //E\r
+ '.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.' //F\r
+ };\r
+\r
+\r
+ // This mapping table associates a codepoint to a String describing the codepoint.\r
+ // This is needed because the trace prints the\r
+ // first codepoint in send and receive buffers.\r
+ // This could be final but there is no need to create the mapping\r
+ // if tracing isn't used. So... this array will only be created when\r
+ // the com buffer trace is started. Note this ref is not protected\r
+ // by final and care must be taken if it's value needs to change.\r
+ private static CodePointNameTable codePointNameTable = null;\r
+\r
+ // This column position header is used to mark offsets into the trace.\r
+ private static final String colPosHeader =\r
+ " 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF 0123456789ABCDEF";\r
+\r
+ // An array of characters used to translate bytes to ebcdic.\r
+ // The position in the array corresponds to the hex value of the\r
+ // character.\r
+ private static final char ebcdicChar[] = {\r
+ // 0 1 2 3 4 5 6 7 8 9 A B C D E F\r
+ '.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.', //0\r
+ '.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.', //1\r
+ '.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.', //2\r
+ '.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.', //3\r
+ ' ','.','.','.','.','.','.','.','.','.','.','.','<','(','+','|', //4\r
+ '&','.','.','.','.','.','.','.','.','.','!','$','*',')',';','.', //5\r
+ '-','/','.','.','.','.','.','.','.','.','|',',','%','_','>','?', //6\r
+ '.','.','.','.','.','.','.','.','.','`',':','#','@','\'','=','"', //7\r
+ '.','a','b','c','d','e','f','g','h','i','.','.','.','.','.','.', //8\r
+ '.','j','k','l','m','n','o','p','q','r','.','.','.','.','.','.', //9\r
+ '.','~','s','t','u','v','w','x','y','z','.','.','.','.','.','.', //A\r
+ '.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.', //B\r
+ '{','A','B','C','D','E','F','G','H','I','.','.','.','.','.','.', //C\r
+ '}','J','K','L','M','N','O','P','Q','R','.','.','.','.','.','.', //D\r
+ '\\','.','S','T','U','V','W','X','Y','Z','.','.','.','.','.','.', //E\r
+ '0','1','2','3','4','5','6','7','8','9','.','.','.','.','.','.' //F\r
+ };\r
+\r
+\r
+ // An array of characters representing hex numbers.\r
+ private static final char hexDigit [] = {\r
+ '0','1','2','3','4','5','6','7',\r
+ '8','9','A','B','C','D','E','F'\r
+ };\r
+\r
+\r
+ // A PrintWriter is used in printing the trace.\r
+ private java.io.PrintWriter comBufferWriter = null;\r
+\r
+\r
+ // The receive header comes befor bytes which would be read from\r
+ // a Stream.\r
+ private static final String receiveHeader =\r
+ " RECEIVE BUFFER: (ASCII) (EBCDIC)";\r
+\r
+\r
+ // The send header comes before bytes which would be written to\r
+ // a Stream.\r
+ private static final String sendHeader =\r
+ " SEND BUFFER: (ASCII) (EBCDIC)";\r
+\r
+\r
+ // The space character is defined for convience.\r
+ private static final char spaceChar = ' ';\r
+\r
+\r
+ // This boolean indicates if the trace is on.\r
+ // It has been declared private now but may be made public at\r
+ // a later time.\r
+ private boolean comBufferTraceOn = false;\r
+\r
+\r
+ // The comBufferSync is an object used for serialization.\r
+ // This separate object is used because this trace code may\r
+ // get eventually placed into another class which performs\r
+ // method entry and exit tracing. Since each trace may be writing\r
+ // to different logs, separate objects will be used to perform the\r
+ // synchronization.\r
+ private Boolean comBufferSync = new Boolean (true);\r
+\r
+\r
+ // The zero character is defined for convinience.\r
+ private static final char zeroChar = '0';\r
+\r
+ // The recevie constant is used to indicate that the bytes were read to a Stream.\r
+ // It indicates to this class that a receive header should be used.\r
+ protected static final int TYPE_TRACE_RECEIVE = 2;\r
+\r
+ // The send constant is used to indicate that the bytes were written to\r
+ // a Stream. It indicates to this class that a send header should be used.\r
+ protected static final int TYPE_TRACE_SEND = 1;\r
+\r
+ // Query if trace is on.\r
+ // This is currently needed since the comBufferTrcOn flag is private.\r
+ protected boolean isComBufferTraceOn()\r
+ {\r
+ // The trace flag indicates if tracing is on.\r
+ return comBufferTraceOn;\r
+ }\r
+\r
+ // Start the communications buffer trace.\r
+ // The name of the file to place the trace is passed to this method.\r
+ // After calling this method, calls to isComBufferTraceOn() will return true.\r
+ protected void startComBufferTrace (final String fileName) throws IOException \r
+ {\r
+ synchronized (comBufferSync) {\r
+ // Only start the trace if it is off.\r
+ if (comBufferTraceOn == false) {\r
+ // The writer will be buffered for effeciency.\r
+ try {\r
+ \r
+ comBufferWriter = ((PrintWriter)AccessController.doPrivileged(\r
+ new PrivilegedExceptionAction() {\r
+ public Object run() throws SecurityException, IOException {\r
+ return new PrintWriter (new java.io.BufferedWriter (new java.io.FileWriter (fileName), 4096));\r
+ }\r
+ }));\r
+ } catch (PrivilegedActionException pae) {\r
+ Exception e = pae.getException();\r
+ if (e instanceof SecurityException)\r
+ throw (SecurityException)pae.getException();\r
+ else\r
+ throw (IOException) pae.getException();\r
+ }\r
+ \r
+ // Turn on the trace flag.\r
+ comBufferTraceOn = true;\r
+ // initialize the codepoint name table if it is null.\r
+ // this is done here so that the CodePointName objects\r
+ // aren't created if the trace isn't used (save some memory).\r
+ // this process should only be done once\r
+ // since after the table is created the ref will\r
+ // no longer be null.\r
+ if (DssTrace.codePointNameTable == null) {\r
+ codePointNameTable = new CodePointNameTable();\r
+ }\r
+ }\r
+ }\r
+ }\r
+ \r
+\r
+ // Stop the communications buffer trace.\r
+ // The trace file is flushed and closed. After calling this method,\r
+ // calls to isComBufferTraceOn () will return false.\r
+ protected void stopComBufferTrace ()\r
+ {\r
+ synchronized (comBufferSync) {\r
+ // Only stop the trace if it is actually on.\r
+ if (comBufferTraceOn == true) {\r
+ // Turn of the trace flag.\r
+ comBufferTraceOn = false;\r
+ // Flush and close the writer used for tracing.\r
+ if (comBufferWriter != null)\r
+ {\r
+ comBufferWriter.flush();\r
+ comBufferWriter.close();\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ // Write the communication buffer data to the trace.\r
+ // The data is passed in via a byte array. The start and length of the data is given.\r
+ // The type is needed to indicate if the data is part of the send or receive buffer.\r
+ // The class name, method name, and trcPt number are also written to the trace.\r
+ // Not much checking is performed on the parameters. This is done to help performance.\r
+ protected void writeComBufferData (byte[] buff,\r
+ int offset,\r
+ int len,\r
+ int type,\r
+ String className,\r
+ String methodName,\r
+ int trcPt)\r
+ {\r
+ // why don't we synchronize the method!!!\r
+\r
+ // Grab the lock to make sure another thread doesn't try to\r
+ // write data or close the writer.\r
+ synchronized (comBufferSync) {\r
+\r
+ // Only take action if the trace is on.\r
+ if (comBufferTraceOn) {\r
+\r
+ // Obtain an instance of the Calendar so a timestamp can be written.\r
+ // this call seems to slow things down a bit.\r
+ java.util.Calendar time = java.util.Calendar.getInstance();\r
+\r
+ // Print the timestamp, class name, method name, thread name, and tracepoint.\r
+ comBufferWriter.println (" (" +\r
+ time.get (java.util.Calendar.YEAR) +\r
+ "." +\r
+ (time.get (java.util.Calendar.MONTH) + 1) +\r
+ "." +\r
+ time.get (java.util.Calendar.DAY_OF_MONTH) +\r
+ " " +\r
+ time.get (java.util.Calendar.HOUR_OF_DAY) +\r
+ ":" +\r
+ time.get (java.util.Calendar.MINUTE) +\r
+ ":" +\r
+ time.get (java.util.Calendar.SECOND) +\r
+ ") " +\r
+ className +\r
+ " " +\r
+ methodName +\r
+ " " +\r
+ Thread.currentThread().getName() +\r
+ " " +\r
+ trcPt);\r
+\r
+ // A newline is added for formatting.\r
+ comBufferWriter.println();\r
+\r
+ // The data will only be written if there is a non-zero positive length.\r
+ if (len != 0) {\r
+ String codePointName = null;\r
+ // If the length <= 10, lookup the first codepoint so it's name can be printed???\r
+ if (len >= 10) {\r
+ // Get the int value of the two byte unsigned codepoint.\r
+ int codePoint = getCodePoint (buff, offset+8);\r
+ codePointName = codePointNameTable.lookup (codePoint);\r
+ }\r
+\r
+ if (codePointName == null) {\r
+ // codePointName was still null so either < 10 bytes were given or\r
+ // the codepoint wasn't found in the table. Just print the plain send header.\r
+ comBufferWriter.println (getHeader (type));\r
+ }\r
+ else {\r
+ // codePointName isn't null so the name of the codepoint will be printed.\r
+ printHeaderWithCodePointName (codePointName, type);\r
+ }\r
+\r
+ // Print the col position header in the trace.\r
+ comBufferWriter.println (colPosHeader);\r
+\r
+ // A char array will be used to translate the bytes to their character\r
+ // representations along with ascii and ebcdic representations.\r
+ char trcDump[] = new char[77];\r
+\r
+ // bCounter, aCounter, eCounter are offsets used to help position the characters\r
+ short bCounter = 7;\r
+ short aCounter = 43;\r
+ short eCounter = 61;\r
+\r
+ // The lines will be counted starting at zero. This is hard coded since we are\r
+ // at the beginning.\r
+ trcDump[0] = DssTrace.zeroChar;\r
+ trcDump[1] = DssTrace.zeroChar;\r
+ trcDump[2] = DssTrace.zeroChar;\r
+ trcDump[3] = DssTrace.zeroChar;\r
+\r
+ // The 0's are already in the trace so bump the line counter up a row.\r
+ int lineCounter = 0x10;\r
+\r
+ // Make sure the character array has all blanks in it.\r
+ // Some of these blanks will be replaced later with values.\r
+ // The 0's were not wrote over.\r
+ for (int j = 4; j < 77; j++) {\r
+ trcDump[j] = DssTrace.spaceChar;\r
+ }\r
+\r
+ // i will maintain the position in the byte array to be traced.\r
+ int i = 0;\r
+\r
+ do {\r
+ // Get the unsigned value of the byte.\r
+ // int num = b[off++] & 0xff;\r
+ int num = (buff[offset] < 0)? buff[offset] + 256 : buff[offset]; // jev\r
+ offset++;\r
+ i++;\r
+ // Place the characters representing the bytes in the array.\r
+ trcDump[bCounter++] = DssTrace.hexDigit[((num >>> 4) & 0xf)];\r
+ trcDump[bCounter++] = DssTrace.hexDigit[(num & 0xf)];\r
+\r
+ // Place the ascii and ebcdc representations in the array.\r
+ trcDump[aCounter++] = DssTrace.asciiChar[num];\r
+ trcDump[eCounter++] = DssTrace.ebcdicChar[num];\r
+\r
+ if (((i%8) == 0)) {\r
+ if (((i%16) == 0)) {\r
+ // Print the array each time 16 bytes are processed.\r
+ comBufferWriter.println (trcDump);\r
+ if (i != len) {\r
+ // Not yet at the end of the byte array.\r
+ if ((len - i) < 16) {\r
+ // This is the last line so blank it all out.\r
+ // This keeps the last line looking pretty in case\r
+ // < 16 bytes remain.\r
+ for (int j = 0; j < trcDump.length; j++) {\r
+ trcDump[j] = DssTrace.spaceChar;\r
+ }\r
+ }\r
+ // Reset the counters.\r
+ bCounter = 0;\r
+ aCounter = 43;\r
+ eCounter = 61;\r
+ // Reset the lineCounter if it starts to get too large.\r
+ if (lineCounter == 0xfff0) {\r
+ lineCounter = 0;\r
+ }\r
+ // Place the characters representing the line counter in the array.\r
+ trcDump[bCounter++] = DssTrace.hexDigit[((lineCounter >>> 12) & 0xf)];\r
+ trcDump[bCounter++] = DssTrace.hexDigit[((lineCounter >>> 8) & 0xf)];\r
+ trcDump[bCounter++] = DssTrace.hexDigit[((lineCounter >>> 4) & 0xf)];\r
+ trcDump[bCounter++] = DssTrace.hexDigit[(lineCounter & 0xf)];\r
+ bCounter += 3;\r
+ // Bump up the line counter.\r
+ lineCounter += 0x10;\r
+ }\r
+ }\r
+ else {\r
+ // 8 bytes were processed so move the counter to adjust for\r
+ // spaces between the columns of bytes.\r
+ bCounter += 2;\r
+ }\r
+ }\r
+ // do this until we all the data has been traced.\r
+ } while (i < len);\r
+\r
+ // print the last line and add some blank lines to make it easier to read.\r
+ if (len % 16 != 0) {\r
+ comBufferWriter.println (trcDump);\r
+ }\r
+ comBufferWriter.println();\r
+ comBufferWriter.println();\r
+ }\r
+ // Flush the writer.\r
+ comBufferWriter.flush();\r
+ }\r
+ }\r
+ }\r
+\r
+ // Gets the int value of the two byte unsigned codepoint.\r
+ private static int getCodePoint (byte[] buff, int offset)\r
+ {\r
+ return ((buff[offset++] & 0xff) << 8) +\r
+ ((buff[offset] & 0xff) << 0);\r
+ }\r
+\r
+ private static String getHeader (int type)\r
+ {\r
+ switch (type) {\r
+ case DssTrace.TYPE_TRACE_SEND:\r
+ return DssTrace.sendHeader;\r
+ case DssTrace.TYPE_TRACE_RECEIVE:\r
+ return DssTrace.receiveHeader;\r
+ default:\r
+ // throw new !!!\r
+ return null;\r
+ }\r
+ }\r
+\r
+ private static int getStartPosition (int type)\r
+ {\r
+ switch (type) {\r
+ case DssTrace.TYPE_TRACE_SEND:\r
+ return 20; // This is right after 'SEND BUFFER: '.\r
+ case DssTrace.TYPE_TRACE_RECEIVE:\r
+ return 23; // This is right after 'RECEIVE BUFFER: '.\r
+ default:\r
+ // throw new !!!\r
+ return 0;\r
+ }\r
+ }\r
+\r
+ private void printHeaderWithCodePointName (String codePointName, int type)\r
+ {\r
+ // Create a char array so some of the characters\r
+ // can be replaced with the name of the codepoint.\r
+ char headerArray[] = DssTrace.getHeader(type).toCharArray();\r
+\r
+ // At most, 16 character name will be used. This is so\r
+ // the headers on top of the ascii and ebcdic rows aren't shifted.\r
+ int replaceLen = (codePointName.length() < 17) ? codePointName.length() : 16;\r
+\r
+ int offset = getStartPosition (type);\r
+ for (int i = 0; i < replaceLen; i++) {\r
+ headerArray[offset++] = codePointName.charAt (i); // make sure charAt() starts at 0!!!\r
+ }\r
+ comBufferWriter.println (headerArray);\r
+ }\r
+\r
+}\r