From fbde9cd0efb8b50098724e668b0474116761cd81 Mon Sep 17 00:00:00 2001 From: Romain Manni-Bucau Date: Tue, 9 Jan 2018 11:59:52 +0100 Subject: [PATCH 1/3] adding log4j2.forceTCLOnly option --- .../apache/logging/log4j/util/LoaderUtil.java | 50 +++++++++++----- .../logging/log4j/util/LoaderUtilTest.java | 58 +++++++++++++++++++ 2 files changed, 93 insertions(+), 15 deletions(-) create mode 100644 log4j-api/src/test/java/org/apache/logging/log4j/util/LoaderUtilTest.java diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/LoaderUtil.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/LoaderUtil.java index 2a153b1bfb3..84ce69f237a 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/util/LoaderUtil.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/LoaderUtil.java @@ -44,6 +44,7 @@ public final class LoaderUtil { * @since 2.1 */ public static final String IGNORE_TCCL_PROPERTY = "log4j.ignoreTCL"; + public static final String FORCE_TCL_ONLY_PROPERTY = "log4j.forceTCLOnly"; private static final SecurityManager SECURITY_MANAGER = System.getSecurityManager(); @@ -53,6 +54,8 @@ public final class LoaderUtil { private static final boolean GET_CLASS_LOADER_DISABLED; + protected static Boolean forceTcclOnly; + private static final PrivilegedAction TCCL_GETTER = new ThreadContextClassLoaderGetter(); static { @@ -109,21 +112,23 @@ public static ClassLoader[] getClassLoaders() { List classLoaders = new ArrayList<>(); ClassLoader tcl = getThreadContextClassLoader(); classLoaders.add(tcl); - ClassLoader current = LoaderUtil.class.getClassLoader(); - if (current != tcl) { - classLoaders.add(current); - ClassLoader parent = current.getParent(); + if (!isForceTccl()) { + ClassLoader current = LoaderUtil.class.getClassLoader(); + if (current != tcl) { + classLoaders.add(current); + ClassLoader parent = current.getParent(); + while (parent != null && !classLoaders.contains(parent)) { + classLoaders.add(parent); + } + } + ClassLoader parent = tcl.getParent(); while (parent != null && !classLoaders.contains(parent)) { classLoaders.add(parent); + parent = parent.getParent(); + } + if (!classLoaders.contains(ClassLoader.getSystemClassLoader())) { + classLoaders.add(ClassLoader.getSystemClassLoader()); } - } - ClassLoader parent = tcl.getParent(); - while (parent != null && !classLoaders.contains(parent)) { - classLoaders.add(parent); - parent = parent.getParent(); - } - if (!classLoaders.contains(ClassLoader.getSystemClassLoader())) { - classLoaders.add(ClassLoader.getSystemClassLoader()); } return classLoaders.toArray(new ClassLoader[classLoaders.size()]); } @@ -260,6 +265,21 @@ private static boolean isIgnoreTccl() { return ignoreTCCL; } + private static boolean isForceTccl() { + if (forceTcclOnly == null) { + // PropertiesUtil.getProperties() uses that code path so don't use that! + forceTcclOnly = System.getSecurityManager() == null ? + Boolean.getBoolean(FORCE_TCL_ONLY_PROPERTY) : + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Boolean run() { + return Boolean.getBoolean(FORCE_TCL_ONLY_PROPERTY); + } + }); + } + return forceTcclOnly; + } + /** * Finds classpath {@linkplain URL resources}. * @@ -279,9 +299,9 @@ public static Collection findResources(final String resource) { static Collection findUrlResources(final String resource) { // @formatter:off final ClassLoader[] candidates = { - getThreadContextClassLoader(), - LoaderUtil.class.getClassLoader(), - GET_CLASS_LOADER_DISABLED ? null : ClassLoader.getSystemClassLoader()}; + getThreadContextClassLoader(), + isForceTccl() ? null : LoaderUtil.class.getClassLoader(), + isForceTccl() || GET_CLASS_LOADER_DISABLED ? null : ClassLoader.getSystemClassLoader()}; // @formatter:on final Collection resources = new LinkedHashSet<>(); for (final ClassLoader cl : candidates) { diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/util/LoaderUtilTest.java b/log4j-api/src/test/java/org/apache/logging/log4j/util/LoaderUtilTest.java new file mode 100644 index 00000000000..c30041e6c7e --- /dev/null +++ b/log4j-api/src/test/java/org/apache/logging/log4j/util/LoaderUtilTest.java @@ -0,0 +1,58 @@ +/* + * 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 static org.junit.Assert.assertEquals; + +import java.net.URL; +import java.util.Collections; +import java.util.Enumeration; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class LoaderUtilTest { + @Before + @After + public void reset() { + LoaderUtil.forceTcclOnly = null; + } + + @Test + public void systemClassLoader() { + final Thread thread = Thread.currentThread(); + final ClassLoader tccl = thread.getContextClassLoader(); + + LoaderUtil.forceTcclOnly = true; + final ClassLoader loader = new ClassLoader(tccl) { + @Override + public Enumeration getResources(final String name) { + return Collections.emptyEnumeration(); + } + }; + thread.setContextClassLoader(loader); + try { + assertEquals(0, LoaderUtil.findUrlResources("Log4j-charsets.properties").size()); + + LoaderUtil.forceTcclOnly = false; + assertEquals(1, LoaderUtil.findUrlResources("Log4j-charsets.properties").size()); + } finally { + thread.setContextClassLoader(tccl); + } + } +} From 28c6ec246f084cea9d58e918c888ef82fc57eb57 Mon Sep 17 00:00:00 2001 From: Romain Manni-Bucau Date: Mon, 9 Apr 2018 13:53:44 +0200 Subject: [PATCH 2/3] fixing tests after rebase + adding jira in changes.xml --- .../main/java/org/apache/logging/log4j/util/LoaderUtil.java | 6 +++++- src/changes/changes.xml | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/LoaderUtil.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/LoaderUtil.java index 84ce69f237a..3923c7cca27 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/util/LoaderUtil.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/LoaderUtil.java @@ -268,7 +268,8 @@ private static boolean isIgnoreTccl() { private static boolean isForceTccl() { if (forceTcclOnly == null) { // PropertiesUtil.getProperties() uses that code path so don't use that! - forceTcclOnly = System.getSecurityManager() == null ? + try { + forceTcclOnly = System.getSecurityManager() == null ? Boolean.getBoolean(FORCE_TCL_ONLY_PROPERTY) : AccessController.doPrivileged(new PrivilegedAction() { @Override @@ -276,6 +277,9 @@ public Boolean run() { return Boolean.getBoolean(FORCE_TCL_ONLY_PROPERTY); } }); + } catch (final SecurityException se) { + forceTcclOnly = false; + } } return forceTcclOnly; } diff --git a/src/changes/changes.xml b/src/changes/changes.xml index ad7e6324117..9ebfc4a7bd2 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -31,6 +31,9 @@ - "remove" - Removed --> + + Allow to force LOG4J2 to use TCCL only. + Convert documentation into AsciiDoc format. From 1ee85dcb6e0ea0e07d840adbe6eb67639aa2283e Mon Sep 17 00:00:00 2001 From: Romain Manni-Bucau Date: Mon, 9 Apr 2018 17:05:32 +0200 Subject: [PATCH 3/3] adding back the doc --- src/site/asciidoc/manual/configuration.adoc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/site/asciidoc/manual/configuration.adoc b/src/site/asciidoc/manual/configuration.adoc index 138e3f07e54..aef2a6bd593 100644 --- a/src/site/asciidoc/manual/configuration.adoc +++ b/src/site/asciidoc/manual/configuration.adoc @@ -1841,6 +1841,14 @@ Windows. Otherwise, an attempt is made to load classes with the current thread's context class loader before falling back to the default class loader. + +|[[forceTCLOnly]]log4j2.forceTCLOnly + +([[log4j.forceTCLOnly]]log4j.forceTCLOnly) +|LOG4J_FORCE_TCL_ONLY +|false +|If `true`, classes and configuration are only loaded with the default context class loader. +Otherwise, log4j also uses the log4j classloader, parent classloaders and the system classloader. + |[[uuidSequence]]log4j2.uuidSequence + ([[org.apache.logging.log4j.uuidSequence]]org.apache.logging.log4j.uuidSequence) |LOG4J_UUID_SEQUENCE