1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.common.util;
17
18 import java.io.File;
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.io.OutputStream;
22 import java.io.Reader;
23 import java.io.Writer;
24 import java.nio.charset.Charset;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.Collections;
28 import java.util.Enumeration;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Properties;
32 import java.util.Set;
33 import java.util.SortedMap;
34 import java.util.TreeSet;
35
36 import org.apache.commons.io.FileUtils;
37 import org.apache.commons.io.IOUtils;
38 import org.apache.commons.lang3.StringUtils;
39 import org.jasypt.util.text.TextEncryptor;
40 import org.kuali.common.util.properties.rice.RiceLoader;
41 import org.kuali.common.util.property.Constants;
42 import org.kuali.common.util.property.GlobalPropertiesMode;
43 import org.kuali.common.util.property.ImmutableProperties;
44 import org.kuali.common.util.property.PropertiesContext;
45 import org.kuali.common.util.property.PropertyFormat;
46 import org.kuali.common.util.property.processor.AddPropertiesProcessor;
47 import org.kuali.common.util.property.processor.PropertyProcessor;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
50 import org.springframework.util.PropertyPlaceholderHelper;
51
52 import com.google.common.base.Optional;
53 import com.google.common.collect.Maps;
54
55
56
57
58
59
60 public class PropertyUtils {
61
62 private static final Logger logger = LoggerFactory.getLogger(PropertyUtils.class);
63
64 public static final String ADDITIONAL_LOCATIONS = "properties.additional.locations";
65 public static final String ADDITIONAL_LOCATIONS_ENCODING = ADDITIONAL_LOCATIONS + ".encoding";
66 public static final Properties EMPTY = new ImmutableProperties(new Properties());
67
68 private static final String XML_EXTENSION = ".xml";
69 private static final PropertyPlaceholderHelper HELPER = new PropertyPlaceholderHelper("${", "}", ":", false);
70 public static final String ENV_PREFIX = "env";
71 private static final String DEFAULT_ENCODING = Charset.defaultCharset().name();
72 private static final String DEFAULT_XML_ENCODING = Encodings.UTF8;
73
74
75
76
77 public static Optional<String> getOptionalString(Properties properties, String key) {
78 if (properties.getProperty(key) == null) {
79 return Optional.absent();
80 } else {
81 return Optional.of(properties.getProperty(key));
82 }
83 }
84
85 public static Optional<String> getString(Properties properties, String key, Optional<String> provided) {
86 Optional<String> value = getOptionalString(properties, key);
87 if (value.isPresent()) {
88 return value;
89 } else {
90 return provided;
91 }
92 }
93
94
95
96
97 public static Properties toImmutable(Properties properties) {
98 return (properties instanceof ImmutableProperties) ? properties : new ImmutableProperties(properties);
99 }
100
101
102
103
104 public static List<Properties> toImmutable(List<Properties> properties) {
105 List<Properties> immutables = new ArrayList<Properties>();
106 for (Properties p : properties) {
107 immutables.add(toImmutable(p));
108 }
109 return Collections.unmodifiableList(immutables);
110 }
111
112
113
114
115
116 public static boolean getGlobalBoolean(String key, Properties properties) {
117 String defaultValue = properties.getProperty(key);
118 String value = getGlobalProperty(key, defaultValue);
119 return Boolean.parseBoolean(value);
120 }
121
122 public static boolean getBoolean(String key, Properties properties, boolean defaultValue) {
123 String value = properties.getProperty(key);
124 if (value == null) {
125 return defaultValue;
126 } else {
127 return Boolean.parseBoolean(value);
128 }
129 }
130
131
132
133
134 public static boolean equals(Properties one, Properties two) {
135
136
137 if (one == two) {
138 return true;
139 }
140
141
142 if (one == null && two == null) {
143 return true;
144 }
145
146
147
148
149 if (one == null || two == null) {
150 return false;
151 }
152
153
154
155
156 List<String> keys1 = getSortedKeys(one);
157 List<String> keys2 = getSortedKeys(two);
158
159
160 if (keys1.size() != keys2.size()) {
161 return false;
162 }
163
164
165
166
167 int size = keys1.size();
168
169
170 for (int i = 0; i < size; i++) {
171
172
173 String key1 = keys1.get(i);
174 String key2 = keys2.get(i);
175
176
177 if (!StringUtils.equals(key1, key2)) {
178 return false;
179 }
180
181
182 String val1 = one.getProperty(key1);
183 String val2 = two.getProperty(key2);
184
185
186 if (!StringUtils.equals(val1, val2)) {
187 return false;
188 }
189 }
190
191
192
193
194
195
196
197
198 return true;
199
200 }
201
202 public static String getProperty(Properties properties, String key, String defaultValue) {
203 String value = properties.getProperty(key);
204 if (StringUtils.isBlank(value)) {
205 return defaultValue;
206 } else {
207 return value;
208 }
209 }
210
211 public static boolean isEmpty(Properties properties) {
212 return properties == null || properties.size() == 0;
213 }
214
215 public static String getRiceXML(Properties properties) {
216 StringBuilder sb = new StringBuilder();
217 sb.append("<config>\n");
218 List<String> keys = getSortedKeys(properties);
219 for (String key : keys) {
220 String value = properties.getProperty(key);
221
222 if (StringUtils.contains(value, "<") || StringUtils.contains(value, "&")) {
223 value = Str.cdata(value);
224 }
225 sb.append(" <param name=" + Str.quote(key) + ">");
226 sb.append(value);
227 sb.append("</param>\n");
228 }
229 sb.append("</config>\n");
230 return sb.toString();
231 }
232
233 public static String getRequiredResolvedProperty(Properties properties, String key) {
234 return getRequiredResolvedProperty(properties, key, null);
235 }
236
237 public static String getRequiredResolvedProperty(Properties properties, String key, String defaultValue) {
238 String value = properties.getProperty(key);
239 value = StringUtils.isBlank(value) ? defaultValue : value;
240 if (StringUtils.isBlank(value)) {
241 throw new IllegalArgumentException("[" + key + "] is not set");
242 } else {
243 return HELPER.replacePlaceholders(value, properties);
244 }
245 }
246
247
248
249
250
251
252
253
254 public static void prepareContextProperties(Properties properties, String encoding) {
255
256
257 properties.putAll(getAdditionalProperties(properties, encoding));
258
259
260 properties.putAll(getGlobalProperties());
261
262
263 decrypt(properties);
264
265
266 resolve(properties);
267 }
268
269
270
271
272
273
274
275
276 public static void prepareContextProperties(Properties properties) {
277 prepareContextProperties(properties, null);
278 }
279
280 public static void resolve(Properties properties, PropertyPlaceholderHelper helper) {
281 List<String> keys = getSortedKeys(properties);
282 for (String key : keys) {
283 String original = properties.getProperty(key);
284 String resolved = helper.replacePlaceholders(original, properties);
285 if (!StringUtils.equals(original, resolved)) {
286 logger.debug("Resolved [{}]", key);
287 properties.setProperty(key, resolved);
288 }
289 }
290 }
291
292 public static void removeSystemProperty(String key) {
293 if (System.getProperty(key) != null) {
294 logger.info("Removing system property [{}]", key);
295 System.getProperties().remove(key);
296 }
297 }
298
299 public static void removeSystemProperties(List<String> keys) {
300 for (String key : keys) {
301 removeSystemProperty(key);
302 }
303 }
304
305 @Deprecated
306 public static void resolve(Properties properties) {
307
308 boolean resolve = new Boolean(getRequiredResolvedProperty(properties, "properties.resolve", "true"));
309 if (resolve) {
310 org.kuali.common.util.property.processor.ResolvePlaceholdersProcessor rpp = new org.kuali.common.util.property.processor.ResolvePlaceholdersProcessor();
311 rpp.setHelper(HELPER);
312 rpp.process(properties);
313 }
314 }
315
316 @Deprecated
317 public static void decrypt(Properties properties) {
318
319 boolean decrypt = Boolean.parseBoolean(getRequiredResolvedProperty(properties, "properties.decrypt", "false"));
320 if (decrypt) {
321
322 String password = getRequiredResolvedProperty(properties, "properties.enc.password");
323
324
325 String defaultStrength = org.kuali.common.util.enc.EncStrength.BASIC.name();
326 String strength = getRequiredResolvedProperty(properties, "properties.enc.strength", defaultStrength);
327 org.kuali.common.util.enc.EncStrength es = org.kuali.common.util.enc.EncStrength.valueOf(strength);
328 TextEncryptor decryptor = org.kuali.common.util.enc.EncUtils.getTextEncryptor(password, es);
329 decrypt(properties, decryptor);
330 }
331 }
332
333 public static Properties getAdditionalProperties(Properties properties) {
334 return getAdditionalProperties(properties, null);
335 }
336
337 public static Properties getAdditionalProperties(Properties properties, String encoding) {
338 String csv = properties.getProperty(ADDITIONAL_LOCATIONS);
339 if (StringUtils.isBlank(csv)) {
340 return new Properties();
341 }
342 if (StringUtils.isBlank(encoding)) {
343 encoding = properties.getProperty(ADDITIONAL_LOCATIONS_ENCODING, DEFAULT_XML_ENCODING);
344 }
345 List<String> locations = CollectionUtils.getTrimmedListFromCSV(csv);
346 PropertiesContext context = new PropertiesContext(locations, encoding);
347 return load(context);
348 }
349
350 public static void appendToOrSetProperty(Properties properties, String key, String value) {
351 Assert.hasText(value);
352 String existingValue = properties.getProperty(key);
353 if (existingValue == null) {
354 existingValue = "";
355 }
356 String newValue = existingValue + value;
357 properties.setProperty(key, newValue);
358 }
359
360 @Deprecated
361 public static Properties load(List<org.kuali.common.util.property.ProjectProperties> pps) {
362
363
364 Properties properties = new Properties();
365
366
367 for (org.kuali.common.util.property.ProjectProperties pp : pps) {
368
369 logger.debug("oracle.dba.url.1={}", properties.getProperty("oracle.dba.url"));
370
371
372 PropertiesContext ctx = pp.getPropertiesContext();
373
374
375 Properties original = PropertyUtils.duplicate(PropertyUtils.toEmpty(ctx.getProperties()));
376
377
378 Properties combined = PropertyUtils.combine(properties, ctx.getProperties());
379
380
381 ctx.setProperties(combined);
382
383
384 Properties loaded = load(ctx);
385
386 logger.debug("oracle.dba.url.2={}", loaded.getProperty("oracle.dba.url"));
387
388
389 properties.putAll(loaded);
390
391
392 properties.putAll(original);
393
394 }
395
396
397 return properties;
398 }
399
400 public static Properties load(PropertiesContext context) {
401
402 if (CollectionUtils.isEmpty(context.getLocations())) {
403 return PropertyUtils.toEmpty(context.getProperties());
404 }
405
406
407 Assert.notNull(context.getHelper(), "helper is null");
408 Assert.notNull(context.getLocations(), "locations are null");
409 Assert.notNull(context.getEncoding(), "encoding is null");
410 Assert.notNull(context.getMissingLocationsMode(), "missingLocationsMode is null");
411
412
413 Properties global = PropertyUtils.getGlobalProperties();
414
415
416 context.setProperties(PropertyUtils.toEmpty(context.getProperties()));
417
418
419 Properties result = new Properties();
420
421
422 result.putAll(context.getProperties());
423
424
425 for (String location : context.getLocations()) {
426
427
428 Properties resolverProperties = PropertyUtils.combine(context.getProperties(), result, global);
429
430
431 String resolvedLocation = context.getHelper().replacePlaceholders(location, resolverProperties);
432
433
434 if (LocationUtils.exists(resolvedLocation)) {
435
436
437 Properties properties = PropertyUtils.load(resolvedLocation, context.getEncoding());
438
439
440 result.putAll(properties);
441 } else {
442
443
444 ModeUtils.validate(context.getMissingLocationsMode(), "Non-existent location [" + resolvedLocation + "]");
445 }
446 }
447
448
449 return result;
450 }
451
452
453
454
455
456
457
458
459 public static void decrypt(Properties properties, TextEncryptor encryptor) {
460 decrypt(properties, encryptor, null, null);
461 }
462
463
464
465
466
467
468
469
470 public static Properties getEncryptedProperties(Properties properties) {
471 List<String> keys = getEncryptedKeys(properties);
472 Properties encrypted = new Properties();
473 for (String key : keys) {
474 String value = properties.getProperty(key);
475 encrypted.setProperty(key, value);
476 }
477 return encrypted;
478 }
479
480
481
482
483
484
485
486
487
488
489 @Deprecated
490 public static List<String> getEncryptedKeys(Properties properties) {
491 List<String> all = getSortedKeys(properties);
492 List<String> encrypted = new ArrayList<String>();
493 for (String key : all) {
494 String value = properties.getProperty(key);
495 if (org.kuali.common.util.enc.EncUtils.isEncrypted(value)) {
496 encrypted.add(key);
497 }
498 }
499 return encrypted;
500 }
501
502
503
504
505
506
507
508
509
510
511 @Deprecated
512 public static void decrypt(Properties properties, TextEncryptor encryptor, List<String> includes, List<String> excludes) {
513 List<String> keys = getSortedKeys(properties, includes, excludes);
514 for (String key : keys) {
515 String value = properties.getProperty(key);
516 if (org.kuali.common.util.enc.EncUtils.isEncrypted(value)) {
517 String decryptedValue = decryptPropertyValue(encryptor, value);
518 properties.setProperty(key, decryptedValue);
519 }
520 }
521 }
522
523
524
525
526
527
528 @Deprecated
529 public static boolean isEncryptedPropertyValue(String value) {
530 return org.kuali.common.util.enc.EncUtils.isEncrypted(value);
531 }
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557 public static void conceal(Properties properties) {
558 List<String> keys = getSortedKeys(properties);
559 for (String key : keys) {
560 String value = properties.getProperty(key);
561 String concealed = Str.conceal(value);
562 properties.setProperty(key, concealed);
563 }
564 }
565
566
567
568
569
570
571
572
573 public static void reveal(Properties properties) {
574 List<String> keys = getSortedKeys(properties);
575 for (String key : keys) {
576 String value = properties.getProperty(key);
577 String revealed = Str.reveal(value);
578 properties.setProperty(key, revealed);
579 }
580 }
581
582
583
584
585
586
587
588
589 public static void encrypt(Properties properties, TextEncryptor encryptor) {
590 encrypt(properties, encryptor, null, null);
591 }
592
593
594
595
596
597
598
599
600 public static void encrypt(Properties properties, TextEncryptor encryptor, List<String> includes, List<String> excludes) {
601 List<String> keys = getSortedKeys(properties, includes, excludes);
602 for (String key : keys) {
603 String originalValue = properties.getProperty(key);
604 String encryptedValue = encryptPropertyValue(encryptor, originalValue);
605 properties.setProperty(key, encryptedValue);
606 }
607 }
608
609
610
611
612
613
614
615
616 public static String decryptPropertyValue(TextEncryptor encryptor, String value) {
617 String unwrapped = unwrapEncryptedValue(value);
618
619
620 return encryptor.decrypt(unwrapped);
621 }
622
623
624
625
626
627
628
629
630
631
632 @Deprecated
633 public static String unwrapEncryptedValue(String encryptedValue) {
634 return org.kuali.common.util.enc.EncUtils.unwrap(encryptedValue);
635 }
636
637
638
639
640
641
642
643
644 public static String encryptPropertyValue(TextEncryptor encryptor, String value) {
645 String encryptedValue = encryptor.encrypt(value);
646 return wrapEncryptedPropertyValue(encryptedValue);
647 }
648
649
650
651
652
653
654
655
656
657
658 @Deprecated
659 public static String wrapEncryptedPropertyValue(String encryptedValue) {
660 return org.kuali.common.util.enc.EncUtils.wrap(encryptedValue);
661 }
662
663 public static void overrideWithGlobalValues(Properties properties, GlobalPropertiesMode mode) {
664 List<String> keys = PropertyUtils.getSortedKeys(properties);
665 Properties global = PropertyUtils.getProperties(mode);
666 for (String key : keys) {
667 String globalValue = global.getProperty(key);
668 if (!StringUtils.isBlank(globalValue)) {
669 properties.setProperty(key, globalValue);
670 }
671 }
672 }
673
674 public static final Properties[] toArray(List<Properties> properties) {
675 return properties.toArray(new Properties[properties.size()]);
676 }
677
678 public static final Properties combine(Properties properties, List<Properties> list) {
679 List<Properties> newList = new ArrayList<Properties>(CollectionUtils.toEmptyList(list));
680 newList.add(0, toEmpty(properties));
681 return combine(newList);
682 }
683
684 public static final Properties combine(List<Properties> properties) {
685 Properties combined = new Properties();
686 for (Properties p : properties) {
687 combined.putAll(toEmpty(p));
688 }
689 return combined;
690 }
691
692 public static final Properties combine(Properties... properties) {
693 return combine(Arrays.asList(properties));
694 }
695
696 public static final void process(Properties properties, PropertyProcessor processor) {
697 process(properties, Collections.singletonList(processor));
698 }
699
700 public static final void process(Properties properties, List<PropertyProcessor> processors) {
701 executeProcessors(properties, processors);
702 }
703
704 public static final void executeProcessors(Properties properties, List<PropertyProcessor> processors) {
705 for (PropertyProcessor processor : CollectionUtils.toEmptyList(processors)) {
706 processor.process(properties);
707 }
708 }
709
710 public static final Properties toEmpty(Properties properties) {
711 return properties == null ? new Properties() : properties;
712 }
713
714 public static final boolean isSingleUnresolvedPlaceholder(String string) {
715 return isSingleUnresolvedPlaceholder(string, Constants.DEFAULT_PLACEHOLDER_PREFIX, Constants.DEFAULT_PLACEHOLDER_SUFFIX);
716 }
717
718 public static final boolean isSingleUnresolvedPlaceholder(String string, String prefix, String suffix) {
719 int prefixMatches = StringUtils.countMatches(string, prefix);
720 int suffixMatches = StringUtils.countMatches(string, suffix);
721 boolean startsWith = StringUtils.startsWith(string, prefix);
722 boolean endsWith = StringUtils.endsWith(string, suffix);
723 return prefixMatches == 1 && suffixMatches == 1 && startsWith && endsWith;
724 }
725
726 public static final boolean containsUnresolvedPlaceholder(String string) {
727 return containsUnresolvedPlaceholder(string, Constants.DEFAULT_PLACEHOLDER_PREFIX, Constants.DEFAULT_PLACEHOLDER_SUFFIX);
728 }
729
730 public static final boolean containsUnresolvedPlaceholder(String string, String prefix, String suffix) {
731 int beginIndex = StringUtils.indexOf(string, prefix);
732 if (beginIndex == -1) {
733 return false;
734 }
735 return StringUtils.indexOf(string, suffix) != -1;
736 }
737
738
739
740
741
742 public static final Properties getResolvedProperties(Properties properties) {
743 return getResolvedProperties(properties, Constants.DEFAULT_PROPERTY_PLACEHOLDER_HELPER, Constants.DEFAULT_GLOBAL_PROPERTIES_MODE);
744 }
745
746
747
748
749
750 public static final Properties getResolvedProperties(Properties properties, GlobalPropertiesMode globalPropertiesMode) {
751 return getResolvedProperties(properties, Constants.DEFAULT_PROPERTY_PLACEHOLDER_HELPER, globalPropertiesMode);
752 }
753
754
755
756
757
758 public static final Properties getResolvedProperties(Properties properties, PropertyPlaceholderHelper helper) {
759 return getResolvedProperties(properties, helper, Constants.DEFAULT_GLOBAL_PROPERTIES_MODE);
760 }
761
762
763
764
765
766 public static final Properties getResolvedProperties(Properties properties, PropertyPlaceholderHelper helper, GlobalPropertiesMode globalPropertiesMode) {
767 Properties global = getProperties(properties, globalPropertiesMode);
768 List<String> keys = getSortedKeys(properties);
769 Properties newProperties = new Properties();
770 for (String key : keys) {
771 String originalValue = properties.getProperty(key);
772 String resolvedValue = helper.replacePlaceholders(originalValue, global);
773 if (!resolvedValue.equals(originalValue)) {
774 logger.debug("Resolved property '" + key + "' [{}] -> [{}]", Str.flatten(originalValue), Str.flatten(resolvedValue));
775 newProperties.setProperty(key, resolvedValue);
776 }
777 }
778 return newProperties;
779 }
780
781
782
783
784 public static final List<String> getValues(Properties properties, List<String> keys) {
785 List<String> values = new ArrayList<String>();
786 for (String key : keys) {
787 values.add(properties.getProperty(key));
788 }
789 return values;
790 }
791
792
793
794
795 public static final List<String> getEndsWithKeys(Properties properties, String suffix) {
796 List<String> keys = getSortedKeys(properties);
797 List<String> matches = new ArrayList<String>();
798 for (String key : keys) {
799 if (StringUtils.endsWith(key, suffix)) {
800 matches.add(key);
801 }
802 }
803 return matches;
804 }
805
806
807
808
809 public static final void trim(Properties properties, String includesCSV, String excludesCSV) {
810 List<String> includes = CollectionUtils.getTrimmedListFromCSV(includesCSV);
811 List<String> excludes = CollectionUtils.getTrimmedListFromCSV(excludesCSV);
812 trim(properties, includes, excludes);
813 }
814
815
816
817
818 public static final void trim(Properties properties, List<String> includes, List<String> excludes) {
819 List<String> keys = getSortedKeys(properties);
820 for (String key : keys) {
821 if (!include(key, includes, excludes)) {
822 logger.debug("Removing [{}]", key);
823 properties.remove(key);
824 }
825 }
826 }
827
828
829
830
831
832
833
834
835
836 public static final boolean include(String value, List<String> includes, List<String> excludes) {
837 if (isSingleWildcardMatch(value, excludes)) {
838
839 return false;
840 } else {
841
842 return CollectionUtils.isEmpty(includes) || isSingleWildcardMatch(value, includes);
843 }
844 }
845
846 public static final boolean isSingleWildcardMatch(String s, List<String> patterns) {
847 for (String pattern : CollectionUtils.toEmptyList(patterns)) {
848 if (isSingleWildcardMatch(s, pattern)) {
849 return true;
850 }
851 }
852 return false;
853 }
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873 public static final boolean isSingleWildcardMatch(String value, String pattern) {
874 if (value == null && pattern == null) {
875
876 return true;
877 } else if (value == null || pattern == null) {
878
879 return false;
880 } else if (pattern.equals(Constants.WILDCARD)) {
881
882
883 return true;
884 } else if (StringUtils.countMatches(pattern, Constants.WILDCARD) > 1) {
885
886 throw new IllegalArgumentException("Pattern [" + pattern + "] is not supported. Only one wildcard is allowed in the pattern");
887 } else if (!StringUtils.contains(pattern, Constants.WILDCARD)) {
888
889 return StringUtils.equals(value, pattern);
890 } else {
891
892
893
894 int pos = StringUtils.indexOf(pattern, Constants.WILDCARD);
895 int suffixPos = pos + Constants.WILDCARD.length();
896 boolean nullPrefix = pos == 0;
897 boolean nullSuffix = suffixPos >= pattern.length();
898 String prefix = nullPrefix ? null : StringUtils.substring(pattern, 0, pos);
899 String suffix = nullSuffix ? null : StringUtils.substring(pattern, suffixPos);
900 boolean prefixMatch = nullPrefix || StringUtils.startsWith(value, prefix);
901 boolean suffixMatch = nullSuffix || StringUtils.endsWith(value, suffix);
902 return prefixMatch && suffixMatch;
903 }
904 }
905
906
907
908
909 public static final Properties getProperties(Properties properties, String include, String exclude) {
910 List<String> keys = getSortedKeys(properties, include, exclude);
911 Properties newProperties = new Properties();
912 for (String key : keys) {
913 String value = properties.getProperty(key);
914 newProperties.setProperty(key, value);
915 }
916 return newProperties;
917 }
918
919
920
921
922 public static final List<String> getSortedKeys(Properties properties, String include, String exclude) {
923 return getSortedKeys(properties, CollectionUtils.toEmptyList(include), CollectionUtils.toEmptyList(exclude));
924 }
925
926
927
928
929 public static final List<String> getSortedKeys(Properties properties, List<String> includes, List<String> excludes) {
930 List<String> keys = getSortedKeys(properties);
931 List<String> includedKeys = new ArrayList<String>();
932 for (String key : keys) {
933 if (include(key, includes, excludes)) {
934 includedKeys.add(key);
935 }
936 }
937 return includedKeys;
938 }
939
940
941
942
943 public static final List<String> getStartsWithKeys(Properties properties, String prefix) {
944 List<String> keys = getSortedKeys(properties);
945 List<String> matches = new ArrayList<String>();
946 for (String key : keys) {
947 if (StringUtils.startsWith(key, prefix)) {
948 matches.add(key);
949 }
950 }
951 return matches;
952 }
953
954
955
956
957 public static final List<String> getSortedKeys(Properties properties) {
958 List<String> keys = new ArrayList<String>(properties.stringPropertyNames());
959 Collections.sort(keys);
960 return keys;
961 }
962
963 public static final String toString(Properties properties) {
964 List<String> keys = getSortedKeys(properties);
965 StringBuilder sb = new StringBuilder();
966 for (String key : keys) {
967 String value = Str.flatten(properties.getProperty(key));
968 sb.append(key + "=" + value + "\n");
969 }
970 return sb.toString();
971 }
972
973 public static final void info(Properties properties) {
974 properties = toEmpty(properties);
975 logger.info("--- Displaying {} properties ---\n\n{}", properties.size(), toString(properties));
976 }
977
978 public static final void debug(Properties properties) {
979 properties = toEmpty(properties);
980 logger.debug("--- Displaying {} properties ---\n\n{}", properties.size(), toString(properties));
981 }
982
983
984
985
986 public static final void store(Properties properties, File file) {
987 store(properties, file, null);
988 }
989
990
991
992
993 public static final void storeSilently(Properties properties, File file) {
994 store(properties, file, null, null, true);
995 }
996
997
998
999
1000 public static final void store(Properties properties, File file, String encoding) {
1001 store(properties, file, encoding, null);
1002 }
1003
1004
1005
1006
1007 public static final void store(Properties properties, File file, String encoding, String comment) {
1008 store(properties, file, encoding, comment, false);
1009 }
1010
1011
1012
1013
1014 public static final void store(Properties properties, File file, String encoding, String comment, boolean silent) {
1015 OutputStream out = null;
1016 Writer writer = null;
1017 try {
1018 out = FileUtils.openOutputStream(file);
1019 String path = file.getCanonicalPath();
1020 boolean xml = isXml(path);
1021 Properties sorted = getSortedProperties(properties);
1022 comment = getComment(encoding, comment, xml);
1023 if (xml) {
1024 if (!silent) {
1025 logger.info("Storing XML properties - [{}] encoding={}", path, StringUtils.defaultIfBlank(encoding, DEFAULT_ENCODING));
1026 }
1027 if (encoding == null) {
1028 sorted.storeToXML(out, comment);
1029 } else {
1030 sorted.storeToXML(out, comment, encoding);
1031 }
1032 } else {
1033 writer = LocationUtils.getWriter(out, encoding);
1034 if (!silent) {
1035 logger.info("Storing properties - [{}] encoding={}", path, StringUtils.defaultIfBlank(encoding, DEFAULT_ENCODING));
1036 }
1037 sorted.store(writer, comment);
1038 }
1039 } catch (IOException e) {
1040 throw new IllegalStateException("Unexpected IO error", e);
1041 } finally {
1042 IOUtils.closeQuietly(writer);
1043 IOUtils.closeQuietly(out);
1044 }
1045 }
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055 public static final String getGlobalProperty(String key) {
1056 return getGlobalProperty(key, null);
1057 }
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067 public static final String getGlobalProperty(String key, String defaultValue) {
1068 Assert.noNullsWithMsg("key is required", key);
1069
1070
1071 String systemValue = System.getProperty(key);
1072
1073
1074 if (systemValue != null) {
1075 return systemValue;
1076 }
1077
1078
1079 String environmentVariable = convertToEnvironmentVariable(key);
1080
1081
1082 String environmentValue = System.getenv(environmentVariable);
1083
1084 if (environmentValue != null) {
1085
1086 return environmentValue;
1087 } else {
1088
1089 return defaultValue;
1090 }
1091 }
1092
1093
1094
1095
1096
1097 public static final Properties getGlobalProperties() {
1098 return getProperties(Constants.DEFAULT_GLOBAL_PROPERTIES_MODE);
1099 }
1100
1101
1102
1103
1104
1105 public static final Properties getGlobalProperties(Properties properties) {
1106 return getProperties(properties, Constants.DEFAULT_GLOBAL_PROPERTIES_MODE);
1107 }
1108
1109
1110
1111
1112
1113
1114
1115 public static final Properties getProperties(Properties properties, GlobalPropertiesMode mode) {
1116 Properties newProperties = duplicate(properties);
1117 List<PropertyProcessor> modifiers = getPropertyProcessors(mode);
1118 for (PropertyProcessor modifier : modifiers) {
1119 modifier.process(newProperties);
1120 }
1121 return newProperties;
1122 }
1123
1124
1125
1126
1127
1128
1129
1130 public static final Properties getProperties(GlobalPropertiesMode mode) {
1131 return getProperties(new Properties(), mode);
1132 }
1133
1134
1135
1136
1137 public static final String getProperty(String key, GlobalPropertiesMode mode) {
1138 return getProperty(key, new Properties(), mode);
1139 }
1140
1141
1142
1143
1144
1145 public static final String getProperty(String key, Properties properties, GlobalPropertiesMode mode) {
1146 return getProperties(properties, mode).getProperty(key);
1147 }
1148
1149
1150
1151
1152 public static final List<PropertyProcessor> getPropertyProcessors(GlobalPropertiesMode mode) {
1153 List<PropertyProcessor> processors = new ArrayList<PropertyProcessor>();
1154 switch (mode) {
1155 case NONE:
1156 return processors;
1157 case ENVIRONMENT:
1158 processors.add(new AddPropertiesProcessor(getEnvAsProperties()));
1159 return processors;
1160 case SYSTEM:
1161 processors.add(new AddPropertiesProcessor(System.getProperties()));
1162 return processors;
1163 case BOTH:
1164 processors.add(new AddPropertiesProcessor(getEnvAsProperties()));
1165 processors.add(new AddPropertiesProcessor(System.getProperties()));
1166 return processors;
1167 default:
1168 throw new IllegalStateException(mode + " is unknown");
1169 }
1170 }
1171
1172
1173
1174
1175 public static final Properties convert(Map<String, String> map) {
1176 return convertToProperties(map);
1177 }
1178
1179
1180
1181
1182 public static final Properties convertToProperties(Map<String, String> map) {
1183 Properties props = new Properties();
1184 for (String key : map.keySet()) {
1185 props.setProperty(key, map.get(key));
1186 }
1187 return props;
1188 }
1189
1190
1191
1192
1193 public static Map<String, String> convert(Properties properties) {
1194 return newHashMap(properties);
1195 }
1196
1197
1198
1199
1200 public static Map<String, String> newHashMap(Properties properties) {
1201 Map<String, String> map = Maps.newHashMap();
1202 for (String key : properties.stringPropertyNames()) {
1203 map.put(key, properties.getProperty(key));
1204 }
1205 return map;
1206 }
1207
1208
1209
1210
1211 public static SortedMap<String, String> newTreeMap(Properties properties) {
1212 SortedMap<String, String> map = Maps.newTreeMap();
1213 for (String key : properties.stringPropertyNames()) {
1214 map.put(key, properties.getProperty(key));
1215 }
1216 return map;
1217 }
1218
1219
1220
1221
1222 public static final Properties duplicate(Properties properties) {
1223 Properties newProperties = new Properties();
1224 newProperties.putAll(properties);
1225 return newProperties;
1226 }
1227
1228
1229
1230
1231 public static Properties getEnvAsProperties() {
1232 return getEnvAsProperties(ENV_PREFIX);
1233 }
1234
1235
1236
1237
1238 public static Properties getEnvAsProperties(String prefix) {
1239 Properties properties = convert(System.getenv());
1240 return getPrefixedProperties(properties, prefix);
1241 }
1242
1243
1244
1245
1246 public static final boolean isXml(String location) {
1247 return StringUtils.endsWithIgnoreCase(location, XML_EXTENSION);
1248 }
1249
1250
1251
1252
1253 public static final boolean isRiceProperties(String location) {
1254 return StringUtils.endsWithIgnoreCase(location, Constants.RICE_PROPERTIES_SUFFIX);
1255 }
1256
1257 public static final Properties loadRiceProps(File file) {
1258 return RiceLoader.load(file);
1259 }
1260
1261 public static final Properties loadRiceProps(String location) {
1262 return RiceLoader.load(location);
1263 }
1264
1265
1266
1267
1268
1269
1270 @Deprecated
1271 public static final Properties loadRiceProperties(File file) {
1272 return loadRiceProperties(LocationUtils.getCanonicalPath(file));
1273 }
1274
1275
1276
1277
1278
1279
1280 @Deprecated
1281 public static final Properties loadRiceProperties(String location) {
1282 logger.info("Loading Rice properties [{}] encoding={}", location, DEFAULT_XML_ENCODING);
1283 String contents = LocationUtils.toString(location, DEFAULT_XML_ENCODING);
1284 String config = StringUtils.substringBetween(contents, "<config>", "</config>");
1285 String[] tokens = StringUtils.substringsBetween(config, "<param", "<param");
1286
1287 Properties properties = new Properties();
1288 for (String token : tokens) {
1289 String key = StringUtils.substringBetween(token, "name=\"", "\">");
1290 validateRiceProperties(token, key);
1291 String value = StringUtils.substringBetween(token + "</param>", "\">", "</param>");
1292 properties.setProperty(key, value);
1293 }
1294 return properties;
1295 }
1296
1297
1298
1299
1300
1301 protected static final void validateRiceProperties(String token, String key) {
1302 if (StringUtils.equalsIgnoreCase("config.location", key)) {
1303 throw new IllegalArgumentException("config.location is not supported");
1304 }
1305 if (StringUtils.contains(token, "override=\"")) {
1306 throw new IllegalArgumentException("override attribute is not supported");
1307 }
1308 if (StringUtils.contains(token, "system=\"")) {
1309 throw new IllegalArgumentException("system attribute is not supported");
1310 }
1311 if (StringUtils.contains(token, "random=\"")) {
1312 throw new IllegalArgumentException("random attribute is not supported");
1313 }
1314 }
1315
1316
1317
1318
1319 public static final Properties load(File file) {
1320 return load(file, null);
1321 }
1322
1323
1324
1325
1326 public static final Properties loadSilently(File file) {
1327 return loadSilently(LocationUtils.getCanonicalPath(file));
1328 }
1329
1330
1331
1332
1333 public static final Properties loadSilently(String location) {
1334 return load(location, null, PropertyFormat.NORMAL, true);
1335 }
1336
1337
1338
1339
1340 public static final Properties load(File file, String encoding) {
1341 String location = LocationUtils.getCanonicalPath(file);
1342 return load(location, encoding);
1343 }
1344
1345
1346
1347
1348 public static final Properties load(String location) {
1349 return load(location, null);
1350 }
1351
1352
1353
1354
1355 public static final Properties loadOrCreateSilently(String location) {
1356 if (LocationUtils.exists(location)) {
1357 return load(location, null, PropertyFormat.NORMAL, true);
1358 } else {
1359 return new Properties();
1360 }
1361 }
1362
1363
1364
1365
1366 public static final Properties load(List<String> locations, String encoding) {
1367 Properties properties = new Properties();
1368 for (String location : locations) {
1369 properties.putAll(load(location, encoding));
1370 }
1371 return properties;
1372 }
1373
1374
1375
1376
1377 public static final Properties load(String location, String encoding) {
1378 return load(location, encoding, PropertyFormat.NORMAL);
1379 }
1380
1381
1382
1383
1384 public static final Properties load(String location, String encoding, PropertyFormat format) {
1385 return load(location, encoding, format, false);
1386 }
1387
1388
1389
1390
1391 public static final Properties load(String location, String encoding, PropertyFormat format, boolean silent) {
1392 InputStream in = null;
1393 Reader reader = null;
1394 try {
1395 Properties properties = new Properties();
1396 boolean xml = isXml(location);
1397 boolean riceProperties = isRiceProperties(location);
1398 location = getCanonicalLocation(location);
1399 if (PropertyFormat.RICE.equals(format) || riceProperties) {
1400 properties = loadRiceProperties(location);
1401 } else if (xml) {
1402 in = LocationUtils.getInputStream(location);
1403 if (!silent) {
1404 logger.info("Loading XML properties - [{}]", location);
1405 }
1406 properties.loadFromXML(in);
1407 } else {
1408 if (!silent) {
1409 logger.info("Loading properties - [{}] encoding={}", location, StringUtils.defaultIfBlank(encoding, DEFAULT_ENCODING));
1410 }
1411 reader = LocationUtils.getBufferedReader(location, encoding);
1412 properties.load(reader);
1413 }
1414 return properties;
1415 } catch (IOException e) {
1416 throw new IllegalStateException("Unexpected IO error", e);
1417 } finally {
1418 IOUtils.closeQuietly(in);
1419 IOUtils.closeQuietly(reader);
1420 }
1421 }
1422
1423 protected static String getCanonicalLocation(String location) {
1424 if (LocationUtils.isExistingFile(location)) {
1425 return LocationUtils.getCanonicalPath(new File(location));
1426 } else {
1427 return location;
1428 }
1429 }
1430
1431
1432
1433
1434
1435 public static final Properties getPrefixedProperties(Properties properties, String prefix) {
1436 if (StringUtils.isBlank(prefix)) {
1437 return duplicate(properties);
1438 }
1439 Properties newProperties = new Properties();
1440 for (String key : properties.stringPropertyNames()) {
1441 String value = properties.getProperty(key);
1442 String newKey = StringUtils.startsWith(key, prefix + ".") ? key : prefix + "." + key;
1443 newProperties.setProperty(newKey, value);
1444 }
1445 return newProperties;
1446 }
1447
1448
1449
1450
1451
1452
1453
1454
1455 public static final String convertToEnvironmentVariable(String key) {
1456 return StringUtils.upperCase(StringUtils.replace(key, ".", "_"));
1457 }
1458
1459
1460
1461
1462
1463
1464
1465
1466 public static final String getEnvironmentVariableKey(String key) {
1467 return ENV_PREFIX + "." + convertToEnvironmentVariable(key);
1468 }
1469
1470
1471
1472
1473 public static final Properties reformatKeysAsEnvVars(Properties properties) {
1474 Properties newProperties = new Properties();
1475 for (String key : properties.stringPropertyNames()) {
1476 String value = properties.getProperty(key);
1477 String newKey = convertToEnvironmentVariable(key);
1478 newProperties.setProperty(newKey, value);
1479 }
1480 return newProperties;
1481 }
1482
1483
1484
1485
1486
1487 public static final void addOrOverrideProperty(Properties properties, String key, String newValue, Mode propertyOverwriteMode) {
1488 addOrOverrideProperty(properties, key, newValue, propertyOverwriteMode, 0);
1489 }
1490
1491 public static final void addOrOverrideProperty(Properties properties, String key, String newValue, Mode overrideMode, int indent) {
1492 String oldValue = properties.getProperty(key);
1493 if (StringUtils.equals(newValue, oldValue)) {
1494
1495 return;
1496 }
1497 boolean overwrite = !StringUtils.isBlank(oldValue);
1498
1499 String logNewValue = newValue;
1500 String logOldValue = oldValue;
1501
1502
1503 if (obscure(key)) {
1504 logNewValue = "*********";
1505 logOldValue = "*********";
1506 }
1507
1508 if (overwrite) {
1509
1510
1511 Object[] args = new Object[] { StringUtils.repeat(" ", indent), key, Str.flatten(logNewValue), Str.flatten(logOldValue) };
1512 ModeUtils.validate(overrideMode, "{}override [{}] -> [{}]", args, "Override of existing property [" + key + "] is not allowed.");
1513 } else {
1514
1515 logger.debug("Adding [{}={}]", key, Str.flatten(logNewValue));
1516 }
1517 properties.setProperty(key, newValue);
1518 }
1519
1520 protected static boolean obscure(String key) {
1521 if (StringUtils.containsIgnoreCase(key, "password")) {
1522 return true;
1523 }
1524 if (StringUtils.containsIgnoreCase(key, "secret")) {
1525 return true;
1526 }
1527 if (StringUtils.containsIgnoreCase(key, "private")) {
1528 return true;
1529 }
1530 return false;
1531 }
1532
1533 private static final String getDefaultComment(String encoding, boolean xml) {
1534 if (encoding == null) {
1535 if (xml) {
1536
1537 return "encoding.default=" + DEFAULT_XML_ENCODING;
1538 } else {
1539
1540 return "encoding.default=" + DEFAULT_ENCODING;
1541 }
1542 } else {
1543 return "encoding.specified=" + encoding;
1544 }
1545 }
1546
1547 private static final String getComment(String encoding, String comment, boolean xml) {
1548 if (StringUtils.isBlank(comment)) {
1549 return getDefaultComment(encoding, xml);
1550 } else {
1551 return comment + "\n#" + getDefaultComment(encoding, xml);
1552 }
1553 }
1554
1555
1556
1557
1558 private static final SortedProperties getSortedProperties(Properties properties) {
1559 SortedProperties sp = new PropertyUtils().new SortedProperties();
1560 sp.putAll(properties);
1561 return sp;
1562 }
1563
1564
1565
1566
1567 private class SortedProperties extends Properties {
1568
1569 private static final long serialVersionUID = 1330825236411537386L;
1570
1571
1572
1573
1574 @Override
1575 public Set<Object> keySet() {
1576 return Collections.unmodifiableSet(new TreeSet<Object>(super.keySet()));
1577 }
1578
1579
1580
1581
1582 @Override
1583 public synchronized Enumeration<Object> keys() {
1584 return Collections.enumeration(new TreeSet<Object>(super.keySet()));
1585 }
1586 }
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599 public static final void addListComparisonProperties(Properties properties, ComparisonResults listComparison, List<String> propertyNames) {
1600
1601 Assert.isTrue(propertyNames.size() == 3);
1602
1603 properties.setProperty(propertyNames.get(0), CollectionUtils.getCSV(listComparison.getAdded()));
1604 properties.setProperty(propertyNames.get(1), CollectionUtils.getCSV(listComparison.getSame()));
1605 properties.setProperty(propertyNames.get(2), CollectionUtils.getCSV(listComparison.getDeleted()));
1606 }
1607
1608 }