alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Glassfish example source code file (AMXDebug.java)

This example Glassfish source code file (AMXDebug.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Glassfish tags/keywords

amx-debug, amxdebug, amxdebug, dashes, file, file, io, newline, object, output, string, string, stringbuilder, threading, threads, util, wrapoutput, wrapoutput

The Glassfish AMXDebug.java source code

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package org.glassfish.admin.amx.util;

import org.glassfish.admin.amx.util.DebugOutImpl;
import org.glassfish.admin.amx.util.FileOutput;
import org.glassfish.admin.amx.util.SetUtil;
import org.glassfish.admin.amx.util.Output;
import org.glassfish.admin.amx.util.OutputIgnore;
import org.glassfish.admin.amx.util.StringUtil;

import java.io.File;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
Internal debug facility.  For development use only.
<b>Do not use this class; it is subject to arbitrary change.
<p>
AMX has some unique issues that make a separate debug
facility highly useful.  These include:

<ul>
<li>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
</li>
<li>
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
</li>
<li>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</li>
</li>
<li>
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.
</li>
</ul>

<p>
<b>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}.
<p>
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.
<p>
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.
<p>
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.
<p>
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.
<p>
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.
<p>
Output may be marked using the {@link #mark} and {@link #markAll} routines.
This aids in visually organizing the output.
<p>
<b>For more information, see the javadoc on individual routines.

 */
public final class AMXDebug
{
    private final ConcurrentMap<String, WrapOutput> mOutputs;

    private static final AMXDebug INSTANCE = new AMXDebug();

    private final File mDir;

    private boolean mMadeDebugDir;

    private boolean mDefaultDebug;

    private final boolean mAppend;

    /** the key for the system property to enable AMX debug facility */
    public static final String AMX_DEBUG_ENABLED_SPROP = "AMX-DEBUG.enabled";

    /** the key for the system property to append to debug files.
    Otherwise they are overwritten each time
     */
    public static final String AMX_DEBUG_APPEND_SPROP = "AMX-DEBUG.append";

    /**
    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.
    <p>
    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 <parent-dir>/{@link #AMX_DEBUG_SUBDIR}.
     */
    public static final String AMX_DEBUG_DIR_SPROP = "AMX-DEBUG.dir";

    /**
    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_SUBDIR = "AMX-DEBUG";

    /**
    Suffix used on all Output files.
     */
    public static final String AMX_DEBUG_SUFFIX = ".debug";

    // Output for AMXDebug itself
    private final WrapOutput mDebug;

    private final String NEWLINE;

    private final Set<Character> ILLEGAL_CHARS;

    private final char[] ILLEGAL_CHARS_ARRAY =
    {
        '\u0000',
        '?', '*', '|', '\'', '|', '\\', '/', ':',
    };

    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 ConcurrentHashMap<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"));
    }

    private void dumpSystemProps(final Output output)
    {
        final java.util.Properties props = System.getProperties();

        final String[] keys = props.keySet().toArray(new String[0]);
        java.util.Arrays.sort(keys);
        for (final String key : keys)
        {
            debug(key + "=" + props.getProperty(key));
        }

    }

    private void makeDebugDir()
    {
        if (!mMadeDebugDir)
        {
            mDir.mkdirs();
            mMadeDebugDir = true;
        }
    }

    private void debug(final String s)
    {
        // we don't use debug() because we don't want/need the "DEBUG:" prefix

        if (mDefaultDebug && mDebug != null)
        {
            mDebug.println("" + s);
        }
    }

    private static String parens(final String s)
    {
        return "(" + s + ")";
    }

    private File getDir()
    {
        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 String[] getOutputIDs()
    {
        return SetUtil.toStringArray(mOutputs.keySet());
    }

    /**
    Get the current default debug state used when any
    new Outputs are created.
     */
    public boolean getDefaultDebug()
    {
        return mDefaultDebug;
    }

    /**
    Set the current default debug state.  Existing outputs
    are not affected.
    @see #setAll
     */
    public void setDefaultDebug(final boolean debug)
    {
        mDefaultDebug = debug;
        if (mDefaultDebug)
        {
            makeDebugDir();
        }
        mDebug.setDebug(debug);
        debug("setDefaultDebug" + parens("" + debug));
    }

    /**
    Get the debug state of a particular Output for the
    specified ID.
     */
    public boolean getDebug(final String id)
    {
        return _getOutput(id).getDebug();
    }

    /**
    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.
     */
    public void setDebug(final String id, final boolean debug)
    {
        if (debug)
        {
            makeDebugDir();
        }
        _getOutput(id).setDebug(debug);
        debug("setDebug" + parens(id + ", " + debug));
    }

    /**
    Set the debug state of all Outputs.
    @see #setDebug
     */
    public void setAll(final boolean debug)
    {
        debug("setAll" + parens("" + debug));

        setDefaultDebug(debug);
        for (final WrapOutput w : mOutputs.values())
        {
            w.setDebug(debug);
        }
    }

    /**
    Turn off all debugging and close all files.
     */
    public void cleanup()
    {
        debug("cleanup()");

        setDefaultDebug(false);
        setAll(false);
    }

    /**
    Turn off debugging any close the associated file (if any)
    for the Output specified by 'id'.
     */
    public void reset(final String id)
    {
        debug("reset" + parens(id));
        _getOutput(id).reset();
    }

    private static final String DASHES = "----------";

    /**
    The standard marker line emitted by {@link #mark}.
     */
    public String getStdMarker()
    {
        return getStdMarker("");
    }

    /**
    The standard marker line emitted by {@link #mark} with a message
    inserted.
     */
    public String getStdMarker(final String msg)
    {
        return (NEWLINE + NEWLINE +
                DASHES + " " + new java.util.Date() + " " + msg + DASHES + NEWLINE);
    }

    /**
    Output a marker into the Output. If 'marker' is null, then
    the std marker is emitted.
     */
    public void mark(final Output output, final String marker)
    {
        output.println(marker == null ? getStdMarker() : marker);
    }

    /**
    Output a marker into the Output associated with 'id'.
     */
    public void mark(final String id, final String marker)
    {
        mark(getOutput(id), marker);
    }

    /**
    Output a standard marker into the Output.
     */
    public void mark(final String id)
    {
        mark(id, null);
    }

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

    /**
    Output a standard marker into the Output.
     */
    public void markAll()
    {
        markAll(null);
    }

    /**
    Get the Singletone AMXDebug instance.
     */
    public static AMXDebug getInstance()
    {
        return INSTANCE;
    }

    /**
    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.
     */
    public File getOutputFile(final String id)
    {
        final String filename = makeSafeForFile(id) + AMX_DEBUG_SUFFIX;

        return new java.io.File(mDir, filename);
    }

    private WrapOutput _getOutput(final String id)
    {
        WrapOutput output = mOutputs.get(id);
        /*
        A "fast path" is important here not for speed, but to avoid the side-effects
        of taking a shared lock while debugging (which serializes threads), thus changing
        the very behavior that might be being debugged.  So we want to take a lock
        only if the WrapOutput must be created.
         */
        if (output == null)
        {
            // Did not exist at the time of the 'if' test above.
            // It might now exist; create a new WrapOutput optimistically.
            output = new WrapOutput(getOutputFile(id), mDefaultDebug);

            // retain existing output if already present
            // Note that ConcurrentHashMap guarantees "happens before" for put/get
            // so all fields of the WrapOutput will be visible to other threads
            final WrapOutput prev = mOutputs.putIfAbsent(id, output);
            if (prev != null)
            {
                output = prev;
            }
            else
            {
                debug("AMXDebug: Created output for " + StringUtil.quote(id));
            }
        }

        return output;
    }

    public Output getShared()
    {
        return getOutput("AMXDebug-Shared");
    }

    /**
    ID is typically a classname, but may be
    anything which can be used for a filename.  The
    id will be used to create file <id>.debug in the
    {@link #AMX_DEBUG_SUBDIR} directory.
     */
    public Output getOutput(final String id)
    {
        return _getOutput(id);
    }

    /**
    Get a form of the ID that is safe to for a filename.
     */
    private String makeSafeForFile(final String id)
    {
        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();
    }

    /**
    Internal class which wraps the Output so that
    debug may be dynamically enabled or disabled without any
    users of the Output having to be aware of it.
     */
    public final class WrapOutput implements Output
    {
        private volatile Output mWrapped;

        private final File mFile;

        // protected by synchronized in checkStatus() and reset();
        private Output mFileOutput;

        // can be changed at any time
        private volatile boolean mDebug;

        private WrapOutput(final File file, final boolean debug)
        {
            mDebug = debug;
            mWrapped = OutputIgnore.INSTANCE;
            mFile = file;
            mFileOutput = new FileOutput(file, mAppend);
            checkStatus();
        }

        public boolean getDebug()
        {
            return mDebug;
        }

        /**
        Change debug status.  If debug is <i>enabled any
        subsequent debugging messages will be written to their outputs,
        creating files if necessary.
        If debug is <i>disabled, all output to files ceases, and
        the files are closed.
         */
        public void setDebug(final boolean debug)
        {
            mDebug = debug;

            checkStatus();
        }

        public void print(final Object o)
        {
            mWrapped.print(o);
        }

        public void println(final Object o)
        {
            mWrapped.println(o);
        }

        public void printError(final Object o)
        {
            mWrapped.printError(o);
        }

        public void printDebug(final Object o)
        {
            mWrapped.printDebug(o);
        }

        public synchronized void reset()
        {
            // change output first!
            mWrapped = OutputIgnore.INSTANCE;

            // OK, now mFileOutput is not used
            mFileOutput.close();

            // the new one is lazy-opened...
            mFileOutput = new FileOutput(mFile);

            checkStatus();
        }

        public void close()
        {
            reset();
        }

        /**
        Switch between FileOutput and OutputIgnore
        if there is a mismatch.
         */
        private synchronized void checkStatus()
        {
            if (getDebug())
            {
                mWrapped = mFileOutput;
            }
            else
            {
                mWrapped.println("turning DEBUG OFF");
                mWrapped = OutputIgnore.INSTANCE;
            }
        }

    }

    public static String methodString(final String name, final Object... args)
    {
        return DebugOutImpl.methodString(name, args);
    }

}












Other Glassfish examples (source code examples)

Here is a short list of links related to this Glassfish AMXDebug.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.