FileDocCategorySizeDatePackage
AMXDebug.javaAPI DocGlassfish v2 API19938Fri May 04 22:30:30 BST 2007com.sun.appserv.management.base

AMXDebug

public final class AMXDebug extends Object
Internal debug facility. For development use only. Do not use this class; it is subject to arbitrary change.

AMX has some unique issues that make a separate debug facility highly useful. These include:

  • The Logging MBean cannot use System.out/err or logging mechanisms to emit debugging information because it can potentially cause infinite recursion (and thus stack overflow). Without anothermechanism, debugging the Logging MBean would be very difficult
  • AMX code has both client and server aspects, and the client code runs in both environments. The client code in particular can't necessarily assume there is any logging infrastructure in place
  • Debugging complex interactions of MBean invocations over a remote connection which may also involve asynchronous Notifications is complicated. This logging facility makes it possible to selectively view specific entities in the interactions that are occuring, on a fine-grained level, something not possible with the server logging mechanism
  • There is reason and justification for considering debugging code a separate issue than the log file that customers are expected to use. Comingling the two has already led to the log file becoming useless when logging is set to FINE[R][EST]. This issue becomes a serious problem when trying to diagnose an issue; one is either stuck with inserting INFO or higher logging messages, or using FINE and dealing with the vast quantify of log messages emitted at that level. It simply is unproductive, and discourages thorough testing.

Usage notes AMXDebug associates a file with each identifier (typically a classname). These files are located within the {@link #AMX_DEBUG_SUBDIR} subdirectory within the directory specified by System.getProperty( "user.home" ) unless the system property {@link #AMX_DEBUG_DIR_SPROP} is specified. All resulting AMXDebug output files end in the suffix {@link #AMX_DEBUG_SUFFIX}.

This fine-grained approach makes it possible to "tail" just the output from just the classes of interest, something that is difficult or impossible otherwise.

AMXDebug is designed as a singleton. However, arbitrary identifiers may be used to associate debugging output with a particular output file. This allows fine-grained and selective debugging of just the items of interest.

When debugging is off, overhead is minimal, because all debugging calls are routed to "dev/null". The caller can also wrap such calls such that they don't make it to AMXDebug at all.

The debug flag may be set via the system property {@link #AMX_DEBUG_ENABLED_SPROP}. Debugging will be enabled if that property has value "true". Otherwise, it is disabled. Debugging may also be programmatically enabled, on a per-ID basis.

The expected usage is per-class and the classname can generally be used as the identifier. However, usage include other patterns; anything that the emitting code can agree on, regardless of whether it is in the same class, or spread across many. One possibility would be to place the Output into the thread context. There are other possibilities.

Output may be marked using the {@link #mark} and {@link #markAll} routines. This aids in visually organizing the output.

For more information, see the javadoc on individual routines.

Fields Summary
private final Map
mOutputs
private static final AMXDebug
INSTANCE
private final File
mDir
private boolean
mMadeDebugDir
private boolean
mDefaultDebug
private final boolean
mAppend
public static final String
AMX_DEBUG_ENABLED_SPROP
the key for the system property to enable AMX debug facility
public static final String
AMX_DEBUG_APPEND_SPROP
the key for the system property to append to debug files. Otherwise they are overwritten each time
public static final String
AMX_DEBUG_DIR_SPROP
The key for the system property to specify a different AMX_DEBUG_DIR. This value is uninterpreted--the result from new File( System.getProperty( {@link #AMX_DEBUG_SUBDIR} ) is used directly.

If the sytem property {@link #AMX_DEBUG_SUBDIR} is not specified, then AMXDebug looks for the system property "com.sun.aas.instanceRoot". If that system property is not found, then "user.home" is used. The result of this is the "parent dir". The resulting output directory is then /{@link #AMX_DEBUG_SUBDIR}.

public static final String
AMX_DEBUG_SUBDIR
The name of the default subdirectory which contains the ".debug" files created by AMXDebug. This is the directory used if {@link #AMX_DEBUG_DIR_SPROP} is not specified.
public static final String
AMX_DEBUG_SUFFIX
Suffix used on all Output files.
private final WrapOutput
mDebug
private final String
NEWLINE
private final Set
ILLEGAL_CHARS
private final char[]
ILLEGAL_CHARS_ARRAY
private static final String
DASHES
Constructors Summary
private AMXDebug()

    
        
    
    
        ILLEGAL_CHARS   = new HashSet<Character>();
        for( final char c : ILLEGAL_CHARS_ARRAY )
        {
            ILLEGAL_CHARS.add( c );
        }
        
        NEWLINE =  System.getProperty( "line.separator" );
        assert( NEWLINE != null );
        
	    String value   = System.getProperty( AMX_DEBUG_ENABLED_SPROP );
	    if ( value == null )
	    {
	        // not the right one, but a common mistake.
	        value   = System.getProperty( "AMX-DEBUG" );
	        if ( value != null && value.equals( "" ) )
	        {
	            value   = "true";
	        }
	    }
	    mDefaultDebug  = (value != null) && Boolean.parseBoolean( value );
	    
	    value   = System.getProperty( AMX_DEBUG_APPEND_SPROP );
	    mAppend  = (value != null) && Boolean.parseBoolean( value );
	    
        mOutputs    = new HashMap<String,WrapOutput>();
        
        mDir    = getDir();
        mMadeDebugDir   = false;
        
        if ( mDefaultDebug )
        {
            makeDebugDir();
        }
        
        mDebug = _getOutput( this.getClass().getName() );
        mark( mDebug, getStdMarker( "AMXDebug started " ) );
        mDebug.println( "*** System Properties ***" );
        dumpSystemProps( mDebug );
        
        mark( mDebug, getStdMarker( "AMXDebug initialization done" ) );
    
Methods Summary
private com.sun.appserv.management.base.AMXDebug$WrapOutput_getOutput(java.lang.String id)

        WrapOutput  output  = mOutputs.get( id );
        if ( output == null )
        {
            synchronized( this )
            {
                if ( mOutputs.get( id ) == null ) 
                {
                    debug( "Creating output for " + StringUtil.quote( id ) );
                    try
                    {
                        output  = new WrapOutput( getOutputFile( id ), mDefaultDebug );
                        mOutputs.put( id, output );
                    }
                    catch( Throwable t )
                    {
                        debug( "Couldn't create output for " + StringUtil.quote( id ) );
                    }
                }
            }
        }
        
        return output;
    
public voidcleanup()
Turn off all debugging and close all files.

        debug( "cleanup()" );
        
        setDefaultDebug( false );
        setAll( false );
    
private voiddebug(java.lang.String s)

        // we don't use debug() because we don't want/need the "DEBUG:" prefix
        
        if ( mDefaultDebug && mDebug != null )
        {
            mDebug.println( "" + s );
        }
    
private voiddumpSystemProps(com.sun.appserv.management.util.misc.Output output)

        final java.util.Properties    props   = System.getProperties();
        
        final String[]  keys    = (String[])props.keySet().toArray( new String[0] );
        java.util.Arrays.sort( keys );
        for( final String key : keys )
        {
            debug( key + "=" + props.getProperty( key ) );
        }
        
    
public booleangetDebug(java.lang.String id)
Get the debug state of a particular Output for the specified ID.

        return _getOutput( id ).getDebug();
    
public booleangetDefaultDebug()
Get the current default debug state used when any new Outputs are created.

        return mDefaultDebug;
    
private java.io.FilegetDir()

	    final String value   = System.getProperty( AMX_DEBUG_DIR_SPROP );
	    
	    File debugDir  = null;
	    
	    
	    if ( value == null )
	    {
	        final String  instanceRoot  = System.getProperty( "com.sun.aas.instanceRoot" );
	        
	        File parentDir  = null;
	        if ( instanceRoot != null )
	        {
	            parentDir   = new File( instanceRoot );
	        }
	        else
	        {
    	        parentDir  = new File( System.getProperty( "user.home" ) );
	        }
    	    debugDir   = new File( parentDir, AMX_DEBUG_SUBDIR );
	    }
	    else
	    {
	        debugDir   = new File( value );
	    }
        
        return debugDir;
    
public static com.sun.appserv.management.base.AMXDebuggetInstance()
Get the Singletone AMXDebug instance.

        return INSTANCE;
    
public com.sun.appserv.management.util.misc.OutputgetOutput(java.lang.String id)
ID is typically a classname, but may be anything which can be used for a filename. The id will be used to create file .debug in the {@link #AMX_DEBUG_SUBDIR} directory.

        return _getOutput( id );
    
public java.io.FilegetOutputFile(java.lang.String id)
Get the File associated with 'id'. The file may or may not exist, and may or may not be open, depending on whether debug is enabled and whether anything was written to the file.

        final String    filename    = makeSafeForFile( id ) + AMX_DEBUG_SUFFIX;
        
        return new java.io.File( mDir, filename );
    
public java.lang.String[]getOutputIDs()

        return GSetUtil.toStringArray( mOutputs.keySet() );
    
public com.sun.appserv.management.util.misc.OutputgetShared()

        return getOutput( "AMXDebug-Shared" );
    
public java.lang.StringgetStdMarker()
The standard marker line emitted by {@link #mark}.

    
                        
         
    
    
        return getStdMarker( "" );
    
public java.lang.StringgetStdMarker(java.lang.String msg)
The standard marker line emitted by {@link #mark} with a message inserted.

        return( NEWLINE + NEWLINE +
            DASHES + " " + new java.util.Date() + " " + msg + DASHES + NEWLINE );
    
private voidmakeDebugDir()

       if ( ! mMadeDebugDir )
       {
            mDir.mkdirs();
            mMadeDebugDir   = true;
       }
    
private java.lang.StringmakeSafeForFile(java.lang.String id)
Get a form of the ID that is safe to for a filename.

        if ( id == null )
        {
            throw new IllegalArgumentException( id );
        }

        final StringBuilder  s   = new StringBuilder();
        
        final char[]    chars   = id.toCharArray();
        for( final char c : chars )
        {
            if (  ILLEGAL_CHARS.contains( c ) )
            {
                s.append( "_" );
            }
            else
            {
                s.append( c );
            }
        }
        
        return s.toString();
    
public voidmark(com.sun.appserv.management.util.misc.Output output, java.lang.String marker)
Output a marker into the Output. If 'marker' is null, then the std marker is emitted.

        output.println( marker == null ? getStdMarker() : marker );
    
public voidmark(java.lang.String id, java.lang.String marker)
Output a marker into the Output associated with 'id'.

        mark( getOutput(id), marker );
    
public voidmark(java.lang.String id)
Output a standard marker into the Output.

        mark( id, null );
    
public voidmarkAll(java.lang.String marker)
Output a standard marker into the Output.

        for( final WrapOutput w : mOutputs.values() )
        {
            if ( w.getDebug() ) // optimization for debug=false
            {
                mark( w, marker );
            }
        }
    
public voidmarkAll()
Output a standard marker into the Output.

        markAll( null );
    
public static java.lang.StringmethodString(java.lang.String name, java.lang.Object args)

        return DebugOutImpl.methodString( name, (Object[])args );
    
private static java.lang.Stringparens(java.lang.String s)

        return "(" + s + ")";
    
public voidreset(java.lang.String id)
Turn off debugging any close the associated file (if any) for the Output specified by 'id'.

        debug( "reset" + parens(id) );
        _getOutput(id).reset();
    
public voidsetAll(boolean debug)
Set the debug state of all Outputs.

see
#setDebug

        debug( "setAll" + parens( "" + debug ) );
        
        setDefaultDebug( debug );
        for( final WrapOutput w : mOutputs.values() )
        {
            w.setDebug( debug );
        }
    
public voidsetDebug(java.lang.String id, boolean debug)
Set the debug state of a particular Output for the specified ID. If the Output currently maintains an open file, and debug is false, the file is closed.

        if ( debug )
        {
            makeDebugDir();
        }
        _getOutput( id ).setDebug( debug );
        debug( "setDebug" + parens( id + ", " + debug ) );
    
public voidsetDefaultDebug(boolean debug)
Set the current default debug state. Existing outputs are not affected.

see
#setAll

        mDefaultDebug   = debug;
        if ( mDefaultDebug )
        {
            makeDebugDir();
        }
        mDebug.setDebug( debug );
        debug( "setDefaultDebug" + parens( "" + debug ) );