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