KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > protomatter > util > Debug


1 package com.protomatter.util;
2
3 /**
4  * {{{ The Protomatter Software License, Version 1.0
5  * derived from The Apache Software License, Version 1.1
6  *
7  * Copyright (c) 1998-2002 Nate Sammons. All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  * notice, this list of conditions and the following disclaimer in
18  * the documentation and/or other materials provided with the
19  * distribution.
20  *
21  * 3. The end-user documentation included with the redistribution,
22  * if any, must include the following acknowledgment:
23  * "This product includes software developed for the
24  * Protomatter Software Project
25  * (http://protomatter.sourceforge.net/)."
26  * Alternately, this acknowledgment may appear in the software itself,
27  * if and wherever such third-party acknowledgments normally appear.
28  *
29  * 4. The names "Protomatter" and "Protomatter Software Project" must
30  * not be used to endorse or promote products derived from this
31  * software without prior written permission. For written
32  * permission, please contact support@protomatter.com.
33  *
34  * 5. Products derived from this software may not be called "Protomatter",
35  * nor may "Protomatter" appear in their name, without prior written
36  * permission of the Protomatter Software Project
37  * (support@protomatter.com).
38  *
39  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
40  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
41  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
42  * DISCLAIMED. IN NO EVENT SHALL THE PROTOMATTER SOFTWARE PROJECT OR
43  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
44  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
45  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
46  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
47  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
48  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
49  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
50  * SUCH DAMAGE. }}}
51  */

52
53 import java.io.*;
54 import java.util.*;
55 import java.text.*;
56 import org.apache.oro.text.regex.*;
57
58 /**
59  * A simple hierarchical namespace utility.
60  * Maintains a static list of names and/or patterns
61  * to match names. Dots (".") provide hierarchy markers.
62  * For instance, if you add the following patterns:<P>
63  *
64  * <dl>
65  * <dd><tt>com.protomatter</tt></dd>
66  * <dd><tt>com.protomatter.syslog.*</tt></dd>
67  * <dd><tt>com.protomatter.util.*</tt></dd>
68  * <dd><tt>com.protomatter.foo.bar.*</tt></dd>
69  * </dl><P>
70  *
71  * Then the following names would "match" the
72  * patterns if the "scanning" feature is turned on:<P>
73  *
74  * <dl>
75  * <dd><tt>com.protomatter</tt></dd>
76  * <dd><tt>com.protomatter.syslog</tt></dd>
77  * <dd><tt>com.protomatter.syslog.xml</tt></dd>
78  * <dd><tt>com.protomatter.util.other.package</tt></dd>
79  * </dl><P>
80  *
81  * If you have "scanning" turned off, then only the
82  * following names would match:<P>
83  *
84  * <dl>
85  * <dd><tt>com.protomatter</tt></dd>
86  * </dl><P>
87  *
88  * And the following names would not "match" the
89  * patterns:<P>
90  *
91  * <dl>
92  * <dd><tt>com.protomatterfoo</tt></dd>
93  * <dd><tt>com.protomatter.foo</tt></dd>
94  * <dd><tt>com.protomatter.jdbc.pool</tt></dd>
95  * </dl><P>
96  *
97  * This class is useful in debugging:<P>
98  *
99  * <UL><TABLE BORDER=1 CELLPADDING=4 CELLSPACING=0>
100  * <TR><TD>
101  * <PRE><B>
102  *
103  * import com.protomatter.util.Debug;
104  * import com.protomatter.util.StackTraceUtil;
105  *
106  * import com.protomatter.syslog.Channel;
107  *
108  * ...
109  *
110  * private final static Debug debug = Debug.forPackage(ThisClass.class); &nbsp;
111  *
112  * private final static Channel channel = Channel.forPackage(ThisClass.class); &nbsp;
113  *
114  * ...
115  *
116  * // three levels... trace, debug and info
117  * if (debug.trace())
118  * channel.debug(this, "Really detailed tracing statements");
119  *
120  * if (debug.debug())
121  * channel.debug(this, "Debugging statements: I'm here -> " + StackTraceUtil.whereAm()); &nbsp;
122  *
123  * if (debug.info())
124  * channel.info(this, "Information messages");
125  *
126  * </B></PRE>
127  * </TD></TR></TABLE></UL><P>
128  *
129  * This can prevent expensive operations involved in debugging
130  * to be quickly bypassed in production environments
131  * by changing a configuration file instead of re-compiling.<P>
132  *
133  * All the work is done in the constructor, which calls
134  * the <tt><a HREF="#match(java.lang.String)">match()</a></tt> method.
135  * Calling the
136  * <tt><a HREF="#trace()">trace()</a></tt>,
137  * <tt><a HREF="#debug()">debug()</a></tt> or
138  * <tt><a HREF="#info()">info()</a></tt>
139  * methods takes almost no time (return values are cached).<P>
140  *
141  * I've tested this operation on a 650MHz PIII Coppermine Sony Vaio
142  * laptop running RedHat Linux 7.2, kernel 2.4.9,
143  * I saw these results for timing "<tt>Debug.forName(name)</tt>":<P>
144  *
145  * <ul><table border=1 cellpadding=4 cellspacing=0>
146  *
147  * <tr>
148  * <th>Virtual Machine</th>
149  * <th>Best&nbsp;Case</th>
150  * <th>Middle&nbsp;Case</th>
151  * <th>Worst&nbsp;Case</th>
152  * </tr>
153  *
154  * <tr>
155  * <td>Classic VM<BR>
156  * (build JDK-1.2.2_012, green threads, nojit)</td>
157  * <td valign=top>0.018627ms</td>
158  * <td valign=top>0.076707ms</td>
159  * <td valign=top>0.625373ms</td>
160  * </tr>
161  *
162  * <tr>
163  * <td>Java(TM) 2 Runtime Environment,<BR>
164  * Standard Edition (build 1.3.1_03-b03)</td>
165  * <td valign=top>0.001347ms</td>
166  * <td valign=top>0.008267ms</td>
167  * <td valign=top>0.08128ms</td>
168  * </tr>
169  *
170  * <tr>
171  * <td>Java HotSpot(TM) Client VM<BR>
172  * (build 1.4.0-b92, mixed mode)</td>
173  * <td valign=top>0.00184ms</td>
174  * <td valign=top>0.008067ms</td>
175  * <td valign=top>0.07928ms</td>
176  * </tr>
177  *
178  * <tr>
179  * <td>JRockit Virtual Machine<BR>
180  * (build 3.1.4-dax.appeal.se-20020319-1000)<BR>
181  * Native Threads, Generational Concurrent Garbage Collector</td>
182  * <td valign=top>0.00092ms</td>
183  * <td valign=top>0.006453ms</td>
184  * <td valign=top>0.065147ms</td>
185  * </tr>
186  *
187  * <tr>
188  * <td>Classic VM<BR>
189  * (build 1.3.1, J2RE 1.3.1 IBM build cxia32131-20020410<BR>
190  * (JIT enabled: jitc))</td>
191  * <td valign=top>0.00204ms</td>
192  * <td valign=top>0.00488ms</td>
193  * <td valign=top>0.039253ms</td>
194  * </tr>
195  *
196  * </table></ul><P>
197  *
198  * "Best Case" above is a direct match, where the name
199  * being matched is the same as one of the patterns.
200  * "Middle Case" is a search only one or two checks
201  * away from success, like matching "<tt>foo.bar.baz</tt>"
202  * against "<tt>foo.bar.*</tt>". "Worst Case" is a match
203  * that's 13 checks away. All cases had around 125 patterns
204  * in the search group. Your mileage may vary.<P>
205  *
206  * If you call <tt>setScan(false)</tt>, then all matches
207  * will be "Best Case" but only exact matches will
208  * return <tt>true</tt>.<P>
209  *
210  * If <tt>disable()</tt> has been called, this method
211  * will return <tt>false</tt> immediately. If there
212  * are no patterns set, it has the same effect.<P>
213  *
214  * @see com.protomatter.syslog.Channel
215  * @see com.protomatter.syslog.Syslog
216  * @see com.protomatter.util.StackTraceUtil
217  * @see com.protomatter.syslog.xml.SyslogXML#configure(org.jdom.Element)
218  */

219 public class Debug
220 implements java.io.Serializable JavaDoc
221 {
222     /**
223      * The set of patterns and/or names to match with for the "trace" severity.
224      */

225     private static Set tracePatternSet = new HashSet();
226
227     /**
228      * The set of patterns and/or names to match with for the "debug" severity.
229      */

230     private static Set debugPatternSet = new HashSet();
231
232     /**
233      * The set of patterns and/or names to match with for the "info" severity.
234      */

235     private static Set infoPatternSet = new HashSet();
236
237     /**
238      * The "." character.
239      */

240     private final static char DOT = '.';
241
242     /**
243      * The String ".*".
244      */

245     private final static String JavaDoc DOT_STAR = ".*";
246
247     /**
248      * Disables everything. <tt>true</tt> = disabled,
249      * <tt>false</tt> = enabled (default).
250      */

251     private static boolean allOff = false;
252
253     /**
254      * Scan "up" the naming hierarchy for
255      * matches. <tt>true</tt> = yes,
256      * <tt>false</tt> = no (default).
257      */

258     private static boolean scan = false;
259
260     /**
261      * Is this instance "on" for "trace"?.
262      */

263     private boolean traceOn = false;
264
265     /**
266      * Is this instance "on" for "debug"?.
267      */

268     private boolean debugOn = false;
269
270     /**
271      * Is this instance "on" for "info"?.
272      */

273     private boolean infoOn = false;
274
275     /**
276      * The name of this instance.
277      */

278     private String JavaDoc name = null;
279     
280     /**
281      * The last time configuration info was updated.
282      */

283     private static long configLastUpdateTime = 0;
284     
285     /**
286      * The last time this instance was configured. This
287      * is used to determine if we should call init() again
288      * before returning from the debug() info() or trace()
289      * methods.
290      */

291     private long lastUpdateTime = 0;
292
293     /**
294      * The name of the "trace" severity, for the <tt>match()</tt> method.
295      */

296     private static final int TRACE = 0;
297
298     /**
299      * The name of the "debug" severity, for the <tt>match()</tt> method.
300      */

301     private static final int DEBUG = 1;
302
303     /**
304      * The name of the "info" severity, for the <tt>match()</tt> method.
305      */

306     private static final int INFO = 2;
307
308     /**
309      * Protected constructor. Calls <tt>init()</tt>.
310      */

311     private Debug(String JavaDoc name)
312     {
313       super();
314
315       this.name = name;
316       init();
317     }
318
319     /**
320      * Initialize this <tt>Debug</tt> instance. Each time
321      * this method is called, the name of this instance
322      * is matched against the various patterns for
323      * each severity level. This method is called
324      * by the constructor.
325      */

326     public final void init()
327     {
328       this.traceOn = match(name, TRACE);
329       this.debugOn = match(name, DEBUG);
330       this.infoOn = match(name, INFO);
331       this.lastUpdateTime = System.currentTimeMillis();
332     }
333
334     /**
335      * Get a debug object with name of the
336      * given class.
337      * Pass in the class <tt>com.protomatter.syslog.Channel</tt>
338      * would use the name "<tt>com.protomatter.syslog.Channel</tt>".
339      * A new instance of this class
340      * is created (and initialized) each time this method is called.
341      */

342     public final static Debug forClass(Class JavaDoc someClass)
343     {
344         return new Debug(someClass.getName());
345     }
346
347     /**
348      * Get a debug object with name of the
349      * given class's package.
350      * Pass in the class <tt>com.protomatter.syslog.Channel</tt>
351      * would use the name "<tt>com.protomatter.syslog</tt>".
352      * If the class has no package, the name of the
353      * class is used. A new instance of this class
354      * is created (and initialized) each time this method is called.
355      */

356     public final static Debug forPackage(Class JavaDoc someClass)
357     {
358         String JavaDoc packageName = someClass.getName();
359         int pos = packageName.lastIndexOf(DOT);
360         if (pos < 0)
361             return new Debug(packageName);
362         return new Debug(packageName.substring(0, pos));
363     }
364
365     /**
366      * Get a debug object with the given name. A new instance of this class
367      * is created (and initialized) each time this method is called.
368      */

369     public final static Debug forName(String JavaDoc name)
370     {
371         return new Debug(name);
372     }
373
374     /**
375      * Disable all checks. This effectively means
376      * that the <tt>on()</tt> and <tt>match()</tt> methods
377      * on all instances of this class will return
378      * <tt>false</tt> until the <tt>enable()</tt>
379      * method is called.
380      */

381     public final static void disable()
382     {
383         allOff = true;
384     }
385
386     /**
387      * Enable checks. This is the default state.
388      */

389     public final static void enable()
390     {
391         allOff = false;
392     }
393
394     /**
395      * Determine if checks are enabled or disabled.
396      */

397     public final static boolean isEnabled()
398     {
399         return (!allOff);
400     }
401
402     /**
403      * Does our name match anything in the list of names and patterns.
404      * This value is cached when this object is instantiated, but is
405      * updated if the configuration is changed after this instance
406      * is created.
407      */

408     public final boolean trace()
409     {
410         if ((this.lastUpdateTime < Debug.configLastUpdateTime) && (!allOff))
411             init();
412         return (allOff) ? false : this.traceOn;
413     }
414
415     /**
416      * Does our name match anything in the list of names and patterns.
417      * This value is cached when this object is instantiated, but is
418      * updated if the configuration is changed after this instance
419      * is created.
420      */

421     public final boolean debug()
422     {
423         if ((this.lastUpdateTime < Debug.configLastUpdateTime) && (!allOff))
424             init();
425         return (allOff) ? false : this.debugOn;
426     }
427
428     /**
429      * Does our name match anything in the list of names and patterns.
430      * This value is cached when this object is instantiated, but is
431      * updated if the configuration is changed after this instance
432      * is created.
433      */

434     public final boolean info()
435     {
436         if ((this.lastUpdateTime < Debug.configLastUpdateTime) && (!allOff))
437             init();
438         return (allOff) ? false : this.infoOn;
439     }
440
441     /**
442      * Does the given name match anything in the list of names and patterns.<P>
443      *
444      * @see #disable()
445      * @see #enable()
446      * @see #setScan(boolean)
447      */

448     private final static boolean match(String JavaDoc name, int severity)
449     {
450       if (name == null)
451         return false;
452
453       // is everything turned off?
454
if (allOff)
455         return false;
456
457       Set patternSet = null;
458       if (TRACE == severity)
459         patternSet = tracePatternSet;
460       else if (DEBUG == severity)
461         patternSet = debugPatternSet;
462       else if (INFO == severity)
463         patternSet = infoPatternSet;
464
465       // no patterns so no matches
466
if (patternSet.isEmpty())
467         return false;
468
469       // scan up the namespace for matches?
470
if (scan)
471       {
472         if (patternSet.contains(name))
473           return true;
474
475         int index = 1;
476         while (index > 0)
477         {
478           if (patternSet.contains(name + DOT_STAR))
479           {
480               return true;
481           }
482           index = name.lastIndexOf(DOT);
483           if (index < 0)
484               return false;
485           name = name.substring(0, index);
486         }
487         return false;
488       }
489
490       // find an exact match
491
return patternSet.contains(name);
492     }
493
494     /**
495      * Set if we scan for matches. Default is false.
496      * If set, we scan "up" the name hierarchy,
497      * which can be a little time consuming. It
498      * may make sense to enable, and then
499      * only allow for exact matches on names.
500      */

501     public final static void setScan(boolean setting)
502     {
503       scan = setting;
504       configLastUpdateTime = System.currentTimeMillis();
505     }
506
507     /**
508      * Should we scan for matches? Default is false.
509      * If set, we scan "up" the name hierarchy,
510      * which can be a little time consuming. It
511      * may make sense to enable, and then
512      * only allow for exact matches on names.
513      */

514     public final static boolean getScan()
515     {
516       return scan;
517     }
518
519     /**
520      * Get the names and/or patterns to match at the "trace" severity.
521      */

522     public final static Iterator getTraceNames()
523     {
524         return tracePatternSet.iterator();
525     }
526
527     /**
528      * Get the names and/or patterns to match at the "debug" severity.
529      */

530     public final static Iterator getDebugNames()
531     {
532         return debugPatternSet.iterator();
533     }
534
535     /**
536      * Get the names and/or patterns to match at the "info" severity.
537      */

538     public final static Iterator getInfoNames()
539     {
540         return infoPatternSet.iterator();
541     }
542
543     /**
544      * Add a name or pattern to the matching set at the "trace" level.
545      */

546     public final static void addTraceName(String JavaDoc name)
547     {
548         tracePatternSet.add(name);
549         configLastUpdateTime = System.currentTimeMillis();
550     }
551
552     /**
553      * Add a name or pattern to the matching set at the "debug" level.
554      */

555     public final static void addDebugName(String JavaDoc name)
556     {
557         debugPatternSet.add(name);
558         configLastUpdateTime = System.currentTimeMillis();
559     }
560
561     /**
562      * Add a name or pattern to the matching set at the "info" level.
563      */

564     public final static void addInfoName(String JavaDoc name)
565     {
566         infoPatternSet.add(name);
567         configLastUpdateTime = System.currentTimeMillis();
568     }
569
570     /**
571      * Clear the sets of patterns and names.
572      */

573     public final static void clear()
574     {
575         tracePatternSet = new HashSet();
576         debugPatternSet = new HashSet();
577         infoPatternSet = new HashSet();
578         configLastUpdateTime = System.currentTimeMillis();
579     }
580
581     /**
582      * Get the name this instance uses.
583      */

584     public final String JavaDoc getName()
585     {
586         return this.name;
587     }
588
589     /**
590      * Return a human-readable representation of this object.
591      */

592     public final String JavaDoc toString()
593     {
594         if (this.lastUpdateTime < Debug.configLastUpdateTime)
595             init();
596         StringBuffer JavaDoc b = new StringBuffer JavaDoc();
597         b.append("Debug[");
598         b.append(name);
599         b.append(" ");
600         if (!traceOn && !debugOn && !infoOn)
601         {
602             b.append("OFF");
603         }
604         else if (traceOn && debugOn && infoOn)
605         {
606             b.append("ON");
607         }
608         else
609         {
610             b.append("trace");
611             b.append((this.traceOn ? "=ON " : "=OFF "));
612
613             b.append("debug");
614             b.append((this.debugOn ? "=ON " : "=OFF "));
615
616             b.append("info");
617             b.append((this.infoOn ? "=ON" : "=OFF"));
618         }
619
620         b.append("]");
621
622         return b.toString();
623     }
624
625
626     /**
627      * Performance testing rig.
628      */

629     public static void main(String JavaDoc args[])
630     {
631         try
632         {
633             if (args.length != 4)
634             {
635                 System.out.println("Usage: java com.protomatter.util.Debug \\");
636                 System.out.println(" scan? numCalls patterns.txt tests.txt");
637                 System.out.println("");
638                 System.out.println(" scan: true/false: passed to Debug.setScan()");
639                 System.out.println(" numCalls: number of Debug.forName() calls to make");
640                 System.out.println(" patterns.txt: a text file containing strings");
641                 System.out.println(" to pass to Debug.addInfoName()");
642                 System.out.println(" tests.txt: a text file containing strings");
643                 System.out.println(" to pass to Debug.forName() repeatedly.");
644                 System.out.println("");
645                 System.exit(0);
646             }
647
648             Debug.setScan("true".equalsIgnoreCase(args[0]));
649             int numCalls = Integer.parseInt(args[1]);
650
651             BufferedReader names = new BufferedReader(new FileReader(new File(args[2])));
652             BufferedReader tests = new BufferedReader(new FileReader(new File(args[3])));
653
654
655             System.out.println("Debug.forName() and Debug.info() performance test");
656             System.out.println("");
657
658             Debug.setScan(true);
659             System.out.println("JVM Information:");
660             System.out.println(" VM Name: " + System.getProperty("java.vm.name"));
661             System.out.println(" VM Version: " + System.getProperty("java.vm.version"));
662             System.out.println(" Runtime name: " + System.getProperty("java.runtime.name"));
663             System.out.println(" Runtime version: " + System.getProperty("java.runtime.version"));
664             System.out.println("");
665             System.out.println("OS Information:");
666             System.out.println(" " + System.getProperty("os.name") + " " + System.getProperty("os.version"));
667             System.out.println("");
668             System.out.println("Scan: " + Debug.getScan());
669
670
671             System.out.println("Adding names:");
672             String JavaDoc line = null;
673             int num = 0;
674             while ((line = names.readLine()) != null)
675             {
676                 //System.out.println(" " + line.trim());
677
Debug.addInfoName(line.trim());
678                 ++num;
679             }
680             System.out.println(" added " + num + " names/patterns");
681
682             System.out.println("");
683             System.out.println("Testing names:");
684             DecimalFormat tf = new DecimalFormat("###,###,###,###");
685             DecimalFormat format = new DecimalFormat("###.#######");
686             Debug debug = null;
687             Debug debug1 = null;
688             while ((line = tests.readLine()) != null)
689             {
690                 line = line.trim();
691                 System.out.println(" instance = Debug.forName(\"" + line + "\");");
692                 long time = System.currentTimeMillis();
693                 for (int i=0; i<numCalls; i++)
694                 {
695                     debug = Debug.forName(line);
696                 }
697                 if (debug1 == null)
698                     debug1 = debug;
699                 time = System.currentTimeMillis() - time;
700                 System.out.println(" " + debug);
701                 System.out.println(" " + tf.format(numCalls) + " calls in " + tf.format(time) + "ms");
702                 double average = ((double)time / (double)numCalls);
703                 double persecond = ((double)1 / average) * 1000;
704                 System.out.println(" Average = " + format.format(average) + "ms");
705                 System.out.println(" Per-second = " + tf.format(persecond));
706                 System.out.println("");
707             }
708             System.out.println("");
709             System.out.println("");
710             numCalls = numCalls * 1000;
711             System.out.println(tf.format(numCalls) + " instance.info() calls");
712             long time = System.currentTimeMillis();
713             boolean on = false;
714             for (int i=0; i<numCalls; i++)
715             {
716                 on = debug1.info();
717             }
718             time = System.currentTimeMillis() - time;
719             System.out.println(" " + debug1);
720             System.out.println(" " + tf.format(numCalls) + " calls in " + tf.format(time) + "ms");
721             double average = ((double)time / (double)numCalls);
722             double persecond = ((double)1 / average) * 1000;
723             System.out.println(" Average = " + format.format(average) + "ms");
724             System.out.println(" Per-second = " + tf.format(persecond));
725             System.out.println("");
726         }
727         catch (Exception JavaDoc x)
728         {
729             x.printStackTrace();
730         }
731     }
732 }
733
Popular Tags