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