1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.configuration2;
18
19 import java.math.BigDecimal;
20 import java.math.BigInteger;
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.Collections;
24 import java.util.HashMap;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Properties;
29 import java.util.Set;
30 import java.util.concurrent.ConcurrentHashMap;
31 import java.util.concurrent.ConcurrentMap;
32
33 import org.apache.commons.configuration2.event.BaseEventSource;
34 import org.apache.commons.configuration2.event.Event;
35 import org.apache.commons.configuration2.event.EventListener;
36 import org.apache.commons.configuration2.event.EventType;
37 import org.apache.commons.configuration2.interpol.ConfigurationInterpolator;
38 import org.apache.commons.configuration2.interpol.Lookup;
39 import org.apache.commons.configuration2.io.ConfigurationLogger;
40 import org.apache.commons.configuration2.tree.ImmutableNode;
41 import org.apache.commons.configuration2.tree.NodeCombiner;
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60 public class DynamicCombinedConfiguration extends CombinedConfiguration {
61
62
63
64
65 static class ConfigData {
66
67
68 private final Configuration configuration;
69
70
71 private final String name;
72
73
74 private final String at;
75
76
77
78
79
80
81
82
83 public ConfigData(final Configuration config, final String n, final String at) {
84 configuration = config;
85 name = n;
86 this.at = at;
87 }
88
89
90
91
92
93
94 public String getAt() {
95 return at;
96 }
97
98
99
100
101
102
103 public Configuration getConfiguration() {
104 return configuration;
105 }
106
107
108
109
110
111
112 public String getName() {
113 return name;
114 }
115
116 }
117
118
119
120
121 private static final class CurrentConfigHolder {
122
123
124 private CombinedConfiguration currentConfiguration;
125
126
127
128
129 private final String key;
130
131
132 private int lockCount;
133
134
135
136
137
138
139 public CurrentConfigHolder(final String curKey) {
140 key = curKey;
141 }
142
143
144
145
146
147
148
149 public boolean decrementLockCountAndCheckRelease() {
150 return --lockCount == 0;
151 }
152
153
154
155
156
157
158 public CombinedConfiguration getCurrentConfiguration() {
159 return currentConfiguration;
160 }
161
162
163
164
165
166
167 public String getKey() {
168 return key;
169 }
170
171
172
173
174 public void incrementLockCount() {
175 lockCount++;
176 }
177
178
179
180
181
182
183 public void setCurrentConfiguration(final CombinedConfiguration currentConfiguration) {
184 this.currentConfiguration = currentConfiguration;
185 }
186 }
187
188
189
190
191
192 private static final ThreadLocal<CurrentConfigHolder> CURRENT_CONFIG = new ThreadLocal<>();
193
194
195 private final ConcurrentMap<String, CombinedConfiguration> configs = new ConcurrentHashMap<>();
196
197
198 private final List<ConfigData> configurations = new ArrayList<>();
199
200
201 private final Map<String, Configuration> namedConfigurations = new HashMap<>();
202
203
204 private String keyPattern;
205
206
207 private NodeCombiner nodeCombiner;
208
209
210 private String loggerName = DynamicCombinedConfiguration.class.getName();
211
212
213 private final ConfigurationInterpolator localSubst;
214
215
216
217
218
219
220 public DynamicCombinedConfiguration() {
221 initLogger(new ConfigurationLogger(DynamicCombinedConfiguration.class));
222 localSubst = initLocalInterpolator();
223 }
224
225
226
227
228
229
230 public DynamicCombinedConfiguration(final NodeCombiner comb) {
231 setNodeCombiner(comb);
232 initLogger(new ConfigurationLogger(DynamicCombinedConfiguration.class));
233 localSubst = initLocalInterpolator();
234 }
235
236
237
238
239
240
241
242
243
244
245
246
247
248 @Override
249 public void addConfiguration(final Configuration config, final String name, final String at) {
250 beginWrite(true);
251 try {
252 final ConfigData cd = new ConfigData(config, name, at);
253 configurations.add(cd);
254 if (name != null) {
255 namedConfigurations.put(name, config);
256 }
257
258
259 configs.clear();
260 } finally {
261 endWrite();
262 }
263 }
264
265 @Override
266 public <T extends Event> void addEventListener(final EventType<T> eventType, final EventListener<? super T> listener) {
267 configs.values().forEach(cc -> cc.addEventListener(eventType, listener));
268 super.addEventListener(eventType, listener);
269 }
270
271 @Override
272 protected void addNodesInternal(final String key, final Collection<? extends ImmutableNode> nodes) {
273 getCurrentConfig().addNodes(key, nodes);
274 }
275
276 @Override
277 protected void addPropertyInternal(final String key, final Object value) {
278 getCurrentConfig().addProperty(key, value);
279 }
280
281
282
283
284
285 @Override
286 protected void beginRead(final boolean optimize) {
287 final CurrentConfigHolder cch = ensureCurrentConfiguration();
288 cch.incrementLockCount();
289 if (!optimize && cch.getCurrentConfiguration() == null) {
290
291 beginWrite(false);
292 endWrite();
293 }
294
295
296 cch.getCurrentConfiguration().beginRead(optimize);
297 }
298
299
300
301
302
303 @Override
304 protected void beginWrite(final boolean optimize) {
305 final CurrentConfigHolder cch = ensureCurrentConfiguration();
306 cch.incrementLockCount();
307
308 super.beginWrite(optimize);
309 if (!optimize && cch.getCurrentConfiguration() == null) {
310 cch.setCurrentConfiguration(createChildConfiguration());
311 configs.put(cch.getKey(), cch.getCurrentConfiguration());
312 initChildConfiguration(cch.getCurrentConfiguration());
313 }
314 }
315
316 @Override
317 public void clearErrorListeners() {
318 configs.values().forEach(BaseEventSource::clearErrorListeners);
319 super.clearErrorListeners();
320 }
321
322 @Override
323 public void clearEventListeners() {
324 configs.values().forEach(CombinedConfiguration::clearEventListeners);
325 super.clearEventListeners();
326 }
327
328 @Override
329 protected void clearInternal() {
330 getCurrentConfig().clear();
331 }
332
333 @Override
334 protected void clearPropertyDirect(final String key) {
335 getCurrentConfig().clearProperty(key);
336 }
337
338 @Override
339 protected Object clearTreeInternal(final String key) {
340 getCurrentConfig().clearTree(key);
341 return Collections.emptyList();
342 }
343
344 @Override
345 public HierarchicalConfiguration<ImmutableNode> configurationAt(final String key) {
346 return getCurrentConfig().configurationAt(key);
347 }
348
349 @Override
350 public HierarchicalConfiguration<ImmutableNode> configurationAt(final String key, final boolean supportUpdates) {
351 return getCurrentConfig().configurationAt(key, supportUpdates);
352 }
353
354 @Override
355 public List<HierarchicalConfiguration<ImmutableNode>> configurationsAt(final String key) {
356 return getCurrentConfig().configurationsAt(key);
357 }
358
359 @Override
360 protected boolean containsKeyInternal(final String key) {
361 return getCurrentConfig().containsKey(key);
362 }
363
364
365
366
367
368
369
370 @Override
371 protected boolean containsValueInternal(final Object value) {
372 return getCurrentConfig().contains(getKeys(), value);
373 }
374
375
376
377
378
379
380 private CombinedConfiguration createChildConfiguration() {
381 return new CombinedConfiguration(getNodeCombiner());
382 }
383
384
385
386
387 @Override
388 protected void endRead() {
389 CURRENT_CONFIG.get().getCurrentConfiguration().endRead();
390 releaseLock();
391 }
392
393
394
395
396 @Override
397 protected void endWrite() {
398 super.endWrite();
399 releaseLock();
400 }
401
402
403
404
405
406
407
408
409 private CurrentConfigHolder ensureCurrentConfiguration() {
410 CurrentConfigHolder cch = CURRENT_CONFIG.get();
411 if (cch == null) {
412 final String key = String.valueOf(localSubst.interpolate(keyPattern));
413 cch = new CurrentConfigHolder(key);
414 cch.setCurrentConfiguration(configs.get(key));
415 CURRENT_CONFIG.set(cch);
416 }
417 return cch;
418 }
419
420 @Override
421 public BigDecimal getBigDecimal(final String key) {
422 return getCurrentConfig().getBigDecimal(key);
423 }
424
425 @Override
426 public BigDecimal getBigDecimal(final String key, final BigDecimal defaultValue) {
427 return getCurrentConfig().getBigDecimal(key, defaultValue);
428 }
429
430 @Override
431 public BigInteger getBigInteger(final String key) {
432 return getCurrentConfig().getBigInteger(key);
433 }
434
435 @Override
436 public BigInteger getBigInteger(final String key, final BigInteger defaultValue) {
437 return getCurrentConfig().getBigInteger(key, defaultValue);
438 }
439
440 @Override
441 public boolean getBoolean(final String key) {
442 return getCurrentConfig().getBoolean(key);
443 }
444
445 @Override
446 public boolean getBoolean(final String key, final boolean defaultValue) {
447 return getCurrentConfig().getBoolean(key, defaultValue);
448 }
449
450 @Override
451 public Boolean getBoolean(final String key, final Boolean defaultValue) {
452 return getCurrentConfig().getBoolean(key, defaultValue);
453 }
454
455 @Override
456 public byte getByte(final String key) {
457 return getCurrentConfig().getByte(key);
458 }
459
460 @Override
461 public byte getByte(final String key, final byte defaultValue) {
462 return getCurrentConfig().getByte(key, defaultValue);
463 }
464
465 @Override
466 public Byte getByte(final String key, final Byte defaultValue) {
467 return getCurrentConfig().getByte(key, defaultValue);
468 }
469
470
471
472
473
474
475
476
477 @Override
478 public Configuration getConfiguration(final int index) {
479 beginRead(false);
480 try {
481 final ConfigData cd = configurations.get(index);
482 return cd.getConfiguration();
483 } finally {
484 endRead();
485 }
486 }
487
488
489
490
491
492
493
494 @Override
495 public Configuration getConfiguration(final String name) {
496 beginRead(false);
497 try {
498 return namedConfigurations.get(name);
499 } finally {
500 endRead();
501 }
502 }
503
504
505
506
507
508
509
510 @Override
511 public Set<String> getConfigurationNames() {
512 beginRead(false);
513 try {
514 return namedConfigurations.keySet();
515 } finally {
516 endRead();
517 }
518 }
519
520
521
522
523
524
525
526
527 private CombinedConfiguration getCurrentConfig() {
528 CombinedConfiguration config;
529 String key;
530 beginRead(false);
531 try {
532 config = CURRENT_CONFIG.get().getCurrentConfiguration();
533 key = CURRENT_CONFIG.get().getKey();
534 } finally {
535 endRead();
536 }
537
538 if (getLogger().isDebugEnabled()) {
539 getLogger().debug("Returning config for " + key + ": " + config);
540 }
541 return config;
542 }
543
544 @Override
545 public double getDouble(final String key) {
546 return getCurrentConfig().getDouble(key);
547 }
548
549 @Override
550 public double getDouble(final String key, final double defaultValue) {
551 return getCurrentConfig().getDouble(key, defaultValue);
552 }
553
554 @Override
555 public Double getDouble(final String key, final Double defaultValue) {
556 return getCurrentConfig().getDouble(key, defaultValue);
557 }
558
559 @Override
560 public float getFloat(final String key) {
561 return getCurrentConfig().getFloat(key);
562 }
563
564 @Override
565 public float getFloat(final String key, final float defaultValue) {
566 return getCurrentConfig().getFloat(key, defaultValue);
567 }
568
569 @Override
570 public Float getFloat(final String key, final Float defaultValue) {
571 return getCurrentConfig().getFloat(key, defaultValue);
572 }
573
574 @Override
575 public int getInt(final String key) {
576 return getCurrentConfig().getInt(key);
577 }
578
579 @Override
580 public int getInt(final String key, final int defaultValue) {
581 return getCurrentConfig().getInt(key, defaultValue);
582 }
583
584 @Override
585 public Integer getInteger(final String key, final Integer defaultValue) {
586 return getCurrentConfig().getInteger(key, defaultValue);
587 }
588
589
590
591
592
593
594 public String getKeyPattern() {
595 return this.keyPattern;
596 }
597
598 @Override
599 protected Iterator<String> getKeysInternal() {
600 return getCurrentConfig().getKeys();
601 }
602
603 @Override
604 protected Iterator<String> getKeysInternal(final String prefix) {
605 return getCurrentConfig().getKeys(prefix);
606 }
607
608 @Override
609 public List<Object> getList(final String key) {
610 return getCurrentConfig().getList(key);
611 }
612
613 @Override
614 public List<Object> getList(final String key, final List<?> defaultValue) {
615 return getCurrentConfig().getList(key, defaultValue);
616 }
617
618 @Override
619 public long getLong(final String key) {
620 return getCurrentConfig().getLong(key);
621 }
622
623 @Override
624 public long getLong(final String key, final long defaultValue) {
625 return getCurrentConfig().getLong(key, defaultValue);
626 }
627
628 @Override
629 public Long getLong(final String key, final Long defaultValue) {
630 return getCurrentConfig().getLong(key, defaultValue);
631 }
632
633 @Override
634 protected int getMaxIndexInternal(final String key) {
635 return getCurrentConfig().getMaxIndex(key);
636 }
637
638
639
640
641
642
643 @Override
644 public NodeCombiner getNodeCombiner() {
645 return nodeCombiner;
646 }
647
648
649
650
651
652
653 @Override
654 public int getNumberOfConfigurations() {
655 beginRead(false);
656 try {
657 return configurations.size();
658 } finally {
659 endRead();
660 }
661 }
662
663 @Override
664 public Properties getProperties(final String key) {
665 return getCurrentConfig().getProperties(key);
666 }
667
668 @Override
669 protected Object getPropertyInternal(final String key) {
670 return getCurrentConfig().getProperty(key);
671 }
672
673 @Override
674 public short getShort(final String key) {
675 return getCurrentConfig().getShort(key);
676 }
677
678 @Override
679 public short getShort(final String key, final short defaultValue) {
680 return getCurrentConfig().getShort(key, defaultValue);
681 }
682
683 @Override
684 public Short getShort(final String key, final Short defaultValue) {
685 return getCurrentConfig().getShort(key, defaultValue);
686 }
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706 @Override
707 public Configuration getSource(final String key) {
708 if (key == null) {
709 throw new IllegalArgumentException("Key must not be null.");
710 }
711 return getCurrentConfig().getSource(key);
712 }
713
714 @Override
715 public String getString(final String key) {
716 return getCurrentConfig().getString(key);
717 }
718
719 @Override
720 public String getString(final String key, final String defaultValue) {
721 return getCurrentConfig().getString(key, defaultValue);
722 }
723
724 @Override
725 public String[] getStringArray(final String key) {
726 return getCurrentConfig().getStringArray(key);
727 }
728
729
730
731
732
733
734
735 private void initChildConfiguration(final CombinedConfiguration config) {
736 if (loggerName != null) {
737 config.setLogger(new ConfigurationLogger(loggerName));
738 }
739 config.setExpressionEngine(getExpressionEngine());
740 config.setConversionExpressionEngine(getConversionExpressionEngine());
741 config.setListDelimiterHandler(getListDelimiterHandler());
742 copyEventListeners(config);
743 configurations.forEach(data -> config.addConfiguration(data.getConfiguration(), data.getName(), data.getAt()));
744 config.setSynchronizer(getSynchronizer());
745 }
746
747
748
749
750
751
752
753
754 private ConfigurationInterpolator initLocalInterpolator() {
755 return new ConfigurationInterpolator() {
756 @Override
757 protected Lookup fetchLookupForPrefix(final String prefix) {
758 return nullSafeLookup(getInterpolator().getLookups().get(prefix));
759 }
760 };
761 }
762
763 @Override
764 public Configuration interpolatedConfiguration() {
765 return getCurrentConfig().interpolatedConfiguration();
766 }
767
768
769
770
771
772
773
774 @Override
775 public void invalidate() {
776 getCurrentConfig().invalidate();
777 }
778
779
780
781
782 public void invalidateAll() {
783 configs.values().forEach(CombinedConfiguration::invalidate);
784 }
785
786 @Override
787 protected boolean isEmptyInternal() {
788 return getCurrentConfig().isEmpty();
789 }
790
791
792
793
794
795 private void releaseLock() {
796 final CurrentConfigHolder cch = CURRENT_CONFIG.get();
797 assert cch != null : "No current configuration!";
798 if (cch.decrementLockCountAndCheckRelease()) {
799 CURRENT_CONFIG.remove();
800 }
801 }
802
803
804
805
806
807
808
809 @Override
810 public boolean removeConfiguration(final Configuration config) {
811 beginWrite(false);
812 try {
813 for (int index = 0; index < getNumberOfConfigurations(); index++) {
814 if (configurations.get(index).getConfiguration() == config) {
815 removeConfigurationAt(index);
816 return true;
817 }
818 }
819
820 return false;
821 } finally {
822 endWrite();
823 }
824 }
825
826
827
828
829
830
831
832 @Override
833 public Configuration removeConfiguration(final String name) {
834 final Configuration conf = getConfiguration(name);
835 if (conf != null) {
836 removeConfiguration(conf);
837 }
838 return conf;
839 }
840
841
842
843
844
845
846
847 @Override
848 public Configuration removeConfigurationAt(final int index) {
849 beginWrite(false);
850 try {
851 final ConfigData cd = configurations.remove(index);
852 if (cd.getName() != null) {
853 namedConfigurations.remove(cd.getName());
854 }
855 return cd.getConfiguration();
856 } finally {
857 endWrite();
858 }
859 }
860
861 @Override
862 public <T extends Event> boolean removeEventListener(final EventType<T> eventType, final EventListener<? super T> listener) {
863 configs.values().forEach(cc -> cc.removeEventListener(eventType, listener));
864 return super.removeEventListener(eventType, listener);
865 }
866
867
868
869
870
871
872 public void setKeyPattern(final String pattern) {
873 this.keyPattern = pattern;
874 }
875
876
877
878
879
880
881 public void setLoggerName(final String name) {
882 this.loggerName = name;
883 }
884
885
886
887
888
889
890
891
892 @Override
893 public void setNodeCombiner(final NodeCombiner nodeCombiner) {
894 if (nodeCombiner == null) {
895 throw new IllegalArgumentException("Node combiner must not be null.");
896 }
897 this.nodeCombiner = nodeCombiner;
898 invalidateAll();
899 }
900
901 @Override
902 protected void setPropertyInternal(final String key, final Object value) {
903 getCurrentConfig().setProperty(key, value);
904 }
905
906 @Override
907 protected int sizeInternal() {
908 return getCurrentConfig().size();
909 }
910
911 @Override
912 public Configuration subset(final String prefix) {
913 return getCurrentConfig().subset(prefix);
914 }
915 }