1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.spi;
18
19 import java.util.Collections;
20 import java.util.HashMap;
21 import java.util.Map;
22 import java.util.Objects;
23
24 import org.apache.logging.log4j.util.ReadOnlyStringMap;
25 import org.apache.logging.log4j.util.SortedArrayStringMap;
26 import org.apache.logging.log4j.util.StringMap;
27 import org.apache.logging.log4j.util.PropertiesUtil;
28
29
30
31
32
33
34
35
36
37
38 class CopyOnWriteSortedArrayThreadContextMap implements ReadOnlyThreadContextMap, ObjectThreadContextMap, CopyOnWrite {
39
40
41
42
43
44 public static final String INHERITABLE_MAP = "isThreadContextMapInheritable";
45
46
47
48
49 protected static final int DEFAULT_INITIAL_CAPACITY = 16;
50
51
52
53
54 protected static final String PROPERTY_NAME_INITIAL_CAPACITY = "log4j2.ThreadContext.initial.capacity";
55
56 private static final StringMap EMPTY_CONTEXT_DATA = new SortedArrayStringMap(1);
57
58 private static volatile int initialCapacity;
59 private static volatile boolean inheritableMap;
60
61
62
63
64
65 static void init() {
66 final PropertiesUtil properties = PropertiesUtil.getProperties();
67 initialCapacity = properties.getIntegerProperty(PROPERTY_NAME_INITIAL_CAPACITY, DEFAULT_INITIAL_CAPACITY);
68 inheritableMap = properties.getBooleanProperty(INHERITABLE_MAP);
69 }
70
71 static {
72 EMPTY_CONTEXT_DATA.freeze();
73 init();
74 }
75
76 private final ThreadLocal<StringMap> localMap;
77
78 public CopyOnWriteSortedArrayThreadContextMap() {
79 this.localMap = createThreadLocalMap();
80 }
81
82
83
84 private ThreadLocal<StringMap> createThreadLocalMap() {
85 if (inheritableMap) {
86 return new InheritableThreadLocal<StringMap>() {
87 @Override
88 protected StringMap childValue(final StringMap parentValue) {
89 if (parentValue == null) {
90 return null;
91 }
92 final StringMap stringMap = createStringMap(parentValue);
93 stringMap.freeze();
94 return stringMap;
95 }
96 };
97 }
98
99 return new ThreadLocal<>();
100 }
101
102
103
104
105
106
107
108
109 protected StringMap createStringMap() {
110 return new SortedArrayStringMap(initialCapacity);
111 }
112
113
114
115
116
117
118
119
120
121
122 protected StringMap createStringMap(final ReadOnlyStringMap original) {
123 return new SortedArrayStringMap(original);
124 }
125
126 @Override
127 public void put(final String key, final String value) {
128 putValue(key, value);
129 }
130
131 @Override
132 public void putValue(final String key, final Object value) {
133 StringMap map = localMap.get();
134 map = map == null ? createStringMap() : createStringMap(map);
135 map.putValue(key, value);
136 map.freeze();
137 localMap.set(map);
138 }
139
140 @Override
141 public void putAll(final Map<String, String> values) {
142 if (values == null || values.isEmpty()) {
143 return;
144 }
145 StringMap map = localMap.get();
146 map = map == null ? createStringMap() : createStringMap(map);
147 for (final Map.Entry<String, String> entry : values.entrySet()) {
148 map.putValue(entry.getKey(), entry.getValue());
149 }
150 map.freeze();
151 localMap.set(map);
152 }
153
154 @Override
155 public <V> void putAllValues(final Map<String, V> values) {
156 if (values == null || values.isEmpty()) {
157 return;
158 }
159 StringMap map = localMap.get();
160 map = map == null ? createStringMap() : createStringMap(map);
161 for (final Map.Entry<String, V> entry : values.entrySet()) {
162 map.putValue(entry.getKey(), entry.getValue());
163 }
164 map.freeze();
165 localMap.set(map);
166 }
167
168 @Override
169 public String get(final String key) {
170 return (String) getValue(key);
171 }
172
173 @Override
174 public <V> V getValue(final String key) {
175 final StringMap map = localMap.get();
176 return map == null ? null : map.<V>getValue(key);
177 }
178
179 @Override
180 public void remove(final String key) {
181 final StringMap map = localMap.get();
182 if (map != null) {
183 final StringMap copy = createStringMap(map);
184 copy.remove(key);
185 copy.freeze();
186 localMap.set(copy);
187 }
188 }
189
190 @Override
191 public void removeAll(final Iterable<String> keys) {
192 final StringMap map = localMap.get();
193 if (map != null) {
194 final StringMap copy = createStringMap(map);
195 for (final String key : keys) {
196 copy.remove(key);
197 }
198 copy.freeze();
199 localMap.set(copy);
200 }
201 }
202
203 @Override
204 public void clear() {
205 localMap.remove();
206 }
207
208 @Override
209 public boolean containsKey(final String key) {
210 final StringMap map = localMap.get();
211 return map != null && map.containsKey(key);
212 }
213
214 @Override
215 public Map<String, String> getCopy() {
216 final StringMap map = localMap.get();
217 return map == null ? new HashMap<>() : map.toMap();
218 }
219
220
221
222
223 @Override
224 public StringMap getReadOnlyContextData() {
225 final StringMap map = localMap.get();
226 return map == null ? EMPTY_CONTEXT_DATA : map;
227 }
228
229 @Override
230 public Map<String, String> getImmutableMapOrNull() {
231 final StringMap map = localMap.get();
232 return map == null ? null : Collections.unmodifiableMap(map.toMap());
233 }
234
235 @Override
236 public boolean isEmpty() {
237 final StringMap map = localMap.get();
238 return map == null || map.isEmpty();
239 }
240
241 @Override
242 public String toString() {
243 final StringMap map = localMap.get();
244 return map == null ? "{}" : map.toString();
245 }
246
247 @Override
248 public int hashCode() {
249 final int prime = 31;
250 int result = 1;
251 final StringMap map = this.localMap.get();
252 result = prime * result + ((map == null) ? 0 : map.hashCode());
253 return result;
254 }
255
256 @Override
257 public boolean equals(final Object obj) {
258 if (this == obj) {
259 return true;
260 }
261 if (obj == null) {
262 return false;
263 }
264 if (!(obj instanceof ThreadContextMap)) {
265 return false;
266 }
267 final ThreadContextMap other = (ThreadContextMap) obj;
268 final Map<String, String> map = this.getImmutableMapOrNull();
269 final Map<String, String> otherMap = other.getImmutableMapOrNull();
270 return Objects.equals(map, otherMap);
271 }
272 }