Skip to content

Commit f27f6bc

Browse files
committed
Clean up ThreadContextMap implementations
- Make `StringArray` the default thread context map - Remove `CopyOnWriteSortedArrayThreadContextMap` - Move `GarbageFreeSortedArrayThreadContextMap` to `log4j-core`
1 parent 200f852 commit f27f6bc

File tree

41 files changed

+1154
-1450
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1154
-1450
lines changed

log4j-api-test/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
<bnd-module-name>org.apache.logging.log4j.test</bnd-module-name>
3838
<bnd-extra-package-options>
3939
org.apache.commons.lang3.*;resolution:=optional,
40+
org.assertj.*;resolution:=optional,
4041
<!-- Both JUnit 4 and JUnit 5 are not required -->
4142
org.junit.*;resolution:=optional,
4243
org.hamcrest.*;resolution:=optional,
@@ -111,7 +112,6 @@
111112
<dependency>
112113
<groupId>org.assertj</groupId>
113114
<artifactId>assertj-core</artifactId>
114-
<scope>test</scope>
115115
</dependency>
116116
<!-- Required for JSON support -->
117117
<dependency>
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to you under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.logging.log4j.test.spi;
18+
19+
import static org.assertj.core.api.Assertions.assertThat;
20+
import static org.assertj.core.api.Assertions.entry;
21+
import static org.junit.jupiter.api.Assertions.assertThrows;
22+
23+
import java.time.Duration;
24+
import java.util.Map;
25+
import java.util.concurrent.ExecutorService;
26+
import java.util.concurrent.Executors;
27+
import org.apache.logging.log4j.spi.ThreadContextMap;
28+
import org.junit.jupiter.api.parallel.Execution;
29+
import org.junit.jupiter.api.parallel.ExecutionMode;
30+
31+
/**
32+
* Provides test cases to apply to all implementations of {@link ThreadContextMap}.
33+
* @since 2.24.0
34+
*/
35+
@Execution(ExecutionMode.CONCURRENT)
36+
public abstract class ThreadContextMapSuite {
37+
38+
private static final String KEY = "key";
39+
40+
/**
41+
* Checks if the context map does not propagate to other threads by default.
42+
*/
43+
protected static void threadLocalNotInheritableByDefault(final ThreadContextMap contextMap) {
44+
contextMap.put(KEY, "threadLocalNotInheritableByDefault");
45+
verifyThreadContextValueFromANewThread(contextMap, null);
46+
}
47+
48+
/**
49+
* Checks if the context map can be configured to propagate to other threads.
50+
*/
51+
protected static void threadLocalInheritableIfConfigured(final ThreadContextMap contextMap) {
52+
contextMap.put(KEY, "threadLocalInheritableIfConfigured");
53+
verifyThreadContextValueFromANewThread(contextMap, "threadLocalInheritableIfConfigured");
54+
}
55+
56+
/**
57+
* Checks basic put/remove pattern.
58+
*/
59+
protected static void singleValue(final ThreadContextMap contextMap) {
60+
assertThat(contextMap.isEmpty()).as("Map is empty").isTrue();
61+
contextMap.put(KEY, "testPut");
62+
assertThat(contextMap.isEmpty()).as("Map is not empty").isFalse();
63+
assertThat(contextMap.containsKey(KEY)).as("Map key exists").isTrue();
64+
assertThat(contextMap.get(KEY)).as("Map contains expected value").isEqualTo("testPut");
65+
contextMap.remove(KEY);
66+
assertThat(contextMap.isEmpty()).as("Map is empty").isTrue();
67+
}
68+
69+
/**
70+
* Checks mutable copy
71+
*/
72+
protected static void getCopyReturnsMutableCopy(final ThreadContextMap contextMap) {
73+
contextMap.put(KEY, "testGetCopyReturnsMutableCopy");
74+
75+
final Map<String, String> copy = contextMap.getCopy();
76+
assertThat(copy).as("Copy contains same value").containsExactly(entry(KEY, "testGetCopyReturnsMutableCopy"));
77+
78+
copy.put(KEY, "testGetCopyReturnsMutableCopy2");
79+
assertThat(contextMap.get(KEY))
80+
.as("Original map is not affected by changes in the copy")
81+
.isEqualTo("testGetCopyReturnsMutableCopy");
82+
83+
contextMap.clear();
84+
assertThat(contextMap.isEmpty()).as("Original map is empty").isTrue();
85+
assertThat(copy)
86+
.as("Copy is not affected by changes in the map.")
87+
.containsExactly(entry(KEY, "testGetCopyReturnsMutableCopy2"));
88+
}
89+
90+
/**
91+
* The immutable copy must be {@code null} if the map is empty.
92+
*/
93+
protected static void getImmutableMapReturnsNullIfEmpty(final ThreadContextMap contextMap) {
94+
assertThat(contextMap.isEmpty()).as("Original map is empty").isTrue();
95+
assertThat(contextMap.getImmutableMapOrNull())
96+
.as("Immutable copy is null")
97+
.isNull();
98+
}
99+
100+
/**
101+
* The result of {@link ThreadContextMap#getImmutableMapOrNull()} must be immutable.
102+
*/
103+
protected static void getImmutableMapReturnsImmutableMapIfNonEmpty(final ThreadContextMap contextMap) {
104+
contextMap.put(KEY, "getImmutableMapReturnsImmutableMapIfNonEmpty");
105+
106+
final Map<String, String> immutable = contextMap.getImmutableMapOrNull();
107+
assertThat(immutable)
108+
.as("Immutable copy contains same value")
109+
.containsExactly(entry(KEY, "getImmutableMapReturnsImmutableMapIfNonEmpty"));
110+
111+
assertThrows(
112+
UnsupportedOperationException.class, () -> immutable.put(KEY, "getImmutableMapReturnsNullIfEmpty2"));
113+
}
114+
115+
/**
116+
* The immutable copy is not affected by changes to the original map.
117+
*/
118+
protected static void getImmutableMapCopyNotAffectedByContextMapChanges(final ThreadContextMap contextMap) {
119+
contextMap.put(KEY, "getImmutableMapCopyNotAffectedByContextMapChanges");
120+
121+
final Map<String, String> immutable = contextMap.getImmutableMapOrNull();
122+
contextMap.put(KEY, "getImmutableMapCopyNotAffectedByContextMapChanges2");
123+
assertThat(immutable)
124+
.as("Immutable copy contains the original value")
125+
.containsExactly(entry(KEY, "getImmutableMapCopyNotAffectedByContextMapChanges"));
126+
}
127+
128+
private static void verifyThreadContextValueFromANewThread(
129+
final ThreadContextMap contextMap, final String expected) {
130+
final ExecutorService executorService = Executors.newSingleThreadExecutor();
131+
try {
132+
assertThat(executorService.submit(() -> contextMap.get(KEY)))
133+
.succeedsWithin(Duration.ofSeconds(1))
134+
.isEqualTo(expected);
135+
} finally {
136+
executorService.shutdown();
137+
}
138+
}
139+
}

log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextInheritanceTest.java

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,18 @@
2323

2424
import org.apache.logging.log4j.spi.DefaultThreadContextMap;
2525
import org.apache.logging.log4j.test.ThreadContextUtilityClass;
26-
import org.apache.logging.log4j.test.junit.InitializesThreadContext;
2726
import org.apache.logging.log4j.test.junit.SetTestProperty;
2827
import org.apache.logging.log4j.test.junit.UsingThreadContextMap;
2928
import org.apache.logging.log4j.test.junit.UsingThreadContextStack;
3029
import org.junit.jupiter.api.AfterAll;
3130
import org.junit.jupiter.api.BeforeAll;
3231
import org.junit.jupiter.api.Tag;
3332
import org.junit.jupiter.api.Test;
34-
import org.junitpioneer.jupiter.SetSystemProperty;
3533

3634
/**
3735
* Tests {@link ThreadContext}.
3836
*/
3937
@SetTestProperty(key = DefaultThreadContextMap.INHERITABLE_MAP, value = "true")
40-
@InitializesThreadContext
4138
@UsingThreadContextMap
4239
@UsingThreadContextStack
4340
public class ThreadContextInheritanceTest {
@@ -63,31 +60,6 @@ public void testPush() {
6360
assertEquals(ThreadContext.pop(), "Hello", "Incorrect simple stack value");
6461
}
6562

66-
@Test
67-
@SetSystemProperty(key = DefaultThreadContextMap.INHERITABLE_MAP, value = "true")
68-
@InitializesThreadContext
69-
public void testInheritanceSwitchedOn() throws Exception {
70-
System.setProperty(DefaultThreadContextMap.INHERITABLE_MAP, "true");
71-
try {
72-
ThreadContext.clearMap();
73-
ThreadContext.put("Greeting", "Hello");
74-
StringBuilder sb = new StringBuilder();
75-
TestThread thread = new TestThread(sb);
76-
thread.start();
77-
thread.join();
78-
String str = sb.toString();
79-
assertEquals("Hello", str, "Unexpected ThreadContext value. Expected Hello. Actual " + str);
80-
sb = new StringBuilder();
81-
thread = new TestThread(sb);
82-
thread.start();
83-
thread.join();
84-
str = sb.toString();
85-
assertEquals("Hello", str, "Unexpected ThreadContext value. Expected Hello. Actual " + str);
86-
} finally {
87-
System.clearProperty(DefaultThreadContextMap.INHERITABLE_MAP);
88-
}
89-
}
90-
9163
@Test
9264
@Tag("performance")
9365
public void perfTest() {

0 commit comments

Comments
 (0)