View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
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>&quot;database.tables&quot;</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      * Don't allow resolveContainerStore to be called recursively.
719      * @param key The key to resolve.
720      * @return The value of the key.
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 }