You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by ma...@apache.org on 2014/09/05 23:54:32 UTC
[1/4] git commit: Add unified caller class utility class.
Repository: logging-log4j2
Updated Branches:
refs/heads/LOG4J2-809 0e9e33156 -> 260b903b0
Add unified caller class utility class.
- Based on ReflectiveCallerClassUtility.
- Adds getCallerClass(String) which is primarily used by ClassLoaderContextSelector.
- Adds getCurrentStackTrace() which is ported from ThrowableProxy.
- Adds a rather complete implementation of getCallerClass(int) based on Throwable.
- Unit tests.
- Updates benchmark tests accordingly.
- Will also help us maintain future compatibility with JDK 9 and beyond (hooray for tests!)
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/3d76316a
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/3d76316a
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/3d76316a
Branch: refs/heads/LOG4J2-809
Commit: 3d76316ab85330ae0c36f56c7bf3c287f156f51c
Parents: 0e9e331
Author: Matt Sicker <ma...@apache.org>
Authored: Fri Sep 5 16:52:29 2014 -0500
Committer: Matt Sicker <ma...@apache.org>
Committed: Fri Sep 5 16:52:48 2014 -0500
----------------------------------------------------------------------
.../logging/log4j/util/ReflectionUtil.java | 276 +++++++++++++++++++
.../logging/log4j/util/ReflectionUtilTest.java | 94 +++++++
.../log4j/perf/jmh/ReflectionBenchmark.java | 6 +-
3 files changed, 373 insertions(+), 3 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/3d76316a/log4j-api/src/main/java/org/apache/logging/log4j/util/ReflectionUtil.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/ReflectionUtil.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/ReflectionUtil.java
new file mode 100644
index 0000000..c9e010b
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/ReflectionUtil.java
@@ -0,0 +1,276 @@
+/*
+ * 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.logging.log4j.util;
+
+import java.lang.reflect.Method;
+import java.util.Stack;
+
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.status.StatusLogger;
+
+/**
+ * <em>Consider this class private.</em> Provides various methods to determine the caller class.
+ * <h3>Background</h3>
+ * <p>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).
+ * </p>
+ * <p>
+ * 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.
+ * </p>
+ * <p>
+ * 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.
+ * </p>
+ */
+public final class ReflectionUtil {
+
+ private static final Logger LOGGER = StatusLogger.getLogger();
+
+ private static final boolean SUN_REFLECTION_SUPPORTED;
+ private static final Method GET_CALLER_CLASS;
+ static final int JDK_7u25_OFFSET;
+ private static final PrivateSecurityManager SECURITY_MANAGER;
+
+ static {
+ Method getCallerClass;
+ int java7u25CompensationOffset = 0;
+ try {
+ final Class<?> sunReflectionClass = LoaderUtil.getThreadContextClassLoader().loadClass(
+ "sun.reflect.Reflection");
+ getCallerClass = sunReflectionClass.getDeclaredMethod("getCallerClass", int.class);
+ Object o = getCallerClass.invoke(null, 0);
+ final Object test1 = getCallerClass.invoke(null, 0);
+ if (o == null || o != sunReflectionClass) {
+ LOGGER.warn("Unexpected return value from Reflection.getCallerClass(): {}", test1);
+ getCallerClass = null;
+ java7u25CompensationOffset = -1;
+ } else {
+ o = getCallerClass.invoke(null, 1);
+ if (o == sunReflectionClass) {
+ LOGGER.warn(
+ "You are using Java 1.7.0_25 which has a broken implementation of Reflection.getCallerClass.");
+ LOGGER.warn("You should upgrade to at least Java 1.7.0_40 or later.");
+ LOGGER.debug("Using stack depth compensation offset of 1 due to Java 7u25.");
+ java7u25CompensationOffset = 1;
+ }
+ }
+ } catch (final Exception e) {
+ LOGGER.info("sun.reflect.Reflection.getCallerClass is not supported. " +
+ "ReflectionUtil.getCallerClass will be much slower due to this.", e);
+ getCallerClass = null;
+ java7u25CompensationOffset = -1;
+ }
+
+ SUN_REFLECTION_SUPPORTED = getCallerClass != null;
+ GET_CALLER_CLASS = getCallerClass;
+ JDK_7u25_OFFSET = java7u25CompensationOffset;
+
+ PrivateSecurityManager psm;
+ if (!SUN_REFLECTION_SUPPORTED) {
+ try {
+ final SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(new RuntimePermission("createSecurityManager"));
+ }
+ psm = new PrivateSecurityManager();
+ } catch (final SecurityException ignored) {
+ LOGGER.debug(
+ "Not allowed to create SecurityManager. Falling back to slowest ReflectionUtil implementation.");
+ psm = null;
+ }
+ } else {
+ psm = null;
+ }
+ SECURITY_MANAGER = psm;
+ }
+
+ public static boolean supportsFastReflection() {
+ return SUN_REFLECTION_SUPPORTED;
+ }
+
+ // migrated from ReflectiveCallerClassUtility
+ public static Class<?> getCallerClass(final int depth) {
+ if (depth < 0) {
+ throw new IndexOutOfBoundsException(Integer.toString(depth));
+ }
+ // 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()
+ if (supportsFastReflection()) {
+ 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
+ LOGGER.error("Error in ReflectionUtil.getCallerClass({}).", depth, e);
+ return null;
+ }
+ }
+ // TODO: SecurityManager-based version?
+ // slower fallback method using stack trace
+ final StackTraceElement element = getEquivalentStackTraceElement(depth + 1);
+ final ClassLoader cl = LoaderUtil.getThreadContextClassLoader();
+ try {
+ return cl.loadClass(element.getClassName());
+ } catch (final ClassNotFoundException e) {
+ LOGGER.error("Could not find class in ReflectionUtil.getCallerClass({}).", depth, e);
+ }
+ return null;
+ }
+
+ static StackTraceElement getEquivalentStackTraceElement(final int depth) {
+ // (MS) I tested the difference between using Throwable.getStackTrace() and Thread.getStackTrace(), and
+ // the version using Throwable was surprisingly faster! at least on Java 1.8. See ReflectionBenchmark.
+ final StackTraceElement[] elements = new Throwable().getStackTrace();
+ int i = 0;
+ for (final StackTraceElement element : elements) {
+ if (isValid(element)) {
+ if (i == depth) {
+ return element;
+ } else {
+ ++i;
+ }
+ }
+ }
+ LOGGER.error("Could not find an appropriate StackTraceElement at index {}", depth);
+ throw new IndexOutOfBoundsException(Integer.toString(depth));
+ }
+
+ private static boolean isValid(final StackTraceElement element) {
+ // ignore native methods (oftentimes are repeated frames)
+ if (element.isNativeMethod()) {
+ return false;
+ }
+ final String cn = element.getClassName();
+ // ignore OpenJDK internal classes involved with reflective invocation
+ if (cn.startsWith("sun.reflect.")) {
+ return false;
+ }
+ final String mn = element.getMethodName();
+ // ignore use of reflection including:
+ // Method.invoke
+ // InvocationHandler.invoke
+ // Constructor.newInstance
+ if (cn.startsWith("java.lang.reflect.") && (mn.equals("invoke") || mn.equals("newInstance"))) {
+ return false;
+ }
+ // ignore Class.newInstance
+ if (cn.equals("java.lang.Class") && mn.equals("newInstance")) {
+ return false;
+ }
+ // ignore use of Java 1.7+ MethodHandle.invokeFoo() methods
+ if (cn.equals("java.lang.invoke.MethodHandle") && mn.startsWith("invoke")) {
+ return false;
+ }
+ // any others?
+ return true;
+ }
+
+ // migrated from ClassLoaderContextSelector
+ public static Class<?> getCallerClass(final String fqcn) {
+ if (supportsFastReflection()) {
+ boolean next = false;
+ Class<?> clazz;
+ for (int i = 2; null != (clazz = getCallerClass(i)); i++) {
+ if (fqcn.equals(clazz.getName())) {
+ next = true;
+ continue;
+ }
+ if (next) {
+ return clazz;
+ }
+ }
+ return null;
+ }
+ if (SECURITY_MANAGER != null) {
+ return SECURITY_MANAGER.getCallerClass(fqcn);
+ }
+ boolean next = false;
+ final StackTraceElement[] elements = new Throwable().getStackTrace();
+ try {
+ for (final StackTraceElement element : elements) {
+ final String className = element.getClassName();
+ if (className.equals(fqcn)) {
+ next = true;
+ continue;
+ }
+ if (next) {
+ return LoaderUtil.getThreadContextClassLoader().loadClass(className);
+ }
+ }
+ } catch (final ClassNotFoundException ignored) {
+ // no problem really
+ }
+ return null;
+ }
+
+ // migrated from ThrowableProxy
+ public static Stack<Class<?>> getCurrentStackTrace() {
+ if (supportsFastReflection()) {
+ final Stack<Class<?>> classes = new Stack<Class<?>>();
+ Class<?> clazz;
+ for (int i = 1; null != (clazz = getCallerClass(i)); i++) {
+ classes.push(clazz);
+ }
+ return classes;
+ } else if (SECURITY_MANAGER != null) {
+ final Class<?>[] array = SECURITY_MANAGER.getClassContext();
+ final Stack<Class<?>> classes = new Stack<Class<?>>();
+ for (final Class<?> clazz : array) {
+ classes.push(clazz);
+ }
+ return classes;
+ }
+ return new Stack<Class<?>>();
+ }
+
+ static final class PrivateSecurityManager extends SecurityManager {
+
+ @Override
+ protected Class<?>[] getClassContext() {
+ return super.getClassContext();
+ }
+
+ protected Class<?> getCallerClass(final String fqcn) {
+ final Class<?>[] classes = getClassContext();
+ boolean next = false;
+ for (final Class<?> clazz : classes) {
+ if (fqcn.equals(clazz.getName())) {
+ next = true;
+ continue;
+ }
+ if (next) {
+ return clazz;
+ }
+ }
+ return null;
+ }
+
+ }
+
+ private ReflectionUtil() {
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/3d76316a/log4j-api/src/test/java/org/apache/logging/log4j/util/ReflectionUtilTest.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/util/ReflectionUtilTest.java b/log4j-api/src/test/java/org/apache/logging/log4j/util/ReflectionUtilTest.java
new file mode 100644
index 0000000..95fb0ad
--- /dev/null
+++ b/log4j-api/src/test/java/org/apache/logging/log4j/util/ReflectionUtilTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.logging.log4j.util;
+
+import java.util.Stack;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runners.BlockJUnit4ClassRunner;
+import sun.reflect.Reflection;
+
+import static org.junit.Assert.*;
+import static org.junit.Assume.assumeTrue;
+
+public class ReflectionUtilTest {
+
+ @Before
+ public void setUp() throws Exception {
+ assumeTrue(ReflectionUtil.supportsFastReflection());
+ }
+
+ @Test
+ public void testSunReflectionEquivalence() throws Exception {
+ // can't start at 0 because Reflection != ReflectionUtil
+ for (int i = 1; i < 6; i++) {
+ assertSame(
+ Reflection.getCallerClass(i + ReflectionUtil.JDK_7u25_OFFSET),
+ ReflectionUtil.getCallerClass(i)
+ );
+ }
+ }
+
+ @Test
+ public void testStackTraceEquivalence() throws Exception {
+ for (int i = 1; i < 15; i++) {
+ final Class<?> expected = Reflection.getCallerClass(i + ReflectionUtil.JDK_7u25_OFFSET);
+ final Class<?> actual = ReflectionUtil.getCallerClass(i);
+ final Class<?> fallbackActual = Class.forName(
+ ReflectionUtil.getEquivalentStackTraceElement(i).getClassName());
+ assertSame(expected, actual);
+ assertSame(expected, fallbackActual);
+ }
+ }
+
+ @Test
+ public void testGetCallerClass() throws Exception {
+ final Class<?> expected = ReflectionUtilTest.class;
+ final Class<?> actual = ReflectionUtil.getCallerClass(1);
+ assertSame(expected, actual);
+ }
+
+ @Test
+ public void testGetCallerClassNameViaStackTrace() throws Exception {
+ final Class<?> expected = ReflectionUtilTest.class;
+ final Class<?> actual = Class.forName(new Throwable().getStackTrace()[0].getClassName());
+ assertSame(expected, actual);
+ }
+
+ @Test
+ public void testGetCurrentStackTrace() throws Exception {
+ final Stack<Class<?>> classes = ReflectionUtil.getCurrentStackTrace();
+ Class<?> prev = null;
+ Class<?> next = null;
+ while (!classes.empty()) {
+ prev = next;
+ next = classes.pop();
+// System.out.println(next);
+ }
+ assertSame(ReflectionUtilTest.class, prev);
+ }
+
+ @Test
+ public void testGetCallerClassViaName() throws Exception {
+ final Class<?> expected = BlockJUnit4ClassRunner.class;
+ final Class<?> actual = ReflectionUtil.getCallerClass("org.junit.runners.ParentRunner");
+ // if this test fails in the future, it's probably because of a JUnit upgrade; check the new stack trace and
+ // update this test accordingly
+ assertSame(expected, actual);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/3d76316a/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/ReflectionBenchmark.java
----------------------------------------------------------------------
diff --git a/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/ReflectionBenchmark.java b/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/ReflectionBenchmark.java
index 2d98f6b..21c8551 100644
--- a/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/ReflectionBenchmark.java
+++ b/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/ReflectionBenchmark.java
@@ -20,9 +20,9 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Random;
-import org.apache.logging.log4j.core.impl.ReflectiveCallerClassUtility;
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.message.StringFormattedMessage;
+import org.apache.logging.log4j.util.ReflectionUtil;
import org.openjdk.jmh.annotations.GenerateMicroBenchmark;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Scope;
@@ -72,7 +72,7 @@ public class ReflectionBenchmark {
@GenerateMicroBenchmark
public String test03_getCallerClassNameReflectively() {
- return ReflectiveCallerClassUtility.getCaller(3).getName();
+ return ReflectionUtil.getCallerClass(3).getName();
}
@GenerateMicroBenchmark
@@ -92,7 +92,7 @@ public class ReflectionBenchmark {
@GenerateMicroBenchmark
public Class<?> test07_getReflectiveCallerClassUtility() {
- return ReflectiveCallerClassUtility.getCaller(3);
+ return ReflectionUtil.getCallerClass(3);
}
@GenerateMicroBenchmark
[3/4] git commit: Use ReflectionUtil.getCallerClass(String)
Posted by ma...@apache.org.
Use ReflectionUtil.getCallerClass(String)
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/cbf0f729
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/cbf0f729
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/cbf0f729
Branch: refs/heads/LOG4J2-809
Commit: cbf0f729d2dfd93220217afa0a5f5c70d013f31f
Parents: 066fb89
Author: Matt Sicker <ma...@apache.org>
Authored: Fri Sep 5 16:54:06 2014 -0500
Committer: Matt Sicker <ma...@apache.org>
Committed: Fri Sep 5 16:54:06 2014 -0500
----------------------------------------------------------------------
.../selector/ClassLoaderContextSelector.java | 102 +------------------
1 file changed, 4 insertions(+), 98 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/cbf0f729/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/ClassLoaderContextSelector.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/ClassLoaderContextSelector.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/ClassLoaderContextSelector.java
index 2e3d93e..fa9a6e2 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/ClassLoaderContextSelector.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/ClassLoaderContextSelector.java
@@ -29,9 +29,8 @@ import java.util.concurrent.atomic.AtomicReference;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.impl.ContextAnchor;
-import org.apache.logging.log4j.core.impl.ReflectiveCallerClassUtility;
-import org.apache.logging.log4j.core.util.Loader;
import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.ReflectionUtil;
/**
* This ContextSelector chooses a LoggerContext based upon the ClassLoader of the caller. This allows Loggers
@@ -48,33 +47,11 @@ public class ClassLoaderContextSelector implements ContextSelector {
private static final AtomicReference<LoggerContext> CONTEXT = new AtomicReference<LoggerContext>();
- private static final PrivateSecurityManager SECURITY_MANAGER;
-
private static final StatusLogger LOGGER = StatusLogger.getLogger();
private static final ConcurrentMap<String, AtomicReference<WeakReference<LoggerContext>>> CONTEXT_MAP =
new ConcurrentHashMap<String, AtomicReference<WeakReference<LoggerContext>>>();
- static {
- if (ReflectiveCallerClassUtility.isSupported()) {
- SECURITY_MANAGER = null;
- } else {
- PrivateSecurityManager securityManager;
- try {
- securityManager = new PrivateSecurityManager();
- if (securityManager.getCaller(ClassLoaderContextSelector.class.getName()) == null) {
- // This shouldn't happen.
- securityManager = null;
- LOGGER.error("Unable to obtain call stack from security manager.");
- }
- } catch (final Exception e) {
- securityManager = null;
- LOGGER.debug("Unable to install security manager", e);
- }
- SECURITY_MANAGER = securityManager;
- }
- }
-
@Override
public LoggerContext getContext(final String fqcn, final ClassLoader loader, final boolean currentContext) {
return getContext(fqcn, loader, currentContext, null);
@@ -92,59 +69,9 @@ public class ClassLoaderContextSelector implements ContextSelector {
} else if (loader != null) {
return locateContext(loader, configLocation);
} else {
- if (ReflectiveCallerClassUtility.isSupported()) {
- try {
- Class<?> clazz = Class.class;
- boolean next = false;
- for (int index = 2; clazz != null; ++index) {
- clazz = ReflectiveCallerClassUtility.getCaller(index);
- if (clazz == null) {
- break;
- }
- if (clazz.getName().equals(fqcn)) {
- next = true;
- continue;
- }
- if (next) {
- break;
- }
- }
- if (clazz != null) {
- return locateContext(clazz.getClassLoader(), configLocation);
- }
- } catch (final Exception ex) {
- // logger.debug("Unable to determine caller class via Sun Reflection", ex);
- }
- }
-
- if (SECURITY_MANAGER != null) {
- final Class<?> clazz = SECURITY_MANAGER.getCaller(fqcn);
- if (clazz != null) {
- final ClassLoader ldr = clazz.getClassLoader() != null ? clazz.getClassLoader() :
- ClassLoader.getSystemClassLoader();
- return locateContext(ldr, configLocation);
- }
- }
-
- final Throwable t = new Throwable();
- boolean next = false;
- String name = null;
- for (final StackTraceElement element : t.getStackTrace()) {
- if (element.getClassName().equals(fqcn)) {
- next = true;
- continue;
- }
- if (next) {
- name = element.getClassName();
- break;
- }
- }
- if (name != null) {
- try {
- return locateContext(Loader.loadClass(name).getClassLoader(), configLocation);
- } catch (final ClassNotFoundException ignore) {
- //this is ok
- }
+ final Class<?> clazz = ReflectionUtil.getCallerClass(fqcn);
+ if (clazz != null) {
+ return locateContext(clazz.getClassLoader(), configLocation);
}
final LoggerContext lc = ContextAnchor.THREAD_CONTEXT.get();
if (lc != null) {
@@ -250,25 +177,4 @@ public class ClassLoaderContextSelector implements ContextSelector {
return CONTEXT.get();
}
- /**
- * SecurityManager that will locate the caller of the Log4j 2 API.
- */
- private static class PrivateSecurityManager extends SecurityManager {
-
- public Class<?> getCaller(final String fqcn) {
- final Class<?>[] classes = getClassContext();
- boolean next = false;
- for (final Class<?> clazz : classes) {
- if (clazz.getName().equals(fqcn)) {
- next = true;
- continue;
- }
- if (next) {
- return clazz;
- }
- }
- return null;
- }
- }
-
}
[2/4] git commit: Use ReflectionUtil.getCurrentStackTrace()
Posted by ma...@apache.org.
Use ReflectionUtil.getCurrentStackTrace()
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/066fb893
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/066fb893
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/066fb893
Branch: refs/heads/LOG4J2-809
Commit: 066fb8936943cae9631413625290e9e0fdd17c97
Parents: 3d76316
Author: Matt Sicker <ma...@apache.org>
Authored: Fri Sep 5 16:53:20 2014 -0500
Committer: Matt Sicker <ma...@apache.org>
Committed: Fri Sep 5 16:53:20 2014 -0500
----------------------------------------------------------------------
.../logging/log4j/core/impl/ThrowableProxy.java | 64 +-------------------
1 file changed, 2 insertions(+), 62 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/066fb893/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThrowableProxy.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThrowableProxy.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThrowableProxy.java
index 2320ad7..8d6f9f3 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThrowableProxy.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThrowableProxy.java
@@ -25,10 +25,10 @@ import java.util.List;
import java.util.Map;
import java.util.Stack;
-import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.util.Loader;
import org.apache.logging.log4j.core.util.Throwables;
import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.ReflectionUtil;
import org.apache.logging.log4j.util.Strings;
/**
@@ -61,45 +61,12 @@ public class ThrowableProxy implements Serializable {
}
}
- /**
- * Security Manager for accessing the call stack.
- */
- private static class PrivateSecurityManager extends SecurityManager {
- public Class<?>[] getClasses() {
- return this.getClassContext();
- }
- }
-
private static final ThrowableProxy[] EMPTY_THROWABLE_PROXY_ARRAY = new ThrowableProxy[0];
private static final char EOL = '\n';
- private static final Logger LOGGER = StatusLogger.getLogger();
-
- private static final PrivateSecurityManager SECURITY_MANAGER;
-
private static final long serialVersionUID = -2752771578252251910L;
- static {
- if (ReflectiveCallerClassUtility.isSupported()) {
- SECURITY_MANAGER = null;
- } else {
- PrivateSecurityManager securityManager;
- try {
- securityManager = new PrivateSecurityManager();
- if (securityManager.getClasses() == null) {
- // This shouldn't happen.
- securityManager = null;
- LOGGER.error("Unable to obtain call stack from security manager.");
- }
- } catch (final Exception e) {
- securityManager = null;
- LOGGER.debug("Unable to install security manager.", e);
- }
- SECURITY_MANAGER = securityManager;
- }
- }
-
private final ThrowableProxy causeProxy;
private int commonElementCount;
@@ -142,7 +109,7 @@ public class ThrowableProxy implements Serializable {
this.message = throwable.getMessage();
this.localizedMessage = throwable.getLocalizedMessage();
final Map<String, CacheEntry> map = new HashMap<String, CacheEntry>();
- final Stack<Class<?>> stack = this.getCurrentStack();
+ final Stack<Class<?>> stack = ReflectionUtil.getCurrentStackTrace();
this.extendedStackTrace = this.toExtendedStackTrace(stack, map, null, throwable.getStackTrace());
final Throwable throwableCause = throwable.getCause();
this.causeProxy = throwableCause == null ? null : new ThrowableProxy(throwable, stack, map, throwableCause);
@@ -339,33 +306,6 @@ public class ThrowableProxy implements Serializable {
}
/**
- * Initialize the cache by resolving everything in the current stack trace via Reflection.getCallerClass or via the
- * SecurityManager if either are available. These are the only Classes that can be trusted to be accurate.
- *
- * @return A Stack containing the current stack of Class objects.
- */
- private Stack<Class<?>> getCurrentStack() {
- if (ReflectiveCallerClassUtility.isSupported()) {
- final Stack<Class<?>> classes = new Stack<Class<?>>();
- int index = 1;
- Class<?> clazz = ReflectiveCallerClassUtility.getCaller(index);
- while (clazz != null) {
- classes.push(clazz);
- clazz = ReflectiveCallerClassUtility.getCaller(++index);
- }
- return classes;
- } else if (SECURITY_MANAGER != null) {
- final Class<?>[] array = SECURITY_MANAGER.getClasses();
- final Stack<Class<?>> classes = new Stack<Class<?>>();
- for (final Class<?> clazz : array) {
- classes.push(clazz);
- }
- return classes;
- }
- return new Stack<Class<?>>();
- }
-
- /**
* Gets the stack trace including packaging information.
*
* @return The stack trace including packaging information.
[4/4] git commit: Remove old caller class utility.
Posted by ma...@apache.org.
Remove old caller class utility.
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/260b903b
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/260b903b
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/260b903b
Branch: refs/heads/LOG4J2-809
Commit: 260b903b00e994c5bf5ee9d4ca6270a10dec2e1a
Parents: cbf0f72
Author: Matt Sicker <ma...@apache.org>
Authored: Fri Sep 5 16:54:23 2014 -0500
Committer: Matt Sicker <ma...@apache.org>
Committed: Fri Sep 5 16:54:23 2014 -0500
----------------------------------------------------------------------
.../core/impl/ReflectiveCallerClassUtility.java | 162 -------------------
.../impl/ReflectiveCallerClassUtilityTest.java | 43 -----
2 files changed, 205 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/260b903b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ReflectiveCallerClassUtility.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ReflectiveCallerClassUtility.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ReflectiveCallerClassUtility.java
deleted file mode 100644
index 93f2e6c..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ReflectiveCallerClassUtility.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * 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.logging.log4j.core.impl;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-
-import org.apache.logging.log4j.Logger;
-import org.apache.logging.log4j.core.util.Loader;
-import org.apache.logging.log4j.status.StatusLogger;
-
-/**
- * Utility class that handles the instability of the Sun/OpenJDK {@code sun.reflect.Reflection.getCallerClass(int)}
- * method.
- * <p>
- * <strong>Background:</strong> 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 Thread#getStackTrace()} must be used, and its
- * {@code String} class name converted to a {@code Class} using the slow {@link Class#forName}.
- * </p>
- * <p>
- * As of Java 8, the {@code getCallerClass(int)} method has been removed from Oracle/OpenJDK and is no longer usable. A
- * back-port of the feature that resulted in this change was made in Java 7u25, but the {@code getCallerClass(int)} was
- * left around for that version and deprecated, with the intention of being removed in 7u40. By coincidence, the change
- * actually broke {@code getCallerClass(int)} (the return value was inadvertently offset by 1 stack frame). This was
- * actually a good thing, because it made the hundreds of libraries and frameworks relying on this method aware of what
- * the JDK developers were up to.
- * </p>
- * <p>
- * 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, supposedly won't be in Java 9 (unless
- * no public API is exposed which may force them to keep it), and so backup options must be used. This class:
- * </p>
- * <ul>
- * <li>Uses {@code getCallerClass(int)} the traditional way when possible.</li>
- * <li>Uses {@code getCallerClass(int)} with an adjusted offset in Oracle/OpenJDK 7u25.</li>
- * <li>Returns null otherwise. (Currently, it is the caller's responsibility to use the backup mechanisms.)</li>
- * </ul>
- * <p>
- * <strong>IMPORTANT NOTE:</strong> This class should not be relied upon. It is considered an internal class and could
- * change at any time, breaking your code if you use it. Specifically, as a possible public API replacement for
- * {@code getCallerClass(int)} develops in Java 9, this class is very likely to change or even go away.
- * </p>
- */
-public final class ReflectiveCallerClassUtility {
-
- private static final Logger LOGGER = StatusLogger.getLogger();
-
- private static final boolean GET_CALLER_CLASS_SUPPORTED;
-
- private static final Method GET_CALLER_CLASS_METHOD;
-
- static final int JAVA_7U25_COMPENSATION_OFFSET;
-
- static {
- Method getCallerClass = null;
- int java7u25CompensationOffset = 0;
-
- try {
- final ClassLoader loader = Loader.getClassLoader();
- // Use wildcard to avoid compile-time reference.
- final Class<?> clazz = loader.loadClass("sun.reflect.Reflection");
- final Method[] methods = clazz.getMethods();
- for (final Method method : methods) {
- final int modifier = method.getModifiers();
- final Class<?>[] parameterTypes = method.getParameterTypes();
- if (method.getName().equals("getCallerClass") && Modifier.isStatic(modifier) &&
- parameterTypes.length == 1 && parameterTypes[0] == int.class) {
- getCallerClass = method;
- break;
- }
- }
-
- if (getCallerClass == null) {
- LOGGER.info("sun.reflect.Reflection#getCallerClass does not exist.");
- } else {
- Object o = getCallerClass.invoke(null, 0);
- if (o == null || o != clazz) {
- getCallerClass = null;
- LOGGER.warn("sun.reflect.Reflection#getCallerClass returned unexpected value of [{}] and is " +
- "unusable. Will fall back to another option.", o);
- } else {
- o = getCallerClass.invoke(null, 1);
- if (o == clazz) {
- java7u25CompensationOffset = 1;
- LOGGER.warn("sun.reflect.Reflection#getCallerClass is broken in Java 7u25. " +
- "You should upgrade to 7u40. Using alternate stack offset to compensate.");
- }
- }
- }
- } catch (final ClassNotFoundException e) {
- LOGGER.info("sun.reflect.Reflection is not installed.");
- } catch (final IllegalAccessException e) {
- LOGGER.info("sun.reflect.Reflection#getCallerClass is not accessible.");
- } catch (final InvocationTargetException e) {
- LOGGER.info("sun.reflect.Reflection#getCallerClass is not supported.");
- }
-
- if (getCallerClass == null) {
- GET_CALLER_CLASS_SUPPORTED = false;
- GET_CALLER_CLASS_METHOD = null;
- JAVA_7U25_COMPENSATION_OFFSET = -1;
- } else {
- GET_CALLER_CLASS_SUPPORTED = true;
- GET_CALLER_CLASS_METHOD = getCallerClass;
- JAVA_7U25_COMPENSATION_OFFSET = java7u25CompensationOffset;
- }
- }
-
- private ReflectiveCallerClassUtility() {
-
- }
-
- /**
- * Indicates whether {@code getCallerClass(int)} can be used on this JVM.
- *
- * @return {@code true} if it can be used. If {@code false}, {@link #getCaller} should not be called. Use a backup
- * mechanism instead.
- */
- public static boolean isSupported() {
- return GET_CALLER_CLASS_SUPPORTED;
- }
-
- /**
- * Reflectively calls {@code getCallerClass(int)}, compensating for the additional frame on the stack, and
- * compensating for the Java 7u25 bug if necessary. You should check with {@link #isSupported} before using this
- * method.
- *
- * @param depth The depth of the caller to retrieve.
- * @return the caller class, or {@code null} if {@code getCallerClass(int)} is not supported.
- */
- public static Class<?> getCaller(final int depth) {
- if (!GET_CALLER_CLASS_SUPPORTED) {
- return null;
- }
-
- try {
- return (Class<?>) GET_CALLER_CLASS_METHOD.invoke(null, depth + 1 + JAVA_7U25_COMPENSATION_OFFSET);
- } catch (final IllegalAccessException ignore) {
- LOGGER.warn("Should not have failed to call getCallerClass.");
- } catch (final InvocationTargetException ignore) {
- LOGGER.warn("Should not have failed to call getCallerClass.");
- }
- return null;
- }
-}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/260b903b/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/ReflectiveCallerClassUtilityTest.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/ReflectiveCallerClassUtilityTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/ReflectiveCallerClassUtilityTest.java
deleted file mode 100644
index 6761163..0000000
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/ReflectiveCallerClassUtilityTest.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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.logging.log4j.core.impl;
-
-import org.junit.Before;
-import org.junit.Test;
-import sun.reflect.Reflection;
-
-import static org.junit.Assert.assertSame;
-import static org.junit.Assume.assumeTrue;
-
-public class ReflectiveCallerClassUtilityTest {
-
- @Before
- public void setUp() throws Exception {
- assumeTrue(ReflectiveCallerClassUtility.isSupported());
- }
-
- @Test
- public void testGetCaller() throws Exception {
- for (int i = 1; i <= 6; i++) {
- assertSame(String.format("%d is not the same", i),
- Reflection.getCallerClass(i + ReflectiveCallerClassUtility.JAVA_7U25_COMPENSATION_OFFSET),
- ReflectiveCallerClassUtility.getCaller(i)
- );
- }
- }
-}