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