1 package com.github.valfirst.slf4jtest;
2
3 import java.util.Collections;
4 import java.util.HashMap;
5 import java.util.Map;
6 import java.util.Objects;
7 import java.util.TreeMap;
8 import org.slf4j.MDC;
9 import org.slf4j.helpers.BasicMDCAdapter;
10
11 public class TestMDCAdapter extends BasicMDCAdapter {
12
13 private final ThreadLocal<Map<String, String>> value;
14 private final boolean initialEnable;
15 private final boolean initialInherit;
16 private final boolean initialReturnNullCopyWhenMdcNotSet;
17 private final boolean initialAllowNullValues;
18 private volatile boolean enable;
19 private volatile boolean inherit;
20 private volatile boolean returnNullCopyWhenMdcNotSet;
21 private volatile boolean allowNullValues;
22
23 public TestMDCAdapter() {
24 this(OverridableProperties.createUnchecked("slf4jtest"));
25 }
26
27 TestMDCAdapter(OverridableProperties properties) {
28 enable = initialEnable = getBooleanProperty(properties, "mdc.enable", true);
29 inherit = initialInherit = getBooleanProperty(properties, "mdc.inherit", false);
30 returnNullCopyWhenMdcNotSet =
31 initialReturnNullCopyWhenMdcNotSet =
32 getBooleanProperty(properties, "mdc.return.null.copy.when.mdc.not.set", false);
33 allowNullValues =
34 initialAllowNullValues = getBooleanProperty(properties, "mdc.allow.null.values", true);
35
36 value =
37 new InheritableThreadLocal<Map<String, String>>() {
38 @Override
39 protected Map<String, String> childValue(Map<String, String> parentValue) {
40 if (enable && inherit && parentValue != null) {
41 return new HashMap<>(parentValue);
42 } else {
43 return null;
44 }
45 }
46 };
47 }
48
49 static boolean getBooleanProperty(
50 OverridableProperties properties, String propertyKey, boolean defaultValue) {
51 return Boolean.parseBoolean(properties.getProperty(propertyKey, String.valueOf(defaultValue)));
52 }
53
54 @Override
55 public void put(final String key, final String val) {
56 if (!enable) {
57 return;
58 }
59 if (key == null) {
60 throw new IllegalArgumentException("key cannot be null");
61 }
62 if (val == null && !allowNullValues) {
63 throw new IllegalArgumentException("val cannot be null");
64 }
65 Map<String, String> map = value.get();
66 if (map == null) {
67 map = new HashMap<>();
68 value.set(map);
69 }
70 map.put(key, val);
71 }
72
73 @Override
74 public String get(final String key) {
75 if (!enable) {
76 return null;
77 }
78 if (key == null) {
79 throw new IllegalArgumentException("key cannot be null");
80 }
81 Map<String, String> map = value.get();
82 if (map != null) {
83 return map.get(key);
84 } else {
85 return null;
86 }
87 }
88
89 @Override
90 public void remove(final String key) {
91 if (!enable) {
92 return;
93 }
94 if (key == null) {
95 throw new IllegalArgumentException("key cannot be null");
96 }
97 Map<String, String> map = value.get();
98 if (map != null) {
99 map.remove(key);
100 }
101 }
102
103 @Override
104 public void clear() {
105 if (!enable) {
106 return;
107 }
108 Map<String, String> map = value.get();
109 if (map == null) {
110 return;
111 }
112 map.clear();
113 value.remove();
114 }
115
116
117
118
119
120
121
122
123
124
125
126
127 @Override
128 public Map<String, String> getCopyOfContextMap() {
129 if (!enable) {
130 return null;
131 }
132 Map<String, String> map = value.get();
133 if (map == null) {
134 if (returnNullCopyWhenMdcNotSet) {
135 return null;
136 } else {
137 return new TreeMap<>();
138 }
139 } else {
140 return new TreeMap<>(map);
141 }
142 }
143
144
145 Map<String, String> getContextMap() {
146 Map<String, String> map = value.get();
147 return map == null ? Collections.emptySortedMap() : map;
148 }
149
150 @Override
151 public void setContextMap(final Map<String, String> contextMap) {
152 if (!enable) {
153 return;
154 }
155 clear();
156 if (contextMap == null) {
157 return;
158 }
159 if (contextMap.keySet().stream().anyMatch(Objects::isNull)) {
160 throw new IllegalArgumentException("key cannot be null");
161 }
162 if (!allowNullValues && contextMap.containsValue(null)) {
163 throw new IllegalArgumentException("val cannot be null");
164 }
165 value.set(new HashMap<>(contextMap));
166 }
167
168
169
170
171
172
173 public void setEnable(boolean enable) {
174 this.enable = enable;
175 }
176
177
178
179
180
181
182
183 public void setInherit(boolean inherit) {
184 this.inherit = inherit;
185 }
186
187
188
189
190
191
192 public void setAllowNullValues(boolean allowNullValues) {
193 this.allowNullValues = allowNullValues;
194 }
195
196
197
198
199
200
201
202
203 public void setReturnNullCopyWhenMdcNotSet(boolean returnNullCopyWhenMdcNotSet) {
204 this.returnNullCopyWhenMdcNotSet = returnNullCopyWhenMdcNotSet;
205 }
206
207
208
209
210
211
212 public boolean getEnable() {
213 return enable;
214 }
215
216
217
218
219
220
221 public boolean getInherit() {
222 return inherit;
223 }
224
225
226
227
228
229
230 public boolean getAllowNullValues() {
231 return allowNullValues;
232 }
233
234
235
236
237
238
239 public boolean getReturnNullCopyWhenMdcNotSet() {
240 return returnNullCopyWhenMdcNotSet;
241 }
242
243
244
245
246
247
248 public void restoreOptions() {
249 enable = initialEnable;
250 inherit = initialInherit;
251 returnNullCopyWhenMdcNotSet = initialReturnNullCopyWhenMdcNotSet;
252 allowNullValues = initialAllowNullValues;
253 }
254
255
256 @SuppressWarnings("unchecked")
257 public static TestMDCAdapter getInstance() {
258 return (TestMDCAdapter) MDC.getMDCAdapter();
259 }
260 }