Skip to content

Commit 80cce50

Browse files
committed
Add inheritability to StringArrayThreadContextMap
1 parent 437a0ac commit 80cce50

File tree

4 files changed

+46
-24
lines changed

4 files changed

+46
-24
lines changed

log4j-core-test/src/test/java/org/apache/logging/log4j/core/context/ThreadContextMapTest.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@ static Stream<ThreadContextMap> inheritableMaps() {
3838
props.setProperty("log4j2.isThreadContextMapInheritable", "true");
3939
final PropertiesUtil util = new PropertiesUtil(props);
4040
return Stream.of(
41-
new CopyOnWriteSortedArrayThreadContextMap(util), new GarbageFreeSortedArrayThreadContextMap(util));
41+
new StringArrayThreadContextMap(util),
42+
new CopyOnWriteSortedArrayThreadContextMap(util),
43+
new GarbageFreeSortedArrayThreadContextMap(util));
4244
}
4345

4446
@ParameterizedTest

log4j-core-test/src/test/java/org/apache/logging/log4j/core/impl/ThreadContextDataInjectorTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public static Collection<String[]> threadContextMapClassNames() {
5252
return asList(new String[][] {
5353
{"org.apache.logging.log4j.core.context.CopyOnWriteSortedArrayThreadContextMap"},
5454
{"org.apache.logging.log4j.core.context.GarbageFreeSortedArrayThreadContextMap"},
55-
// {"org.apache.logging.log4j.core.context.StringArrayThreadContextMap"}
55+
{"org.apache.logging.log4j.core.context.StringArrayThreadContextMap"}
5656
});
5757
}
5858

log4j-core/src/main/java/org/apache/logging/log4j/core/context/AbstractSortedArrayThreadContextMap.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,15 @@
3131

3232
/**
3333
* Commons base class for {@link StringMap}-based implementations of {@code ThreadContextMap}.
34+
* @since 2.24.0
3435
*/
3536
public abstract class AbstractSortedArrayThreadContextMap implements ReadOnlyThreadContextMap, ObjectThreadContextMap {
3637

3738
/**
3839
* Property name ({@value} ) for selecting {@code InheritableThreadLocal} (value "true") or plain
3940
* {@code ThreadLocal} (value is not "true") in the implementation.
4041
*/
41-
public static final String INHERITABLE_MAP = "isThreadContextMapInheritable";
42+
private static final String INHERITABLE_MAP = "isThreadContextMapInheritable";
4243

4344
/**
4445
* The default initial capacity.

log4j-core/src/main/java/org/apache/logging/log4j/core/context/StringArrayThreadContextMap.java

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,14 @@
1616
*/
1717
package org.apache.logging.log4j.core.context;
1818

19+
import java.util.Arrays;
1920
import java.util.HashMap;
2021
import java.util.Map;
2122
import java.util.Objects;
2223
import org.apache.logging.log4j.core.context.internal.UnmodifiableArrayBackedMap;
2324
import org.apache.logging.log4j.spi.ThreadContextMap;
2425
import org.apache.logging.log4j.util.BiConsumer;
26+
import org.apache.logging.log4j.util.PropertiesUtil;
2527
import org.apache.logging.log4j.util.ReadOnlyStringMap;
2628
import org.apache.logging.log4j.util.TriConsumer;
2729
import org.jspecify.annotations.NullMarked;
@@ -41,31 +43,48 @@
4143
public class StringArrayThreadContextMap implements ThreadContextMap, ReadOnlyStringMap {
4244
private static final long serialVersionUID = -2635197170958057849L;
4345

44-
private ThreadLocal<Object @Nullable []> threadLocalMapState;
46+
/**
47+
* Property name ({@value} ) for selecting {@code InheritableThreadLocal} (value "true") or plain
48+
* {@code ThreadLocal} (value is not "true") in the implementation.
49+
*/
50+
private static final String INHERITABLE_MAP = "isThreadContextMapInheritable";
51+
52+
private ThreadLocal<Object @Nullable []> localMap;
4553

4654
public StringArrayThreadContextMap() {
47-
threadLocalMapState = new ThreadLocal<>();
55+
localMap = new ThreadLocal<>();
56+
}
57+
58+
StringArrayThreadContextMap(final PropertiesUtil properties) {
59+
localMap = properties.getBooleanProperty(INHERITABLE_MAP)
60+
? new InheritableThreadLocal<Object @Nullable []>() {
61+
@Override
62+
protected Object @Nullable [] childValue(final Object @Nullable [] parentValue) {
63+
return parentValue != null ? Arrays.copyOf(parentValue, parentValue.length) : null;
64+
}
65+
}
66+
: new ThreadLocal<>();
4867
}
4968

5069
@Override
5170
public void put(final String key, final String value) {
52-
final Object[] state = threadLocalMapState.get();
71+
final Object[] state = localMap.get();
5372
final UnmodifiableArrayBackedMap modifiedMap =
5473
UnmodifiableArrayBackedMap.getInstance(state).copyAndPut(key, value);
55-
threadLocalMapState.set(modifiedMap.getBackingArray());
74+
localMap.set(modifiedMap.getBackingArray());
5675
}
5776

5877
@Override
5978
public void putAll(final Map<String, String> m) {
60-
final Object[] state = threadLocalMapState.get();
79+
final Object[] state = localMap.get();
6180
final UnmodifiableArrayBackedMap modifiedMap =
6281
UnmodifiableArrayBackedMap.getInstance(state).copyAndPutAll(m);
63-
threadLocalMapState.set(modifiedMap.getBackingArray());
82+
localMap.set(modifiedMap.getBackingArray());
6483
}
6584

6685
@Override
6786
public @Nullable String get(final String key) {
68-
final Object[] state = threadLocalMapState.get();
87+
final Object[] state = localMap.get();
6988
if (state == null) {
7089
return null;
7190
}
@@ -74,27 +93,27 @@ public void putAll(final Map<String, String> m) {
7493

7594
@Override
7695
public void remove(final String key) {
77-
final Object[] state = threadLocalMapState.get();
96+
final Object[] state = localMap.get();
7897
if (state != null) {
7998
final UnmodifiableArrayBackedMap modifiedMap =
8099
UnmodifiableArrayBackedMap.getInstance(state).copyAndRemove(key);
81-
threadLocalMapState.set(modifiedMap.getBackingArray());
100+
localMap.set(modifiedMap.getBackingArray());
82101
}
83102
}
84103

85104
@Override
86105
public void removeAll(final Iterable<String> keys) {
87-
final Object[] state = threadLocalMapState.get();
106+
final Object[] state = localMap.get();
88107
if (state != null) {
89108
final UnmodifiableArrayBackedMap modifiedMap =
90109
UnmodifiableArrayBackedMap.getInstance(state).copyAndRemoveAll(keys);
91-
threadLocalMapState.set(modifiedMap.getBackingArray());
110+
localMap.set(modifiedMap.getBackingArray());
92111
}
93112
}
94113

95114
@Override
96115
public void clear() {
97-
threadLocalMapState.remove();
116+
localMap.remove();
98117
}
99118

100119
@Override
@@ -104,13 +123,13 @@ public Map<String, String> toMap() {
104123

105124
@Override
106125
public boolean containsKey(final String key) {
107-
final Object @Nullable [] state = threadLocalMapState.get();
126+
final Object @Nullable [] state = localMap.get();
108127
return (state != null && (UnmodifiableArrayBackedMap.getInstance(state)).containsKey(key));
109128
}
110129

111130
@Override
112131
public <V> void forEach(final BiConsumer<String, ? super V> action) {
113-
final Object[] state = threadLocalMapState.get();
132+
final Object[] state = localMap.get();
114133
if (state == null) {
115134
return;
116135
}
@@ -120,7 +139,7 @@ public <V> void forEach(final BiConsumer<String, ? super V> action) {
120139

121140
@Override
122141
public <V, S> void forEach(final TriConsumer<String, ? super V, S> action, final S state) {
123-
final Object[] localState = threadLocalMapState.get();
142+
final Object[] localState = localMap.get();
124143
if (localState == null) {
125144
return;
126145
}
@@ -136,7 +155,7 @@ public <V, S> void forEach(final TriConsumer<String, ? super V, S> action, final
136155

137156
@Override
138157
public Map<String, String> getCopy() {
139-
final Object[] state = threadLocalMapState.get();
158+
final Object[] state = localMap.get();
140159
if (state == null) {
141160
return new HashMap<>(0);
142161
}
@@ -145,7 +164,7 @@ public Map<String, String> getCopy() {
145164

146165
@Override
147166
public @Nullable Map<String, String> getImmutableMapOrNull() {
148-
final Object[] state = threadLocalMapState.get();
167+
final Object[] state = localMap.get();
149168
return (state == null ? null : UnmodifiableArrayBackedMap.getInstance(state));
150169
}
151170

@@ -156,13 +175,13 @@ public boolean isEmpty() {
156175

157176
@Override
158177
public int size() {
159-
final Object[] state = threadLocalMapState.get();
178+
final Object[] state = localMap.get();
160179
return UnmodifiableArrayBackedMap.getInstance(state).size();
161180
}
162181

163182
@Override
164183
public String toString() {
165-
final Object[] state = threadLocalMapState.get();
184+
final Object[] state = localMap.get();
166185
return state == null
167186
? "{}"
168187
: UnmodifiableArrayBackedMap.getInstance(state).toString();
@@ -172,7 +191,7 @@ public String toString() {
172191
public int hashCode() {
173192
final int prime = 31;
174193
int result = 1;
175-
final Object[] state = threadLocalMapState.get();
194+
final Object[] state = localMap.get();
176195
result = prime * result
177196
+ ((state == null)
178197
? 0
@@ -192,7 +211,7 @@ public boolean equals(final Object obj) {
192211
return false;
193212
}
194213
final ThreadContextMap other = (ThreadContextMap) obj;
195-
final Map<String, String> map = UnmodifiableArrayBackedMap.getInstance(this.threadLocalMapState.get());
214+
final Map<String, String> map = UnmodifiableArrayBackedMap.getInstance(this.localMap.get());
196215
final Map<String, String> otherMap = other.getImmutableMapOrNull();
197216
return Objects.equals(map, otherMap);
198217
}

0 commit comments

Comments
 (0)