com.google.gwt.dev.util.log.AbstractTreeLogger.java Source code

Java tutorial

Introduction

Here is the source code for com.google.gwt.dev.util.log.AbstractTreeLogger.java

Source

/*
 * Copyright 2007 Google Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.google.gwt.dev.util.log;

import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;

import java.util.HashSet;

/**
 * Abstract base class for TreeLoggers.
 */
public abstract class AbstractTreeLogger extends TreeLogger {

    private static class UncommittedBranchData {

        public final Throwable caught;
        public final String message;
        public final TreeLogger.Type type;
        private final HelpInfo helpInfo;

        public UncommittedBranchData(Type type, String message, Throwable caught, HelpInfo helpInfo) {
            this.caught = caught;
            this.message = message;
            this.type = type;
            this.helpInfo = helpInfo;
        }
    }

    // This message is package-protected so that the unit test can access it.
    static final String OUT_OF_MEMORY_MSG = "Out of memory; to increase the "
            + "amount of memory, use the -Xmx flag at startup (java -Xmx128M ...)";

    // This message is package-protected so that the unit test can access it.
    static final String STACK_OVERFLOW_MSG = "Stack overflow; to increase the "
            + "stack size, use the -Xss flag at startup (java -Xss1M ...)";

    public static String getStackTraceAsString(Throwable e) {
        // Show the exception info for anything other than "UnableToComplete".
        if (e == null || e instanceof UnableToCompleteException) {
            return null;
        }
        // For each cause, print the requested number of entries of its stack
        // trace, being careful to avoid getting stuck in an infinite loop.
        //
        StringBuffer message = new StringBuffer();
        Throwable currentCause = e;
        String causedBy = "";
        HashSet<Throwable> seenCauses = new HashSet<Throwable>();
        while (currentCause != null && !seenCauses.contains(currentCause)) {
            seenCauses.add(currentCause);

            message.append(causedBy);
            causedBy = "\nCaused by: "; // after 1st, all say "caused by"
            message.append(currentCause.getClass().getName());
            message.append(": " + currentCause.getMessage());
            StackTraceElement[] stackElems = currentCause.getStackTrace();
            if (stackElems != null) {
                for (int i = 0; i < stackElems.length; ++i) {
                    message.append("\n\tat ");
                    message.append(stackElems[i].toString());
                }
            }

            currentCause = currentCause.getCause();
        }
        return message.toString();
    }

    protected static String getExceptionName(Throwable e) {
        if (e == null || e instanceof UnableToCompleteException) {
            return null;
        }
        return e.getClass().getSimpleName();
    }

    protected TreeLogger.Type logLevel = TreeLogger.ALL;

    protected AbstractTreeLogger parent;

    private int indexWithinMyParent;

    private int nextChildIndex;

    private final Object nextChildIndexLock = new Object();

    private UncommittedBranchData uncommitted;

    /**
     * The constructor used when creating a top-level logger.
     */
    protected AbstractTreeLogger() {
    }

    /**
     * Implements branching behavior that supports lazy logging for low-priority
     * branched loggers.
     */
    @Override
    public final synchronized TreeLogger branch(TreeLogger.Type type, String msg, Throwable caught,
            HelpInfo helpInfo) {

        if (msg == null) {
            msg = "(Null branch message)";
        }

        // Compute at which index the new child will be placed.
        //
        int childIndex = allocateNextChildIndex();

        // The derived class creates the child logger.
        AbstractTreeLogger childLogger = doBranch();

        // Set up the child logger.
        //
        // Unsynchronized operations on childLogger are safe since no other
        // thread could have a reference to it yet.
        childLogger.logLevel = logLevel;

        // Take a snapshot of the index that the branched child should have.
        //
        childLogger.indexWithinMyParent = childIndex;

        // Have the child hang onto this (its parent logger).
        //
        childLogger.parent = this;

        // We can avoid committing this branch entry until and unless some
        // child (or grandchild) tries to log something that is loggable,
        // in which case there will be cascading commits of the parent branches.
        //
        childLogger.uncommitted = new UncommittedBranchData(type, msg, caught, helpInfo);

        // This logic is intertwined with log(). If a log message is associated
        // with a special error condition, then we turn it into a branch,
        // so this method can be called directly from log(). It is of course
        // also possible for someone to call branch() directly. In either case, we
        // (1) turn the original message into an ERROR and
        // (2) drop an extra log message that explains how to recover
        String specialErrorMessage = causedBySpecialError(caught);
        if (specialErrorMessage != null) {
            type = TreeLogger.ERROR;
            childLogger.log(type, specialErrorMessage, null);
        }

        // Decide whether we want to log the branch message eagerly or lazily.
        //
        if (isLoggable(type)) {
            // We can commit this branch entry eagerly since it is a-priori loggable.
            // Commit the parent logger if necessary before continuing.
            //
            childLogger.commitMyBranchEntryInMyParentLogger();
        }

        return childLogger;
    }

    public final int getBranchedIndex() {
        return indexWithinMyParent;
    }

    public final synchronized TreeLogger.Type getMaxDetail() {
        return logLevel;
    }

    public final AbstractTreeLogger getParentLogger() {
        return parent;
    }

    @Override
    public final synchronized boolean isLoggable(TreeLogger.Type type) {
        return !type.isLowerPriorityThan(logLevel);
    }

    /**
     * Immediately logs or ignores the specified messages, based on the specified
     * message type and this logger's settings. If the message is loggable, then
     * parent branches may be lazily created before the log can take place.
     */
    @Override
    public final synchronized void log(TreeLogger.Type type, String msg, Throwable caught, HelpInfo helpInfo) {

        if (msg == null) {
            msg = "(Null log message)";
        }

        // If this log message is caused by out of memory or stack overflow, we
        // provide a little extra help by creating a child log message.
        if (causedBySpecialError(caught) != null) {
            branch(TreeLogger.ERROR, msg, caught);
            return;
        }

        int childIndex = allocateNextChildIndex();
        if (isLoggable(type)) {
            commitMyBranchEntryInMyParentLogger();
            doLog(childIndex, type, msg, caught, helpInfo);
        }
    }

    /**
     * @param type the log type representing the most detailed level of logging
     *          that the caller is interested in, or <code>null</code> to choose
     *          the default level.
     */
    public final synchronized void setMaxDetail(TreeLogger.Type type) {
        if (type == null) {
            type = TreeLogger.INFO;
        }
        logLevel = type;
    }

    @Override
    public String toString() {
        return getLoggerId();
    }

    protected int allocateNextChildIndex() {
        synchronized (nextChildIndexLock) {
            // postincrement because we want indices to start at 0
            return nextChildIndex++;
        }
    }

    /**
     * Commits the branch after ensuring that the parent logger (if there is one)
     * has been committed first.
     */
    protected synchronized void commitMyBranchEntryInMyParentLogger() {
        // (Only the root logger doesn't have a parent.)
        //
        if (parent != null) {
            if (uncommitted != null) {
                // Commit the parent first.
                //
                parent.commitMyBranchEntryInMyParentLogger();

                // Let the subclass do its thing to commit this branch.
                //
                parent.doCommitBranch(this, uncommitted.type, uncommitted.message, uncommitted.caught,
                        uncommitted.helpInfo);

                // Release the uncommitted state.
                //
                uncommitted = null;
            }
        }
    }

    /**
     * Derived classes should override this method to return a branched logger.
     */
    protected abstract AbstractTreeLogger doBranch();

    /**
     * @deprecated This method has been deprecated; override
     *             {@link #doCommitBranch(AbstractTreeLogger, com.google.gwt.core.ext.TreeLogger.Type, String, Throwable, com.google.gwt.core.ext.TreeLogger.HelpInfo)}
     *             instead.
     * 
     * @param childBeingCommitted
     * @param type
     * @param msg
     * @param caught
     */
    @Deprecated
    protected final void doCommitBranch(AbstractTreeLogger childBeingCommitted, TreeLogger.Type type, String msg,
            Throwable caught) {
    }

    /**
     * Derived classes should override this method to actually commit the
     * specified message associated with this the root of this branch.
     */
    protected abstract void doCommitBranch(AbstractTreeLogger childBeingCommitted, TreeLogger.Type type, String msg,
            Throwable caught, HelpInfo helpInfo);

    /**
     * @deprecated This method has been deprecated; override
     *             {@link #branch(com.google.gwt.core.ext.TreeLogger.Type, String, Throwable, com.google.gwt.core.ext.TreeLogger.HelpInfo)
     *             instead.
     * 
     * @param indexOfLogEntryWithinParentLogger
     * @param type
     * @param msg
     * @param caught
     */
    @Deprecated
    protected final void doLog(int indexOfLogEntryWithinParentLogger, TreeLogger.Type type, String msg,
            Throwable caught) {
    }

    /**
     * Derived classes should override this method to actually write a log
     * message. Note that {@link #isLoggable(TreeLogger.Type)} will have already
     * been called.
     */
    protected abstract void doLog(int indexOfLogEntryWithinParentLogger, TreeLogger.Type type, String msg,
            Throwable caught, HelpInfo helpInfo);

    /**
     * Scans <code>t</code> and its causes for {@link OutOfMemoryError} or
     * {@link StackOverflowError}.
     * 
     * @param t a possibly null {@link Throwable}
     * @return true if {@link OutOfMemoryError} or {@link StackOverflowError}
     *         appears anywhere in the cause list or if <code>t</code> is an
     *         {@link OutOfMemoryError} or {@link StackOverflowError.
     */
    private String causedBySpecialError(Throwable t) {
        while (t != null) {
            if (t instanceof OutOfMemoryError) {
                return OUT_OF_MEMORY_MSG;
            } else if (t instanceof StackOverflowError) {
                return STACK_OVERFLOW_MSG;
            }
            t = t.getCause();
        }
        return null;
    }

    private String getLoggerId() {
        if (parent != null) {
            if (parent.parent == null) {
                // Top-level
                return parent.getLoggerId() + getBranchedIndex();
            } else {
                // Nested
                return parent.getLoggerId() + "." + getBranchedIndex();
            }
        } else {
            // The root
            return "#";
        }
    }
}