Source code: com/flexstor/common/util/Diagnostic.java
1 /*
2 * Diagnostic.java
3 *
4 * Copyright $Date: 2003/08/11 02:22:31 $ FLEXSTOR.net Inc.
5 *
6 * This work is licensed for use and distribution under license terms found at
7 * http://www.flexstor.org/license.html
8 *
9 */
10
11 package com.flexstor.common.util;
12
13 import java.io.FileOutputStream;
14 import java.io.FileWriter;
15 import java.io.IOException;
16 import java.io.PrintStream;
17 import java.io.PrintWriter;
18 import java.text.SimpleDateFormat;
19 import java.util.Date;
20 import java.util.Enumeration;
21 import java.util.Hashtable;
22
23 import com.flexstor.common.settings.ObjectHolder;
24 import com.flexstor.common.settings.Settings;
25
26 /**
27 * Revision 1.8 2000-09-28 : 22:51:00 Visesh
28 * Removed constructors particular to DiagnosticCategory when it was in the hiearchy
29 *
30 * Diagnostic is a utility class for tracing and debugging. It is not intended
31 * for error and exception handling. It provides methods to print, trace, and warn
32 * and also to check for conditions. One or more output streams can be registered
33 * so that output can be saved in a file and shown on the console simultaneously.<P>
34 * Diagnostic.initialize(section name) must be called to read properties from the
35 * 'diagnostic.properties' file. Different section can be defined with different
36 * properties, e.g. for different applications.<P>
37 * These are the available properties <BR><UL>
38 * <LI>traceDir - Directory for output files. If omitted, an output file will not be created.
39 * <LI>traceFile - File name. If omitted, a unique file name will be generated.
40 * This file will be added as an output device.
41 * <LI>traceLevel - Global trace level, used for warnings and traces based on level or if a
42 * category trace level has not been specified.
43 * <LI>redirectOutput - true or false. Indicates if System.out/err are to be redirected to above file.
44 * If traceDir is not specified, redirection is disabled.
45 * <LI>output - true or false. Indicates if output is turned on. If false and
46 * traceDir is not specified, however, System.out will still be output to the console.
47 * <LI>printTimeStamp - true or false. Indicates if each line will be printed with date/time.
48 * The format can be specified with the timeFormat property, see below.
49 * <LI>printThreadId - true or false. Indicates if each line will be printed with the thread id.
50 * <LI>outputToConsole - true or false. Indicates if output to System.out is turned on. This
51 * will always print to the console, even if redirection is enabled.
52 * <LI>checking - true or false. Enables/disables cheching for check and precondition.
53 * <LI>category.name. - prefix for categories
54 * <LI>category.traceLevel. - prefix for category trace levels
55 * <LI>category.generateFile. - true or false. Prefix for category file indicator. If set to true,
56 * a separate output file will be created for the category.
57 * <LI>timeFormat - format string (default: "yyyy-MM-dd kk:mm:ss:SSS"),
58 * defined as follows:
59 * </UL><P>
60 * Time Format Syntax: <BR>
61 * To specify the time format use a time pattern string. In this pattern, all ASCII
62 * letters are reserved as pattern letters, which are defined as the following:
63 * <pre>
64 *
65 * Symbol Meaning Presentation Example
66 * ------ ------- ------------ -------
67 * G era designator (Text) AD
68 * y year (Number) 1996
69 * M month in year (Text & Number) July & 07
70 * d day in month (Number) 10
71 * h hour in am/pm (1~12) (Number) 12
72 * H hour in day (0~23) (Number) 0
73 * m minute in hour (Number) 30
74 * s second in minute (Number) 55
75 * S millisecond (Number) 978
76 * E day in week (Text) Tuesday
77 * D day in year (Number) 189
78 * F day of week in month (Number) 2 (2nd Wed in July)
79 * w week in year (Number) 27
80 * W week in month (Number) 2
81 * a am/pm marker (Text) PM
82 * k hour in day (1~24) (Number) 24
83 * K hour in am/pm (0~11) (Number) 0
84 * z time zone (Text) Pacific Standard Time
85 * ' escape for text (Delimiter)
86 * '' single quote (Literal) '
87 * </pre>
88 *
89 * The count of pattern letters determine the format.
90 * (Text): 4 or more pattern letters--use full form, < 4--use short or abbreviated form if one exists.
91 * (Number): the minimum number of digits. Shorter numbers are zero-padded to this amount. Year is handled specially; that is, if the count of 'y' is 2, the Year will be truncated to 2 digits.
92 * (Text & Number): 3 or over, use text, otherwise use number.
93 * Any characters in the pattern that are not in the ranges of ['a'..'z'] and ['A'..'Z'] will be treated as quoted text. For instance, characters like ':', '.', ' ', '#' and '@' will appear in the resulting time text even they are not embraced within single quotes.
94 *
95 * A pattern containing any invalid pattern letter will result in a thrown exception during formatting or parsing.
96 * Examples Using the US Locale:
97 *
98 * <pre>
99 * Format Pattern Result
100 * -------------- -------
101 * "yyyy.MM.dd G 'at' hh:mm:ss z" ->> 1996.07.10 AD at 15:08:56 PDT
102 * "EEE, MMM d, ''yy" ->> Wed, July 10, '96
103 * "h:mm a" ->> 12:08 PM
104 * "hh 'o''clock' a, zzzz" ->> 12 o'clock PM, Pacific Daylight Time
105 * "K:mm a, z" ->> 0:00 PM, PST
106 * "yyyyy.MMMMM.dd GGG hh:mm aaa" ->> 1996.July.10 AD 12:08 PM
107 * </pre>
108 *
109 * <P>
110 * Categories must be defined in DiagnosticCategoryI. <P>
111 * Categories must start with the prefix 'category.name.', the trace level for each
112 * category has a prefix of 'category.traceLevel.' with the category name following.<P>
113 * A sample diagnostic section in 'diagnostic.properties'. This file is to be expected
114 * in the conf directory.
115 * <pre>
116 *
117 * [client]
118 * output=true
119 * printTimeStamp=true
120 * printThreadId=true
121 * timeFormat=dd-kk:mm:ss:SSS
122 * outputToConsole=true
123 * redirectOutput=true
124 * traceDir=d:\fsdb\debug\
125 * traceFile=diagnostic.txt
126 * checking=true
127 * traceLevel=10
128 * category.name.Send=Send
129 * category.traceLevel.Send=5
130 *
131 *
132 * Test Run Output: (*** indicates the code executing)
133 *
134 * 21-12:36:49:482 | main | Current test settings:
135 * 21-12:36:49:492 | main | Number of output devices:2
136 * 21-12:36:49:502 | main | Default time stamp=1999-04-21 12:36:49:492
137 * 21-12:36:49:502 | main | Current time stamp=21-12:36:49:502
138 * 21-12:36:49:502 | main | TraceLevel=10
139 * 21-12:36:49:512 | main | Checking=true
140 * 21-12:36:49:522 | main | ThreadId=main
141 * 21-12:36:49:522 | main | Categories:
142 * 21-12:36:49:532 | main | Process
143 * 21-12:36:49:532 | main | Send
144 * 21-12:36:49:532 | main |
145 * 21-12:36:49:532 | main | Test begin
146 * 21-12:36:49:662 | 1 | *** Diagnostic.trace(1, "Trace for level 1")
147 * 21-12:36:49:662 | 1 | [1] Trace for level 1
148 * 21-12:36:49:662 | 1 | *** Diagnostic.trace(3, "Trace for level 3")
149 * 21-12:36:49:672 | 1 | [3] Trace for level 3
150 * 21-12:36:49:672 | 1 | *** Diagnostic.trace(5, "Trace for level 5")
151 * 21-12:36:49:682 | 1 | [5] Trace for level 5
152 * 21-12:36:49:682 | 1 | *** Diagnostic.trace(Diagnostic.CAT_ARCHIVE, "Trace for category Archive")
153 * 21-12:36:49:682 | 1 | *** Diagnostic.trace(Diagnostic.CAT_SEND, "Trace for category Send")
154 * 21-12:36:49:692 | 1 | [Send] Trace for category Send
155 * 21-12:36:49:692 | 1 | *** Diagnostic.precondition((5 < 9), "Precondition 5 < 9")
156 * 21-12:36:49:702 | 1 | *** Diagnostic.precondition((5 > 9), "Precondition 5 > 9")
157 * 21-12:36:49:702 | 1 | Precondition failed: Precondition 5 > 9
158 * 21-12:36:49:792 | 1 | com.flexstor.common.util.DiagnosticPreconditionException: Precondition 5 > 9
159 * 21-12:36:49:792 | 1 | at com.flexstor.common.util.Diagnostic.precondition(Diagnostic.java:359)
160 * 21-12:36:49:792 | 1 | at com.flexstor.common.util.Diagnostic$TestThread.run(Diagnostic.java:1019)
161 * 21-12:36:49:852 | 1 | *** Diagnostic.warn(2, true, "Warning level 2, condition true")
162 * 21-12:36:49:852 | 1 | wn[2] Warning level 2, condition true
163 * 21-12:36:49:852 | 1 | *** Diagnostic.warn(2, false, "Warning level 2, condition false")
164 * 21-12:36:49:852 | 1 | *** Diagnostic.warn(5, true, "Warning level 5, condition true")
165 * 21-12:36:49:862 | 1 | wn[5] Warning level 5, condition true
166 * 21-12:36:49:862 | 1 | *** Diagnostic.warn(Diagnostic.CAT_SEND, "Warning category 'Send'")
167 * 21-12:36:49:862 | 1 | wn[Send] Warning category 'Send'
168 * 21-12:36:49:872 | 1 | *** Diagnostic.warn(Diagnostic.CAT_ARCHIVE, "Warning for category 'Archive'")
169 * 21-12:36:49:872 | 1 | *** Diagnostic.needException()
170 * 21-12:36:49:872 | 1 | Check failed: Need Exception
171 * 21-12:36:49:882 | 1 | *** Diagnostic.fail(new Exception("Failing"))
172 * 21-12:36:49:882 | 1 | Failing
173 * 21-12:36:49:892 | 1 | java.lang.Exception: Failing
174 * 21-12:36:49:892 | 1 | at com.flexstor.common.util.Diagnostic$TestThread.run(Diagnostic.java:1034)
175 * 21-12:36:49:892 | 1 | *** Diagnostic.printStackTrace()
176 * 21-12:36:49:932 | 1 | java.lang.Exception: Diagnostic Stack Trace
177 * 21-12:36:49:932 | 1 | at com.flexstor.common.util.Diagnostic.printStackTrace(Diagnostic.java:539)
178 * 21-12:36:49:932 | 1 | at com.flexstor.common.util.Diagnostic$TestThread.run(Diagnostic.java:1037)
179 * 21-12:36:49:942 | 1 | *** Diagnostic.printStackTrace(Diagnostic.CAT_SEND, 5, new Exception())
180 * 21-12:36:49:952 | 1 | java.lang.Exception
181 * 21-12:36:49:952 | 1 | at com.flexstor.common.util.Diagnostic$TestThread.run(Diagnostic.java:1039)
182 * 21-12:36:49:952 | 1 | *** Test completed
183 * </pre>
184 *
185 * @see com.flexstor.common.util.DiagnosticCategoryI
186 */
187 public class Diagnostic
188 extends DiagnosticBase
189 {
190 private static final String DIAGNOSTIC_PROPERTIES_FILE = "diagnostic.properties";
191 private static final String DIAGNOSTIC_TRACE_DIR = "traceDir";
192 private static final String DIAGNOSTIC_TRACE_FILE = "traceFile";
193 private static final String DIAGNOSTIC_TRACE_LEVEL = "traceLevel";
194 private static final String DIAGNOSTIC_REDIR_OUTPUT = "redirectOutput";
195 private static final String DIAGNOSTIC_OUTPUT_ENABLED = "output";
196 private static final String DIAGNOSTIC_PRINT_TIME = "printTimeStamp";
197 private static final String DIAGNOSTIC_TIME_FORMAT = "timeFormat";
198 private static final String DIAGNOSTIC_PRINT_THREADID = "printThreadId";
199 private static final String DIAGNOSTIC_OUTPUT_CONSOLE = "outputToConsole";
200 private static final String DIAGNOSTIC_CHECKING_ENABLED = "checking";
201 private static final String DIAGNOSTIC_CATEGORY_PREFIX = "category.name.";
202 private static final String DIAGNOSTIC_CATEGORY_LEVEL = "category.traceLevel.";
203 private static final String DIAGNOSTIC_CATEGORY_FILE = "category.generateFile.";
204
205 private static Diagnostic instance;
206
207 //No longer needed after removing circular dependencies
208 /* public Diagnostic() { super(); }
209
210 public Diagnostic(String name)
211 {
212 super(name);
213 }*/
214
215 /**
216 * Initializes Diagnostic by reading a section from the diagnostic.properties file
217 * and adding an instance to ObjectHolder to prevent garbage collection.
218 * @param sSectionName the section to load
219 */
220 public static void initialize(String sSectionName)
221 {
222 if (instance == null)
223 {
224 ObjectHolder.add(instance = new Diagnostic());
225 }
226 getProperties(sSectionName);
227 }
228
229 /**
230 * Gets the properties from the properties file. Uses an PropertyMapper to read
231 * the file.
232 * @param sDiagnosticSection the section name to use for configuration
233 * @see com.flexstor.common.util.PropertyMapper
234 */
235 private static void getProperties(String sDiagnosticSection)
236 {
237 if ( !bPropertiesLoaded )
238 {
239 PropertyMapper mapper = new PropertyMapper();
240 Hashtable properties = null;
241 String dirName = Settings.getString(Settings.CONFIG_DIRECTORY);
242 String[] saKeys;
243 String sTraceFile = ""; // name only, from properties or generated
244 String sTraceDir = ""; // directory of trace file from properties
245 String sFullTracePath= ""; // absolute path to trace file
246 String traceLevelValue;
247 String checkStatusValue;
248 DiagnosticCategory category;
249 String traceKey;
250 String categoryLevel;
251 PrintStream outStream = null; // for System.out redirection
252 boolean bRedirectOutput = false;
253
254 try
255 {
256 mapper.readInputFile( dirName + DIAGNOSTIC_PROPERTIES_FILE );
257 properties = mapper.getSection(sDiagnosticSection);
258 saKeys = mapper.getProperties( sDiagnosticSection );
259
260 // try to get the output status flag
261 checkStatusValue = (String)properties.get(DIAGNOSTIC_OUTPUT_ENABLED);
262 if (checkStatusValue != null
263 && checkStatusValue.toLowerCase().equals("true"))
264 bOutput = true;
265 else
266 bOutput = false;
267
268 // check if redirection is enabled
269 checkStatusValue = (String)properties.get(DIAGNOSTIC_REDIR_OUTPUT);
270 if ( bOutput
271 && checkStatusValue != null
272 && checkStatusValue.toLowerCase().equals("true"))
273 {
274 // try to register a trace file
275 sTraceDir = (String)properties.get(DIAGNOSTIC_TRACE_DIR);
276
277 if (sTraceDir != null && sTraceDir.length() > 0)
278 {
279 bRedirectOutput = true;
280 if (!sTraceDir.endsWith(System.getProperty("file.separator")))
281 sTraceDir += System.getProperty("file.separator");
282
283 sTraceFile = (String)properties.get(DIAGNOSTIC_TRACE_FILE);
284
285 if (sTraceFile == null || sTraceFile.length() == 0)
286 // generate unique file name
287 sTraceFile = InetUtil.getIPAddress() + "_" + Math.abs ( (new Date()).getTime() );
288
289 sFullTracePath = sTraceDir + sTraceFile + ".out";
290
291 if (bOutput)
292 {
293 try
294 {
295 outStream = new PrintStream(new FileOutputStream(sFullTracePath)); //new FileWriter(sFullTracePath)
296 Diagnostic.addOutputDevice(outStream);
297 System.out.println("Diagnostic Output written to '" + sFullTracePath + "'");
298 }
299 catch(IOException ioe)
300 {
301 // could not open file
302 }
303 }
304 }
305 }
306 // try to get the output console property and add System.out to output devices
307 checkStatusValue = (String)properties.get(DIAGNOSTIC_OUTPUT_CONSOLE);
308 if (bOutput
309 && checkStatusValue != null
310 && checkStatusValue.toLowerCase().equals("true"))
311 {
312 bOutputConsole = true;
313 Diagnostic.addOutputDevice(System.out);
314 }
315
316 System.setErr(System.out);
317
318 try
319 {
320 traceLevelValue = (String)properties.get(DIAGNOSTIC_TRACE_LEVEL);
321 if (traceLevelValue != null)
322 outLevel = Integer.parseInt(traceLevelValue);
323 }
324 catch (NumberFormatException nfe)
325 {
326 // just leave the outLevel the way it was
327 }
328
329 // try to get the checking status flag
330 checkStatusValue = (String)properties.get(DIAGNOSTIC_CHECKING_ENABLED);
331 if (checkStatusValue != null
332 && checkStatusValue.toLowerCase().equals("true"))
333 bChecking = true;
334 else
335 bChecking = false;
336
337 // try to get the print time status flag
338 checkStatusValue = (String)properties.get(DIAGNOSTIC_PRINT_TIME);
339 if (checkStatusValue != null
340 && checkStatusValue.toLowerCase().equals("true"))
341 bPrintTime = true;
342 else
343 bPrintTime = false;
344
345 // try to get the print time status flag
346 checkStatusValue = (String)properties.get(DIAGNOSTIC_TIME_FORMAT);
347 if (checkStatusValue != null && checkStatusValue.length() > 0)
348 {
349 try
350 {
351 timeFormat = new SimpleDateFormat(checkStatusValue);
352 }
353 catch(IllegalArgumentException iae) {}
354 }
355
356 // try to get the print time status flag
357 checkStatusValue = (String)properties.get(DIAGNOSTIC_PRINT_THREADID);
358 if (checkStatusValue != null
359 && checkStatusValue.toLowerCase().equals("true"))
360 bPrintThreadId = true;
361 else
362 bPrintThreadId = false;
363
364 // clear the list of categories in the file list */
365 fileTraceCategories.removeAllElements();
366
367 categoryTraceLevels.clear(); // clear the existing category trace levels
368 for (int i = 0; i < saKeys.length; i++)
369 {
370 // check to see if the key is a category key */
371 if (saKeys[i].startsWith(DIAGNOSTIC_CATEGORY_PREFIX))
372 {
373 // get the category value */
374 category = DiagnosticCategory.getCategory((String)properties.get(saKeys[i]));
375 if (category == null)
376 {
377 Diagnostic.println("Trace category '" + saKeys[i] + "' invalid.");
378 }
379 else
380 {
381 // if the category isn't already registered explicitly by a class */
382 if ( !traceCategories.contains(category) )
383 {
384 fileTraceCategories.addElement(category);
385 //Diagnostic.println("added " + category);
386 }
387 // see if there is a key that corresponds to the category trace level
388 traceKey = DIAGNOSTIC_CATEGORY_LEVEL + category.toString();
389 categoryLevel = (String)properties.get(traceKey);
390 if (categoryLevel != null && categoryLevel.length() > 0)
391 {
392 // we found a key/value pair, so store the level in our level properties
393 categoryTraceLevels.put(category, categoryLevel);
394 }
395 if (bRedirectOutput) // no file automatic file generation if output flag is false
396 {
397 // see if there is a key that corresponds to the category file generate
398 traceKey = DIAGNOSTIC_CATEGORY_FILE + category.toString();
399 checkStatusValue = (String)properties.get(traceKey);
400 if (checkStatusValue != null
401 && checkStatusValue.equalsIgnoreCase("true"))
402 {
403 try
404 {
405 //sFullTracePath = sTraceDir + category + "_" + sTraceFile + ".out";
406 addOutputDevice(category, new PrintWriter(new FileWriter(sTraceDir + category + "_" + sTraceFile + ".out")));
407 }
408 catch(IOException ioe)
409 {
410 // ignore it
411 }
412 } // if
413 } // if bOutput
414 } // else
415 } // if
416 } // for
417 bPropertiesLoaded = true;
418 } // try
419 catch (PropertyMapperException pme)
420 {
421 // section not found
422 return;
423 }
424 catch(IOException e)
425 {
426 // error reading input stream
427 return;
428 }
429 } // if
430 }
431
432 /**
433 * For demonstration purposes. Run this class to see how to use it.
434 */
435 public static void main(String[] args)
436 {
437 try
438 {
439 // these statements just set the config directory
440 Settings.registerDataSource(
441 Settings.RUNTIME,
442 (new com.flexstor.common.settings.datasource.RuntimeDataSource())
443 );
444 if (args.length > 0)
445 Settings.addString(Settings.CONFIG_DIRECTORY, args[0]);
446
447 /*
448 Diagnostic.enableOutput(true);
449 Diagnostic.enableChecking(true);
450 Diagnostic.setTraceLevel(3);
451 Diagnostic.addTraceCategory(Diagnostic.CAT_SEND);
452 */
453
454 // try to get properties, overwrites defaults
455 Diagnostic.initialize("client");
456
457 Diagnostic.println("Current test settings:");
458 Diagnostic.println("Number of output devices:" + outputDevices.length);
459 Diagnostic.println("Default time stamp="+(new SimpleDateFormat(DEFAULT_TIME_FORMAT)).format(new Date()));
460 Diagnostic.println("Current time stamp="+timeFormat.format(new Date()));
461
462 Diagnostic.println("TraceLevel="+getTraceLevel());
463 Diagnostic.println("Checking="+bChecking);
464 Diagnostic.println("ThreadId=" + getThreadId());
465 Diagnostic.println("Categories:");
466 Enumeration e = fileTraceCategories.elements();
467 while(e.hasMoreElements())
468 Diagnostic.println(e.nextElement().toString());
469 e = traceCategories.elements();
470 while(e.hasMoreElements())
471 Diagnostic.println(e.nextElement().toString());
472 Diagnostic.println("");
473 Diagnostic.println("Test begin");
474
475 // trace based on level
476 Diagnostic.println("*** Diagnostic.trace(1, \"Trace for level 1\")");
477 Diagnostic.trace(1, "Trace for level 1");
478 Diagnostic.println("*** Diagnostic.trace(3, \"Trace for level 3\")");
479 Diagnostic.trace(3, "Trace for level 3");
480 Diagnostic.println("*** Diagnostic.trace(5, \"Trace for level 5\")");
481 Diagnostic.trace(5, "Trace for level 5");
482
483 // trace based on category
484 Diagnostic.println("*** Diagnostic.trace(Diagnostic.CAT_ARCHIVE, \"Trace for category Archive\")");
485 Diagnostic.trace(Diagnostic.CAT_ARCHIVE, "Trace for category Archive");
486 Diagnostic.println("*** Diagnostic.trace(Diagnostic.CAT_SEND, \"Trace for category Send\")");
487 Diagnostic.trace(Diagnostic.CAT_SEND, "Trace for category Send");
488
489 Diagnostic.println("*** Diagnostic.precondition((5 < 9), \"Precondition 5 < 9\")");
490 try{Diagnostic.precondition((5 < 9), "Precondition 5 < 9");}catch(DiagnosticPreconditionException pce){}
491 Diagnostic.println("*** Diagnostic.precondition((5 > 9), \"Precondition 5 > 9\")");
492 try{Diagnostic.precondition((5 > 9), "Precondition 5 > 9");}catch(DiagnosticPreconditionException pce){printStackTrace(pce);}
493
494 Diagnostic.println("*** Diagnostic.warn(2, true, \"Warning level 2, condition true\")");
495 Diagnostic.warn(2, true, "Warning level 2, condition true");
496 Diagnostic.println("*** Diagnostic.warn(2, false, \"Warning level 2, condition false\")");
497 Diagnostic.warn(2, false, "Warning level 2, condition false");
498 Diagnostic.println("*** Diagnostic.warn(5, true, \"Warning level 5, condition true\")");
499 Diagnostic.warn(5, true, "Warning level 5, condition true");
500 Diagnostic.println("*** Diagnostic.warn(Diagnostic.CAT_SEND, \"Warning category 'Send'\")");
501 Diagnostic.warn(Diagnostic.CAT_SEND, "Warning category 'Send'");
502 Diagnostic.println("*** Diagnostic.warn(Diagnostic.CAT_ARCHIVE, \"Warning for category 'Archive'\")");
503 Diagnostic.warn(Diagnostic.CAT_ARCHIVE, "Warning for category 'Archive'");
504 Diagnostic.println("*** Diagnostic.needException()");
505 try{Diagnostic.needException();}catch(DiagnosticCheckException ce){}
506 Diagnostic.println("*** Diagnostic.fail(new Exception(\"Failing\"))");
507 try{Diagnostic.fail(new Exception("Failing"));}catch(Exception pce){}
508
509 Diagnostic.println("*** Diagnostic.printStackTrace()");
510 Diagnostic.printStackTrace();
511 Diagnostic.println("*** Diagnostic.printStackTrace(Diagnostic.CAT_SEND, 5, new Exception())");
512 Diagnostic.printStackTrace(Diagnostic.CAT_SEND, 5, new Exception());
513 Diagnostic.println("*** Test completed");
514 }
515 catch(Exception e)
516 {
517 System.out.println(e);
518 }
519
520 System.out.println("*** Test completed. Press <enter>");
521 try{System.in.read();}catch(Exception ie){}
522
523 } // main
524
525 } // end of class
526