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