001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017package org.apache.logging.log4j.util; 018 019import java.lang.reflect.Method; 020import java.util.Stack; 021 022import org.apache.logging.log4j.Logger; 023import org.apache.logging.log4j.status.StatusLogger; 024 025/** 026 * <em>Consider this class private.</em> Provides various methods to determine the caller class. 027 * <h3>Background</h3> 028 * <p>This method, available only in the Oracle/Sun/OpenJDK implementations of the Java 029 * Virtual Machine, is a much more efficient mechanism for determining the {@link Class} of the caller of a particular 030 * method. When it is not available, a {@link SecurityManager} is the second-best option. When this is also not 031 * possible, the {@code StackTraceElement[]} returned by {@link Throwable#getStackTrace()} must be used, and its 032 * {@code String} class name converted to a {@code Class} using the slow {@link Class#forName} (which can add an extra 033 * microsecond or more for each invocation depending on the runtime ClassLoader hierarchy). 034 * </p> 035 * <p> 036 * During Java 8 development, the {@code sun.reflect.Reflection.getCallerClass(int)} was removed from OpenJDK, and this 037 * 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 038 * by one stack frame. This turned out to be beneficial for the survival of this API as the change broke hundreds of 039 * libraries and frameworks relying on the API which brought much more attention to the intended API removal. 040 * </p> 041 * <p> 042 * After much community backlash, the JDK team agreed to restore {@code getCallerClass(int)} and keep its existing 043 * behavior for the rest of Java 7. However, the method is deprecated in Java 8, and current Java 9 development has not 044 * addressed this API. Therefore, the functionality of this class cannot be relied upon for all future versions of 045 * Java. It does, however, work just fine in Sun JDK 1.6, OpenJDK 1.6, Oracle/OpenJDK 1.7, and Oracle/OpenJDK 1.8. 046 * Other Java environments may fall back to using {@link Throwable#getStackTrace()} which is significantly slower due 047 * to examination of every virtual frame of execution. 048 * </p> 049 */ 050public final class ReflectionUtil { 051 052 private static final Logger LOGGER = StatusLogger.getLogger(); 053 054 private static final boolean SUN_REFLECTION_SUPPORTED; 055 private static final Method GET_CALLER_CLASS; 056 static final int JDK_7u25_OFFSET; 057 private static final PrivateSecurityManager SECURITY_MANAGER; 058 059 static { 060 Method getCallerClass; 061 int java7u25CompensationOffset = 0; 062 try { 063 final Class<?> sunReflectionClass = LoaderUtil.loadClass("sun.reflect.Reflection"); 064 getCallerClass = sunReflectionClass.getDeclaredMethod("getCallerClass", int.class); 065 Object o = getCallerClass.invoke(null, 0); 066 final Object test1 = getCallerClass.invoke(null, 0); 067 if (o == null || o != sunReflectionClass) { 068 LOGGER.warn("Unexpected return value from Reflection.getCallerClass(): {}", test1); 069 getCallerClass = null; 070 java7u25CompensationOffset = -1; 071 } else { 072 o = getCallerClass.invoke(null, 1); 073 if (o == sunReflectionClass) { 074 LOGGER.warn( 075 "You are using Java 1.7.0_25 which has a broken implementation of Reflection.getCallerClass."); 076 LOGGER.warn("You should upgrade to at least Java 1.7.0_40 or later."); 077 LOGGER.debug("Using stack depth compensation offset of 1 due to Java 7u25."); 078 java7u25CompensationOffset = 1; 079 } 080 } 081 } catch (final Exception e) { 082 LOGGER.info("sun.reflect.Reflection.getCallerClass is not supported. " + 083 "ReflectionUtil.getCallerClass will be much slower due to this.", e); 084 getCallerClass = null; 085 java7u25CompensationOffset = -1; 086 } 087 088 SUN_REFLECTION_SUPPORTED = getCallerClass != null; 089 GET_CALLER_CLASS = getCallerClass; 090 JDK_7u25_OFFSET = java7u25CompensationOffset; 091 092 PrivateSecurityManager psm; 093 try { 094 final SecurityManager sm = System.getSecurityManager(); 095 if (sm != null) { 096 sm.checkPermission(new RuntimePermission("createSecurityManager")); 097 } 098 psm = new PrivateSecurityManager(); 099 } catch (final SecurityException ignored) { 100 LOGGER.debug( 101 "Not allowed to create SecurityManager. Falling back to slowest ReflectionUtil implementation."); 102 psm = null; 103 } 104 SECURITY_MANAGER = psm; 105 } 106 107 public static boolean supportsFastReflection() { 108 return SUN_REFLECTION_SUPPORTED; 109 } 110 111 // TODO: return Object.class instead of null (though it will have a null ClassLoader) 112 // (MS) I believe this would work without any modifications elsewhere, but I could be wrong 113 114 // migrated from ReflectiveCallerClassUtility 115 public static Class<?> getCallerClass(final int depth) { 116 if (depth < 0) { 117 throw new IndexOutOfBoundsException(Integer.toString(depth)); 118 } 119 // note that we need to add 1 to the depth value to compensate for this method, but not for the Method.invoke 120 // since Reflection.getCallerClass ignores the call to Method.invoke() 121 if (supportsFastReflection()) { 122 try { 123 return (Class<?>) GET_CALLER_CLASS.invoke(null, depth + 1 + JDK_7u25_OFFSET); 124 } catch (final Exception e) { 125 // theoretically this could happen if the caller class were native code 126 LOGGER.error("Error in ReflectionUtil.getCallerClass({}).", depth, e); 127 // TODO: return Object.class 128 return null; 129 } 130 } 131 // TODO: SecurityManager-based version? 132 // slower fallback method using stack trace 133 final StackTraceElement element = getEquivalentStackTraceElement(depth + 1); 134 try { 135 return LoaderUtil.loadClass(element.getClassName()); 136 } catch (final ClassNotFoundException e) { 137 LOGGER.error("Could not find class in ReflectionUtil.getCallerClass({}).", depth, e); 138 } 139 // TODO: return Object.class 140 return null; 141 } 142 143 static StackTraceElement getEquivalentStackTraceElement(final int depth) { 144 // (MS) I tested the difference between using Throwable.getStackTrace() and Thread.getStackTrace(), and 145 // the version using Throwable was surprisingly faster! at least on Java 1.8. See ReflectionBenchmark. 146 final StackTraceElement[] elements = new Throwable().getStackTrace(); 147 int i = 0; 148 for (final StackTraceElement element : elements) { 149 if (isValid(element)) { 150 if (i == depth) { 151 return element; 152 } else { 153 ++i; 154 } 155 } 156 } 157 LOGGER.error("Could not find an appropriate StackTraceElement at index {}", depth); 158 throw new IndexOutOfBoundsException(Integer.toString(depth)); 159 } 160 161 private static boolean isValid(final StackTraceElement element) { 162 // ignore native methods (oftentimes are repeated frames) 163 if (element.isNativeMethod()) { 164 return false; 165 } 166 final String cn = element.getClassName(); 167 // ignore OpenJDK internal classes involved with reflective invocation 168 if (cn.startsWith("sun.reflect.")) { 169 return false; 170 } 171 final String mn = element.getMethodName(); 172 // ignore use of reflection including: 173 // Method.invoke 174 // InvocationHandler.invoke 175 // Constructor.newInstance 176 if (cn.startsWith("java.lang.reflect.") && (mn.equals("invoke") || mn.equals("newInstance"))) { 177 return false; 178 } 179 // ignore Class.newInstance 180 if (cn.equals("java.lang.Class") && mn.equals("newInstance")) { 181 return false; 182 } 183 // ignore use of Java 1.7+ MethodHandle.invokeFoo() methods 184 if (cn.equals("java.lang.invoke.MethodHandle") && mn.startsWith("invoke")) { 185 return false; 186 } 187 // any others? 188 return true; 189 } 190 191 // migrated from ClassLoaderContextSelector 192 public static Class<?> getCallerClass(final String fqcn) { 193 return getCallerClass(fqcn, Strings.EMPTY); 194 } 195 196 // migrated from Log4jLoggerFactory 197 public static Class<?> getCallerClass(final String fqcn, final String pkg) { 198 if (supportsFastReflection()) { 199 boolean next = false; 200 Class<?> clazz; 201 for (int i = 2; null != (clazz = getCallerClass(i)); i++) { 202 if (fqcn.equals(clazz.getName())) { 203 next = true; 204 continue; 205 } 206 if (next && clazz.getName().startsWith(pkg)) { 207 return clazz; 208 } 209 } 210 // TODO: return Object.class 211 return null; 212 } 213 if (SECURITY_MANAGER != null) { 214 return SECURITY_MANAGER.getCallerClass(fqcn, pkg); 215 } 216 try { 217 return LoaderUtil.loadClass(getCallerClassName(fqcn, pkg, new Throwable().getStackTrace())); 218 } catch (final ClassNotFoundException ignored) { 219 // no problem really 220 } 221 // TODO: return Object.class 222 return null; 223 } 224 225 // added for use in LoggerAdapter implementations mainly 226 public static Class<?> getCallerClass(final Class<?> anchor) { 227 if (supportsFastReflection()) { 228 boolean next = false; 229 Class<?> clazz; 230 for (int i = 2; null != (clazz = getCallerClass(i)); i++) { 231 if (anchor.equals(clazz)) { 232 next = true; 233 continue; 234 } 235 if (next) { 236 return clazz; 237 } 238 } 239 return Object.class; 240 } 241 if (SECURITY_MANAGER != null) { 242 return SECURITY_MANAGER.getCallerClass(anchor); 243 } 244 try { 245 return LoaderUtil.loadClass(getCallerClassName(anchor.getName(), Strings.EMPTY, 246 new Throwable().getStackTrace())); 247 } catch (final ClassNotFoundException ignored) { 248 // no problem really 249 } 250 return Object.class; 251 } 252 253 private static String getCallerClassName(final String fqcn, final String pkg, final StackTraceElement... elements) { 254 boolean next = false; 255 for (final StackTraceElement element : elements) { 256 final String className = element.getClassName(); 257 if (className.equals(fqcn)) { 258 next = true; 259 continue; 260 } 261 if (next && className.startsWith(pkg)) { 262 return className; 263 } 264 } 265 return Object.class.getName(); 266 } 267 268 // migrated from ThrowableProxy 269 public static Stack<Class<?>> getCurrentStackTrace() { 270 // benchmarks show that using the SecurityManager is much faster than looping through getCallerClass(int) 271 if (SECURITY_MANAGER != null) { 272 final Class<?>[] array = SECURITY_MANAGER.getClassContext(); 273 final Stack<Class<?>> classes = new Stack<Class<?>>(); 274 classes.ensureCapacity(array.length); 275 for (final Class<?> clazz : array) { 276 classes.push(clazz); 277 } 278 return classes; 279 } 280 // slower version using getCallerClass where we cannot use a SecurityManager 281 if (supportsFastReflection()) { 282 final Stack<Class<?>> classes = new Stack<Class<?>>(); 283 Class<?> clazz; 284 for (int i = 1; null != (clazz = getCallerClass(i)); i++) { 285 classes.push(clazz); 286 } 287 return classes; 288 } 289 return new Stack<Class<?>>(); 290 } 291 292 static final class PrivateSecurityManager extends SecurityManager { 293 294 @Override 295 protected Class<?>[] getClassContext() { 296 return super.getClassContext(); 297 } 298 299 protected Class<?> getCallerClass(final String fqcn, final String pkg) { 300 boolean next = false; 301 for (final Class<?> clazz : getClassContext()) { 302 if (fqcn.equals(clazz.getName())) { 303 next = true; 304 continue; 305 } 306 if (next && clazz.getName().startsWith(pkg)) { 307 return clazz; 308 } 309 } 310 // TODO: return Object.class 311 return null; 312 } 313 314 protected Class<?> getCallerClass(final Class<?> anchor) { 315 boolean next = false; 316 for (final Class<?> clazz : getClassContext()) { 317 if (anchor.equals(clazz)) { 318 next = true; 319 continue; 320 } 321 if (next) { 322 return clazz; 323 } 324 } 325 return Object.class; 326 } 327 328 } 329 330 private ReflectionUtil() { 331 } 332}