1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.configuration;
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.HashMap;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Properties;
28 import java.util.Set;
29
30 import org.apache.commons.configuration.event.ConfigurationErrorListener;
31 import org.apache.commons.configuration.event.ConfigurationListener;
32 import org.apache.commons.configuration.tree.ConfigurationNode;
33 import org.apache.commons.configuration.tree.ExpressionEngine;
34 import org.apache.commons.configuration.tree.NodeCombiner;
35
36 /***
37 * DynamicCombinedConfiguration allows a set of CombinedConfigurations to be used. Each CombinedConfiguration
38 * is referenced by a key that is dynamically constructed from a key pattern on each call. The key pattern
39 * will be resolved using the configured ConfigurationInterpolator.
40 * @since 1.6
41 * @author <a
42 * href="http://commons.apache.org/configuration/team-list.html">Commons
43 * Configuration team</a>
44 * @version $Id: DynamicCombinedConfiguration.java 727955 2008-12-19 07:06:16Z oheger $
45 */
46 public class DynamicCombinedConfiguration extends CombinedConfiguration
47 {
48 /***
49 * Prevent recursion while resolving unprefixed properties.
50 */
51 private static ThreadLocal recursive = new ThreadLocal()
52 {
53 protected synchronized Object initialValue()
54 {
55 return Boolean.FALSE;
56 }
57 };
58
59 /*** The CombinedConfigurations */
60 private Map configs = new HashMap();
61
62 /*** Stores a list with the contained configurations. */
63 private List configurations = new ArrayList();
64
65 /*** Stores a map with the named configurations. */
66 private Map namedConfigurations = new HashMap();
67
68 /*** The key pattern for the CombinedConfiguration map */
69 private String keyPattern;
70
71 /*** Stores the combiner. */
72 private NodeCombiner nodeCombiner;
73
74 /***
75 * Creates a new instance of <code>CombinedConfiguration</code> and
76 * initializes the combiner to be used.
77 *
78 * @param comb the node combiner (can be <b>null</b>, then a union combiner
79 * is used as default)
80 */
81 public DynamicCombinedConfiguration(NodeCombiner comb)
82 {
83 super();
84 setNodeCombiner(comb);
85 }
86
87 /***
88 * Creates a new instance of <code>CombinedConfiguration</code> that uses
89 * a union combiner.
90 *
91 * @see org.apache.commons.configuration.tree.UnionCombiner
92 */
93 public DynamicCombinedConfiguration()
94 {
95 super();
96 }
97
98 public void setKeyPattern(String pattern)
99 {
100 this.keyPattern = pattern;
101 }
102
103 public String getKeyPattern()
104 {
105 return this.keyPattern;
106 }
107
108 /***
109 * Returns the node combiner that is used for creating the combined node
110 * structure.
111 *
112 * @return the node combiner
113 */
114 public NodeCombiner getNodeCombiner()
115 {
116 return nodeCombiner;
117 }
118
119 /***
120 * Sets the node combiner. This object will be used when the combined node
121 * structure is to be constructed. It must not be <b>null</b>, otherwise an
122 * <code>IllegalArgumentException</code> exception is thrown. Changing the
123 * node combiner causes an invalidation of this combined configuration, so
124 * that the new combiner immediately takes effect.
125 *
126 * @param nodeCombiner the node combiner
127 */
128 public void setNodeCombiner(NodeCombiner nodeCombiner)
129 {
130 if (nodeCombiner == null)
131 {
132 throw new IllegalArgumentException(
133 "Node combiner must not be null!");
134 }
135 this.nodeCombiner = nodeCombiner;
136 invalidateAll();
137 }
138 /***
139 * Adds a new configuration to this combined configuration. It is possible
140 * (but not mandatory) to give the new configuration a name. This name must
141 * be unique, otherwise a <code>ConfigurationRuntimeException</code> will
142 * be thrown. With the optional <code>at</code> argument you can specify
143 * where in the resulting node structure the content of the added
144 * configuration should appear. This is a string that uses dots as property
145 * delimiters (independent on the current expression engine). For instance
146 * if you pass in the string <code>"database.tables"</code>,
147 * all properties of the added configuration will occur in this branch.
148 *
149 * @param config the configuration to add (must not be <b>null</b>)
150 * @param name the name of this configuration (can be <b>null</b>)
151 * @param at the position of this configuration in the combined tree (can be
152 * <b>null</b>)
153 */
154 public void addConfiguration(AbstractConfiguration config, String name,
155 String at)
156 {
157 ConfigData cd = new ConfigData(config, name, at);
158 configurations.add(cd);
159 if (name != null)
160 {
161 namedConfigurations.put(name, config);
162 }
163 }
164 /***
165 * Returns the number of configurations that are contained in this combined
166 * configuration.
167 *
168 * @return the number of contained configurations
169 */
170 public int getNumberOfConfigurations()
171 {
172 return configurations.size();
173 }
174
175 /***
176 * Returns the configuration at the specified index. The contained
177 * configurations are numbered in the order they were added to this combined
178 * configuration. The index of the first configuration is 0.
179 *
180 * @param index the index
181 * @return the configuration at this index
182 */
183 public Configuration getConfiguration(int index)
184 {
185 ConfigData cd = (ConfigData) configurations.get(index);
186 return cd.getConfiguration();
187 }
188
189 /***
190 * Returns the configuration with the given name. This can be <b>null</b>
191 * if no such configuration exists.
192 *
193 * @param name the name of the configuration
194 * @return the configuration with this name
195 */
196 public Configuration getConfiguration(String name)
197 {
198 return (Configuration) namedConfigurations.get(name);
199 }
200
201 /***
202 * Returns a set with the names of all configurations contained in this
203 * combined configuration. Of course here are only these configurations
204 * listed, for which a name was specified when they were added.
205 *
206 * @return a set with the names of the contained configurations (never
207 * <b>null</b>)
208 */
209 public Set getConfigurationNames()
210 {
211 return namedConfigurations.keySet();
212 }
213
214 /***
215 * Removes the configuration with the specified name.
216 *
217 * @param name the name of the configuration to be removed
218 * @return the removed configuration (<b>null</b> if this configuration
219 * was not found)
220 */
221 public Configuration removeConfiguration(String name)
222 {
223 Configuration conf = getConfiguration(name);
224 if (conf != null)
225 {
226 removeConfiguration(conf);
227 }
228 return conf;
229 }
230
231 /***
232 * Removes the specified configuration from this combined configuration.
233 *
234 * @param config the configuration to be removed
235 * @return a flag whether this configuration was found and could be removed
236 */
237 public boolean removeConfiguration(Configuration config)
238 {
239 for (int index = 0; index < getNumberOfConfigurations(); index++)
240 {
241 if (((ConfigData) configurations.get(index)).getConfiguration() == config)
242 {
243 removeConfigurationAt(index);
244
245 }
246 }
247
248 return super.removeConfiguration(config);
249 }
250
251 /***
252 * Removes the configuration at the specified index.
253 *
254 * @param index the index
255 * @return the removed configuration
256 */
257 public Configuration removeConfigurationAt(int index)
258 {
259 ConfigData cd = (ConfigData) configurations.remove(index);
260 if (cd.getName() != null)
261 {
262 namedConfigurations.remove(cd.getName());
263 }
264 return super.removeConfigurationAt(index);
265 }
266 /***
267 * Returns the configuration root node of this combined configuration. This
268 * method will construct a combined node structure using the current node
269 * combiner if necessary.
270 *
271 * @return the combined root node
272 */
273 public ConfigurationNode getRootNode()
274 {
275 return getCurrentConfig().getRootNode();
276 }
277
278 public void setRootNode(ConfigurationNode rootNode)
279 {
280 if (configs != null)
281 {
282 this.getCurrentConfig().setRootNode(rootNode);
283 }
284 else
285 {
286 super.setRootNode(rootNode);
287 }
288 }
289
290 public void addProperty(String key, Object value)
291 {
292 this.getCurrentConfig().addProperty(key, value);
293 }
294
295 public void clear()
296 {
297 if (configs != null)
298 {
299 this.getCurrentConfig().clear();
300 }
301 }
302
303 public void clearProperty(String key)
304 {
305 this.getCurrentConfig().clearProperty(key);
306 }
307
308 public boolean containsKey(String key)
309 {
310 return this.getCurrentConfig().containsKey(key);
311 }
312
313 public BigDecimal getBigDecimal(String key, BigDecimal defaultValue)
314 {
315 return this.getCurrentConfig().getBigDecimal(key, defaultValue);
316 }
317
318 public BigDecimal getBigDecimal(String key)
319 {
320 return this.getCurrentConfig().getBigDecimal(key);
321 }
322
323 public BigInteger getBigInteger(String key, BigInteger defaultValue)
324 {
325 return this.getCurrentConfig().getBigInteger(key, defaultValue);
326 }
327
328 public BigInteger getBigInteger(String key)
329 {
330 return this.getCurrentConfig().getBigInteger(key);
331 }
332
333 public boolean getBoolean(String key, boolean defaultValue)
334 {
335 return this.getCurrentConfig().getBoolean(key, defaultValue);
336 }
337
338 public Boolean getBoolean(String key, Boolean defaultValue)
339 {
340 return this.getCurrentConfig().getBoolean(key, defaultValue);
341 }
342
343 public boolean getBoolean(String key)
344 {
345 return this.getCurrentConfig().getBoolean(key);
346 }
347
348 public byte getByte(String key, byte defaultValue)
349 {
350 return this.getCurrentConfig().getByte(key, defaultValue);
351 }
352
353 public Byte getByte(String key, Byte defaultValue)
354 {
355 return this.getCurrentConfig().getByte(key, defaultValue);
356 }
357
358 public byte getByte(String key)
359 {
360 return this.getCurrentConfig().getByte(key);
361 }
362
363 public double getDouble(String key, double defaultValue)
364 {
365 return this.getCurrentConfig().getDouble(key, defaultValue);
366 }
367
368 public Double getDouble(String key, Double defaultValue)
369 {
370 return this.getCurrentConfig().getDouble(key, defaultValue);
371 }
372
373 public double getDouble(String key)
374 {
375 return this.getCurrentConfig().getDouble(key);
376 }
377
378 public float getFloat(String key, float defaultValue)
379 {
380 return this.getCurrentConfig().getFloat(key, defaultValue);
381 }
382
383 public Float getFloat(String key, Float defaultValue)
384 {
385 return this.getCurrentConfig().getFloat(key, defaultValue);
386 }
387
388 public float getFloat(String key)
389 {
390 return this.getCurrentConfig().getFloat(key);
391 }
392
393 public int getInt(String key, int defaultValue)
394 {
395 return this.getCurrentConfig().getInt(key, defaultValue);
396 }
397
398 public int getInt(String key)
399 {
400 return this.getCurrentConfig().getInt(key);
401 }
402
403 public Integer getInteger(String key, Integer defaultValue)
404 {
405 return this.getCurrentConfig().getInteger(key, defaultValue);
406 }
407
408 public Iterator getKeys()
409 {
410 return this.getCurrentConfig().getKeys();
411 }
412
413 public Iterator getKeys(String prefix)
414 {
415 return this.getCurrentConfig().getKeys(prefix);
416 }
417
418 public List getList(String key, List defaultValue)
419 {
420 return this.getCurrentConfig().getList(key, defaultValue);
421 }
422
423 public List getList(String key)
424 {
425 return this.getCurrentConfig().getList(key);
426 }
427
428 public long getLong(String key, long defaultValue)
429 {
430 return this.getCurrentConfig().getLong(key, defaultValue);
431 }
432
433 public Long getLong(String key, Long defaultValue)
434 {
435 return this.getCurrentConfig().getLong(key, defaultValue);
436 }
437
438 public long getLong(String key)
439 {
440 return this.getCurrentConfig().getLong(key);
441 }
442
443 public Properties getProperties(String key)
444 {
445 return this.getCurrentConfig().getProperties(key);
446 }
447
448 public Object getProperty(String key)
449 {
450 return this.getCurrentConfig().getProperty(key);
451 }
452
453 public short getShort(String key, short defaultValue)
454 {
455 return this.getCurrentConfig().getShort(key, defaultValue);
456 }
457
458 public Short getShort(String key, Short defaultValue)
459 {
460 return this.getCurrentConfig().getShort(key, defaultValue);
461 }
462
463 public short getShort(String key)
464 {
465 return this.getCurrentConfig().getShort(key);
466 }
467
468 public String getString(String key, String defaultValue)
469 {
470 return this.getCurrentConfig().getString(key, defaultValue);
471 }
472
473 public String getString(String key)
474 {
475 return this.getCurrentConfig().getString(key);
476 }
477
478 public String[] getStringArray(String key)
479 {
480 return this.getCurrentConfig().getStringArray(key);
481 }
482
483 public boolean isEmpty()
484 {
485 return this.getCurrentConfig().isEmpty();
486 }
487
488 public void setProperty(String key, Object value)
489 {
490 if (configs != null)
491 {
492 this.getCurrentConfig().setProperty(key, value);
493 }
494 }
495
496 public Configuration subset(String prefix)
497 {
498 return this.getCurrentConfig().subset(prefix);
499 }
500
501 public Node getRoot()
502 {
503 return this.getCurrentConfig().getRoot();
504 }
505
506 public void setRoot(Node node)
507 {
508 if (configs != null)
509 {
510 this.getCurrentConfig().setRoot(node);
511 }
512 else
513 {
514 super.setRoot(node);
515 }
516 }
517
518 public ExpressionEngine getExpressionEngine()
519 {
520 return super.getExpressionEngine();
521 }
522
523 public void setExpressionEngine(ExpressionEngine expressionEngine)
524 {
525 super.setExpressionEngine(expressionEngine);
526 }
527
528 public void addNodes(String key, Collection nodes)
529 {
530 this.getCurrentConfig().addNodes(key, nodes);
531 }
532
533 public SubnodeConfiguration configurationAt(String key, boolean supportUpdates)
534 {
535 return this.getCurrentConfig().configurationAt(key, supportUpdates);
536 }
537
538 public SubnodeConfiguration configurationAt(String key)
539 {
540 return this.getCurrentConfig().configurationAt(key);
541 }
542
543 public List configurationsAt(String key)
544 {
545 return this.getCurrentConfig().configurationsAt(key);
546 }
547
548 public void clearTree(String key)
549 {
550 this.getCurrentConfig().clearTree(key);
551 }
552
553 public int getMaxIndex(String key)
554 {
555 return this.getCurrentConfig().getMaxIndex(key);
556 }
557
558 public Configuration interpolatedConfiguration()
559 {
560 return this.getCurrentConfig().interpolatedConfiguration();
561 }
562
563
564 /***
565 * Returns the configuration source, in which the specified key is defined.
566 * This method will determine the configuration node that is identified by
567 * the given key. The following constellations are possible:
568 * <ul>
569 * <li>If no node object is found for this key, <b>null</b> is returned.</li>
570 * <li>If the key maps to multiple nodes belonging to different
571 * configuration sources, a <code>IllegalArgumentException</code> is
572 * thrown (in this case no unique source can be determined).</li>
573 * <li>If exactly one node is found for the key, the (child) configuration
574 * object, to which the node belongs is determined and returned.</li>
575 * <li>For keys that have been added directly to this combined
576 * configuration and that do not belong to the namespaces defined by
577 * existing child configurations this configuration will be returned.</li>
578 * </ul>
579 *
580 * @param key the key of a configuration property
581 * @return the configuration, to which this property belongs or <b>null</b>
582 * if the key cannot be resolved
583 * @throws IllegalArgumentException if the key maps to multiple properties
584 * and the source cannot be determined, or if the key is <b>null</b>
585 */
586 public Configuration getSource(String key)
587 {
588 if (key == null)
589 {
590 throw new IllegalArgumentException("Key must not be null!");
591 }
592 return getCurrentConfig().getSource(key);
593 }
594
595 public void addConfigurationListener(ConfigurationListener l)
596 {
597 super.addConfigurationListener(l);
598
599 Iterator iter = configs.values().iterator();
600 while (iter.hasNext())
601 {
602 CombinedConfiguration config = (CombinedConfiguration) iter.next();
603 config.addConfigurationListener(l);
604 }
605 }
606
607 public boolean removeConfigurationListener(ConfigurationListener l)
608 {
609 Iterator iter = configs.values().iterator();
610 while (iter.hasNext())
611 {
612 CombinedConfiguration config = (CombinedConfiguration) iter.next();
613 config.removeConfigurationListener(l);
614 }
615 return super.removeConfigurationListener(l);
616 }
617
618 public Collection getConfigurationListeners()
619 {
620 return super.getConfigurationListeners();
621 }
622
623 public void clearConfigurationListeners()
624 {
625 Iterator iter = configs.values().iterator();
626 while (iter.hasNext())
627 {
628 CombinedConfiguration config = (CombinedConfiguration) iter.next();
629 config.clearConfigurationListeners();
630 }
631 super.clearConfigurationListeners();
632 }
633
634 public void addErrorListener(ConfigurationErrorListener l)
635 {
636 Iterator iter = configs.values().iterator();
637 while (iter.hasNext())
638 {
639 CombinedConfiguration config = (CombinedConfiguration) iter.next();
640 config.addErrorListener(l);
641 }
642 super.addErrorListener(l);
643 }
644
645 public boolean removeErrorListener(ConfigurationErrorListener l)
646 {
647 Iterator iter = configs.values().iterator();
648 while (iter.hasNext())
649 {
650 CombinedConfiguration config = (CombinedConfiguration) iter.next();
651 config.removeErrorListener(l);
652 }
653 return super.removeErrorListener(l);
654 }
655
656 public void clearErrorListeners()
657 {
658 Iterator iter = configs.values().iterator();
659 while (iter.hasNext())
660 {
661 CombinedConfiguration config = (CombinedConfiguration) iter.next();
662 config.clearErrorListeners();
663 }
664 super.clearErrorListeners();
665 }
666
667 public Collection getErrorListeners()
668 {
669 return super.getErrorListeners();
670 }
671
672
673
674 /***
675 * Returns a copy of this object. This implementation performs a deep clone,
676 * i.e. all contained configurations will be cloned, too. For this to work,
677 * all contained configurations must be cloneable. Registered event
678 * listeners won't be cloned. The clone will use the same node combiner than
679 * the original.
680 *
681 * @return the copied object
682 */
683 public Object clone()
684 {
685 return super.clone();
686 }
687
688
689
690 /***
691 * Invalidates the current combined configuration. This means that the next time a
692 * property is accessed the combined node structure must be re-constructed.
693 * Invalidation of a combined configuration also means that an event of type
694 * <code>EVENT_COMBINED_INVALIDATE</code> is fired. Note that while other
695 * events most times appear twice (once before and once after an update),
696 * this event is only fired once (after update).
697 */
698 public void invalidate()
699 {
700 getCurrentConfig().invalidate();
701 }
702
703 public void invalidateAll()
704 {
705 if (configs == null)
706 {
707 return;
708 }
709 Iterator iter = configs.values().iterator();
710 while (iter.hasNext())
711 {
712 CombinedConfiguration config = (CombinedConfiguration) iter.next();
713 config.invalidate();
714 }
715 }
716
717
718
719
720
721
722 protected Object resolveContainerStore(String key)
723 {
724 if (((Boolean) recursive.get()).booleanValue())
725 {
726 return null;
727 }
728 recursive.set(Boolean.TRUE);
729 try
730 {
731 return super.resolveContainerStore(key);
732 }
733 finally
734 {
735 recursive.set(Boolean.FALSE);
736 }
737 }
738
739 private CombinedConfiguration getCurrentConfig()
740 {
741 String key = getSubstitutor().replace(keyPattern);
742 CombinedConfiguration config;
743 synchronized (getNodeCombiner())
744 {
745 config = (CombinedConfiguration) configs.get(key);
746 if (config == null)
747 {
748 config = new CombinedConfiguration(getNodeCombiner());
749 config.setExpressionEngine(this.getExpressionEngine());
750 Iterator iter = config.getErrorListeners().iterator();
751 while (iter.hasNext())
752 {
753 ConfigurationErrorListener listener = (ConfigurationErrorListener) iter.next();
754 config.addErrorListener(listener);
755 }
756 iter = config.getConfigurationListeners().iterator();
757 while (iter.hasNext())
758 {
759 ConfigurationListener listener = (ConfigurationListener) iter.next();
760 config.addConfigurationListener(listener);
761 }
762 config.setForceReloadCheck(isForceReloadCheck());
763 iter = configurations.iterator();
764 while (iter.hasNext())
765 {
766 ConfigData data = (ConfigData) iter.next();
767 config.addConfiguration(data.getConfiguration(), data.getName(),
768 data.getAt());
769 }
770 configs.put(key, config);
771 }
772 }
773 return config;
774 }
775
776 /***
777 * Internal class that identifies each Configuration.
778 */
779 static class ConfigData
780 {
781 /*** Stores a reference to the configuration. */
782 private AbstractConfiguration configuration;
783
784 /*** Stores the name under which the configuration is stored. */
785 private String name;
786
787 /*** Stores the at string.*/
788 private String at;
789
790 /***
791 * Creates a new instance of <code>ConfigData</code> and initializes
792 * it.
793 *
794 * @param config the configuration
795 * @param n the name
796 * @param at the at position
797 */
798 public ConfigData(AbstractConfiguration config, String n, String at)
799 {
800 configuration = config;
801 name = n;
802 this.at = at;
803 }
804
805 /***
806 * Returns the stored configuration.
807 *
808 * @return the configuration
809 */
810 public AbstractConfiguration getConfiguration()
811 {
812 return configuration;
813 }
814
815 /***
816 * Returns the configuration's name.
817 *
818 * @return the name
819 */
820 public String getName()
821 {
822 return name;
823 }
824
825 /***
826 * Returns the at position of this configuration.
827 *
828 * @return the at position
829 */
830 public String getAt()
831 {
832 return at;
833 }
834
835 }
836 }