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.SortedArrayStringMap;
25 import org.apache.logging.log4j.util.StringMap;
26 import org.apache.logging.log4j.util.PropertiesUtil;
27
28
29
30
31
32
33
34
35
36
37 class CopyOnWriteSortedArrayThreadContextMap implements ThreadContextMap2, CopyOnWrite {
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 private static final StringMap EMPTY_CONTEXT_DATA = new SortedArrayStringMap();
56 static {
57 EMPTY_CONTEXT_DATA.freeze();
58 }
59
60 private final ThreadLocal<StringMap> localMap;
61
62 public CopyOnWriteSortedArrayThreadContextMap() {
63 this.localMap = createThreadLocalMap();
64 }
65
66
67
68 private ThreadLocal<StringMap> createThreadLocalMap() {
69 final PropertiesUtil managerProps = PropertiesUtil.getProperties();
70 final boolean inheritable = managerProps.getBooleanProperty(INHERITABLE_MAP);
71 if (inheritable) {
72 return new InheritableThreadLocal<StringMap>() {
73 @Override
74 protected StringMap childValue(final StringMap parentValue) {
75 return parentValue != null ? createStringMap(parentValue) : null;
76 }
77 };
78 }
79
80 return new ThreadLocal<>();
81 }
82
83
84
85
86
87
88
89
90 protected StringMap createStringMap() {
91 return new SortedArrayStringMap(PropertiesUtil.getProperties().getIntegerProperty(
92 PROPERTY_NAME_INITIAL_CAPACITY, DEFAULT_INITIAL_CAPACITY));
93 }
94
95
96
97
98
99
100
101
102
103
104 protected StringMap createStringMap(final ReadOnlyStringMap original) {
105 return new SortedArrayStringMap(original);
106 }
107
108 @Override
109 public void put(final String key, final String value) {
110 StringMap map = localMap.get();
111 map = map == null ? createStringMap() : createStringMap(map);
112 map.putValue(key, value);
113 map.freeze();
114 localMap.set(map);
115 }
116
117 @Override
118 public void putAll(final Map<String, String> values) {
119 if (values == null || values.isEmpty()) {
120 return;
121 }
122 StringMap map = localMap.get();
123 map = map == null ? createStringMap() : createStringMap(map);
124 for (final Map.Entry<String, String> entry : values.entrySet()) {
125 map.putValue(entry.getKey(), entry.getValue());
126 }
127 map.freeze();
128 localMap.set(map);
129 }
130
131 @Override
132 public String get(final String key) {
133 final StringMap map = localMap.get();
134 return map == null ? null : (String) map.getValue(key);
135 }
136
137 @Override
138 public void remove(final String key) {
139 final StringMap map = localMap.get();
140 if (map != null) {
141 final StringMap copy = createStringMap(map);
142 copy.remove(key);
143 copy.freeze();
144 localMap.set(copy);
145 }
146 }
147
148 @Override
149 public void clear() {
150 localMap.remove();
151 }
152
153 @Override
154 public boolean containsKey(final String key) {
155 final StringMap map = localMap.get();
156 return map != null && map.containsKey(key);
157 }
158
159 @Override
160 public Map<String, String> getCopy() {
161 final StringMap map = localMap.get();
162 return map == null ? new HashMap<String, String>() : map.toMap();
163 }
164
165
166
167
168 @Override
169 public StringMap getReadOnlyContextData() {
170 final StringMap map = localMap.get();
171 return map == null ? EMPTY_CONTEXT_DATA : map;
172 }
173
174 @Override
175 public Map<String, String> getImmutableMapOrNull() {
176 final StringMap map = localMap.get();
177 return map == null ? null : Collections.unmodifiableMap(map.toMap());
178 }
179
180 @Override
181 public boolean isEmpty() {
182 final StringMap map = localMap.get();
183 return map == null || map.size() == 0;
184 }
185
186 @Override
187 public String toString() {
188 final StringMap map = localMap.get();
189 return map == null ? "{}" : map.toString();
190 }
191
192 @Override
193 public int hashCode() {
194 final int prime = 31;
195 int result = 1;
196 final StringMap map = this.localMap.get();
197 result = prime * result + ((map == null) ? 0 : map.hashCode());
198 return result;
199 }
200
201 @Override
202 public boolean equals(final Object obj) {
203 if (this == obj) {
204 return true;
205 }
206 if (obj == null) {
207 return false;
208 }
209 if (!(obj instanceof ThreadContextMap)) {
210 return false;
211 }
212 final ThreadContextMap other = (ThreadContextMap) obj;
213 final Map<String, String> map = this.getImmutableMapOrNull();
214 final Map<String, String> otherMap = other.getImmutableMapOrNull();
215 if (map == null) {
216 if (otherMap != null) {
217 return false;
218 }
219 } else if (!map.equals(otherMap)) {
220 return false;
221 }
222 return true;
223 }
224 }