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.io.File;
20  import java.io.FileNotFoundException;
21  import java.io.InputStream;
22  import java.io.OutputStream;
23  import java.io.Reader;
24  import java.io.Writer;
25  import java.math.BigDecimal;
26  import java.math.BigInteger;
27  import java.net.MalformedURLException;
28  import java.net.URL;
29  import java.util.Collection;
30  import java.util.HashMap;
31  import java.util.Iterator;
32  import java.util.List;
33  import java.util.Map;
34  import java.util.Properties;
35  
36  import org.apache.commons.configuration.event.ConfigurationErrorEvent;
37  import org.apache.commons.configuration.event.ConfigurationErrorListener;
38  import org.apache.commons.configuration.event.ConfigurationEvent;
39  import org.apache.commons.configuration.event.ConfigurationListener;
40  import org.apache.commons.configuration.tree.ConfigurationNode;
41  import org.apache.commons.configuration.tree.ExpressionEngine;
42  
43  /***
44   * This class provides access to multiple configuration files that reside in a location that
45   * can be specified by a pattern allowing applications to be multi-tenant.  For example,
46   * providing a pattern of "file:///opt/config/${product}/${client}/config.xml" will result in
47   * "product" and "client" being resolved on every call. The configuration resulting from the
48   * resolved pattern will be saved for future access.
49   * @since 1.6
50   * @author <a
51   * href="http://commons.apache.org/configuration/team-list.html">Commons
52   * Configuration team</a>
53   * @version $Id: MultiFileHierarchicalConfiguration.java 727958 2008-12-19 07:19:24Z oheger $
54   */
55  public class MultiFileHierarchicalConfiguration extends AbstractHierarchicalFileConfiguration
56      implements ConfigurationListener, ConfigurationErrorListener
57  {
58      /*** FILE URL prefix */
59      private static final String FILE_URL_PREFIX = "file:";
60  
61      /***
62       * Prevent recursion while resolving unprefixed properties.
63       */
64      private static ThreadLocal recursive = new ThreadLocal()
65      {
66          protected synchronized Object initialValue()
67          {
68              return Boolean.FALSE;
69          }
70      };
71  
72      /*** Map of configurations */
73      private final Map configurationsMap = new HashMap();
74  
75      /*** key pattern for configurationsMap */
76      private String pattern;
77  
78      /*** True if the constructor has finished */
79      private boolean init;
80  
81      /***
82       * Default Constructor.
83       */
84      public MultiFileHierarchicalConfiguration()
85      {
86          super();
87          this.init = true;
88      }
89  
90      /***
91       * Construct the configuration with the specified pattern.
92       * @param pathPattern The pattern to use to locate configuration files.
93       */
94      public MultiFileHierarchicalConfiguration(String pathPattern)
95      {
96          super();
97          this.pattern = pathPattern;
98          this.init = true;
99      }
100 
101     /***
102      * Set the File pattern
103      * @param pathPattern The pattern for the path to the configuration.
104      */
105     public void setFilePattern(String pathPattern)
106     {
107         this.pattern = pathPattern;
108     }
109 
110     /***
111      * Creates the file configuration delegate, i.e. the object that implements
112      * functionality required by the <code>FileConfiguration</code> interface.
113      * This base implementation will return an instance of the
114      * <code>FileConfigurationDelegate</code> class. Derived classes may
115      * override it to create a different delegate object.
116      *
117      * @return the file configuration delegate
118      */
119     protected FileConfigurationDelegate createDelegate()
120     {
121         return new FileConfigurationDelegate();
122     }
123 
124     public void addProperty(String key, Object value)
125     {
126         this.getConfiguration().addProperty(key, value);
127     }
128 
129     public void clear()
130     {
131         this.getConfiguration().clear();
132     }
133 
134     public void clearProperty(String key)
135     {
136         this.getConfiguration().clearProperty(key);
137     }
138 
139     public boolean containsKey(String key)
140     {
141         return this.getConfiguration().containsKey(key);
142     }
143 
144     public BigDecimal getBigDecimal(String key, BigDecimal defaultValue)
145     {
146         return this.getConfiguration().getBigDecimal(key, defaultValue);
147     }
148 
149     public BigDecimal getBigDecimal(String key)
150     {
151         return this.getConfiguration().getBigDecimal(key);
152     }
153 
154     public BigInteger getBigInteger(String key, BigInteger defaultValue)
155     {
156         return this.getConfiguration().getBigInteger(key, defaultValue);
157     }
158 
159     public BigInteger getBigInteger(String key)
160     {
161         return this.getConfiguration().getBigInteger(key);
162     }
163 
164     public boolean getBoolean(String key, boolean defaultValue)
165     {
166         return this.getConfiguration().getBoolean(key, defaultValue);
167     }
168 
169     public Boolean getBoolean(String key, Boolean defaultValue)
170     {
171         return this.getConfiguration().getBoolean(key, defaultValue);
172     }
173 
174     public boolean getBoolean(String key)
175     {
176         return this.getConfiguration().getBoolean(key);
177     }
178 
179     public byte getByte(String key, byte defaultValue)
180     {
181         return this.getConfiguration().getByte(key, defaultValue);
182     }
183 
184     public Byte getByte(String key, Byte defaultValue)
185     {
186         return this.getConfiguration().getByte(key, defaultValue);
187     }
188 
189     public byte getByte(String key)
190     {
191         return this.getConfiguration().getByte(key);
192     }
193 
194     public double getDouble(String key, double defaultValue)
195     {
196         return this.getConfiguration().getDouble(key, defaultValue);
197     }
198 
199     public Double getDouble(String key, Double defaultValue)
200     {
201         return this.getConfiguration().getDouble(key, defaultValue);
202     }
203 
204     public double getDouble(String key)
205     {
206         return this.getConfiguration().getDouble(key);
207     }
208 
209     public float getFloat(String key, float defaultValue)
210     {
211         return this.getConfiguration().getFloat(key, defaultValue);
212     }
213 
214     public Float getFloat(String key, Float defaultValue)
215     {
216         return this.getConfiguration().getFloat(key, defaultValue);
217     }
218 
219     public float getFloat(String key)
220     {
221         return this.getConfiguration().getFloat(key);
222     }
223 
224     public int getInt(String key, int defaultValue)
225     {
226         return this.getConfiguration().getInt(key, defaultValue);
227     }
228 
229     public int getInt(String key)
230     {
231         return this.getConfiguration().getInt(key);
232     }
233 
234     public Integer getInteger(String key, Integer defaultValue)
235     {
236         return this.getConfiguration().getInteger(key, defaultValue);
237     }
238 
239     public Iterator getKeys()
240     {
241         return this.getConfiguration().getKeys();
242     }
243 
244     public Iterator getKeys(String prefix)
245     {
246         return this.getConfiguration().getKeys(prefix);
247     }
248 
249     public List getList(String key, List defaultValue)
250     {
251         return this.getConfiguration().getList(key, defaultValue);
252     }
253 
254     public List getList(String key)
255     {
256         return this.getConfiguration().getList(key);
257     }
258 
259     public long getLong(String key, long defaultValue)
260     {
261         return this.getConfiguration().getLong(key, defaultValue);
262     }
263 
264     public Long getLong(String key, Long defaultValue)
265     {
266         return this.getConfiguration().getLong(key, defaultValue);
267     }
268 
269     public long getLong(String key)
270     {
271         return this.getConfiguration().getLong(key);
272     }
273 
274     public Properties getProperties(String key)
275     {
276         return this.getConfiguration().getProperties(key);
277     }
278 
279     public Object getProperty(String key)
280     {
281         return this.getConfiguration().getProperty(key);
282     }
283 
284     public short getShort(String key, short defaultValue)
285     {
286         return this.getConfiguration().getShort(key, defaultValue);
287     }
288 
289     public Short getShort(String key, Short defaultValue)
290     {
291         return this.getConfiguration().getShort(key, defaultValue);
292     }
293 
294     public short getShort(String key)
295     {
296         return this.getConfiguration().getShort(key);
297     }
298 
299     public String getString(String key, String defaultValue)
300     {
301         return this.getConfiguration().getString(key, defaultValue);
302     }
303 
304     public String getString(String key)
305     {
306         return this.getConfiguration().getString(key);
307     }
308 
309     public String[] getStringArray(String key)
310     {
311         return this.getConfiguration().getStringArray(key);
312     }
313 
314     public boolean isEmpty()
315     {
316         return this.getConfiguration().isEmpty();
317     }
318 
319     public void setProperty(String key, Object value)
320     {
321         if (init)
322         {
323             this.getConfiguration().setProperty(key, value);
324         }
325     }
326 
327     public Configuration subset(String prefix)
328     {
329         return this.getConfiguration().subset(prefix);
330     }
331 
332     public Node getRoot()
333     {
334         return this.getConfiguration().getRoot();
335     }
336 
337     public void setRoot(Node node)
338     {
339         if (init)
340         {
341             this.getConfiguration().setRoot(node);
342         }
343         else
344         {
345             super.setRoot(node);
346         }
347     }
348 
349     public ConfigurationNode getRootNode()
350     {
351         return this.getConfiguration().getRootNode();
352     }
353 
354     public void setRootNode(ConfigurationNode rootNode)
355     {
356         if (init)
357         {
358             this.getConfiguration().setRootNode(rootNode);
359         }
360         else
361         {
362             super.setRootNode(rootNode);
363         }
364     }
365 
366     public ExpressionEngine getExpressionEngine()
367     {
368         return super.getExpressionEngine();
369     }
370 
371     public void setExpressionEngine(ExpressionEngine expressionEngine)
372     {
373         super.setExpressionEngine(expressionEngine);
374     }
375 
376     public void addNodes(String key, Collection nodes)
377     {
378         this.getConfiguration().addNodes(key, nodes);
379     }
380 
381     public SubnodeConfiguration configurationAt(String key, boolean supportUpdates)
382     {
383         return this.getConfiguration().configurationAt(key, supportUpdates);
384     }
385 
386     public SubnodeConfiguration configurationAt(String key)
387     {
388         return this.getConfiguration().configurationAt(key);
389     }
390 
391     public List configurationsAt(String key)
392     {
393         return this.getConfiguration().configurationsAt(key);
394     }
395 
396     public void clearTree(String key)
397     {
398         this.getConfiguration().clearTree(key);
399     }
400 
401     public int getMaxIndex(String key)
402     {
403         return this.getConfiguration().getMaxIndex(key);
404     }
405 
406     public Configuration interpolatedConfiguration()
407     {
408         return this.getConfiguration().interpolatedConfiguration();
409     }
410 
411     public void addConfigurationListener(ConfigurationListener l)
412     {
413         super.addConfigurationListener(l);
414     }
415 
416     public boolean removeConfigurationListener(ConfigurationListener l)
417     {
418         return super.removeConfigurationListener(l);
419     }
420 
421     public Collection getConfigurationListeners()
422     {
423         return super.getConfigurationListeners();
424     }
425 
426     public void clearConfigurationListeners()
427     {
428         super.clearConfigurationListeners();
429     }
430 
431     public void addErrorListener(ConfigurationErrorListener l)
432     {
433         super.addErrorListener(l);
434     }
435 
436     public boolean removeErrorListener(ConfigurationErrorListener l)
437     {
438         return super.removeErrorListener(l);
439     }
440 
441     public void clearErrorListeners()
442     {
443         super.clearErrorListeners();
444     }
445 
446     public Collection getErrorListeners()
447     {
448         return super.getErrorListeners();
449     }
450 
451     public void save(Writer writer) throws ConfigurationException
452     {
453         if (init)
454         {
455             this.getConfiguration().save(writer);
456         }
457     }
458 
459     public void load(Reader reader) throws ConfigurationException
460     {
461         if (init)
462         {
463             this.getConfiguration().load(reader);
464         }
465     }
466 
467     public void load() throws ConfigurationException
468     {
469         this.getConfiguration().load();
470     }
471 
472     public void load(String fileName) throws ConfigurationException
473     {
474         this.getConfiguration().load(fileName);
475     }
476 
477     public void load(File file) throws ConfigurationException
478     {
479         this.getConfiguration().load(file);
480     }
481 
482     public void load(URL url) throws ConfigurationException
483     {
484         this.getConfiguration().load(url);
485     }
486 
487     public void load(InputStream in) throws ConfigurationException
488     {
489         this.getConfiguration().load(in);
490     }
491 
492     public void load(InputStream in, String encoding) throws ConfigurationException
493     {
494         this.getConfiguration().load(in, encoding);
495     }
496 
497     public void save() throws ConfigurationException
498     {
499         this.getConfiguration().save();
500     }
501 
502     public void save(String fileName) throws ConfigurationException
503     {
504         this.getConfiguration().save(fileName);
505     }
506 
507     public void save(File file) throws ConfigurationException
508     {
509         this.getConfiguration().save(file);
510     }
511 
512     public void save(URL url) throws ConfigurationException
513     {
514         this.getConfiguration().save(url);
515     }
516 
517     public void save(OutputStream out) throws ConfigurationException
518     {
519         this.getConfiguration().save(out);
520     }
521 
522     public void save(OutputStream out, String encoding) throws ConfigurationException
523     {
524         this.getConfiguration().save(out, encoding);
525     }
526 
527     public void configurationChanged(ConfigurationEvent event)
528     {
529         if (event.getSource() instanceof XMLConfiguration)
530         {
531             Iterator iter = getConfigurationListeners().iterator();
532             while (iter.hasNext())
533             {
534                 ConfigurationListener listener = (ConfigurationListener) iter.next();
535                 listener.configurationChanged(event);
536             }
537         }
538     }
539 
540     public void configurationError(ConfigurationErrorEvent event)
541     {
542         if (event.getSource() instanceof XMLConfiguration)
543         {
544             Iterator iter = getErrorListeners().iterator();
545             while (iter.hasNext())
546             {
547                 ConfigurationErrorListener listener = (ConfigurationErrorListener) iter.next();
548                 listener.configurationError(event);
549             }
550         }
551     }
552 
553     /*
554      * Don't allow resolveContainerStore to be called recursively.
555      * @param key The key to resolve.
556      * @return The value of the key.
557      */
558     protected Object resolveContainerStore(String key)
559     {
560         if (((Boolean) recursive.get()).booleanValue())
561         {
562             return null;
563         }
564         recursive.set(Boolean.TRUE);
565         try
566         {
567             return super.resolveContainerStore(key);
568         }
569         finally
570         {
571             recursive.set(Boolean.FALSE);
572         }
573     }
574 
575     /***
576      * Remove the current Configuration.
577      */
578     public void removeConfiguration()
579     {
580         String path = getSubstitutor().replace(pattern);
581         synchronized (configurationsMap)
582         {
583             configurationsMap.remove(path);
584         }
585     }
586 
587     /***
588      * First checks to see if the cache exists, if it does, get the associated Configuration.
589      * If not it will load a new Configuration and save it in the cache.
590      *
591      * @return the Configuration associated with the current value of the path pattern.
592      */
593     private AbstractHierarchicalFileConfiguration getConfiguration()
594     {
595         if (pattern == null)
596         {
597             throw new ConfigurationRuntimeException("File pattern must be defined");
598         }
599         String path = getSubstitutor().replace(pattern);
600         synchronized (configurationsMap)
601         {
602             if (configurationsMap.containsKey(path))
603             {
604                 return (AbstractHierarchicalFileConfiguration) configurationsMap.get(path);
605             }
606         }
607 
608         if (path.equals(pattern))
609         {
610             XMLConfiguration configuration = new XMLConfiguration()
611             {
612                 public void load() throws ConfigurationException
613                 {
614                 }
615                 public void save() throws ConfigurationException
616                 {
617                 }
618             };
619             synchronized (configurationsMap)
620             {
621                 configurationsMap.put(pattern, configuration);
622             }
623             return configuration;
624         }
625 
626         XMLConfiguration configuration = new XMLConfiguration();
627         try
628         {
629             URL url = getURL(path);
630             configuration.setURL(url);
631             configuration.load();
632             configuration.setExpressionEngine(getExpressionEngine());
633             configuration.setReloadingStrategy(getReloadingStrategy());
634             configuration.addConfigurationListener(this);
635             configuration.addErrorListener(this);
636             synchronized (configurationsMap)
637             {
638                 if (!configurationsMap.containsKey(path))
639                 {
640                     configurationsMap.put(path, configuration);
641                 }
642             }
643         }
644         catch (ConfigurationException ce)
645         {
646             throw new ConfigurationRuntimeException(ce);
647         }
648         catch (FileNotFoundException fnfe)
649         {
650             throw new ConfigurationRuntimeException(fnfe);
651         }
652 
653         return configuration;
654     }
655 
656     private URL getURL(String resourceLocation) throws FileNotFoundException
657     {
658         if (resourceLocation == null)
659         {
660             throw new IllegalArgumentException("A path pattern must be configured");
661         }
662         try
663         {
664             // try URL
665             return new URL(resourceLocation);
666         }
667         catch (MalformedURLException ex)
668         {
669             // no URL -> treat as file path
670             try
671             {
672                 return new URL(FILE_URL_PREFIX + resourceLocation);
673             }
674             catch (MalformedURLException ex2)
675             {
676                 throw new FileNotFoundException("Resource location [" + resourceLocation
677                         + "] is not a URL or a well-formed file path");
678             }
679         }
680     }
681 }