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