KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > appserv > management > base > AMXDebug


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the License). You may not use this file except in
5  * compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * https://glassfish.dev.java.net/public/CDDLv1.0.html or
9  * glassfish/bootstrap/legal/CDDLv1.0.txt.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * Header Notice in each file and include the License file
15  * at glassfish/bootstrap/legal/CDDLv1.0.txt.
16  * If applicable, add the following below the CDDL Header,
17  * with the fields enclosed by brackets [] replaced by
18  * you own identifying information:
19  * "Portions Copyrighted [year] [name of copyright owner]"
20  *
21  * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
22  */

23 package com.sun.appserv.management.base;
24
25 import java.util.Map JavaDoc;
26 import java.util.HashMap JavaDoc;
27 import java.util.Set JavaDoc;
28 import java.util.HashSet JavaDoc;
29
30 import java.io.File JavaDoc;
31
32 import com.sun.appserv.management.util.misc.Output;
33 import com.sun.appserv.management.util.misc.FileOutput;
34 import com.sun.appserv.management.util.misc.OutputIgnore;
35 import com.sun.appserv.management.util.misc.StringUtil;
36 import com.sun.appserv.management.util.misc.GSetUtil;
37 import com.sun.appserv.management.util.misc.DebugOutImpl;
38
39
40 /**
41     Internal debug facility. For development use only.
42     <b>Do not use this class; it is subject to arbitrary change.</b>
43     <p>
44     AMX has some unique issues that make a separate debug
45     facility highly useful. These include:
46     
47     <ul>
48     <li>The Logging MBean cannot use System.out/err or logging
49     mechanisms to emit debugging information because it can
50     potentially cause infinite recursion (and thus stack overflow).
51     Without anothermechanism, debugging the Logging MBean would
52     be very difficult
53     </li>
54     <li>
55     AMX code has both client and server aspects, and the
56     client code runs in both environments. The client code
57     in particular can't necessarily assume there is any
58     logging infrastructure in place
59     </li>
60     <li>Debugging complex interactions of MBean invocations
61     over a remote connection which may also involve asynchronous
62     Notifications is complicated. This logging facility makes
63     it possible to selectively view specific entities in the
64     interactions that are occuring, on a fine-grained level,
65     something not possible with the server logging mechanism</li>
66     </li>
67     <li>
68     There is reason and justification for considering debugging
69     code a separate issue than the log file that customers are
70     expected to use. Comingling the two has already led to
71     the log file becoming useless when logging is set to FINE[R][EST].
72     This issue becomes a serious problem when trying to diagnose
73     an issue; one is either stuck with inserting INFO or higher
74     logging messages, or using FINE and dealing with the vast
75     quantify of log messages emitted at that level. It simply
76     is unproductive, and discourages thorough testing.
77     </li>
78     </ul>
79     
80     <p>
81     <b>Usage notes</b>
82     AMXDebug associates a file with each identifier (typically a classname).
83     These files are located within the {@link #AMX_DEBUG_SUBDIR}
84     subdirectory within the directory specified by
85     System.getProperty( "user.home" ) unless the system property
86     {@link #AMX_DEBUG_DIR_SPROP} is specified.
87     All resulting AMXDebug output files
88     end in the suffix {@link #AMX_DEBUG_SUFFIX}.
89     <p>
90     This fine-grained approach makes it possible to "tail" just the
91     output from just the classes of interest,
92     something that is difficult or impossible otherwise.
93     <p>
94     AMXDebug is designed as a singleton. However, arbitrary identifiers
95     may be used to associate debugging output with a particular
96     output file. This allows fine-grained and selective debugging
97     of just the items of interest.
98     <p>
99     When debugging is off, overhead is minimal, because all debugging
100     calls are routed to "dev/null". The caller can also wrap
101     such calls such that they don't make it to AMXDebug at all.
102     <p>
103     The debug flag may be set via the system property
104     {@link #AMX_DEBUG_ENABLED_SPROP}. Debugging will be enabled if that
105     property has value "true". Otherwise, it is disabled.
106     Debugging may also be programmatically enabled, on a per-ID
107     basis.
108     <p>
109     The expected usage is per-class and the classname can generally
110     be used as the identifier. However, usage include other
111     patterns; anything that the emitting code can agree on, regardless
112     of whether it is in the same class, or spread across many. One
113     possibility would be to place the Output into the thread context.
114     There are other possibilities.
115     <p>
116     Output may be marked using the {@link #mark} and {@link #markAll} routines.
117     This aids in visually organizing the output.
118     <p>
119     <b>For more information, see the javadoc on individual routines.</b>
120     
121  */

122 public final class AMXDebug
123 {
124     private final Map JavaDoc<String JavaDoc,WrapOutput> mOutputs;
125     
126     private static final AMXDebug INSTANCE = new AMXDebug();
127     
128     private final File JavaDoc mDir;
129     private boolean mMadeDebugDir;
130     private boolean mDefaultDebug;
131     private final boolean mAppend;
132     
133     /** the key for the system property to enable AMX debug facility */
134     public static final String JavaDoc AMX_DEBUG_ENABLED_SPROP = "AMX-DEBUG.enabled";
135     
136     /** the key for the system property to append to debug files.
137         Otherwise they are overwritten each time
138       */

139     public static final String JavaDoc AMX_DEBUG_APPEND_SPROP = "AMX-DEBUG.append";
140     
141     /**
142         The key for the system property to specify a different AMX_DEBUG_DIR.
143         This value is uninterpreted--the result from
144         new File( System.getProperty( {@link #AMX_DEBUG_SUBDIR} ) is used directly.
145         <p>
146         If the sytem property {@link #AMX_DEBUG_SUBDIR} is not specified,
147         then AMXDebug looks for the system property
148         "com.sun.aas.instanceRoot". If that system property
149         is not found, then "user.home" is used. The result of this is
150         the "parent dir". The resulting output
151         directory is then <parent-dir>/{@link #AMX_DEBUG_SUBDIR}.
152       */

153     public static final String JavaDoc AMX_DEBUG_DIR_SPROP = "AMX-DEBUG.dir";
154     
155     /**
156         The name of the default subdirectory which contains
157         the ".debug" files created by AMXDebug. This is the directory
158         used if {@link #AMX_DEBUG_DIR_SPROP} is not specified.
159       */

160     public static final String JavaDoc AMX_DEBUG_SUBDIR = "AMX-DEBUG";
161     
162     /**
163         Suffix used on all Output files.
164      */

165     public static final String JavaDoc AMX_DEBUG_SUFFIX = ".debug";
166     
167     // Output for AMXDebug itself
168
private final WrapOutput mDebug;
169     
170     private final String JavaDoc NEWLINE;
171     private final Set JavaDoc<Character JavaDoc> ILLEGAL_CHARS;
172     
173     private final char[] ILLEGAL_CHARS_ARRAY =
174     {
175         '\u0000',
176         '?', '*', '|', '\'', '|', '\\', '/', ':',
177     };
178     
179         private
180     AMXDebug()
181     {
182         ILLEGAL_CHARS = new HashSet JavaDoc<Character JavaDoc>();
183         for( final char c : ILLEGAL_CHARS_ARRAY )
184         {
185             ILLEGAL_CHARS.add( c );
186         }
187         
188         NEWLINE = System.getProperty( "line.separator" );
189         assert( NEWLINE != null );
190         
191         String JavaDoc value = System.getProperty( AMX_DEBUG_ENABLED_SPROP );
192         if ( value == null )
193         {
194             // not the right one, but a common mistake.
195
value = System.getProperty( "AMX-DEBUG" );
196             if ( value != null && value.equals( "" ) )
197             {
198                 value = "true";
199             }
200         }
201         mDefaultDebug = (value != null) && Boolean.parseBoolean( value );
202         
203         value = System.getProperty( AMX_DEBUG_APPEND_SPROP );
204         mAppend = (value != null) && Boolean.parseBoolean( value );
205         
206         mOutputs = new HashMap JavaDoc<String JavaDoc,WrapOutput>();
207         
208         mDir = getDir();
209         mMadeDebugDir = false;
210         
211         if ( mDefaultDebug )
212         {
213             makeDebugDir();
214         }
215         
216         mDebug = _getOutput( this.getClass().getName() );
217         mark( mDebug, getStdMarker( "AMXDebug started " ) );
218         mDebug.println( "*** System Properties ***" );
219         dumpSystemProps( mDebug );
220         
221         mark( mDebug, getStdMarker( "AMXDebug initialization done" ) );
222     }
223     
224         private void
225     dumpSystemProps( final Output output )
226     {
227         final java.util.Properties JavaDoc props = System.getProperties();
228         
229         final String JavaDoc[] keys = (String JavaDoc[])props.keySet().toArray( new String JavaDoc[0] );
230         java.util.Arrays.sort( keys );
231         for( final String JavaDoc key : keys )
232         {
233             debug( key + "=" + props.getProperty( key ) );
234         }
235         
236     }
237     
238         private void
239     makeDebugDir()
240     {
241        if ( ! mMadeDebugDir )
242        {
243             mDir.mkdirs();
244             mMadeDebugDir = true;
245        }
246     }
247
248         private void
249     debug( final String JavaDoc s )
250     {
251         // we don't use debug() because we don't want/need the "DEBUG:" prefix
252

253         if ( mDefaultDebug && mDebug != null )
254         {
255             mDebug.println( "" + s );
256         }
257     }
258     
259         private static String JavaDoc
260     parens( final String JavaDoc s )
261     {
262         return "(" + s + ")";
263     }
264     
265         private File JavaDoc
266     getDir()
267     {
268         final String JavaDoc value = System.getProperty( AMX_DEBUG_DIR_SPROP );
269         
270         File JavaDoc debugDir = null;
271         
272         
273         if ( value == null )
274         {
275             final String JavaDoc instanceRoot = System.getProperty( "com.sun.aas.instanceRoot" );
276             
277             File JavaDoc parentDir = null;
278             if ( instanceRoot != null )
279             {
280                 parentDir = new File JavaDoc( instanceRoot );
281             }
282             else
283             {
284                 parentDir = new File JavaDoc( System.getProperty( "user.home" ) );
285             }
286             debugDir = new File JavaDoc( parentDir, AMX_DEBUG_SUBDIR );
287         }
288         else
289         {
290             debugDir = new File JavaDoc( value );
291         }
292         
293         return debugDir;
294     }
295     
296         public String JavaDoc[]
297     getOutputIDs()
298     {
299         return GSetUtil.toStringArray( mOutputs.keySet() );
300     }
301     
302     /**
303         Get the current default debug state used when any
304         new Outputs are created.
305      */

306         public boolean
307     getDefaultDebug()
308     {
309         return mDefaultDebug;
310     }
311     
312     /**
313         Set the current default debug state. Existing outputs
314         are not affected.
315         @see #setAll
316      */

317         public void
318     setDefaultDebug( final boolean debug )
319     {
320         mDefaultDebug = debug;
321         if ( mDefaultDebug )
322         {
323             makeDebugDir();
324         }
325         mDebug.setDebug( debug );
326         debug( "setDefaultDebug" + parens( "" + debug ) );
327     }
328     
329     /**
330         Get the debug state of a particular Output for the
331         specified ID.
332      */

333         public boolean
334     getDebug( final String JavaDoc id )
335     {
336         return _getOutput( id ).getDebug();
337     }
338     
339     /**
340         Set the debug state of a particular Output for the
341         specified ID. If the Output currently maintains
342         an open file, and debug is false, the file is closed.
343      */

344         public void
345     setDebug( final String JavaDoc id, final boolean debug)
346     {
347         if ( debug )
348         {
349             makeDebugDir();
350         }
351         _getOutput( id ).setDebug( debug );
352         debug( "setDebug" + parens( id + ", " + debug ) );
353     }
354     
355     /**
356         Set the debug state of all Outputs.
357         @see #setDebug
358      */

359         public void
360     setAll( final boolean debug )
361     {
362         debug( "setAll" + parens( "" + debug ) );
363         
364         setDefaultDebug( debug );
365         for( final WrapOutput w : mOutputs.values() )
366         {
367             w.setDebug( debug );
368         }
369     }
370     
371     /**
372         Turn off all debugging and close all files.
373      */

374         public void
375     cleanup()
376     {
377         debug( "cleanup()" );
378         
379         setDefaultDebug( false );
380         setAll( false );
381     }
382     
383     /**
384         Turn off debugging any close the associated file (if any)
385         for the Output specified by 'id'.
386      */

387         public void
388     reset( final String JavaDoc id )
389     {
390         debug( "reset" + parens(id) );
391         _getOutput(id).reset();
392     }
393     
394     
395     private static final String JavaDoc DASHES = "----------";
396     
397     /**
398         The standard marker line emitted by {@link #mark}.
399      */

400         public String JavaDoc
401     getStdMarker()
402     {
403         return getStdMarker( "" );
404     }
405     
406     /**
407         The standard marker line emitted by {@link #mark} with a message
408         inserted.
409      */

410         public String JavaDoc
411     getStdMarker( final String JavaDoc msg)
412     {
413         return( NEWLINE + NEWLINE +
414             DASHES + " " + new java.util.Date JavaDoc() + " " + msg + DASHES + NEWLINE );
415     }
416     
417     /**
418         Output a marker into the Output. If 'marker' is null, then
419         the std marker is emitted.
420      */

421         public void
422     mark( final Output output, final String JavaDoc marker )
423     {
424         output.println( marker == null ? getStdMarker() : marker );
425     }
426     
427     /**
428         Output a marker into the Output associated with 'id'.
429      */

430         public void
431     mark( final String JavaDoc id, final String JavaDoc marker )
432     {
433         mark( getOutput(id), marker );
434     }
435     
436     /**
437         Output a standard marker into the Output.
438      */

439         public void
440     mark( final String JavaDoc id )
441     {
442         mark( id, null );
443     }
444     
445     /**
446         Output a standard marker into the Output.
447      */

448         public void
449     markAll( final String JavaDoc marker )
450     {
451         for( final WrapOutput w : mOutputs.values() )
452         {
453             if ( w.getDebug() ) // optimization for debug=false
454
{
455                 mark( w, marker );
456             }
457         }
458     }
459     
460     /**
461         Output a standard marker into the Output.
462      */

463         public void
464     markAll()
465     {
466         markAll( null );
467     }
468     
469     /**
470         Get the Singletone AMXDebug instance.
471      */

472         public static AMXDebug
473     getInstance()
474     {
475         return INSTANCE;
476     }
477     
478     /**
479         Get the File associated with 'id'. The file may or
480         may not exist, and may or may not be open, depending
481         on whether debug is enabled and whether anything was
482         written to the file.
483      */

484         public File JavaDoc
485     getOutputFile( final String JavaDoc id )
486     {
487         final String JavaDoc filename = makeSafeForFile( id ) + AMX_DEBUG_SUFFIX;
488         
489         return new java.io.File JavaDoc( mDir, filename );
490     }
491     
492         private WrapOutput
493     _getOutput( final String JavaDoc id )
494     {
495         WrapOutput output = mOutputs.get( id );
496         if ( output == null )
497         {
498             synchronized( this )
499             {
500                 if ( mOutputs.get( id ) == null )
501                 {
502                     debug( "Creating output for " + StringUtil.quote( id ) );
503                     try
504                     {
505                         output = new WrapOutput( getOutputFile( id ), mDefaultDebug );
506                         mOutputs.put( id, output );
507                     }
508                     catch( Throwable JavaDoc t )
509                     {
510                         debug( "Couldn't create output for " + StringUtil.quote( id ) );
511                     }
512                 }
513             }
514         }
515         
516         return output;
517     }
518     
519         public Output
520     getShared()
521     {
522         return getOutput( "AMXDebug-Shared" );
523     }
524     
525     /**
526         ID is typically a classname, but may be
527         anything which can be used for a filename. The
528         id will be used to create file <id>.debug in the
529         {@link #AMX_DEBUG_SUBDIR} directory.
530      */

531         public Output
532     getOutput( final String JavaDoc id )
533     {
534         return _getOutput( id );
535     }
536         
537         
538     /**
539         Get a form of the ID that is safe to for a filename.
540      */

541         private String JavaDoc
542     makeSafeForFile( final String JavaDoc id )
543     {
544         if ( id == null )
545         {
546             throw new IllegalArgumentException JavaDoc( id );
547         }
548
549         final StringBuilder JavaDoc s = new StringBuilder JavaDoc();
550         
551         final char[] chars = id.toCharArray();
552         for( final char c : chars )
553         {
554             if ( ILLEGAL_CHARS.contains( c ) )
555             {
556                 s.append( "_" );
557             }
558             else
559             {
560                 s.append( c );
561             }
562         }
563         
564         return s.toString();
565     }
566     
567     /**
568         Internal class which wraps the Output so that
569         debug may be dynamically enabled or disable without any
570         users of the Output having to be aware of it.
571      */

572     public final class WrapOutput implements Output
573     {
574         private Output mWrapped;
575         private File JavaDoc mFile;
576         private Output mFileOutput;
577         private boolean mDebug;
578         
579             private
580         WrapOutput( final File JavaDoc file, final boolean debug )
581         {
582             mDebug = debug;
583             mWrapped = OutputIgnore.INSTANCE;
584             mFile = file;
585             mFileOutput = new FileOutput( file, mAppend);
586             checkStatus();
587         }
588             public boolean
589         getDebug()
590         {
591             return mDebug;
592         }
593     
594         /**
595             Change debug status. If debug is <i>enabled</i> any
596             subsequent debugging messages will be written to their outputs,
597             creating files if necessary.
598             If debug is <i>disabled</i>, all output to files ceases, and
599             the files are closed.
600          */

601             public void
602         setDebug( final boolean debug )
603         {
604             mDebug = debug;
605             
606             checkStatus();
607         }
608         
609             public void
610         print( final Object JavaDoc o )
611         {
612             mWrapped.print( o );
613         }
614         
615             public void
616         println( final Object JavaDoc o )
617         {
618             mWrapped.println( o );
619         }
620         
621             public void
622         printError( final Object JavaDoc o )
623         {
624             mWrapped.printError( o );
625         }
626         
627             public void
628         printDebug( final Object JavaDoc o )
629         {
630             mWrapped.printDebug( o );
631         }
632         
633             public synchronized void
634         reset()
635         {
636             mWrapped = OutputIgnore.INSTANCE;
637             mFileOutput.close();
638             
639             // the new one is lazy-opened...
640
mFileOutput = new FileOutput( mFile );
641             
642             checkStatus();
643         }
644         
645             public void
646         close()
647         {
648             reset();
649         }
650         
651         /**
652             Switch between FileOutput and OutputIgnore
653             if there is a mismatch.
654          */

655             private synchronized void
656         checkStatus()
657         {
658             if ( getDebug() )
659             {
660                 mWrapped = mFileOutput;
661             }
662             else
663             {
664                 mWrapped.println( "turning DEBUG OFF" );
665                 mWrapped = OutputIgnore.INSTANCE;
666             }
667         }
668     }
669     
670   
671         public static String JavaDoc
672     methodString( final String JavaDoc name, final Object JavaDoc... args )
673     {
674         return DebugOutImpl.methodString( name, (Object JavaDoc[])args );
675     }
676 }
677
678
679
680
681
682
683
684
685
686
687
688
689
Popular Tags