Skip to content

Commit 7818b0f

Browse files
jengebrppkarwasz
authored andcommittedMar 12, 2024··
Updated per comments on PR
1 parent f8453c0 commit 7818b0f

File tree

6 files changed

+332
-240
lines changed

6 files changed

+332
-240
lines changed
 

‎log4j-api-test/src/test/java/org/apache/logging/log4j/spi/StringArrayThreadContextMapTest.java ‎log4j-api-test/src/test/java/org/apache/logging/log4j/internal/map/StringArrayThreadContextMapTest.java

+66-44
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,21 @@
1414
* See the License for the specific language governing permissions and
1515
* limitations under the License.
1616
*/
17-
package org.apache.logging.log4j.spi;
17+
package org.apache.logging.log4j.internal.map;
1818

19-
import static org.junit.Assert.assertNull;
2019
import static org.junit.jupiter.api.Assertions.assertEquals;
2120
import static org.junit.jupiter.api.Assertions.assertFalse;
21+
import static org.junit.jupiter.api.Assertions.assertNull;
2222
import static org.junit.jupiter.api.Assertions.assertThrows;
2323
import static org.junit.jupiter.api.Assertions.assertTrue;
2424

2525
import java.util.HashMap;
26+
import java.util.HashSet;
2627
import java.util.Map;
27-
import org.apache.logging.log4j.test.junit.InitializesThreadContext;
28-
import org.apache.logging.log4j.test.junit.SetTestProperty;
28+
import java.util.Set;
2929
import org.apache.logging.log4j.test.junit.UsingThreadContextMap;
30+
import org.apache.logging.log4j.util.TriConsumer;
3031
import org.junit.jupiter.api.Test;
31-
import org.junitpioneer.jupiter.ClearSystemProperty;
3232

3333
/**
3434
* Tests the {@code StringArrayThreadContextMap} class.
@@ -56,27 +56,20 @@ public void testHashCodeVsSameKind() {
5656
@Test
5757
public void testGet() {
5858
final StringArrayThreadContextMap map1 = createMap();
59-
assertEquals(null, map1.get("test"));
59+
assertNull(map1.get("test"));
6060
map1.put("test", "test");
6161
assertEquals("test", map1.get("test"));
62-
assertEquals(null, map1.get("not_present"));
63-
}
64-
65-
@Test
66-
public void testDoesNothingIfConstructedWithUseMapIsFalse() {
67-
final StringArrayThreadContextMap map = new StringArrayThreadContextMap(false);
68-
assertTrue(map.isEmpty());
69-
assertFalse(map.containsKey("key"));
70-
map.put("key", "value");
62+
assertNull(map1.get("not_present"));
63+
assertEquals("test", map1.getValue("test"));
64+
assertNull(map1.getValue("not_present"));
7165

72-
assertTrue(map.isEmpty());
73-
assertFalse(map.containsKey("key"));
74-
assertNull(map.get("key"));
66+
map1.clear();
67+
assertNull(map1.get("not_present"));
7568
}
7669

7770
@Test
7871
public void testPut() {
79-
final StringArrayThreadContextMap map = new StringArrayThreadContextMap(true);
72+
final StringArrayThreadContextMap map = new StringArrayThreadContextMap();
8073
assertTrue(map.isEmpty());
8174
assertFalse(map.containsKey("key"));
8275
map.put("key", "value");
@@ -88,7 +81,7 @@ public void testPut() {
8881

8982
@Test
9083
public void testPutAll() {
91-
final StringArrayThreadContextMap map = new StringArrayThreadContextMap(true);
84+
final StringArrayThreadContextMap map = new StringArrayThreadContextMap();
9285
assertTrue(map.isEmpty());
9386
assertFalse(map.containsKey("key"));
9487
final int mapSize = 10;
@@ -106,7 +99,7 @@ public void testPutAll() {
10699

107100
/**
108101
* Test method for
109-
* {@link org.apache.logging.log4j.spi.StringArrayThreadContextMap#remove(java.lang.String)}
102+
* {@link org.apache.logging.log4j.internal.map.StringArrayThreadContextMap#remove(java.lang.String)}
110103
* .
111104
*/
112105
@Test
@@ -118,6 +111,9 @@ public void testRemove() {
118111
map.remove("key");
119112
assertFalse(map.containsKey("key"));
120113
assertEquals("value2", map.get("key2"));
114+
115+
map.clear();
116+
map.remove("test");
121117
}
122118

123119
@Test
@@ -132,6 +128,9 @@ public void testRemoveAll() {
132128
map.removeAll(newValues.keySet());
133129

134130
map.put("3", "value3");
131+
132+
map.clear();
133+
map.removeAll(newValues.keySet());
135134
}
136135

137136
@Test
@@ -148,7 +147,7 @@ public void testClear() {
148147
* @return
149148
*/
150149
private StringArrayThreadContextMap createMap() {
151-
final StringArrayThreadContextMap map = new StringArrayThreadContextMap(true);
150+
final StringArrayThreadContextMap map = new StringArrayThreadContextMap();
152151
assertTrue(map.isEmpty());
153152
map.put("key", "value");
154153
map.put("key2", "value2");
@@ -159,7 +158,7 @@ private StringArrayThreadContextMap createMap() {
159158

160159
@Test
161160
public void testGetCopyReturnsMutableMap() {
162-
final StringArrayThreadContextMap map = new StringArrayThreadContextMap(true);
161+
final StringArrayThreadContextMap map = new StringArrayThreadContextMap();
163162
assertTrue(map.isEmpty());
164163
final Map<String, String> copy = map.getCopy();
165164
assertTrue(copy.isEmpty());
@@ -173,7 +172,7 @@ public void testGetCopyReturnsMutableMap() {
173172

174173
@Test
175174
public void testGetCopyReturnsMutableCopy() {
176-
final StringArrayThreadContextMap map = new StringArrayThreadContextMap(true);
175+
final StringArrayThreadContextMap map = new StringArrayThreadContextMap();
177176
map.put("key1", "value1");
178177
assertFalse(map.isEmpty());
179178
final Map<String, String> copy = map.getCopy();
@@ -194,14 +193,14 @@ public void testGetCopyReturnsMutableCopy() {
194193

195194
@Test
196195
public void testGetImmutableMapReturnsNullIfEmpty() {
197-
final StringArrayThreadContextMap map = new StringArrayThreadContextMap(true);
196+
final StringArrayThreadContextMap map = new StringArrayThreadContextMap();
198197
assertTrue(map.isEmpty());
199198
assertNull(map.getImmutableMapOrNull());
200199
}
201200

202201
@Test
203202
public void testGetImmutableMapReturnsImmutableMapIfNonEmpty() {
204-
final StringArrayThreadContextMap map = new StringArrayThreadContextMap(true);
203+
final StringArrayThreadContextMap map = new StringArrayThreadContextMap();
205204
map.put("key1", "value1");
206205
assertFalse(map.isEmpty());
207206

@@ -214,7 +213,7 @@ public void testGetImmutableMapReturnsImmutableMapIfNonEmpty() {
214213

215214
@Test
216215
public void testGetImmutableMapCopyNotAffectdByContextMapChanges() {
217-
final StringArrayThreadContextMap map = new StringArrayThreadContextMap(true);
216+
final StringArrayThreadContextMap map = new StringArrayThreadContextMap();
218217
map.put("key1", "value1");
219218
assertFalse(map.isEmpty());
220219

@@ -230,7 +229,7 @@ public void testGetImmutableMapCopyNotAffectdByContextMapChanges() {
230229

231230
@Test
232231
public void testToStringShowsMapContext() {
233-
final StringArrayThreadContextMap map = new StringArrayThreadContextMap(true);
232+
final StringArrayThreadContextMap map = new StringArrayThreadContextMap();
234233
assertEquals("{}", map.toString());
235234

236235
map.put("key1", "value1");
@@ -242,24 +241,47 @@ public void testToStringShowsMapContext() {
242241
}
243242

244243
@Test
245-
@ClearSystemProperty(key = StringArrayThreadContextMap.INHERITABLE_MAP)
246-
@InitializesThreadContext
247-
public void testThreadLocalNotInheritableByDefault() {
248-
ThreadContextMapFactory.init();
249-
final ThreadLocal<Object[]> threadLocal = StringArrayThreadContextMap.createThreadLocalMap(true);
250-
assertFalse(threadLocal instanceof InheritableThreadLocal<?>);
244+
public void testEmptyMap() {
245+
assertNull(UnmodifiableArrayBackedMap.EMPTY_MAP.get("test"));
251246
}
252247

253248
@Test
254-
@SetTestProperty(key = StringArrayThreadContextMap.INHERITABLE_MAP, value = "true")
255-
@InitializesThreadContext
256-
public void testThreadLocalInheritableIfConfigured() {
257-
ThreadContextMapFactory.init();
258-
try {
259-
final ThreadLocal<Object[]> threadLocal = StringArrayThreadContextMap.createThreadLocalMap(true);
260-
assertTrue(threadLocal instanceof InheritableThreadLocal<?>);
261-
} finally {
262-
System.clearProperty(StringArrayThreadContextMap.INHERITABLE_MAP);
263-
}
249+
public void testForEachBiConsumer_Log4jUtil() {
250+
StringArrayThreadContextMap map = createMap();
251+
Set<String> keys = new HashSet<>();
252+
org.apache.logging.log4j.util.BiConsumer<String, String> log4j_util_action =
253+
new org.apache.logging.log4j.util.BiConsumer<String, String>() {
254+
@Override
255+
public void accept(String key, String value) {
256+
keys.add(key);
257+
}
258+
};
259+
map.forEach(log4j_util_action);
260+
assertEquals(map.toMap().keySet(), keys);
261+
262+
map.clear();
263+
keys.clear();
264+
map.forEach(log4j_util_action);
265+
assertTrue(keys.isEmpty());
266+
}
267+
268+
@Test
269+
public void testForEachTriConsumer() {
270+
StringArrayThreadContextMap map = createMap();
271+
HashMap<String, String> iterationResultMap = new HashMap<>();
272+
TriConsumer<String, String, Map<String, String>> triConsumer =
273+
new TriConsumer<String, String, Map<String, String>>() {
274+
@Override
275+
public void accept(String k, String v, Map<String, String> s) {
276+
s.put(k, v);
277+
}
278+
};
279+
map.forEach(triConsumer, iterationResultMap);
280+
assertEquals(map.toMap(), iterationResultMap);
281+
282+
map.clear();
283+
iterationResultMap.clear();
284+
map.forEach(triConsumer, iterationResultMap);
285+
assertTrue(iterationResultMap.isEmpty());
264286
}
265287
}

‎log4j-api-test/src/test/java/org/apache/logging/log4j/spi/UnmodifiableArrayBackedMapTest.java ‎log4j-api-test/src/test/java/org/apache/logging/log4j/internal/map/UnmodifiableArrayBackedMapTest.java

+178-119
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@
1414
* See the License for the specific language governing permissions and
1515
* limitations under the License.
1616
*/
17-
package org.apache.logging.log4j.spi;
17+
package org.apache.logging.log4j.internal.map;
1818

19-
import static org.junit.Assert.assertEquals;
20-
import static org.junit.Assert.assertFalse;
21-
import static org.junit.Assert.assertTrue;
22-
import static org.junit.Assert.fail;
19+
import static org.junit.jupiter.api.Assertions.assertEquals;
20+
import static org.junit.jupiter.api.Assertions.assertFalse;
2321
import static org.junit.jupiter.api.Assertions.assertNotEquals;
22+
import static org.junit.jupiter.api.Assertions.assertNull;
23+
import static org.junit.jupiter.api.Assertions.assertTrue;
24+
import static org.junit.jupiter.api.Assertions.fail;
2425

2526
import java.util.ArrayList;
2627
import java.util.Collections;
@@ -30,6 +31,7 @@
3031
import java.util.LinkedHashSet;
3132
import java.util.Map;
3233
import java.util.Set;
34+
import org.apache.logging.log4j.util.TriConsumer;
3335
import org.junit.jupiter.api.Test;
3436

3537
public class UnmodifiableArrayBackedMapTest {
@@ -49,20 +51,52 @@ private HashMap<String, String> getTestParameters(int numParams) {
4951
}
5052

5153
@Test
52-
public void testReads() {
53-
assertEquals(UnmodifiableArrayBackedMap.EMPTY_MAP.get("test"), null);
54+
public void testCopyAndPut() {
55+
UnmodifiableArrayBackedMap testMap = UnmodifiableArrayBackedMap.EMPTY_MAP;
56+
testMap = testMap.copyAndPut("1", "value1");
57+
assertTrue(testMap.containsKey("1"));
58+
assertEquals(testMap.get("1"), "value1");
59+
60+
testMap = testMap.copyAndPut("1", "another value");
61+
assertTrue(testMap.containsKey("1"));
62+
assertEquals(testMap.get("1"), "another value");
63+
64+
HashMap<String, String> newValues = getTestParameters();
65+
testMap = testMap.copyAndPutAll(newValues);
66+
assertEquals(testMap.get("1"), "value1");
67+
assertEquals(testMap.get("4"), "value4");
68+
}
69+
70+
@Test
71+
public void testCopyAndRemove() {
72+
// general removing from well-populated set
5473
HashMap<String, String> params = getTestParameters();
5574
UnmodifiableArrayBackedMap testMap = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPutAll(params);
56-
for (Map.Entry<String, String> entry : params.entrySet()) {
57-
String key = entry.getKey();
58-
String value = entry.getValue();
59-
assertTrue(testMap.containsKey(key));
60-
assertTrue(testMap.containsValue(value));
61-
assertEquals(testMap.get(key), params.get(key));
62-
}
63-
assertFalse(testMap.containsKey("not_present"));
64-
assertFalse(testMap.containsValue("not_present"));
65-
assertEquals(null, testMap.get("not_present"));
75+
testMap = testMap.copyAndRemove("2");
76+
testMap = testMap.copyAndRemove("not_present");
77+
assertEquals(4, testMap.size());
78+
assertFalse(testMap.containsKey("2"));
79+
assertTrue(testMap.containsKey("1"));
80+
assertFalse(testMap.containsValue("value2"));
81+
82+
// test removing from empty set
83+
testMap = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPut("test", "test");
84+
testMap = testMap.copyAndRemove("test");
85+
assertTrue(testMap.isEmpty());
86+
87+
// test removing first of two elements
88+
testMap = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPut("test1", "test1");
89+
testMap = testMap.copyAndPut("test2", "test2");
90+
testMap = testMap.copyAndRemove("test1");
91+
assertFalse(testMap.containsKey("test1"));
92+
assertTrue(testMap.containsKey("test2"));
93+
94+
// test removing second of two elements
95+
testMap = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPut("test1", "test1");
96+
testMap = testMap.copyAndPut("test2", "test2");
97+
testMap = testMap.copyAndRemove("test2");
98+
assertTrue(testMap.containsKey("test1"));
99+
assertFalse(testMap.containsKey("test2"));
66100
}
67101

68102
@Test
@@ -111,29 +145,8 @@ public void testCopyAndRemoveAll() {
111145
}
112146

113147
@Test
114-
public void testCopyAndPut() {
115-
UnmodifiableArrayBackedMap testMap = UnmodifiableArrayBackedMap.EMPTY_MAP;
116-
testMap = testMap.copyAndPut("1", "value1");
117-
assertTrue(testMap.containsKey("1"));
118-
assertEquals(testMap.get("1"), "value1");
119-
120-
testMap = testMap.copyAndPut("1", "another value");
121-
assertTrue(testMap.containsKey("1"));
122-
assertEquals(testMap.get("1"), "another value");
123-
124-
HashMap<String, String> newValues = getTestParameters();
125-
testMap = testMap.copyAndPutAll(newValues);
126-
assertEquals(testMap.get("1"), "value1");
127-
assertEquals(testMap.get("4"), "value4");
128-
}
129-
130-
@Test
131-
public void testInstanceCopy() {
132-
HashMap<String, String> params = getTestParameters();
133-
UnmodifiableArrayBackedMap testMap = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPutAll(params);
134-
135-
UnmodifiableArrayBackedMap testMap2 = new UnmodifiableArrayBackedMap(testMap);
136-
assertEquals(testMap, testMap2);
148+
public void testEmptyMap() {
149+
assertNull(UnmodifiableArrayBackedMap.EMPTY_MAP.get("test"));
137150
}
138151

139152
@Test
@@ -176,76 +189,6 @@ public void testEntrySetMutatorsBlocked() {
176189
}
177190
}
178191

179-
@Test
180-
public void testNullValue() {
181-
UnmodifiableArrayBackedMap testMap = UnmodifiableArrayBackedMap.EMPTY_MAP;
182-
testMap = testMap.copyAndPut("key", null);
183-
assertTrue(testMap.containsKey("key"));
184-
assertTrue(testMap.containsValue(null));
185-
assertTrue(testMap.size() == 1);
186-
assertEquals(testMap.get("key"), null);
187-
}
188-
189-
@Test
190-
public void testMutatorsBlocked() {
191-
UnmodifiableArrayBackedMap testMap = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPutAll(getTestParameters());
192-
try {
193-
testMap.put("a", "a");
194-
fail("put() wasn't blocked");
195-
} catch (UnsupportedOperationException e) {
196-
}
197-
198-
try {
199-
testMap.putAll(new HashMap<>());
200-
fail("putAll() wasn't blocked");
201-
} catch (UnsupportedOperationException e) {
202-
}
203-
204-
try {
205-
testMap.remove("1");
206-
fail("remove() wasn't blocked");
207-
} catch (UnsupportedOperationException e) {
208-
}
209-
210-
try {
211-
testMap.clear();
212-
fail("clear() wasn't blocked");
213-
} catch (UnsupportedOperationException e) {
214-
}
215-
}
216-
217-
@Test
218-
public void testCopyAndRemove() {
219-
// general removing from well-populated set
220-
HashMap<String, String> params = getTestParameters();
221-
UnmodifiableArrayBackedMap testMap = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPutAll(params);
222-
testMap = testMap.copyAndRemove("2");
223-
testMap = testMap.copyAndRemove("not_present");
224-
assertEquals(4, testMap.size());
225-
assertFalse(testMap.containsKey("2"));
226-
assertTrue(testMap.containsKey("1"));
227-
assertFalse(testMap.containsValue("value2"));
228-
229-
// test removing from empty set
230-
testMap = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPut("test", "test");
231-
testMap = testMap.copyAndRemove("test");
232-
assertTrue(testMap.isEmpty());
233-
234-
// test removing first of two elements
235-
testMap = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPut("test1", "test1");
236-
testMap = testMap.copyAndPut("test2", "test2");
237-
testMap = testMap.copyAndRemove("test1");
238-
assertFalse(testMap.containsKey("test1"));
239-
assertTrue(testMap.containsKey("test2"));
240-
241-
// test removing second of two elements
242-
testMap = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPut("test1", "test1");
243-
testMap = testMap.copyAndPut("test2", "test2");
244-
testMap = testMap.copyAndRemove("test2");
245-
assertTrue(testMap.containsKey("test1"));
246-
assertFalse(testMap.containsKey("test2"));
247-
}
248-
249192
/**
250193
* Tests various situations with .equals(). Test tries comparisons in both
251194
* directions, to make sure that HashMap.equals(UnmodifiableArrayBackedMap) work
@@ -261,11 +204,14 @@ public void testEqualsHashCodeWithIdenticalContent() {
261204
}
262205

263206
@Test
264-
public void testEqualsWhenOneValueDiffers() {
207+
public void testEqualsHashCodeWithOneEmptyMap() {
265208
HashMap<String, String> params = getTestParameters();
266209
UnmodifiableArrayBackedMap testMap = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPutAll(params);
267-
assertNotEquals(params, testMap.copyAndPut("1", "different value"));
268-
assertNotEquals(testMap.copyAndPut("1", "different value"), params);
210+
// verify empty maps are not equal to non-empty maps
211+
assertNotEquals(params, UnmodifiableArrayBackedMap.EMPTY_MAP);
212+
assertNotEquals(new HashMap<>(), testMap);
213+
assertNotEquals(UnmodifiableArrayBackedMap.EMPTY_MAP, params);
214+
assertNotEquals(testMap, new HashMap<>());
269215
}
270216

271217
@Test
@@ -283,14 +229,56 @@ public void testEqualsHashCodeWithOneKeyRemoved() {
283229
}
284230

285231
@Test
286-
public void testEqualsHashCodeWithOneEmptyMap() {
232+
public void testEqualsWhenOneValueDiffers() {
287233
HashMap<String, String> params = getTestParameters();
288234
UnmodifiableArrayBackedMap testMap = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPutAll(params);
289-
// verify empty maps are not equal to non-empty maps
290-
assertNotEquals(params, UnmodifiableArrayBackedMap.EMPTY_MAP);
291-
assertNotEquals(new HashMap<>(), testMap);
292-
assertNotEquals(UnmodifiableArrayBackedMap.EMPTY_MAP, params);
293-
assertNotEquals(testMap, new HashMap<>());
235+
assertNotEquals(params, testMap.copyAndPut("1", "different value"));
236+
assertNotEquals(testMap.copyAndPut("1", "different value"), params);
237+
}
238+
239+
@Test
240+
public void testForEachBiConsumer_JavaUtil() {
241+
UnmodifiableArrayBackedMap map = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPutAll(getTestParameters());
242+
Set<String> keys = new HashSet<>();
243+
java.util.function.BiConsumer<String, String> java_util_action =
244+
new java.util.function.BiConsumer<String, String>() {
245+
@Override
246+
public void accept(String key, String value) {
247+
keys.add(key);
248+
}
249+
};
250+
map.forEach(java_util_action);
251+
assertEquals(map.keySet(), keys);
252+
}
253+
254+
@Test
255+
public void testForEachBiConsumer_Log4jUtil() {
256+
UnmodifiableArrayBackedMap map = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPutAll(getTestParameters());
257+
Set<String> keys = new HashSet<>();
258+
org.apache.logging.log4j.util.BiConsumer<String, String> log4j_util_action =
259+
new org.apache.logging.log4j.util.BiConsumer<String, String>() {
260+
@Override
261+
public void accept(String key, String value) {
262+
keys.add(key);
263+
}
264+
};
265+
map.forEach(log4j_util_action);
266+
assertEquals(map.keySet(), keys);
267+
}
268+
269+
@Test
270+
public void testForEachTriConsumer() {
271+
UnmodifiableArrayBackedMap map = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPutAll(getTestParameters());
272+
HashMap<String, String> iterationResultMap = new HashMap<>();
273+
TriConsumer<String, String, Map<String, String>> triConsumer =
274+
new TriConsumer<String, String, Map<String, String>>() {
275+
@Override
276+
public void accept(String k, String v, Map<String, String> s) {
277+
s.put(k, v);
278+
}
279+
};
280+
map.forEach(triConsumer, iterationResultMap);
281+
assertEquals(map, iterationResultMap);
294282
}
295283

296284
@Test
@@ -306,6 +294,70 @@ public void testImmutability() {
306294
assertEquals(originalMap, params);
307295
}
308296

297+
@Test
298+
public void testInstanceCopy() {
299+
HashMap<String, String> params = getTestParameters();
300+
UnmodifiableArrayBackedMap testMap = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPutAll(params);
301+
302+
UnmodifiableArrayBackedMap testMap2 = new UnmodifiableArrayBackedMap(testMap);
303+
assertEquals(testMap, testMap2);
304+
}
305+
306+
@Test
307+
public void testMutatorsBlocked() {
308+
UnmodifiableArrayBackedMap testMap = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPutAll(getTestParameters());
309+
try {
310+
testMap.put("a", "a");
311+
fail("put() wasn't blocked");
312+
} catch (UnsupportedOperationException e) {
313+
}
314+
315+
try {
316+
testMap.putAll(new HashMap<>());
317+
fail("putAll() wasn't blocked");
318+
} catch (UnsupportedOperationException e) {
319+
}
320+
321+
try {
322+
testMap.remove("1");
323+
fail("remove() wasn't blocked");
324+
} catch (UnsupportedOperationException e) {
325+
}
326+
327+
try {
328+
testMap.clear();
329+
fail("clear() wasn't blocked");
330+
} catch (UnsupportedOperationException e) {
331+
}
332+
}
333+
334+
@Test
335+
public void testNullValue() {
336+
UnmodifiableArrayBackedMap testMap = UnmodifiableArrayBackedMap.EMPTY_MAP;
337+
testMap = testMap.copyAndPut("key", null);
338+
assertTrue(testMap.containsKey("key"));
339+
assertTrue(testMap.containsValue(null));
340+
assertTrue(testMap.size() == 1);
341+
assertEquals(testMap.get("key"), null);
342+
}
343+
344+
@Test
345+
public void testReads() {
346+
assertEquals(UnmodifiableArrayBackedMap.EMPTY_MAP.get("test"), null);
347+
HashMap<String, String> params = getTestParameters();
348+
UnmodifiableArrayBackedMap testMap = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPutAll(params);
349+
for (Map.Entry<String, String> entry : params.entrySet()) {
350+
String key = entry.getKey();
351+
String value = entry.getValue();
352+
assertTrue(testMap.containsKey(key));
353+
assertTrue(testMap.containsValue(value));
354+
assertEquals(testMap.get(key), params.get(key));
355+
}
356+
assertFalse(testMap.containsKey("not_present"));
357+
assertFalse(testMap.containsValue("not_present"));
358+
assertEquals(null, testMap.get("not_present"));
359+
}
360+
309361
@Test
310362
public void testState() {
311363
UnmodifiableArrayBackedMap originalMap;
@@ -325,4 +377,11 @@ public void testState() {
325377
newMap = UnmodifiableArrayBackedMap.getInstance(originalMap.getBackingArray());
326378
assertEquals(originalMap, newMap);
327379
}
380+
381+
@Test
382+
public void testToMap() {
383+
UnmodifiableArrayBackedMap map = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPut("test", "test");
384+
// verify same instance, not just equals()
385+
assertTrue(map == map.toMap());
386+
}
328387
}

‎log4j-api/src/main/java/org/apache/logging/log4j/ThreadContext.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@
2424
import java.util.List;
2525
import java.util.Map;
2626
import java.util.NoSuchElementException;
27+
import org.apache.logging.log4j.internal.map.StringArrayThreadContextMap;
2728
import org.apache.logging.log4j.message.ParameterizedMessage;
2829
import org.apache.logging.log4j.spi.CleanableThreadContextMap;
2930
import org.apache.logging.log4j.spi.DefaultThreadContextMap;
3031
import org.apache.logging.log4j.spi.DefaultThreadContextStack;
3132
import org.apache.logging.log4j.spi.NoOpThreadContextMap;
3233
import org.apache.logging.log4j.spi.ReadOnlyThreadContextMap;
33-
import org.apache.logging.log4j.spi.StringArrayThreadContextMap;
3434
import org.apache.logging.log4j.spi.ThreadContextMap;
3535
import org.apache.logging.log4j.spi.ThreadContextMap2;
3636
import org.apache.logging.log4j.spi.ThreadContextMapFactory;

‎log4j-api/src/main/java/org/apache/logging/log4j/spi/StringArrayThreadContextMap.java ‎log4j-api/src/main/java/org/apache/logging/log4j/internal/map/StringArrayThreadContextMap.java

+8-62
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@
1414
* See the License for the specific language governing permissions and
1515
* limitations under the License.
1616
*/
17-
package org.apache.logging.log4j.spi;
17+
package org.apache.logging.log4j.internal.map;
1818

1919
import java.util.HashMap;
2020
import java.util.Map;
2121
import java.util.Objects;
22+
import org.apache.logging.log4j.spi.ThreadContextMap;
2223
import org.apache.logging.log4j.util.BiConsumer;
23-
import org.apache.logging.log4j.util.PropertiesUtil;
2424
import org.apache.logging.log4j.util.ReadOnlyStringMap;
2525
import org.apache.logging.log4j.util.TriConsumer;
2626

@@ -43,58 +43,21 @@ public class StringArrayThreadContextMap implements ThreadContextMap, ReadOnlySt
4343
*/
4444
public static final String INHERITABLE_MAP = "isThreadContextMapInheritable";
4545

46-
private final boolean useMap;
47-
private final ThreadLocal<Object[]> threadLocalMapState;
48-
49-
private static boolean inheritableMap;
50-
51-
static {
52-
init();
53-
}
54-
55-
// LOG4J2-479: by default, use a plain ThreadLocal, only use InheritableThreadLocal if configured.
56-
// (This method is package protected for JUnit tests.)
57-
static ThreadLocal<Object[]> createThreadLocalMap(final boolean isMapEnabled) {
58-
if (inheritableMap) {
59-
return new InheritableThreadLocal<Object[]>() {
60-
@Override
61-
protected Object[] childValue(final Object[] parentValue) {
62-
return parentValue;
63-
}
64-
};
65-
}
66-
// if not inheritable, return plain ThreadLocal with null as initial value
67-
return new ThreadLocal<>();
68-
}
69-
70-
static void init() {
71-
inheritableMap = PropertiesUtil.getProperties().getBooleanProperty(INHERITABLE_MAP);
72-
}
46+
private ThreadLocal<Object[]> threadLocalMapState;
7347

7448
public StringArrayThreadContextMap() {
75-
this(true);
76-
}
77-
78-
public StringArrayThreadContextMap(final boolean useMap) {
79-
this.useMap = useMap;
80-
this.threadLocalMapState = createThreadLocalMap(useMap);
49+
threadLocalMapState = new ThreadLocal<>();
8150
}
8251

8352
@Override
8453
public void put(final String key, final String value) {
85-
if (!useMap) {
86-
return;
87-
}
8854
final Object[] state = threadLocalMapState.get();
8955
final UnmodifiableArrayBackedMap modifiedMap =
9056
UnmodifiableArrayBackedMap.getInstance(state).copyAndPut(key, value);
9157
threadLocalMapState.set(modifiedMap.getBackingArray());
9258
}
9359

9460
public void putAll(final Map<String, String> m) {
95-
if (!useMap) {
96-
return;
97-
}
9861
final Object[] state = threadLocalMapState.get();
9962
final UnmodifiableArrayBackedMap modifiedMap =
10063
UnmodifiableArrayBackedMap.getInstance(state).copyAndPutAll(m);
@@ -152,27 +115,17 @@ public <V> void forEach(final BiConsumer<String, ? super V> action) {
152115
return;
153116
}
154117
final UnmodifiableArrayBackedMap map = UnmodifiableArrayBackedMap.getInstance(state);
155-
for (final Map.Entry<String, String> entry : map.entrySet()) {
156-
// BiConsumer should be able to handle values of any type V. In our case the values are of type String.
157-
@SuppressWarnings("unchecked")
158-
final V value = (V) entry.getValue();
159-
action.accept(entry.getKey(), value);
160-
}
118+
map.forEach(action);
161119
}
162120

163121
@Override
164122
public <V, S> void forEach(final TriConsumer<String, ? super V, S> action, final S state) {
165-
Object[] localState = threadLocalMapState.get();
123+
final Object[] localState = threadLocalMapState.get();
166124
if (localState == null) {
167125
return;
168126
}
169-
UnmodifiableArrayBackedMap map = UnmodifiableArrayBackedMap.getInstance(localState);
170-
for (final Map.Entry<String, String> entry : map.entrySet()) {
171-
// TriConsumer should be able to handle values of any type V. In our case the values are of type String.
172-
@SuppressWarnings("unchecked")
173-
final V value = (V) entry.getValue();
174-
action.accept(entry.getKey(), value, state);
175-
}
127+
final UnmodifiableArrayBackedMap map = UnmodifiableArrayBackedMap.getInstance(localState);
128+
map.forEach(action, state);
176129
}
177130

178131
@SuppressWarnings("unchecked")
@@ -224,7 +177,6 @@ public int hashCode() {
224177
+ ((state == null)
225178
? 0
226179
: UnmodifiableArrayBackedMap.getInstance(state).hashCode());
227-
result = prime * result + Boolean.valueOf(this.useMap).hashCode();
228180
return result;
229181
}
230182

@@ -236,12 +188,6 @@ public boolean equals(final Object obj) {
236188
if (obj == null) {
237189
return false;
238190
}
239-
if (obj instanceof StringArrayThreadContextMap) {
240-
final StringArrayThreadContextMap other = (StringArrayThreadContextMap) obj;
241-
if (this.useMap != other.useMap) {
242-
return false;
243-
}
244-
}
245191
if (!(obj instanceof ThreadContextMap)) {
246192
return false;
247193
}

‎log4j-api/src/main/java/org/apache/logging/log4j/spi/UnmodifiableArrayBackedMap.java ‎log4j-api/src/main/java/org/apache/logging/log4j/internal/map/UnmodifiableArrayBackedMap.java

+79-13
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* See the License for the specific language governing permissions and
1515
* limitations under the License.
1616
*/
17-
package org.apache.logging.log4j.spi;
17+
package org.apache.logging.log4j.internal.map;
1818

1919
import java.io.Serializable;
2020
import java.util.AbstractMap;
@@ -25,6 +25,8 @@
2525
import java.util.Map;
2626
import java.util.Objects;
2727
import java.util.Set;
28+
import org.apache.logging.log4j.util.ReadOnlyStringMap;
29+
import org.apache.logging.log4j.util.TriConsumer;
2830

2931
/**
3032
* This class represents an immutable map, which stores its state inside a single Object[]:
@@ -55,14 +57,19 @@
5557
* </ul>
5658
*
5759
*/
58-
class UnmodifiableArrayBackedMap extends AbstractMap<String, String> implements Serializable {
60+
class UnmodifiableArrayBackedMap extends AbstractMap<String, String> implements Serializable, ReadOnlyStringMap {
5961
/**
6062
* Implementation of Map.Entry. The implementation is simple since each instance
6163
* contains an index in the array, then getKey() and getValue() retrieve from
6264
* the array. Blocks modifications.
6365
*/
6466
private class UnmodifiableEntry implements Map.Entry<String, String> {
65-
private final int index;
67+
/**
68+
* This field is functionally final, but marking it as such can cause
69+
* performance problems. Consider marking it final after
70+
* https://bugs.openjdk.org/browse/JDK-8324186 is solved.
71+
*/
72+
private int index;
6673

6774
public UnmodifiableEntry(int index) {
6875
this.index = index;
@@ -143,9 +150,6 @@ public int size() {
143150

144151
public static final UnmodifiableArrayBackedMap EMPTY_MAP = new UnmodifiableArrayBackedMap(0);
145152

146-
private final Object[] backingArray;
147-
private int numEntries;
148-
149153
private static final int NUM_FIXED_ARRAY_ENTRIES = 1;
150154

151155
private static int getArrayIndexForKey(int entryIndex) {
@@ -156,11 +160,6 @@ private static int getArrayIndexForValue(int entryIndex) {
156160
return 2 * entryIndex + 1 + NUM_FIXED_ARRAY_ENTRIES;
157161
}
158162

159-
private UnmodifiableArrayBackedMap(int capacity) {
160-
this.backingArray = new Object[capacity * 2 + 1];
161-
this.backingArray[0] = 0;
162-
}
163-
164163
static UnmodifiableArrayBackedMap getInstance(Object[] backingArray) {
165164
if (backingArray == null || backingArray.length == 1) {
166165
return EMPTY_MAP;
@@ -169,6 +168,20 @@ static UnmodifiableArrayBackedMap getInstance(Object[] backingArray) {
169168
}
170169
}
171170

171+
/**
172+
* backingArray is functionally final, but marking it as such can cause
173+
* performance problems. Consider marking it final after
174+
* https://bugs.openjdk.org/browse/JDK-8324186 is solved.
175+
*/
176+
private Object[] backingArray;
177+
178+
private int numEntries;
179+
180+
private UnmodifiableArrayBackedMap(int capacity) {
181+
this.backingArray = new Object[capacity * 2 + 1];
182+
this.backingArray[0] = 0;
183+
}
184+
172185
private UnmodifiableArrayBackedMap(Object[] backingArray) {
173186
this.numEntries = (backingArray == null ? 0 : (int) backingArray[0]);
174187
this.backingArray = backingArray;
@@ -195,6 +208,11 @@ public void clear() {
195208
*/
196209
@Override
197210
public boolean containsKey(Object key) {
211+
return containsKey((String) key);
212+
}
213+
214+
@Override
215+
public boolean containsKey(String key) {
198216
int hashCode = key.hashCode();
199217
for (int i = 0; i < numEntries; i++) {
200218
if (backingArray[getArrayIndexForKey(i)].hashCode() == hashCode
@@ -292,7 +310,6 @@ UnmodifiableArrayBackedMap copyAndPutAll(Map<String, String> entriesToAdd) {
292310
* @return
293311
*/
294312
UnmodifiableArrayBackedMap copyAndRemove(String key) {
295-
UnmodifiableArrayBackedMap newMap = new UnmodifiableArrayBackedMap(numEntries);
296313
int indexToRemove = -1;
297314
for (int oldIndex = 0; oldIndex < numEntries; oldIndex++) {
298315
if (backingArray[getArrayIndexForKey(oldIndex)].hashCode() == key.hashCode()
@@ -309,6 +326,7 @@ UnmodifiableArrayBackedMap copyAndRemove(String key) {
309326
// we have 1 item and we're about to remove it
310327
return EMPTY_MAP;
311328
}
329+
UnmodifiableArrayBackedMap newMap = new UnmodifiableArrayBackedMap(numEntries);
312330
if (indexToRemove > 0) {
313331
// copy entries before the removed one
314332
System.arraycopy(backingArray, 1, newMap.backingArray, 1, indexToRemove * 2);
@@ -419,6 +437,43 @@ private void updateNumEntriesInArray() {
419437
backingArray[0] = numEntries;
420438
}
421439

440+
/**
441+
* This version of forEach is defined on the Map interface.
442+
*/
443+
@Override
444+
public void forEach(java.util.function.BiConsumer<? super String, ? super String> action) {
445+
for (int i = 0; i < numEntries; i++) {
446+
// BiConsumer should be able to handle values of any type V. In our case the values are of type String.
447+
final String key = (String) backingArray[getArrayIndexForKey(i)];
448+
final String value = (String) backingArray[getArrayIndexForValue(i)];
449+
action.accept(key, value);
450+
}
451+
}
452+
453+
/**
454+
* This version of forEach is defined on the ReadOnlyStringMap interface.
455+
*/
456+
@SuppressWarnings("unchecked")
457+
@Override
458+
public <V> void forEach(final org.apache.logging.log4j.util.BiConsumer<String, ? super V> action) {
459+
for (int i = 0; i < numEntries; i++) {
460+
// BiConsumer should be able to handle values of any type V. In our case the values are of type String.
461+
final String key = (String) backingArray[getArrayIndexForKey(i)];
462+
final V value = (V) backingArray[getArrayIndexForValue(i)];
463+
action.accept(key, value);
464+
}
465+
}
466+
467+
@SuppressWarnings("unchecked")
468+
public <V, S> void forEach(final TriConsumer<String, ? super V, S> action, final S state) {
469+
for (int i = 0; i < numEntries; i++) {
470+
// TriConsumer should be able to handle values of any type V. In our case the values are of type String.
471+
final String key = (String) backingArray[getArrayIndexForKey(i)];
472+
final V value = (V) backingArray[getArrayIndexForValue(i)];
473+
action.accept(key, value, state);
474+
}
475+
}
476+
422477
@Override
423478
public Set<Entry<String, String>> entrySet() {
424479
return new UnmodifiableEntrySet();
@@ -429,14 +484,20 @@ public Set<Entry<String, String>> entrySet() {
429484
*/
430485
@Override
431486
public String get(Object key) {
487+
return getValue((String) key);
488+
}
489+
490+
@SuppressWarnings("unchecked")
491+
@Override
492+
public <V> V getValue(String key) {
432493
if (numEntries == 0) {
433494
return null;
434495
}
435496
int hashCode = key.hashCode();
436497
for (int i = 0; i < numEntries; i++) {
437498
if (backingArray[getArrayIndexForKey(i)].hashCode() == hashCode
438499
&& backingArray[getArrayIndexForKey(i)].equals(key)) {
439-
return (String) backingArray[getArrayIndexForValue(i)];
500+
return (V) backingArray[getArrayIndexForValue(i)];
440501
}
441502
}
442503
return null;
@@ -483,4 +544,9 @@ public String remove(Object key) {
483544
public int size() {
484545
return numEntries;
485546
}
547+
548+
@Override
549+
public Map<String, String> toMap() {
550+
return this;
551+
}
486552
}

‎log4j-api/src/main/java/org/apache/logging/log4j/spi/ThreadContextMapFactory.java

-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ public static void init() {
7070
CopyOnWriteSortedArrayThreadContextMap.init();
7171
GarbageFreeSortedArrayThreadContextMap.init();
7272
DefaultThreadContextMap.init();
73-
StringArrayThreadContextMap.init();
7473
initPrivate();
7574
}
7675

0 commit comments

Comments
 (0)
Please sign in to comment.