1 | /* |
2 | |
3 | Derby - Class org.apache.derby.client.net.NetLogWriter |
4 | |
5 | Copyright (c) 2001, 2005 The Apache Software Foundation or its licensors, where applicable. |
6 | |
7 | Licensed under the Apache License, Version 2.0 (the "License"); |
8 | you may not use this file except in compliance with the License. |
9 | You may obtain a copy of the License at |
10 | |
11 | http://www.apache.org/licenses/LICENSE-2.0 |
12 | |
13 | Unless required by applicable law or agreed to in writing, software |
14 | distributed under the License is distributed on an "AS IS" BASIS, |
15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | See the License for the specific language governing permissions and |
17 | limitations under the License. |
18 | |
19 | */ |
20 | |
21 | package org.apache.derby.client.net; |
22 | |
23 | // network traffic tracer. |
24 | // This class traces communication buffers for both sends and receives. |
25 | // The value of the hex bytes are traced along with the ascii and ebcdic translations. |
26 | |
27 | public class NetLogWriter extends org.apache.derby.client.am.LogWriter { |
28 | |
29 | // The recevie constant is used to indicate that the bytes were read to a Stream. |
30 | // It indicates to this class that a receive header should be used. |
31 | public static final int TYPE_TRACE_RECEIVE = 2; |
32 | |
33 | // The send constant is used to indicate that the bytes were written to |
34 | // a Stream. It indicates to this class that a send header should be used. |
35 | public static final int TYPE_TRACE_SEND = 1; |
36 | |
37 | //------------------------------ internal constants -------------------------- |
38 | |
39 | // This class was implemented using character arrays to translate bytes |
40 | // into ascii and ebcdic. The goal was to be able to quickly index into the |
41 | // arrays to find the characters. Char arrays instead of strings were used as |
42 | // much as possible in an attempt to help speed up performance. |
43 | |
44 | // An array of characters used to translate bytes to ascii. |
45 | // The position in the array corresponds to the hex value of the character. |
46 | private static final char asciiChar__ [] = { |
47 | // 0 1 2 3 4 5 6 7 8 9 A B C D E F |
48 | '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', //0 |
49 | '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', //1 |
50 | ' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', //2 |
51 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', //3 |
52 | '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', //4 |
53 | 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', //5 |
54 | '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', //6 |
55 | 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', '.', //7 |
56 | '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', //8 |
57 | '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', //9 |
58 | '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', //A |
59 | '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', //B |
60 | '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', //C |
61 | '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', //D |
62 | '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', //E |
63 | '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.' //F |
64 | }; |
65 | |
66 | // This column position header is used to mark offsets into the trace. |
67 | private static final String colPosHeader__ = |
68 | " 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF 0123456789ABCDEF"; |
69 | |
70 | // An array of characters used to translate bytes to ebcdic. |
71 | // The position in the array corresponds to the hex value of the |
72 | // character. |
73 | private static final char ebcdicChar__[] = { |
74 | // 0 1 2 3 4 5 6 7 8 9 A B C D E F |
75 | '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', //0 |
76 | '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', //1 |
77 | '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', //2 |
78 | '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', //3 |
79 | ' ', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '<', '(', '+', '|', //4 |
80 | '&', '.', '.', '.', '.', '.', '.', '.', '.', '.', '!', '$', '*', ')', ';', '.', //5 |
81 | '-', '/', '.', '.', '.', '.', '.', '.', '.', '.', '|', ',', '%', '_', '>', '?', //6 |
82 | '.', '.', '.', '.', '.', '.', '.', '.', '.', '`', ':', '#', '@', '\'', '=', '"', //7 |
83 | '.', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', '.', '.', '.', '.', '.', '.', //8 |
84 | '.', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', '.', '.', '.', '.', '.', '.', //9 |
85 | '.', '~', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '.', '.', '.', '.', '.', '.', //A |
86 | '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', //B |
87 | '{', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', '.', '.', '.', '.', '.', '.', //C |
88 | '}', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', '.', '.', '.', '.', '.', '.', //D |
89 | '\\', '.', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '.', '.', '.', '.', '.', '.', //E |
90 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', '.', '.', '.', '.', '.' //F |
91 | }; |
92 | |
93 | // An array of characters representing hex numbers. |
94 | private static final char hexDigit__ [] = { |
95 | '0', '1', '2', '3', '4', '5', '6', '7', |
96 | '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' |
97 | }; |
98 | |
99 | // The receive header comes befor bytes which would be read from a stream. |
100 | private static final String receiveHeader__ = |
101 | " RECEIVE BUFFER: (ASCII) (EBCDIC)"; |
102 | |
103 | // The send header comes before bytes which would be written to a stream. |
104 | private static final String sendHeader__ = |
105 | " SEND BUFFER: (ASCII) (EBCDIC)"; |
106 | |
107 | private static final char spaceChar__ = ' '; |
108 | |
109 | private static final char zeroChar__ = '0'; |
110 | |
111 | // This mapping table associates a codepoint to a String describing the codepoint. |
112 | // This is needed because the trace prints the first codepoint in send and receive buffers. |
113 | // This is created lazily because there is no need to create the mapping if tracing isn't used. |
114 | // So this array will only be created when the com buffer trace is started. |
115 | private static CodePointNameTable codePointNameTable__ = null; |
116 | |
117 | //-----------------------------internal state--------------------------------- |
118 | |
119 | //-----------------------------constructors/finalizer------------------------- |
120 | |
121 | // One NetLogWriter object is created per data source, iff tracing is enabled. |
122 | public NetLogWriter(java.io.PrintWriter printWriter, int traceLevel) { |
123 | super(printWriter, traceLevel); |
124 | |
125 | // Initialize the codepoint name table if not previously initialized. |
126 | // This is done lazily so that it is not created if the trace isn't used (save some init time). |
127 | if (codePointNameTable__ == null) { |
128 | codePointNameTable__ = new CodePointNameTable(); |
129 | } |
130 | } |
131 | |
132 | //------------------------------entry points---------------------------------- |
133 | |
134 | // Specialization of LogWriter.traceConnectsExit() |
135 | public void traceConnectsExit(org.apache.derby.client.am.Connection connection) { |
136 | if (traceSuspended()) { |
137 | return; |
138 | } |
139 | NetConnection c = (NetConnection) connection; |
140 | synchronized (printWriter_) { |
141 | super.traceConnectsExit(c); |
142 | dncnetprint(" PROTOCOL manager levels: { "); |
143 | printWriter_.print("SQLAM=" + c.getSQLAM() + ", "); |
144 | printWriter_.print("AGENT=" + c.getAGENT() + ", "); |
145 | printWriter_.print("CMNTCPIP=" + c.getCMNTCPIP() + ", "); |
146 | printWriter_.print("RDB=" + c.getRDB() + ", "); |
147 | printWriter_.print("SECMGR=" + c.getSECMGR() + ", "); |
148 | printWriter_.print("XAMGR=" + c.getXAMGR() + ", "); |
149 | printWriter_.print("SYNCPTMGR=" + c.getSYNCPTMGR() + ", "); |
150 | printWriter_.print("RSYNCMGR=" + c.getRSYNCMGR()); |
151 | printWriter_.println(" }"); |
152 | printWriter_.flush(); |
153 | } |
154 | } |
155 | |
156 | public void traceConnectsResetExit(org.apache.derby.client.am.Connection connection) { |
157 | if (traceSuspended()) { |
158 | return; |
159 | } |
160 | NetConnection c = (NetConnection) connection; |
161 | synchronized (printWriter_) { |
162 | super.traceConnectsResetExit(c); |
163 | dncnetprint(" PROTOCOL manager levels: { "); |
164 | printWriter_.print("SQLAM=" + c.getSQLAM() + ", "); |
165 | printWriter_.print("AGENT=" + c.getAGENT() + ", "); |
166 | printWriter_.print("CMNTCPIP=" + c.getCMNTCPIP() + ", "); |
167 | printWriter_.print("RDB=" + c.getRDB() + ", "); |
168 | printWriter_.print("SECMGR=" + c.getSECMGR() + ", "); |
169 | printWriter_.print("XAMGR=" + c.getXAMGR() + ", "); |
170 | printWriter_.print("SYNCPTMGR=" + c.getSYNCPTMGR() + ", "); |
171 | printWriter_.print("RSYNCMGR=" + c.getRSYNCMGR()); |
172 | printWriter_.println(" }"); |
173 | printWriter_.flush(); |
174 | } |
175 | } |
176 | |
177 | // Pass the connection handle and print it in the header |
178 | // What exactly is supposed to be passed, assume one complete DSS packet |
179 | // Write the communication buffer data to the trace. |
180 | // The data is passed in via a byte array. The start and length of the data is given. |
181 | // The type is needed to indicate if the data is part of the send or receive buffer. |
182 | // The class name, method name, and trcPt number are also written to the trace. |
183 | // Not much checking is performed on the parameters. This is done to help performance. |
184 | synchronized public void traceProtocolFlow(byte[] buff, |
185 | int offset, |
186 | int len, |
187 | int type, |
188 | String className, |
189 | String methodName, |
190 | int tracepoint) { |
191 | if (traceSuspended()) { |
192 | return; |
193 | } |
194 | if (!loggingEnabled(org.apache.derby.jdbc.ClientDataSource.TRACE_PROTOCOL_FLOWS)) { |
195 | return; |
196 | } |
197 | synchronized (printWriter_) { |
198 | super.tracepoint("[net]", tracepoint, className, methodName); |
199 | |
200 | int fullLen = len; |
201 | boolean printColPos = true; |
202 | while (fullLen >= 2) { // format each DssHdr seperately |
203 | // get the length of this DssHdr |
204 | len = ((buff[offset] & 0xff) << 8) + ((buff[offset + 1] & 0xff) << 0); |
205 | |
206 | // check for valid dss header or not all of dss block |
207 | if ((len < 10) || (len > fullLen)) { |
208 | len = fullLen; |
209 | } |
210 | |
211 | // subtract that length from the full length |
212 | fullLen -= len; |
213 | // The data will only be written if there is a non-zero positive length. |
214 | if (len != 0) { |
215 | String codePointName = null; |
216 | // If the length <= 10, lookup the first codepoint so it's name can be printed |
217 | if (len >= 10) { |
218 | // Get the int value of the two byte unsigned codepoint. |
219 | int codePoint = getCodePoint(buff, offset + 8); |
220 | codePointName = codePointNameTable__.lookup(codePoint); |
221 | |
222 | // if this is not a valid codepoint then format the entire buffer |
223 | // as one block. |
224 | if (codePointName == null) { |
225 | len += fullLen; |
226 | fullLen = 0; |
227 | } |
228 | } |
229 | |
230 | if (!printColPos) { // not 1st Dss header of this buffer, write seperator |
231 | dncnetprintln(""); |
232 | } |
233 | |
234 | if (codePointName == null) { |
235 | // codePointName was still null so either < 10 bytes were given or |
236 | // the codepoint wasn't found in the table. Just print the plain send header. |
237 | dncnetprintln(getHeader(type)); |
238 | } else { |
239 | // codePointName isn't null so the name of the codepoint will be printed. |
240 | printHeaderWithCodePointName(codePointName, type); |
241 | } |
242 | |
243 | // Print the col position header in the trace. |
244 | if (printColPos) { // first Dss header of buffer, need column position header |
245 | dncnetprintln(colPosHeader__); |
246 | printColPos = false; |
247 | } |
248 | |
249 | // A char array will be used to translate the bytes to their character |
250 | // representations along with ascii and ebcdic representations. |
251 | char trcDump[] = new char[77]; |
252 | |
253 | // bCounter, aCounter, eCounter are offsets used to help position the characters |
254 | short bCounter = 7; |
255 | short aCounter = 43; |
256 | short eCounter = 61; |
257 | |
258 | // The lines will be counted starting at zero. |
259 | // This is hard coded since we are at the beginning. |
260 | trcDump[0] = zeroChar__; |
261 | trcDump[1] = zeroChar__; |
262 | trcDump[2] = zeroChar__; |
263 | trcDump[3] = zeroChar__; |
264 | |
265 | // The 0's are already in the trace so bump the line counter up a row. |
266 | int lineCounter = 0x10; |
267 | |
268 | // Make sure the character array has all blanks in it. |
269 | // Some of these blanks will be replaced later with values. |
270 | // The 0's were not wrote over. |
271 | for (int j = 4; j < 77; j++) { |
272 | trcDump[j] = spaceChar__; |
273 | } |
274 | |
275 | // i will maintain the position in the byte array to be traced. |
276 | int i = 0; |
277 | |
278 | do { |
279 | // Get the unsigned value of the byte. |
280 | // int num = b[off++] & 0xff; |
281 | int num = (buff[offset] < 0) ? buff[offset] + 256 : buff[offset]; |
282 | offset++; |
283 | i++; |
284 | // Place the characters representing the bytes in the array. |
285 | trcDump[bCounter++] = hexDigit__[((num >>> 4) & 0xf)]; |
286 | trcDump[bCounter++] = hexDigit__[(num & 0xf)]; |
287 | |
288 | // Place the ascii and ebcdc representations in the array. |
289 | trcDump[aCounter++] = asciiChar__[num]; |
290 | trcDump[eCounter++] = ebcdicChar__[num]; |
291 | |
292 | if (((i % 8) == 0)) { |
293 | if (((i % 16) == 0)) { |
294 | // Print the array each time 16 bytes are processed. |
295 | dncnetprintln(trcDump); |
296 | if (i != len) { |
297 | // Not yet at the end of the byte array. |
298 | if ((len - i) < 16) { |
299 | // This is the last line so blank it all out. |
300 | // This keeps the last line looking pretty in case |
301 | // < 16 bytes remain. |
302 | for (int j = 0; j < trcDump.length; j++) { |
303 | trcDump[j] = spaceChar__; |
304 | } |
305 | } |
306 | // Reset the counters. |
307 | bCounter = 0; |
308 | aCounter = 43; |
309 | eCounter = 61; |
310 | // Reset the lineCounter if it starts to get too large. |
311 | if (lineCounter == 0x100000) { |
312 | lineCounter = 0; |
313 | } |
314 | // Place the characters representing the line counter in the array. |
315 | trcDump[bCounter++] = hexDigit__[((lineCounter >>> 12) & 0xf)]; |
316 | trcDump[bCounter++] = hexDigit__[((lineCounter >>> 8) & 0xf)]; |
317 | trcDump[bCounter++] = hexDigit__[((lineCounter >>> 4) & 0xf)]; |
318 | trcDump[bCounter++] = hexDigit__[(lineCounter & 0xf)]; |
319 | bCounter += 3; |
320 | // Bump up the line counter. |
321 | lineCounter += 0x10; |
322 | } |
323 | } else { |
324 | // 8 bytes were processed so move the counter to adjust for |
325 | // spaces between the columns of bytes. |
326 | bCounter += 2; |
327 | } |
328 | } |
329 | // do this until we all the data has been traced. |
330 | } while (i < len); |
331 | |
332 | // print the last line and add some blank lines to make it easier to read. |
333 | if (len % 16 != 0) { |
334 | dncnetprintln(trcDump); |
335 | } |
336 | } |
337 | } |
338 | dncnetprintln(""); |
339 | } |
340 | } |
341 | |
342 | // Gets the int value of the two byte unsigned codepoint. |
343 | private static int getCodePoint(byte[] buff, int offset) { |
344 | return ((buff[offset++] & 0xff) << 8) + |
345 | ((buff[offset] & 0xff) << 0); |
346 | } |
347 | |
348 | private static String getHeader(int type) { |
349 | switch (type) { |
350 | case TYPE_TRACE_SEND: |
351 | return sendHeader__; |
352 | case TYPE_TRACE_RECEIVE: |
353 | return receiveHeader__; |
354 | default: |
355 | return null; |
356 | } |
357 | } |
358 | |
359 | private static int getStartPosition(int type) { |
360 | switch (type) { |
361 | case TYPE_TRACE_SEND: |
362 | return 20; // This is right after 'SEND BUFFER: '. |
363 | case TYPE_TRACE_RECEIVE: |
364 | return 23; // This is right after 'RECEIVE BUFFER: '. |
365 | default: |
366 | return 0; |
367 | } |
368 | } |
369 | |
370 | private void printHeaderWithCodePointName(String codePointName, int type) { |
371 | // Create a char array so some of the characters |
372 | // can be replaced with the name of the codepoint. |
373 | char headerArray[] = getHeader(type).toCharArray(); |
374 | |
375 | // At most, 16 character name will be used. This is so |
376 | // the headers on top of the ascii and ebcdic rows aren't shifted. |
377 | int replaceLen = (codePointName.length() < 17) ? codePointName.length() : 16; |
378 | |
379 | int offset = getStartPosition(type); |
380 | for (int i = 0; i < replaceLen; i++) { |
381 | headerArray[offset++] = codePointName.charAt(i); |
382 | } |
383 | dncnetprintln(headerArray); |
384 | } |
385 | |
386 | private void dncnetprint(String s) { |
387 | synchronized (printWriter_) { |
388 | printWriter_.print("[derby] " + s); |
389 | printWriter_.flush(); |
390 | } |
391 | } |
392 | |
393 | private void dncnetprintln(String s) { |
394 | synchronized (printWriter_) { |
395 | printWriter_.println("[derby] " + s); |
396 | printWriter_.flush(); |
397 | } |
398 | } |
399 | |
400 | private void dncnetprintln(char[] s) { |
401 | synchronized (printWriter_) { |
402 | printWriter_.print("[derby] "); |
403 | printWriter_.println(s); |
404 | printWriter_.flush(); |
405 | } |
406 | } |
407 | } |