You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by pk...@apache.org on 2022/10/20 03:19:59 UTC

[logging-log4j2] branch release-2.x updated (a2e53fade9 -> f6829882ee)

This is an automated email from the ASF dual-hosted git repository.

pkarwasz pushed a change to branch release-2.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git


    from a2e53fade9 Remove no-op plugin execution
     new 97b4a19064 Refactor `MarkerManager` and other resource locks
     new da0b463fd5 Adds a thread-bound `ExtensionContext`
     new 4997a45ebc Marks tests that reinitialize the ThreadContextFactory
     new f6829882ee Fixes `ThreadContext` test interference

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 log4j-api-test/pom.xml                             |   9 +-
 .../logging/log4j/ThreadContextUtilityClass.java   |  99 +++++++++++++++++-
 .../log4j/test/junit/ExtensionContextAnchor.java   |  90 ++++++++++++++++
 ...gerLevel.java => InitializesThreadContext.java} |  22 ++--
 .../test/junit/{Mutable.java => Resources.java}    |  18 ++--
 .../log4j/test/junit/ThreadContextInitializer.java |  57 +++++++++++
 .../test/junit/ThreadContextMapExtension.java      |  47 +++++++++
 .../log4j/test/junit/UsingThreadContextMap.java    |  17 +--
 .../org.junit.jupiter.api.extension.Extension}     |   3 +-
 .../apache/logging/log4j/AbstractLoggerTest.java   |  22 ++--
 .../logging/log4j/CloseableThreadContextTest.java  |  12 ++-
 .../org/apache/logging/log4j/EventLoggerTest.java  |   6 +-
 .../org/apache/logging/log4j/LambdaLoggerTest.java |  11 +-
 .../java/org/apache/logging/log4j/LevelTest.java   |   4 +-
 .../org/apache/logging/log4j/LogManagerTest.java   |   6 +-
 .../apache/logging/log4j/LoggerSupplierTest.java   |   8 +-
 .../java/org/apache/logging/log4j/LoggerTest.java  |  34 +++---
 .../java/org/apache/logging/log4j/MarkerTest.java  |   9 +-
 .../logging/log4j/NoopThreadContextTest.java       |  30 ++----
 .../log4j/ThreadContextInheritanceTest.java        |  18 ++--
 .../apache/logging/log4j/ThreadContextTest.java    |   8 +-
 .../logging/log4j/ThreadContextUtilityClass.java   | 114 ---------------------
 .../org/apache/logging/log4j/TraceLoggingTest.java |   4 +-
 .../logging/log4j/simple/SimpleLoggerTest.java     |   2 -
 .../log4j/spi/DefaultThreadContextMapTest.java     |  26 +++--
 .../log4j/spi/DefaultThreadContextStackTest.java   |  18 ++--
 26 files changed, 440 insertions(+), 254 deletions(-)
 create mode 100644 log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ExtensionContextAnchor.java
 copy log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/{StatusLoggerLevel.java => InitializesThreadContext.java} (75%)
 copy log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/{Mutable.java => Resources.java} (75%)
 create mode 100644 log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextInitializer.java
 create mode 100644 log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextMapExtension.java
 copy log4j-api-test/src/{test/resources/META-INF/services/org.apache.logging.log4j.util.test.BetterService => main/resources/META-INF/services/org.junit.jupiter.api.extension.Extension} (93%)
 delete mode 100644 log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextUtilityClass.java


[logging-log4j2] 01/04: Refactor `MarkerManager` and other resource locks

Posted by pk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pkarwasz pushed a commit to branch release-2.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git

commit 97b4a19064bbfd72f650ef74e6291e1978f41350
Author: Piotr P. Karwasz <pi...@karwasz.org>
AuthorDate: Mon Jun 20 21:31:02 2022 +0200

    Refactor `MarkerManager` and other resource locks
    
    Only one test needs write permission to `MarkerManager`
---
 .../{UsingThreadContextMap.java => Resources.java} | 26 ++++-------------
 .../log4j/test/junit/UsingThreadContextMap.java    |  2 +-
 .../apache/logging/log4j/AbstractLoggerTest.java   | 22 +++++++-------
 .../java/org/apache/logging/log4j/LoggerTest.java  | 34 ++++++++++++++--------
 .../java/org/apache/logging/log4j/MarkerTest.java  | 11 +++++--
 .../logging/log4j/simple/SimpleLoggerTest.java     |  2 --
 6 files changed, 49 insertions(+), 48 deletions(-)

diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingThreadContextMap.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/Resources.java
similarity index 51%
copy from log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingThreadContextMap.java
copy to log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/Resources.java
index b42902cd8b..4f85703d85 100644
--- a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingThreadContextMap.java
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/Resources.java
@@ -17,29 +17,15 @@
 
 package org.apache.logging.log4j.test.junit;
 
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.junit.jupiter.api.parallel.ResourceAccessMode;
 import org.junit.jupiter.api.parallel.ResourceLock;
-import org.junit.jupiter.api.parallel.Resources;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Inherited;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
 
 /**
- * Marks a test class as using {@link org.apache.logging.log4j.spi.ThreadContextMap} APIs. This will automatically clear and
- * restore the thread context map (MDC) for each test invocation.
+ * Constants to use the the {@link ResourceLock} annotation.
  *
- * @since 2.14.0
  */
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.TYPE)
-@Documented
-@Inherited
-@ExtendWith(ThreadContextExtension.class)
-@ResourceLock(value = Resources.SYSTEM_PROPERTIES, mode = ResourceAccessMode.READ)
-public @interface UsingThreadContextMap {
+public class Resources {
+
+    public static final String THREAD_CONTEXT = "log4j2.ThreadContext";
+
+    public static final String MARKER_MANAGER = "log4j2.MarkerManager";
 }
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingThreadContextMap.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingThreadContextMap.java
index b42902cd8b..074ba0b58c 100644
--- a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingThreadContextMap.java
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingThreadContextMap.java
@@ -36,7 +36,7 @@ import java.lang.annotation.Target;
  * @since 2.14.0
  */
 @Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.TYPE)
+@Target({ElementType.TYPE, ElementType.METHOD})
 @Documented
 @Inherited
 @ExtendWith(ThreadContextExtension.class)
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/AbstractLoggerTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/AbstractLoggerTest.java
index f563c5f769..a51612dbf0 100644
--- a/log4j-api-test/src/test/java/org/apache/logging/log4j/AbstractLoggerTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/AbstractLoggerTest.java
@@ -16,14 +16,6 @@
  */
 package org.apache.logging.log4j;
 
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
-
 import java.util.List;
 
 import org.apache.logging.log4j.message.Message;
@@ -35,15 +27,25 @@ import org.apache.logging.log4j.spi.AbstractLogger;
 import org.apache.logging.log4j.spi.MessageFactory2Adapter;
 import org.apache.logging.log4j.status.StatusData;
 import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.test.junit.Resources;
 import org.apache.logging.log4j.test.junit.StatusLoggerLevel;
 import org.apache.logging.log4j.util.Constants;
 import org.apache.logging.log4j.util.MessageSupplier;
 import org.apache.logging.log4j.util.Supplier;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.parallel.ResourceAccessMode;
 import org.junit.jupiter.api.parallel.ResourceLock;
 
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
 @StatusLoggerLevel("WARN")
-@ResourceLock("log4j2.MarkerManager")
+@ResourceLock(value = Resources.MARKER_MANAGER, mode = ResourceAccessMode.READ)
 public class AbstractLoggerTest {
 
     private static final StringBuilder CHAR_SEQ = new StringBuilder("CharSeq");
@@ -64,7 +66,7 @@ public class AbstractLoggerTest {
 
     private static final Message param = new ParameterizedMessage(pattern, p1, p2);
 
-    private static final Marker MARKER = MarkerManager.getMarker("TEST");
+    private final Marker MARKER = MarkerManager.getMarker("TEST");
     private static final String MARKER_NAME = "TEST";
 
     private static final LogEvent[] EVENTS = new LogEvent[] {
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/LoggerTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/LoggerTest.java
index 39637040b2..0eb5f8d6d5 100644
--- a/log4j-api-test/src/test/java/org/apache/logging/log4j/LoggerTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/LoggerTest.java
@@ -16,6 +16,11 @@
  */
 package org.apache.logging.log4j;
 
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+import java.util.Properties;
+
 import org.apache.logging.log4j.message.EntryMessage;
 import org.apache.logging.log4j.message.JsonMessage;
 import org.apache.logging.log4j.message.Message;
@@ -28,22 +33,27 @@ import org.apache.logging.log4j.message.StringFormatterMessageFactory;
 import org.apache.logging.log4j.message.StructuredDataMessage;
 import org.apache.logging.log4j.spi.MessageFactory2Adapter;
 import org.apache.logging.log4j.test.TestLogger;
+import org.apache.logging.log4j.test.junit.Resources;
+import org.apache.logging.log4j.test.junit.UsingThreadContextMap;
 import org.apache.logging.log4j.util.Strings;
 import org.apache.logging.log4j.util.Supplier;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.parallel.ResourceAccessMode;
 import org.junit.jupiter.api.parallel.ResourceLock;
-import java.util.Date;
-import java.util.List;
-import java.util.Locale;
-import java.util.Properties;
+import org.junitpioneer.jupiter.ReadsSystemProperty;
 
-import static org.hamcrest.CoreMatchers.*;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.endsWith;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.startsWith;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
-@ResourceLock("log4j2.MarkerManager")
-@ResourceLock("log4j2.TestLogger")
+@ResourceLock(value = Resources.MARKER_MANAGER, mode = ResourceAccessMode.READ)
+@ReadsSystemProperty
 public class LoggerTest {
 
     private static class TestParameterizedMessageFactory {
@@ -54,7 +64,7 @@ public class LoggerTest {
         // empty
     }
 
-    private final TestLogger logger = (TestLogger) LogManager.getLogger("LoggerTest");
+    private final TestLogger logger = (TestLogger) LogManager.getLogger(LoggerTest.class);
     private final Marker marker = MarkerManager.getMarker("test");
     private final List<String> results = logger.getEntries();
 
@@ -65,12 +75,12 @@ public class LoggerTest {
         logger.atWarn().withThrowable(new Throwable("This is a test")).log((Message) new SimpleMessage("Log4j rocks!"));
         assertEquals(3, results.size());
         assertThat("Incorrect message 1", results.get(0),
-                equalTo(" DEBUG org.apache.logging.log4j.LoggerTest.builder(LoggerTest.java:63) Hello"));
+                equalTo(" DEBUG org.apache.logging.log4j.LoggerTest.builder(LoggerTest.java:73) Hello"));
         assertThat("Incorrect message 2", results.get(1), equalTo("test ERROR Hello John"));
         assertThat("Incorrect message 3", results.get(2),
                 startsWith(" WARN Log4j rocks! java.lang.Throwable: This is a test"));
         assertThat("Throwable incorrect in message 3", results.get(2),
-                containsString("at org.apache.logging.log4j.LoggerTest.builder(LoggerTest.java:65)"));
+                containsString("org.apache.logging.log4j.LoggerTest.builder(LoggerTest.java:75)"));
     }
 
     @Test
@@ -533,8 +543,8 @@ public class LoggerTest {
     }
 
     @Test
+    @UsingThreadContextMap
     public void mdc() {
-
         ThreadContext.put("TestYear", Integer.toString(2010));
         logger.debug("Debug message");
         final String testYear = ThreadContext.get("TestYear");
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/MarkerTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/MarkerTest.java
index a286ca2ba5..5c652341fc 100644
--- a/log4j-api-test/src/test/java/org/apache/logging/log4j/MarkerTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/MarkerTest.java
@@ -16,13 +16,18 @@
  */
 package org.apache.logging.log4j;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.logging.log4j.test.junit.Resources;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.parallel.ResourceAccessMode;
 import org.junit.jupiter.api.parallel.ResourceLock;
 
-import static org.junit.jupiter.api.Assertions.*;
-
-@ResourceLock("log4j2.MarkerManager")
+@ResourceLock(value = Resources.MARKER_MANAGER, mode = ResourceAccessMode.READ_WRITE)
 public class MarkerTest {
 
     @BeforeEach
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/simple/SimpleLoggerTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/simple/SimpleLoggerTest.java
index ab1c328e3e..8b0586e7f7 100644
--- a/log4j-api-test/src/test/java/org/apache/logging/log4j/simple/SimpleLoggerTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/simple/SimpleLoggerTest.java
@@ -23,10 +23,8 @@ import org.apache.logging.log4j.util.Constants;
 import org.junit.jupiter.api.Tag;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.RegisterExtension;
-import org.junit.jupiter.api.parallel.ResourceLock;
 
 @Tag("smoke")
-@ResourceLock("log4j2.LoggerContextFactory")
 public class SimpleLoggerTest {
 
     @RegisterExtension


[logging-log4j2] 02/04: Adds a thread-bound `ExtensionContext`

Posted by pk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pkarwasz pushed a commit to branch release-2.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git

commit da0b463fd54488949ed3978ff79f83714f593d98
Author: Piotr P. Karwasz <pi...@karwasz.org>
AuthorDate: Mon Jun 20 21:44:43 2022 +0200

    Adds a thread-bound `ExtensionContext`
    
    The `ExtensionContextAnchor` extension can be auto-discovered by JUnit
    5, if autodiscovery is enabled. It will be used whenever a static access
    to per-test objects is needed.
---
 .../log4j/test/junit/ExtensionContextAnchor.java   | 90 ++++++++++++++++++++++
 .../org.junit.jupiter.api.extension.Extension      | 15 ++++
 2 files changed, 105 insertions(+)

diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ExtensionContextAnchor.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ExtensionContextAnchor.java
new file mode 100644
index 0000000000..0b4cc50d60
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ExtensionContextAnchor.java
@@ -0,0 +1,90 @@
+/*
+ * 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.test.junit;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import org.junit.jupiter.api.extension.AfterAllCallback;
+import org.junit.jupiter.api.extension.AfterEachCallback;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
+
+public class ExtensionContextAnchor
+        implements BeforeAllCallback, BeforeEachCallback, AfterAllCallback, AfterEachCallback {
+
+    public static Namespace LOG4J2_NAMESPACE = Namespace.create("org.apache.logging.log4j.junit");
+    private static final ThreadLocal<ExtensionContext> EXTENSION_CONTEXT = new InheritableThreadLocal<>();
+
+    private static void bind(ExtensionContext context) {
+        EXTENSION_CONTEXT.set(context);
+    }
+
+    private static void unbind(ExtensionContext context) {
+        EXTENSION_CONTEXT.set(context.getParent().orElse(null));
+    }
+
+    public static ExtensionContext getContext() {
+        return EXTENSION_CONTEXT.get();
+    }
+
+    public static ExtensionContext getContext(ExtensionContext context) {
+        return context != null ? context : EXTENSION_CONTEXT.get();
+    }
+
+    static <T> T getAttribute(Object key, Class<T> clazz, ExtensionContext context) {
+        final ExtensionContext actualContext = getContext(context);
+        assertNotNull(actualContext, "missing ExtensionContext");
+        return actualContext.getStore(LOG4J2_NAMESPACE).get(key, clazz);
+    }
+
+    static void setAttribute(Object key, Object value, ExtensionContext context) {
+        final ExtensionContext actualContext = getContext(context);
+        assertNotNull(actualContext, "missing ExtensionContext");
+        actualContext.getStore(LOG4J2_NAMESPACE).put(key, value);
+    }
+
+    static void removeAttribute(Object key, ExtensionContext context) {
+        final ExtensionContext actualContext = getContext(context);
+        if (actualContext != null) {
+            actualContext.getStore(LOG4J2_NAMESPACE).remove(key);
+        }
+    }
+
+    @Override
+    public void afterEach(ExtensionContext context) throws Exception {
+        unbind(context);
+    }
+
+    @Override
+    public void afterAll(ExtensionContext context) throws Exception {
+        unbind(context);
+    }
+
+    @Override
+    public void beforeEach(ExtensionContext context) throws Exception {
+        bind(context);
+    }
+
+    @Override
+    public void beforeAll(ExtensionContext context) throws Exception {
+        bind(context);
+    }
+
+}
diff --git a/log4j-api-test/src/main/resources/META-INF/services/org.junit.jupiter.api.extension.Extension b/log4j-api-test/src/main/resources/META-INF/services/org.junit.jupiter.api.extension.Extension
new file mode 100644
index 0000000000..ca7ce84edd
--- /dev/null
+++ b/log4j-api-test/src/main/resources/META-INF/services/org.junit.jupiter.api.extension.Extension
@@ -0,0 +1,15 @@
+# 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.
+org.apache.logging.log4j.test.junit.ExtensionContextAnchor
\ No newline at end of file


[logging-log4j2] 03/04: Marks tests that reinitialize the ThreadContextFactory

Posted by pk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pkarwasz pushed a commit to branch release-2.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git

commit 4997a45ebc929c2c32dacdc9cdc8fb73809a6e88
Author: Piotr P. Karwasz <pi...@karwasz.org>
AuthorDate: Mon Jun 20 21:49:57 2022 +0200

    Marks tests that reinitialize the ThreadContextFactory
    
    A JUnit5 extension is provided to restore the default
    ThreadContextFactory after the test finishes.
---
 log4j-api-test/pom.xml                             |   9 +-
 .../logging/log4j/ThreadContextUtilityClass.java   |  99 +++++++++++++++++-
 ...ntextMap.java => InitializesThreadContext.java} |  23 ++---
 .../log4j/test/junit/ThreadContextInitializer.java |  25 +++++
 .../test/junit/ThreadContextMapExtension.java      |  47 +++++++++
 .../log4j/test/junit/UsingThreadContextMap.java    |  15 +--
 .../logging/log4j/CloseableThreadContextTest.java  |  12 ++-
 .../org/apache/logging/log4j/EventLoggerTest.java  |   6 +-
 .../org/apache/logging/log4j/LambdaLoggerTest.java |  11 +-
 .../java/org/apache/logging/log4j/LevelTest.java   |   4 +-
 .../org/apache/logging/log4j/LogManagerTest.java   |   6 +-
 .../apache/logging/log4j/LoggerSupplierTest.java   |   8 +-
 .../java/org/apache/logging/log4j/MarkerTest.java  |  10 +-
 .../logging/log4j/NoopThreadContextTest.java       |  32 ++----
 .../log4j/ThreadContextInheritanceTest.java        |  14 +--
 .../apache/logging/log4j/ThreadContextTest.java    |   5 +-
 .../logging/log4j/ThreadContextUtilityClass.java   | 114 ---------------------
 .../org/apache/logging/log4j/TraceLoggingTest.java |   4 +-
 18 files changed, 246 insertions(+), 198 deletions(-)

diff --git a/log4j-api-test/pom.xml b/log4j-api-test/pom.xml
index bc5def2d2c..0b23a52822 100644
--- a/log4j-api-test/pom.xml
+++ b/log4j-api-test/pom.xml
@@ -54,6 +54,10 @@
       <groupId>org.junit.jupiter</groupId>
       <artifactId>junit-jupiter-api</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.junit-pioneer</groupId>
+      <artifactId>junit-pioneer</artifactId>
+    </dependency>
     <dependency>
       <groupId>org.apache.maven</groupId>
       <artifactId>maven-core</artifactId>
@@ -90,11 +94,6 @@
       <artifactId>junit-jupiter-params</artifactId>
       <scope>test</scope>
     </dependency>
-    <dependency>
-      <groupId>org.junit-pioneer</groupId>
-      <artifactId>junit-pioneer</artifactId>
-      <scope>test</scope>
-    </dependency>
     <dependency>
       <groupId>org.junit.vintage</groupId>
       <artifactId>junit-vintage-engine</artifactId>
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/ThreadContextUtilityClass.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/ThreadContextUtilityClass.java
index 215994fbcc..475009f9ed 100644
--- a/log4j-api-test/src/main/java/org/apache/logging/log4j/ThreadContextUtilityClass.java
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/ThreadContextUtilityClass.java
@@ -16,10 +16,107 @@
  */
 package org.apache.logging.log4j;
 
-import org.apache.logging.log4j.ThreadContext;
+import java.util.Map;
+
+import org.apache.logging.log4j.util.Timer;
+
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 public class ThreadContextUtilityClass {
 
+    public static void perfTest() {
+        ThreadContext.clearMap();
+        final Timer complete = new Timer("ThreadContextTest");
+        complete.start();
+        ThreadContext.put("Var1", "value 1");
+        ThreadContext.put("Var2", "value 2");
+        ThreadContext.put("Var3", "value 3");
+        ThreadContext.put("Var4", "value 4");
+        ThreadContext.put("Var5", "value 5");
+        ThreadContext.put("Var6", "value 6");
+        ThreadContext.put("Var7", "value 7");
+        ThreadContext.put("Var8", "value 8");
+        ThreadContext.put("Var9", "value 9");
+        ThreadContext.put("Var10", "value 10");
+        final int loopCount = 1000000;
+        final Timer timer = new Timer("ThreadContextCopy", loopCount);
+        timer.start();
+        for (int i = 0; i < loopCount; ++i) {
+            final Map<String, String> map = ThreadContext.getImmutableContext();
+            assertNotNull(map);
+        }
+        timer.stop();
+        complete.stop();
+        System.out.println(timer.toString());
+        System.out.println(complete.toString());
+    }
+
+
+    public static void testGetContextReturnsEmptyMapIfEmpty() {
+        ThreadContext.clearMap();
+        assertTrue(ThreadContext.getContext().isEmpty());
+    }
+
+
+    public static void testGetContextReturnsMutableCopy() {
+        ThreadContext.clearMap();
+        final Map<String, String> map1 = ThreadContext.getContext();
+        assertTrue(map1.isEmpty());
+        map1.put("K", "val"); // no error
+        assertEquals("val", map1.get("K"));
+
+        // adding to copy does not affect thread context map
+        assertTrue(ThreadContext.getContext().isEmpty());
+
+        ThreadContext.put("key", "val2");
+        final Map<String, String> map2 = ThreadContext.getContext();
+        assertEquals(1, map2.size());
+        assertEquals("val2", map2.get("key"));
+        map2.put("K", "val"); // no error
+        assertEquals("val", map2.get("K"));
+
+        // first copy is not affected
+        assertNotSame(map1, map2);
+        assertEquals(1, map1.size());
+    }
+
+    public static void testGetImmutableContextReturnsEmptyMapIfEmpty() {
+        ThreadContext.clearMap();
+        assertTrue(ThreadContext.getImmutableContext().isEmpty());
+    }
+
+
+    public static void testGetImmutableContextReturnsImmutableMapIfNonEmpty() {
+        ThreadContext.clearMap();
+        ThreadContext.put("key", "val");
+        final Map<String, String> immutable = ThreadContext.getImmutableContext();
+        assertThrows(UnsupportedOperationException.class, () -> immutable.put("otherkey", "otherval"));
+    }
+
+    public static void testGetImmutableContextReturnsImmutableMapIfEmpty() {
+        ThreadContext.clearMap();
+        final Map<String, String> immutable = ThreadContext.getImmutableContext();
+        assertThrows(UnsupportedOperationException.class, () -> immutable.put("otherkey", "otherval"));
+    }
+
+    public static void testGetImmutableStackReturnsEmptyStackIfEmpty() {
+        ThreadContext.clearStack();
+        assertTrue(ThreadContext.getImmutableStack().asList().isEmpty());
+    }
+
+
+    public static void testPut() {
+        ThreadContext.clearMap();
+        assertNull(ThreadContext.get("testKey"));
+        ThreadContext.put("testKey", "testValue");
+        assertEquals("testValue", ThreadContext.get("testKey"));
+    }
+
     public static void reset() {
         ThreadContext.init();
     }
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingThreadContextMap.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/InitializesThreadContext.java
similarity index 73%
copy from log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingThreadContextMap.java
copy to log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/InitializesThreadContext.java
index 074ba0b58c..493d6b9bf5 100644
--- a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingThreadContextMap.java
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/InitializesThreadContext.java
@@ -17,11 +17,6 @@
 
 package org.apache.logging.log4j.test.junit;
 
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.junit.jupiter.api.parallel.ResourceAccessMode;
-import org.junit.jupiter.api.parallel.ResourceLock;
-import org.junit.jupiter.api.parallel.Resources;
-
 import java.lang.annotation.Documented;
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Inherited;
@@ -29,17 +24,19 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
+import org.apache.logging.log4j.ThreadContext;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.parallel.ResourceAccessMode;
+import org.junit.jupiter.api.parallel.ResourceLock;
+
 /**
- * Marks a test class as using {@link org.apache.logging.log4j.spi.ThreadContextMap} APIs. This will automatically clear and
- * restore the thread context map (MDC) for each test invocation.
- *
- * @since 2.14.0
+ * Marks a test class that initializes the {@link ThreadContext} class;
  */
 @Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.TYPE, ElementType.METHOD})
+@Target(ElementType.TYPE)
 @Documented
 @Inherited
-@ExtendWith(ThreadContextExtension.class)
-@ResourceLock(value = Resources.SYSTEM_PROPERTIES, mode = ResourceAccessMode.READ)
-public @interface UsingThreadContextMap {
+@ExtendWith(ThreadContextInitializer.class)
+@ResourceLock(value = Resources.THREAD_CONTEXT, mode = ResourceAccessMode.READ_WRITE)
+public @interface InitializesThreadContext {
 }
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextInitializer.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextInitializer.java
new file mode 100644
index 0000000000..ed246d4386
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextInitializer.java
@@ -0,0 +1,25 @@
+package org.apache.logging.log4j.test.junit;
+
+import org.apache.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.ThreadContextUtilityClass;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ExtensionContext.Store.CloseableResource;
+
+class ThreadContextInitializer implements BeforeAllCallback {
+
+    @Override
+    public void beforeAll(ExtensionContext context) throws Exception {
+        ThreadContextUtilityClass.reset();
+        // We use `CloseableResource` instead of `afterAll` to reset the ThreadContextFactory
+        // *after* the `@SetSystemProperty` extension has restored the properties
+        ExtensionContextAnchor.setAttribute(ThreadContext.class, new CloseableResource() {
+            @Override
+            public void close() throws Throwable {
+                ThreadContextUtilityClass.reset();
+            }
+
+        }, context);
+    }
+
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextMapExtension.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextMapExtension.java
new file mode 100644
index 0000000000..cb68eb295d
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextMapExtension.java
@@ -0,0 +1,47 @@
+/*
+ * 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.test.junit;
+
+import java.util.Map;
+
+import org.apache.logging.log4j.ThreadContext;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+
+class ThreadContextMapExtension implements BeforeEachCallback {
+    private static class ThreadContextMapStore implements ExtensionContext.Store.CloseableResource {
+        private final Map<String, String> previousMap = ThreadContext.getImmutableContext();
+
+        private ThreadContextMapStore() {
+            ThreadContext.clearMap();
+        }
+
+        @Override
+        public void close() throws Throwable {
+            // TODO LOG4J2-1517 Add ThreadContext.setContext(Map<String, String>)
+            ThreadContext.clearMap();
+            ThreadContext.putAll(previousMap);
+        }
+    }
+
+    @Override
+    public void beforeEach(final ExtensionContext context) throws Exception {
+        context.getStore(ExtensionContextAnchor.LOG4J2_NAMESPACE)
+                .getOrComputeIfAbsent(ThreadContextMapStore.class);
+    }
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingThreadContextMap.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingThreadContextMap.java
index 074ba0b58c..c942ce114c 100644
--- a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingThreadContextMap.java
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingThreadContextMap.java
@@ -17,11 +17,6 @@
 
 package org.apache.logging.log4j.test.junit;
 
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.junit.jupiter.api.parallel.ResourceAccessMode;
-import org.junit.jupiter.api.parallel.ResourceLock;
-import org.junit.jupiter.api.parallel.Resources;
-
 import java.lang.annotation.Documented;
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Inherited;
@@ -29,6 +24,11 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.parallel.ResourceAccessMode;
+import org.junit.jupiter.api.parallel.ResourceLock;
+import org.junitpioneer.jupiter.ReadsSystemProperty;
+
 /**
  * Marks a test class as using {@link org.apache.logging.log4j.spi.ThreadContextMap} APIs. This will automatically clear and
  * restore the thread context map (MDC) for each test invocation.
@@ -39,7 +39,8 @@ import java.lang.annotation.Target;
 @Target({ElementType.TYPE, ElementType.METHOD})
 @Documented
 @Inherited
-@ExtendWith(ThreadContextExtension.class)
-@ResourceLock(value = Resources.SYSTEM_PROPERTIES, mode = ResourceAccessMode.READ)
+@ExtendWith(ThreadContextMapExtension.class)
+@ReadsSystemProperty
+@ResourceLock(value = Resources.THREAD_CONTEXT, mode = ResourceAccessMode.READ)
 public @interface UsingThreadContextMap {
 }
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/CloseableThreadContextTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/CloseableThreadContextTest.java
index 9acafadc62..829a7cf440 100644
--- a/log4j-api-test/src/test/java/org/apache/logging/log4j/CloseableThreadContextTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/CloseableThreadContextTest.java
@@ -16,6 +16,10 @@
  */
 package org.apache.logging.log4j;
 
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -23,11 +27,9 @@ import org.junit.jupiter.api.parallel.ResourceAccessMode;
 import org.junit.jupiter.api.parallel.ResourceLock;
 import org.junit.jupiter.api.parallel.Resources;
 
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
 
 /**
  * Tests {@link CloseableThreadContext}.
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/EventLoggerTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/EventLoggerTest.java
index 75eab77867..e02ff7b155 100644
--- a/log4j-api-test/src/test/java/org/apache/logging/log4j/EventLoggerTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/EventLoggerTest.java
@@ -16,15 +16,15 @@
  */
 package org.apache.logging.log4j;
 
+import java.util.List;
+import java.util.Locale;
+
 import org.apache.logging.log4j.message.StructuredDataMessage;
 import org.apache.logging.log4j.test.TestLogger;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.parallel.ResourceLock;
 
-import java.util.List;
-import java.util.Locale;
-
 import static org.assertj.core.api.Assertions.assertThat;
 
 @ResourceLock("log4j2.TestLogger")
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/LambdaLoggerTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/LambdaLoggerTest.java
index 422eacdde1..735a8824e0 100644
--- a/log4j-api-test/src/test/java/org/apache/logging/log4j/LambdaLoggerTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/LambdaLoggerTest.java
@@ -17,6 +17,9 @@
 
 package org.apache.logging.log4j;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import org.apache.logging.log4j.message.Message;
 import org.apache.logging.log4j.message.ReusableMessage;
 import org.apache.logging.log4j.message.SimpleMessage;
@@ -25,10 +28,10 @@ import org.apache.logging.log4j.util.Supplier;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
-import java.util.ArrayList;
-import java.util.List;
-
-import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /**
  * Tests the AbstractLogger implementation of the Logger2 interface.
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/LevelTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/LevelTest.java
index 6a85e357dd..d86489b4ab 100644
--- a/log4j-api-test/src/test/java/org/apache/logging/log4j/LevelTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/LevelTest.java
@@ -16,6 +16,8 @@
  */
 package org.apache.logging.log4j;
 
+import org.junit.jupiter.api.Test;
+
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -23,8 +25,6 @@ import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertThrowsExactly;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
-import org.junit.jupiter.api.Test;
-
 public class LevelTest {
 
     @Test
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/LogManagerTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/LogManagerTest.java
index 0b10b5b2de..3dc9f0e597 100644
--- a/log4j-api-test/src/test/java/org/apache/logging/log4j/LogManagerTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/LogManagerTest.java
@@ -16,15 +16,15 @@
  */
 package org.apache.logging.log4j;
 
+import java.io.Closeable;
+import java.io.IOException;
+
 import org.apache.logging.log4j.message.ParameterizedMessageFactory;
 import org.apache.logging.log4j.spi.LoggerContext;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.parallel.ResourceAccessMode;
 import org.junit.jupiter.api.parallel.ResourceLock;
 
-import java.io.Closeable;
-import java.io.IOException;
-
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/LoggerSupplierTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/LoggerSupplierTest.java
index 825079823a..12188b1101 100644
--- a/log4j-api-test/src/test/java/org/apache/logging/log4j/LoggerSupplierTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/LoggerSupplierTest.java
@@ -16,6 +16,10 @@
  */
 package org.apache.logging.log4j;
 
+import java.util.List;
+import java.util.Locale;
+import java.util.Properties;
+
 import org.apache.logging.log4j.message.FormattedMessage;
 import org.apache.logging.log4j.message.JsonMessage;
 import org.apache.logging.log4j.message.LocalizedMessage;
@@ -34,10 +38,6 @@ import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.parallel.ResourceLock;
 import org.junit.jupiter.api.parallel.Resources;
 
-import java.util.List;
-import java.util.Locale;
-import java.util.Properties;
-
 import static org.assertj.core.api.Assertions.assertThat;
 
 /**
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/MarkerTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/MarkerTest.java
index 5c652341fc..5c8a8499c1 100644
--- a/log4j-api-test/src/test/java/org/apache/logging/log4j/MarkerTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/MarkerTest.java
@@ -16,17 +16,17 @@
  */
 package org.apache.logging.log4j;
 
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
 import org.apache.logging.log4j.test.junit.Resources;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.parallel.ResourceAccessMode;
 import org.junit.jupiter.api.parallel.ResourceLock;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
 @ResourceLock(value = Resources.MARKER_MANAGER, mode = ResourceAccessMode.READ_WRITE)
 public class MarkerTest {
 
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/NoopThreadContextTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/NoopThreadContextTest.java
index 5c7910616c..be556abeb4 100644
--- a/log4j-api-test/src/test/java/org/apache/logging/log4j/NoopThreadContextTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/NoopThreadContextTest.java
@@ -16,38 +16,22 @@
  */
 package org.apache.logging.log4j;
 
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
+import org.apache.logging.log4j.test.junit.InitializesThreadContext;
+import org.apache.logging.log4j.test.junit.UsingThreadContextMap;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.parallel.ResourceLock;
-import org.junit.jupiter.api.parallel.Resources;
+import org.junitpioneer.jupiter.SetSystemProperty;
 
-import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.Assert.assertNull;
 
 /**
  * Tests {@link ThreadContext}.
  */
-@ResourceLock(Resources.SYSTEM_PROPERTIES)
+@SetSystemProperty(key = "disableThreadContext", value = "true")
+@SetSystemProperty(key = "disableThreadContextMap", value = "true")
+@InitializesThreadContext
+@UsingThreadContextMap
 public class NoopThreadContextTest {
 
-    private static final String TRUE = "true";
-    private static final String PROPERY_KEY_ALL = "disableThreadContext";
-    private static final String PROPERY_KEY_MAP = "disableThreadContextMap";
-
-    @BeforeAll
-    public static void before() {
-        System.setProperty(PROPERY_KEY_ALL, TRUE);
-        System.setProperty(PROPERY_KEY_MAP, TRUE);
-        ThreadContext.init();
-    }
-
-    @AfterAll
-    public static void after() {
-        System.clearProperty(PROPERY_KEY_ALL);
-        System.clearProperty(PROPERY_KEY_MAP);
-        ThreadContext.init();
-    }
-
     @Test
     public void testNoop() {
         ThreadContext.put("Test", "Test");
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextInheritanceTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextInheritanceTest.java
index 9cbc91cb9e..b6f43d967c 100644
--- a/log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextInheritanceTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextInheritanceTest.java
@@ -17,21 +17,23 @@
 package org.apache.logging.log4j;
 
 import org.apache.logging.log4j.spi.DefaultThreadContextMap;
-import org.apache.logging.log4j.test.junit.UsingAnyThreadContext;
+import org.apache.logging.log4j.test.junit.InitializesThreadContext;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Tag;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.parallel.ResourceLock;
-import org.junit.jupiter.api.parallel.Resources;
+import org.junitpioneer.jupiter.SetSystemProperty;
 
-import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /**
  * Tests {@link ThreadContext}.
  */
-@UsingAnyThreadContext
-@ResourceLock(Resources.SYSTEM_PROPERTIES)
+@SetSystemProperty(key = DefaultThreadContextMap.INHERITABLE_MAP, value = "true")
+@InitializesThreadContext
 public class ThreadContextInheritanceTest {
 
     @BeforeAll
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextTest.java
index d8290a084f..5d73ec2c25 100644
--- a/log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextTest.java
@@ -24,7 +24,10 @@ import org.apache.logging.log4j.test.junit.UsingAnyThreadContext;
 import org.junit.jupiter.api.Tag;
 import org.junit.jupiter.api.Test;
 
-import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 @UsingAnyThreadContext
 public class ThreadContextTest {
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextUtilityClass.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextUtilityClass.java
deleted file mode 100644
index 00d1786d94..0000000000
--- a/log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextUtilityClass.java
+++ /dev/null
@@ -1,114 +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;
-
-import org.apache.logging.log4j.util.Timer;
-
-import java.util.Map;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-public class ThreadContextUtilityClass {
-
-    public static void perfTest() {
-        ThreadContext.clearMap();
-        final Timer complete = new Timer("ThreadContextTest");
-        complete.start();
-        ThreadContext.put("Var1", "value 1");
-        ThreadContext.put("Var2", "value 2");
-        ThreadContext.put("Var3", "value 3");
-        ThreadContext.put("Var4", "value 4");
-        ThreadContext.put("Var5", "value 5");
-        ThreadContext.put("Var6", "value 6");
-        ThreadContext.put("Var7", "value 7");
-        ThreadContext.put("Var8", "value 8");
-        ThreadContext.put("Var9", "value 9");
-        ThreadContext.put("Var10", "value 10");
-        final int loopCount = 1000000;
-        final Timer timer = new Timer("ThreadContextCopy", loopCount);
-        timer.start();
-        for (int i = 0; i < loopCount; ++i) {
-            final Map<String, String> map = ThreadContext.getImmutableContext();
-            assertNotNull(map);
-        }
-        timer.stop();
-        complete.stop();
-        System.out.println(timer.toString());
-        System.out.println(complete.toString());
-    }
-
-
-    public static void testGetContextReturnsEmptyMapIfEmpty() {
-        ThreadContext.clearMap();
-        assertTrue(ThreadContext.getContext().isEmpty());
-    }
-
-
-    public static void testGetContextReturnsMutableCopy() {
-        ThreadContext.clearMap();
-        final Map<String, String> map1 = ThreadContext.getContext();
-        assertTrue(map1.isEmpty());
-        map1.put("K", "val"); // no error
-        assertEquals("val", map1.get("K"));
-
-        // adding to copy does not affect thread context map
-        assertTrue(ThreadContext.getContext().isEmpty());
-
-        ThreadContext.put("key", "val2");
-        final Map<String, String> map2 = ThreadContext.getContext();
-        assertEquals(1, map2.size());
-        assertEquals("val2", map2.get("key"));
-        map2.put("K", "val"); // no error
-        assertEquals("val", map2.get("K"));
-
-        // first copy is not affected
-        assertNotSame(map1, map2);
-        assertEquals(1, map1.size());
-    }
-
-    public static void testGetImmutableContextReturnsEmptyMapIfEmpty() {
-        ThreadContext.clearMap();
-        assertTrue(ThreadContext.getImmutableContext().isEmpty());
-    }
-
-
-    public static void testGetImmutableContextReturnsImmutableMapIfNonEmpty() {
-        ThreadContext.clearMap();
-        ThreadContext.put("key", "val");
-        final Map<String, String> immutable = ThreadContext.getImmutableContext();
-        assertThrows(UnsupportedOperationException.class, () -> immutable.put("otherkey", "otherval"));
-    }
-
-    public static void testGetImmutableContextReturnsImmutableMapIfEmpty() {
-        ThreadContext.clearMap();
-        final Map<String, String> immutable = ThreadContext.getImmutableContext();
-        assertThrows(UnsupportedOperationException.class, () -> immutable.put("otherkey", "otherval"));
-    }
-
-    public static void testGetImmutableStackReturnsEmptyStackIfEmpty() {
-        ThreadContext.clearStack();
-        assertTrue(ThreadContext.getImmutableStack().asList().isEmpty());
-    }
-
-
-    public static void testPut() {
-        ThreadContext.clearMap();
-        assertNull(ThreadContext.get("testKey"));
-        ThreadContext.put("testKey", "testValue");
-        assertEquals("testValue", ThreadContext.get("testKey"));
-    }
-}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/TraceLoggingTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/TraceLoggingTest.java
index 4aac63ec12..4dc5047297 100644
--- a/log4j-api-test/src/test/java/org/apache/logging/log4j/TraceLoggingTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/TraceLoggingTest.java
@@ -28,7 +28,9 @@ import org.apache.logging.log4j.message.SimpleMessage;
 import org.apache.logging.log4j.spi.AbstractLogger;
 import org.junit.jupiter.api.Test;
 
-import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
 
 public class TraceLoggingTest extends AbstractLogger {
     static final StringBuilder CHAR_SEQ = new StringBuilder("CharSeq");


[logging-log4j2] 04/04: Fixes `ThreadContext` test interference

Posted by pk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pkarwasz pushed a commit to branch release-2.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git

commit f6829882ee14a9ee90f3c0915f84de4f17ba91d3
Author: Piotr P. Karwasz <pi...@karwasz.org>
AuthorDate: Mon Jun 20 23:22:13 2022 +0200

    Fixes `ThreadContext` test interference
    
    A `PropertiesUtil#reload()` switched the `ThreadContext` to inheritable
    permanently.
---
 .../log4j/test/junit/InitializesThreadContext.java |  2 +-
 .../log4j/test/junit/ThreadContextInitializer.java | 36 ++++++++++++++++++++--
 .../logging/log4j/NoopThreadContextTest.java       |  6 ++--
 .../log4j/ThreadContextInheritanceTest.java        |  4 ++-
 .../apache/logging/log4j/ThreadContextTest.java    |  3 --
 .../log4j/spi/DefaultThreadContextMapTest.java     | 26 ++++++++++------
 .../log4j/spi/DefaultThreadContextStackTest.java   | 18 +++++------
 7 files changed, 66 insertions(+), 29 deletions(-)

diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/InitializesThreadContext.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/InitializesThreadContext.java
index 493d6b9bf5..33a8d9cc39 100644
--- a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/InitializesThreadContext.java
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/InitializesThreadContext.java
@@ -33,7 +33,7 @@ import org.junit.jupiter.api.parallel.ResourceLock;
  * Marks a test class that initializes the {@link ThreadContext} class;
  */
 @Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.TYPE)
+@Target({ ElementType.TYPE, ElementType.METHOD })
 @Documented
 @Inherited
 @ExtendWith(ThreadContextInitializer.class)
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextInitializer.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextInitializer.java
index ed246d4386..4c14328b1c 100644
--- a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextInitializer.java
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextInitializer.java
@@ -1,17 +1,49 @@
+/*
+ * 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.test.junit;
 
 import org.apache.logging.log4j.ThreadContext;
 import org.apache.logging.log4j.ThreadContextUtilityClass;
 import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
 import org.junit.jupiter.api.extension.ExtensionContext;
 import org.junit.jupiter.api.extension.ExtensionContext.Store.CloseableResource;
+import org.junit.platform.commons.util.AnnotationUtils;
 
-class ThreadContextInitializer implements BeforeAllCallback {
+class ThreadContextInitializer implements BeforeAllCallback, BeforeEachCallback {
 
     @Override
     public void beforeAll(ExtensionContext context) throws Exception {
+        if (AnnotationUtils.isAnnotated(context.getRequiredTestClass(), InitializesThreadContext.class)) {
+            resetThreadContext(context);
+        }
+    }
+
+    @Override
+    public void beforeEach(ExtensionContext context) throws Exception {
+        if (AnnotationUtils.isAnnotated(context.getRequiredTestMethod(), InitializesThreadContext.class)) {
+            resetThreadContext(context);
+        }
+    }
+
+    private void resetThreadContext(ExtensionContext context) {
         ThreadContextUtilityClass.reset();
-        // We use `CloseableResource` instead of `afterAll` to reset the ThreadContextFactory
+        // We use `CloseableResource` instead of `afterAll` to reset the
+        // ThreadContextFactory
         // *after* the `@SetSystemProperty` extension has restored the properties
         ExtensionContextAnchor.setAttribute(ThreadContext.class, new CloseableResource() {
             @Override
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/NoopThreadContextTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/NoopThreadContextTest.java
index be556abeb4..28b4d9dbd9 100644
--- a/log4j-api-test/src/test/java/org/apache/logging/log4j/NoopThreadContextTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/NoopThreadContextTest.java
@@ -21,13 +21,13 @@ import org.apache.logging.log4j.test.junit.UsingThreadContextMap;
 import org.junit.jupiter.api.Test;
 import org.junitpioneer.jupiter.SetSystemProperty;
 
-import static org.junit.Assert.assertNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
 
 /**
  * Tests {@link ThreadContext}.
  */
-@SetSystemProperty(key = "disableThreadContext", value = "true")
-@SetSystemProperty(key = "disableThreadContextMap", value = "true")
+@SetSystemProperty(key = "log4j2.disableThreadContext", value = "true")
+@SetSystemProperty(key = "log4j2.disableThreadContextMap", value = "true")
 @InitializesThreadContext
 @UsingThreadContextMap
 public class NoopThreadContextTest {
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextInheritanceTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextInheritanceTest.java
index b6f43d967c..803991422c 100644
--- a/log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextInheritanceTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextInheritanceTest.java
@@ -58,9 +58,11 @@ public class ThreadContextInheritanceTest {
     }
 
     @Test
+
+    @SetSystemProperty(key = DefaultThreadContextMap.INHERITABLE_MAP, value = "true")
+    @InitializesThreadContext
     public void testInheritanceSwitchedOn() throws Exception {
         System.setProperty(DefaultThreadContextMap.INHERITABLE_MAP, "true");
-        ThreadContext.init();
         try {
             ThreadContext.clearMap();
             ThreadContext.put("Greeting", "Hello");
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextTest.java
index 5d73ec2c25..d8835e0fbf 100644
--- a/log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextTest.java
@@ -31,9 +31,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 
 @UsingAnyThreadContext
 public class ThreadContextTest {
-    public static void reinitThreadContext() {
-        ThreadContext.init();
-    }
 
     @Test
     public void testPush() {
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextMapTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextMapTest.java
index a7a12147ab..e91137a951 100644
--- a/log4j-api-test/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextMapTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextMapTest.java
@@ -16,15 +16,20 @@
  */
 package org.apache.logging.log4j.spi;
 
-import org.apache.logging.log4j.test.junit.UsingThreadContextMap;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.parallel.ResourceLock;
-import org.junit.jupiter.api.parallel.Resources;
-
 import java.util.HashMap;
 import java.util.Map;
 
-import static org.junit.jupiter.api.Assertions.*;
+import org.apache.logging.log4j.test.junit.InitializesThreadContext;
+import org.apache.logging.log4j.test.junit.UsingThreadContextMap;
+import org.junit.jupiter.api.Test;
+import org.junitpioneer.jupiter.ClearSystemProperty;
+import org.junitpioneer.jupiter.SetSystemProperty;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /**
  * Tests the {@code DefaultThreadContextMap} class.
@@ -215,17 +220,18 @@ public class DefaultThreadContextMapTest {
     }
 
     @Test
-    @ResourceLock(Resources.SYSTEM_PROPERTIES)
+    @ClearSystemProperty(key = DefaultThreadContextMap.INHERITABLE_MAP)
+    @InitializesThreadContext
     public void testThreadLocalNotInheritableByDefault() {
-        System.clearProperty(DefaultThreadContextMap.INHERITABLE_MAP);
+        ThreadContextMapFactory.init();
         final ThreadLocal<Map<String, String>> threadLocal = DefaultThreadContextMap.createThreadLocalMap(true);
         assertFalse(threadLocal instanceof InheritableThreadLocal<?>);
     }
     
     @Test
-    @ResourceLock(Resources.SYSTEM_PROPERTIES)
+    @SetSystemProperty(key = DefaultThreadContextMap.INHERITABLE_MAP, value = "true")
+    @InitializesThreadContext
     public void testThreadLocalInheritableIfConfigured() {
-        System.setProperty(DefaultThreadContextMap.INHERITABLE_MAP, "true");
         ThreadContextMapFactory.init();
         try {
             final ThreadLocal<Map<String, String>> threadLocal = DefaultThreadContextMap.createThreadLocalMap(true);
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextStackTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextStackTest.java
index b9a1761cff..6ec90475b9 100644
--- a/log4j-api-test/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextStackTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextStackTest.java
@@ -16,26 +16,26 @@
  */
 package org.apache.logging.log4j.spi;
 
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Iterator;
 
 import org.apache.logging.log4j.ThreadContext.ContextStack;
 import org.apache.logging.log4j.test.junit.UsingAnyThreadContext;
-import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
-import static org.junit.jupiter.api.Assertions.*;
-
 @UsingAnyThreadContext
 public class DefaultThreadContextStackTest {
 
-    @BeforeEach
-    public void before() {
-        // clear the thread-local map
-        new DefaultThreadContextMap(true).clear();
-    }
-
     @Test
     public void testEqualsVsSameKind() {
         final DefaultThreadContextStack stack1 = createStack();