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