org.apache.brooklyn.util.javalang.StackTraceSimplifier.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.brooklyn.util.javalang.StackTraceSimplifier.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.brooklyn.util.javalang;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.apache.brooklyn.util.text.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.annotations.Beta;
import com.google.common.collect.ImmutableSet;

/**
 * Utility class for cleaning up stacktraces.
 */
public class StackTraceSimplifier {

    private static final Logger log = LoggerFactory.getLogger(StackTraceSimplifier.class);

    /** comma-separated prefixes (not regexes) */
    public static final String DEFAULT_BLACKLIST_SYSTEM_PROPERTY_NAME = "org.apache.brooklyn.util.javalang.StackTraceSimplifier.blacklist";

    /** @deprecated since 0.8.0 use {@link #DEFAULT_BLACKLIST_SYSTEM_PROPERTY_NAME} */
    @Deprecated
    public static final String LEGACY_DEFAULT_BLACKLIST_SYSTEM_PROPERTY_NAME = "brooklyn.util.javalang.StackTraceSimplifier.blacklist";

    private static final Collection<String> DEFAULT_BLACKLIST;

    static {
        ImmutableSet.Builder<String> blacklist = ImmutableSet.builder();
        blacklist.addAll(Arrays.asList(System
                .getProperty(DEFAULT_BLACKLIST_SYSTEM_PROPERTY_NAME,
                        "java.," + "javax.," + "sun.," + "groovy.," + "org.codehaus.groovy.," + "gjdk.groovy.,")
                .split("(\\s|,)+")));

        String legacyDefaults = System.getProperty(LEGACY_DEFAULT_BLACKLIST_SYSTEM_PROPERTY_NAME);
        if (Strings.isNonBlank(legacyDefaults)) {
            log.warn("Detected ude of legacy system property " + LEGACY_DEFAULT_BLACKLIST_SYSTEM_PROPERTY_NAME);
            blacklist.addAll(Arrays.asList(legacyDefaults.split("(\\s|,)+")));
        }

        DEFAULT_BLACKLIST = blacklist.build();
    }

    private static final StackTraceSimplifier DEFAULT_INSTACE = newInstance();

    private final Collection<String> blacklist;

    protected StackTraceSimplifier() {
        this(true);
    }

    protected StackTraceSimplifier(boolean includeDefaultBlacklist, String... packages) {
        ImmutableSet.Builder<String> blacklistB = ImmutableSet.builder();
        if (includeDefaultBlacklist)
            blacklistB.addAll(DEFAULT_BLACKLIST);
        blacklistB.add(packages);
        blacklist = blacklistB.build();
    }

    public static StackTraceSimplifier newInstance() {
        return new StackTraceSimplifier();
    }

    public static StackTraceSimplifier newInstance(String... additionalBlacklistPackagePrefixes) {
        return new StackTraceSimplifier(true, additionalBlacklistPackagePrefixes);
    }

    public static StackTraceSimplifier newInstanceExcludingOnly(String... blacklistPackagePrefixes) {
        return new StackTraceSimplifier(false, blacklistPackagePrefixes);
    }

    /** @return whether the given element is useful, that is, not in the blacklist */
    public boolean isUseful(StackTraceElement el) {
        for (String s : blacklist) {
            if (el.getClassName().startsWith(s))
                return false;
            ;
            // gets underscores in some contexts ?
            if (el.getClassName().replace('_', '.').startsWith(s))
                return false;
        }

        return true;
    }

    /** @return new list containing just the {@link #isUseful(StackTraceElement)} stack trace elements */
    public List<StackTraceElement> clean(Iterable<StackTraceElement> st) {
        List<StackTraceElement> result = new LinkedList<StackTraceElement>();
        for (StackTraceElement element : st) {
            if (isUseful(element)) {
                result.add(element);
            }
        }

        return result;
    }

    /** @return new array containing just the {@link #isUseful(StackTraceElement)} stack trace elements */
    public StackTraceElement[] clean(StackTraceElement[] st) {
        List<StackTraceElement> result = clean(Arrays.asList(st));
        return result.toArray(new StackTraceElement[result.size()]);
    }

    /** @return first {@link #isUseful(StackTraceElement)} stack trace elements, or null */
    public StackTraceElement firstUseful(StackTraceElement[] st) {
        return nthUseful(0, st);
    }

    /** @return (n+1)th {@link #isUseful(StackTraceElement)} stack trace elements (ie 0 is {@link #firstUseful(StackTraceElement[])}), or null */
    public StackTraceElement nthUseful(int n, StackTraceElement[] st) {
        for (StackTraceElement element : st) {
            if (isUseful(element)) {
                if (n == 0)
                    return element;
                n--;
            }
        }
        return null;
    }

    /** {@link #clean(StackTraceElement[])} the given throwable instance, returning the same instance for convenience */
    public <T extends Throwable> T cleaned(T t) {
        t.setStackTrace(clean(t.getStackTrace()));
        return t;
    }

    // ---- statics

    /** static convenience for {@link #isUseful(StackTraceElement)} */
    public static boolean isStackTraceElementUseful(StackTraceElement el) {
        return DEFAULT_INSTACE.isUseful(el);
    }

    /** static convenience for {@link #clean(Iterable)} */
    public static List<StackTraceElement> cleanStackTrace(Iterable<StackTraceElement> st) {
        return DEFAULT_INSTACE.clean(st);
    }

    /** static convenience for {@link #clean(StackTraceElement[])} */
    public static StackTraceElement[] cleanStackTrace(StackTraceElement[] st) {
        return DEFAULT_INSTACE.clean(st);
    }

    /** static convenience for {@link #cleaned(Throwable)} */
    public static <T extends Throwable> T cleanedStackTrace(T t) {
        return DEFAULT_INSTACE.cleaned(t);
    }

    public static String toString(Throwable t) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        t.printStackTrace(pw);
        return sw.getBuffer().toString();
    }

    /** returns the number of times the calling method occurs elsewhere in the stack trace;
     * 0 if no recursion, 1 if it has cycled three times, etc. */
    @Beta // useful to track down things like https://github.com/apache/incubator-brooklyn/pull/489
    public static int getRecursiveCallCount() {
        StackTraceElement[] t = cleanStackTrace(new Throwable().getStackTrace());
        Iterator<StackTraceElement> ti = Arrays.asList(t).iterator();
        ti.next();
        if (!ti.hasNext())
            return 0;
        // t0 is the caller
        StackTraceElement t0 = ti.next();
        String l0 = t0.getClassName() + "." + t0.getMethodName() + "(" + t0.getFileName() + ")";
        int count = 0;
        while (ti.hasNext()) {
            StackTraceElement ta = ti.next();
            String li = ta.getClassName() + "." + ta.getMethodName() + "(" + ta.getFileName() + ")";
            // if we have something in a different method, then something back in the method 
            // from which the recursive check came, then return true
            if (li.equals(l0))
                count++;
        }
        return count;
    }
}