1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.util;
18
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.nio.charset.Charset;
22 import java.time.Duration;
23 import java.time.temporal.ChronoUnit;
24 import java.time.temporal.TemporalUnit;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Properties;
30 import java.util.ResourceBundle;
31 import java.util.ServiceLoader;
32 import java.util.Set;
33 import java.util.TreeSet;
34 import java.util.concurrent.ConcurrentHashMap;
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49 public final class PropertiesUtil {
50
51 private static final String LOG4J_PROPERTIES_FILE_NAME = "log4j2.component.properties";
52 private static final String LOG4J_SYSTEM_PROPERTIES_FILE_NAME = "log4j2.system.properties";
53 private static final String SYSTEM = "system:";
54 private static final PropertiesUtil LOG4J_PROPERTIES = new PropertiesUtil(LOG4J_PROPERTIES_FILE_NAME);
55
56 private final Environment environment;
57
58
59
60
61
62
63 public PropertiesUtil(final Properties props) {
64 this.environment = new Environment(new PropertiesPropertySource(props));
65 }
66
67
68
69
70
71
72
73 public PropertiesUtil(final String propertiesFileName) {
74 this.environment = new Environment(new PropertyFilePropertySource(propertiesFileName));
75 }
76
77
78
79
80
81
82
83
84 static Properties loadClose(final InputStream in, final Object source) {
85 final Properties props = new Properties();
86 if (null != in) {
87 try {
88 props.load(in);
89 } catch (final IOException e) {
90 LowLevelLogUtil.logException("Unable to read " + source, e);
91 } finally {
92 try {
93 in.close();
94 } catch (final IOException e) {
95 LowLevelLogUtil.logException("Unable to close " + source, e);
96 }
97 }
98 }
99 return props;
100 }
101
102
103
104
105
106
107 public static PropertiesUtil getProperties() {
108 return LOG4J_PROPERTIES;
109 }
110
111
112
113
114
115
116
117 public boolean hasProperty(final String name) {
118 return environment.containsKey(name);
119 }
120
121
122
123
124
125
126
127
128
129 public boolean getBooleanProperty(final String name) {
130 return getBooleanProperty(name, false);
131 }
132
133
134
135
136
137
138
139
140 public boolean getBooleanProperty(final String name, final boolean defaultValue) {
141 final String prop = getStringProperty(name);
142 return prop == null ? defaultValue : "true".equalsIgnoreCase(prop);
143 }
144
145
146
147
148
149
150
151
152
153 public boolean getBooleanProperty(final String name, final boolean defaultValueIfAbsent,
154 final boolean defaultValueIfPresent) {
155 final String prop = getStringProperty(name);
156 return prop == null ? defaultValueIfAbsent
157 : prop.isEmpty() ? defaultValueIfPresent : "true".equalsIgnoreCase(prop);
158 }
159
160
161
162
163
164
165
166
167
168
169 public Boolean getBooleanProperty(final String[] prefixes, String key, Supplier<Boolean> supplier) {
170 for (String prefix : prefixes) {
171 if (hasProperty(prefix + key)) {
172 return getBooleanProperty(prefix + key);
173 }
174 }
175 return supplier != null ? supplier.get() : null;
176 }
177
178
179
180
181
182
183
184 public Charset getCharsetProperty(final String name) {
185 return getCharsetProperty(name, Charset.defaultCharset());
186 }
187
188
189
190
191
192
193
194
195
196 public Charset getCharsetProperty(final String name, final Charset defaultValue) {
197 final String charsetName = getStringProperty(name);
198 if (charsetName == null) {
199 return defaultValue;
200 }
201 if (Charset.isSupported(charsetName)) {
202 return Charset.forName(charsetName);
203 }
204 final ResourceBundle bundle = getCharsetsResourceBundle();
205 if (bundle.containsKey(name)) {
206 final String mapped = bundle.getString(name);
207 if (Charset.isSupported(mapped)) {
208 return Charset.forName(mapped);
209 }
210 }
211 LowLevelLogUtil.log("Unable to get Charset '" + charsetName + "' for property '" + name + "', using default "
212 + defaultValue + " and continuing.");
213 return defaultValue;
214 }
215
216
217
218
219
220
221
222
223 public double getDoubleProperty(final String name, final double defaultValue) {
224 final String prop = getStringProperty(name);
225 if (prop != null) {
226 try {
227 return Double.parseDouble(prop);
228 } catch (final Exception ignored) {
229 }
230 }
231 return defaultValue;
232 }
233
234
235
236
237
238
239
240
241
242 public int getIntegerProperty(final String name, final int defaultValue) {
243 final String prop = getStringProperty(name);
244 if (prop != null) {
245 try {
246 return Integer.parseInt(prop);
247 } catch (final Exception ignored) {
248
249 }
250 }
251 return defaultValue;
252 }
253
254
255
256
257
258
259
260
261
262
263 public Integer getIntegerProperty(final String[] prefixes, String key, Supplier<Integer> supplier) {
264 for (String prefix : prefixes) {
265 if (hasProperty(prefix + key)) {
266 return getIntegerProperty(prefix + key, 0);
267 }
268 }
269 return supplier != null ? supplier.get() : null;
270 }
271
272
273
274
275
276
277
278
279 public long getLongProperty(final String name, final long defaultValue) {
280 final String prop = getStringProperty(name);
281 if (prop != null) {
282 try {
283 return Long.parseLong(prop);
284 } catch (final Exception ignored) {
285 }
286 }
287 return defaultValue;
288 }
289
290
291
292
293
294
295
296
297
298
299 public Long getLongProperty(final String[] prefixes, String key, Supplier<Long> supplier) {
300 for (String prefix : prefixes) {
301 if (hasProperty(prefix + key)) {
302 return getLongProperty(prefix + key, 0);
303 }
304 }
305 return supplier != null ? supplier.get() : null;
306 }
307
308
309
310
311
312
313
314
315
316 public Duration getDurationProperty(final String name, Duration defaultValue) {
317 final String prop = getStringProperty(name);
318 if (prop != null) {
319 return TimeUnit.getDuration(prop);
320 }
321 return defaultValue;
322 }
323
324
325
326
327
328
329
330
331
332
333 public Duration getDurationProperty(final String[] prefixes, String key, Supplier<Duration> supplier) {
334 for (String prefix : prefixes) {
335 if (hasProperty(prefix + key)) {
336 return getDurationProperty(prefix + key, null);
337 }
338 }
339 return supplier != null ? supplier.get() : null;
340 }
341
342
343
344
345
346
347
348
349
350
351 public String getStringProperty(final String[] prefixes, String key, Supplier<String> supplier) {
352 for (String prefix : prefixes) {
353 String result = getStringProperty(prefix + key);
354 if (result != null) {
355 return result;
356 }
357 }
358 return supplier != null ? supplier.get() : null;
359 }
360
361
362
363
364
365
366
367 public String getStringProperty(final String name) {
368 return environment.get(name);
369 }
370
371
372
373
374
375
376
377
378 public String getStringProperty(final String name, final String defaultValue) {
379 final String prop = getStringProperty(name);
380 return (prop == null) ? defaultValue : prop;
381 }
382
383
384
385
386
387
388 public static Properties getSystemProperties() {
389 try {
390 return new Properties(System.getProperties());
391 } catch (final SecurityException ex) {
392 LowLevelLogUtil.logException("Unable to access system properties.", ex);
393
394 return new Properties();
395 }
396 }
397
398
399
400
401
402
403 public void reload() {
404 environment.reload();
405 }
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420 private static class Environment {
421
422 private final Set<PropertySource> sources = new TreeSet<>(new PropertySource.Comparator());
423 private final Map<CharSequence, String> literal = new ConcurrentHashMap<>();
424 private final Map<CharSequence, String> normalized = new ConcurrentHashMap<>();
425 private final Map<List<CharSequence>, String> tokenized = new ConcurrentHashMap<>();
426
427 private Environment(final PropertySource propertySource) {
428 PropertyFilePropertySource sysProps = new PropertyFilePropertySource(LOG4J_SYSTEM_PROPERTIES_FILE_NAME);
429 try {
430 sysProps.forEach((key, value) -> {
431 if (System.getProperty(key) == null) {
432 System.setProperty(key, value);
433 }
434 });
435 } catch (SecurityException ex) {
436
437 }
438 sources.add(propertySource);
439 for (final ClassLoader classLoader : LoaderUtil.getClassLoaders()) {
440 try {
441 for (final PropertySource source : ServiceLoader.load(PropertySource.class, classLoader)) {
442 sources.add(source);
443 }
444 } catch (final Throwable ex) {
445
446
447
448 }
449 }
450
451 reload();
452 }
453
454 private synchronized void reload() {
455 literal.clear();
456 normalized.clear();
457 tokenized.clear();
458 for (final PropertySource source : sources) {
459 source.forEach((key, value) -> {
460 if (key != null && value != null) {
461 literal.put(key, value);
462 final List<CharSequence> tokens = PropertySource.Util.tokenize(key);
463 if (tokens.isEmpty()) {
464 normalized.put(source.getNormalForm(Collections.singleton(key)), value);
465 } else {
466 normalized.put(source.getNormalForm(tokens), value);
467 tokenized.put(tokens, value);
468 }
469 }
470 });
471 }
472 }
473
474 private static boolean hasSystemProperty(final String key) {
475 try {
476 return System.getProperties().containsKey(key);
477 } catch (final SecurityException ignored) {
478 return false;
479 }
480 }
481
482 private String get(final String key) {
483 if (normalized.containsKey(key)) {
484 return normalized.get(key);
485 }
486 if (literal.containsKey(key)) {
487 return literal.get(key);
488 }
489 if (hasSystemProperty(key)) {
490 return System.getProperty(key);
491 }
492 for (final PropertySource source : sources) {
493 if (source.containsProperty(key)) {
494 return source.getProperty(key);
495 }
496 }
497 return tokenized.get(PropertySource.Util.tokenize(key));
498 }
499
500 private boolean containsKey(final String key) {
501 return normalized.containsKey(key) ||
502 literal.containsKey(key) ||
503 hasSystemProperty(key) ||
504 tokenized.containsKey(PropertySource.Util.tokenize(key));
505 }
506 }
507
508
509
510
511
512
513
514
515
516 public static Properties extractSubset(final Properties properties, final String prefix) {
517 final Properties subset = new Properties();
518
519 if (prefix == null || prefix.length() == 0) {
520 return subset;
521 }
522
523 final String prefixToMatch = prefix.charAt(prefix.length() - 1) != '.' ? prefix + '.' : prefix;
524
525 final List<String> keys = new ArrayList<>();
526
527 for (final String key : properties.stringPropertyNames()) {
528 if (key.startsWith(prefixToMatch)) {
529 subset.setProperty(key.substring(prefixToMatch.length()), properties.getProperty(key));
530 keys.add(key);
531 }
532 }
533 for (final String key : keys) {
534 properties.remove(key);
535 }
536
537 return subset;
538 }
539
540 static ResourceBundle getCharsetsResourceBundle() {
541 return ResourceBundle.getBundle("Log4j-charsets");
542 }
543
544
545
546
547
548
549
550
551
552 public static Map<String, Properties> partitionOnCommonPrefixes(final Properties properties) {
553 final Map<String, Properties> parts = new ConcurrentHashMap<>();
554 for (final String key : properties.stringPropertyNames()) {
555 final String prefix = key.substring(0, key.indexOf('.'));
556 if (!parts.containsKey(prefix)) {
557 parts.put(prefix, new Properties());
558 }
559 parts.get(prefix).setProperty(key.substring(key.indexOf('.') + 1), properties.getProperty(key));
560 }
561 return parts;
562 }
563
564
565
566
567
568
569 public boolean isOsWindows() {
570 return getStringProperty("os.name", "").startsWith("Windows");
571 }
572
573 private enum TimeUnit {
574 NANOS("ns,nano,nanos,nanosecond,nanoseconds", ChronoUnit.NANOS),
575 MICROS("us,micro,micros,microsecond,microseconds", ChronoUnit.MICROS),
576 MILLIS("ms,milli,millis,millsecond,milliseconds", ChronoUnit.MILLIS),
577 SECONDS("s,second,seconds", ChronoUnit.SECONDS),
578 MINUTES("m,minute,minutes", ChronoUnit.MINUTES),
579 HOURS("h,hour,hours", ChronoUnit.HOURS),
580 DAYS("d,day,days", ChronoUnit.DAYS);
581
582 private final String[] descriptions;
583 private final ChronoUnit timeUnit;
584
585 TimeUnit(String descriptions, ChronoUnit timeUnit) {
586 this.descriptions = descriptions.split(",");
587 this.timeUnit = timeUnit;
588 }
589
590 ChronoUnit getTimeUnit() {
591 return this.timeUnit;
592 }
593
594 static Duration getDuration(String time) {
595 String value = time.trim();
596 TemporalUnit temporalUnit = ChronoUnit.MILLIS;
597 long timeVal = 0;
598 for (TimeUnit timeUnit : values()) {
599 for (String suffix : timeUnit.descriptions) {
600 if (value.endsWith(suffix)) {
601 temporalUnit = timeUnit.timeUnit;
602 timeVal = Long.parseLong(value.substring(0, value.length() - suffix.length()));
603 }
604 }
605 }
606 return Duration.of(timeVal, temporalUnit);
607 }
608 }
609 }