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     */
017    package org.apache.logging.log4j.util;
018    
019    import java.lang.reflect.Method;
020    import java.util.Stack;
021    
022    import org.apache.logging.log4j.Logger;
023    import 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     */
050    public 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    }