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