1616 */
1717package org .apache .logging .log4j .core .context ;
1818
19+ import java .util .Arrays ;
1920import java .util .HashMap ;
2021import java .util .Map ;
2122import java .util .Objects ;
2223import org .apache .logging .log4j .core .context .internal .UnmodifiableArrayBackedMap ;
2324import org .apache .logging .log4j .spi .ThreadContextMap ;
2425import org .apache .logging .log4j .util .BiConsumer ;
26+ import org .apache .logging .log4j .util .PropertiesUtil ;
2527import org .apache .logging .log4j .util .ReadOnlyStringMap ;
2628import org .apache .logging .log4j .util .TriConsumer ;
2729import org .jspecify .annotations .NullMarked ;
4143public 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