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