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