++ * This method, available only in the Oracle/Sun/OpenJDK implementations of the Java Virtual Machine, is a much more ++ * efficient mechanism for determining the {@link Class} of the caller of a particular method. When it is not available, ++ * a {@link SecurityManager} is the second-best option. When this is also not possible, the {@code StackTraceElement[]} ++ * returned by {@link Throwable#getStackTrace()} must be used, and its {@code String} class name converted to a ++ * {@code Class} using the slow {@link Class#forName} (which can add an extra microsecond or more for each invocation ++ * depending on the runtime ClassLoader hierarchy). ++ *
++ *++ * During Java 8 development, the {@code sun.reflect.Reflection.getCallerClass(int)} was removed from OpenJDK, and this ++ * change was back-ported to Java 7 in version 1.7.0_25 which changed the behavior of the call and caused it to be off ++ * by one stack frame. This turned out to be beneficial for the survival of this API as the change broke hundreds of ++ * libraries and frameworks relying on the API which brought much more attention to the intended API removal. ++ *
++ *++ * After much community backlash, the JDK team agreed to restore {@code getCallerClass(int)} and keep its existing ++ * behavior for the rest of Java 7. However, the method is deprecated in Java 8, and current Java 9 development has not ++ * addressed this API. Therefore, the functionality of this class cannot be relied upon for all future versions of Java. ++ * It does, however, work just fine in Sun JDK 1.6, OpenJDK 1.6, Oracle/OpenJDK 1.7, and Oracle/OpenJDK 1.8. Other Java ++ * environments may fall back to using {@link Throwable#getStackTrace()} which is significantly slower due to ++ * examination of every virtual frame of execution. ++ *
++ */ ++public final class StackLocator { ++ ++ // Checkstyle Suppress: the lower-case 'u' ticks off CheckStyle... ++ // CHECKSTYLE:OFF ++ static final int JDK_7u25_OFFSET; ++ // CHECKSTYLE:OFF ++ ++ private static final Method GET_CALLER_CLASS; ++ ++ private static final StackLocator INSTANCE; ++ ++ static { ++ Method getCallerClass; ++ int java7u25CompensationOffset = 0; ++ try { ++ final Class> sunReflectionClass = LoaderUtil.loadClass("sun.reflect.Reflection"); ++ getCallerClass = sunReflectionClass.getDeclaredMethod("getCallerClass", int.class); ++ Object o = getCallerClass.invoke(null, 0); ++ getCallerClass.invoke(null, 0); ++ if (o == null || o != sunReflectionClass) { ++ getCallerClass = null; ++ java7u25CompensationOffset = -1; ++ } else { ++ o = getCallerClass.invoke(null, 1); ++ if (o == sunReflectionClass) { ++ System.out.println("WARNING: Java 1.7.0_25 is in use which has a broken implementation of Reflection.getCallerClass(). " + ++ " Please consider upgrading to Java 1.7.0_40 or later."); ++ java7u25CompensationOffset = 1; ++ } ++ } ++ } catch (final Exception | LinkageError e) { ++ //System.out.println("WARNING: sun.reflect.Reflection.getCallerClass is not supported. This will impact performance."); // Yatopia - Fix Error Message ++ getCallerClass = null; ++ java7u25CompensationOffset = -1; ++ } ++ ++ GET_CALLER_CLASS = getCallerClass; ++ JDK_7u25_OFFSET = java7u25CompensationOffset; ++ ++ INSTANCE = new StackLocator(); ++ } ++ ++ public static StackLocator getInstance() { ++ return INSTANCE; ++ } ++ ++ private StackLocator() { ++ } ++ ++ // TODO: return Object.class instead of null (though it will have a null ClassLoader) ++ // (MS) I believe this would work without any modifications elsewhere, but I could be wrong ++ ++ // migrated from ReflectiveCallerClassUtility ++ @PerformanceSensitive ++ public Class> getCallerClass(final int depth) { ++ if (depth < 0) { ++ throw new IndexOutOfBoundsException(Integer.toString(depth)); ++ } ++ if (GET_CALLER_CLASS == null) { ++ return null; ++ } ++ // note that we need to add 1 to the depth value to compensate for this method, but not for the Method.invoke ++ // since Reflection.getCallerClass ignores the call to Method.invoke() ++ try { ++ return (Class>) GET_CALLER_CLASS.invoke(null, depth + 1 + JDK_7u25_OFFSET); ++ } catch (final Exception e) { ++ // theoretically this could happen if the caller class were native code ++ // TODO: return Object.class ++ return null; ++ } ++ } ++ ++ // migrated from Log4jLoggerFactory ++ @PerformanceSensitive ++ public Class> getCallerClass(final String fqcn, final String pkg) { ++ return getCallerClass(fqcn, pkg, 0); ++ } ++ ++ @PerformanceSensitive ++ public Class> getCallerClass(final String fqcn, final String pkg, final int skipDepth) { ++ if (skipDepth < 0) { ++ throw new IllegalArgumentException("skipDepth cannot be negative"); ++ } ++ boolean next = false; ++ Class> clazz; ++ for (int i = 2; null != (clazz = getCallerClass(i)); i++) { ++ if (fqcn.equals(clazz.getName())) { ++ next = true; ++ continue; ++ } ++ if (next && clazz.getName().startsWith(pkg)) { ++ return skipDepth == 0 ++ ? clazz ++ : getCallerClass(i + skipDepth); ++ } ++ } ++ // TODO: return Object.class ++ return null; ++ } ++ ++ // added for use in LoggerAdapter implementations mainly ++ @PerformanceSensitive ++ public Class> getCallerClass(final Class> anchor) { ++ boolean next = false; ++ Class> clazz; ++ for (int i = 2; null != (clazz = getCallerClass(i)); i++) { ++ if (anchor.equals(clazz)) { ++ next = true; ++ continue; ++ } ++ if (next) { ++ return clazz; ++ } ++ } ++ return Object.class; ++ } ++ ++ // migrated from ThrowableProxy ++ @PerformanceSensitive ++ public Stack