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 }