All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.atlassian.bamboo.specs.maven.sandbox.StackTraceUtils Maven / Gradle / Ivy

There is a newer version: 10.1.0
Show newest version
package com.atlassian.bamboo.specs.maven.sandbox;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.net.URISyntaxException;
import java.security.CodeSource;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

final class StackTraceUtils {
    private static final Map CODESOURCES = new HashMap<>();

    private StackTraceUtils() {
    }

    /**
     * Returns the caller stack to a point of last call of a method on the supplied class.
     * For example if A calls B that calls B that calls C, if we call the method with B, we will get A in return.
     * If we call it with C, we will get A and B in return.
     */
    @Nullable
    static StackTraceElement[] getCallerStack(final Class... beforeClasses) {
        return getCallerStack(0, beforeClasses);
    }

    /**
     * Returns the caller stack to a point of last call of a method on the supplied class.
     * For example if A calls B that calls B that calls C, if we call the method with B, we will get A in return.
     * If we call it with C, we will get A and B in return.
     * @param skipTopN number of top stack element to ignore. Useful when scanning for uses of Thread, but not necessarily Thread.run() that started the current thread.
     * @param beforeClasses classes calls to which look for
     */
    @Nullable
    static StackTraceElement[] getCallerStack(int skipTopN, final Class... beforeClasses) {
        final Set classNames = Arrays.stream(beforeClasses).map(Class::getName).collect(Collectors.toSet());
        final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        int classFound = -1;
        for (int i = 0; i < stackTrace.length; ++i) {
            if (classNames.contains(stackTrace[i].getClassName())) {
                classFound = i;
            } else if (classFound >= skipTopN){
                break;
            }
        }

        return classFound >= skipTopN ? Arrays.copyOfRange(stackTrace, classFound + 1, stackTrace.length - 1) : null;
    }

    /**
     * Returns the locations from which the classes of the supplied stack traces were loaded, assuming that they were loaded
     * using the supplied class loader.
     */
    @NotNull
    static Set getClassLocationsOnStack(final ClassLoader classLoader, final StackTraceElement[] callerStackTrace) {
        final Set jars = new HashSet<>();
        for (final StackTraceElement stackTraceElement : callerStackTrace) {
            jars.add(classNameToJar(classLoader, stackTraceElement.getClassName()));
        }

        return jars;
    }

    private static File classNameToJar(final ClassLoader classLoader, final String className) {
        return CODESOURCES.computeIfAbsent(className, classNameToJar(classLoader));
    }

    @NotNull
    private static Function classNameToJar(final ClassLoader classLoader) {
        return className -> {
            try {
                final Class aClass = classLoader.loadClass(className);
                final CodeSource codeSource = aClass.getProtectionDomain().getCodeSource();
                if (codeSource == null) {
                    throw new NullPointerException("Unknown code source for " + aClass);
                }
                return new File(codeSource.getLocation().toURI());
            } catch (final ClassNotFoundException | URISyntaxException e) {
                throw new RuntimeException(e);
            }
        };
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy