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
    18  package org.apache.commons.configuration;
    19
    20  import java.io.File;
    21  import java.io.FileOutputStream;
    22  import java.io.IOException;
    23  import java.io.InputStream;
    24  import java.io.InputStreamReader;
    25  import java.io.OutputStream;
    26  import java.io.OutputStreamWriter;
    27  import java.io.Reader;
    28  import java.io.UnsupportedEncodingException;
    29  import java.io.Writer;
    30  import java.net.HttpURLConnection;
    31  import java.net.MalformedURLException;
    32  import java.net.URL;
    33  import java.net.URLConnection;
    34  import java.util.Iterator;
    35
    36  import org.apache.commons.configuration.reloading.InvariantReloadingStrategy;
    37  import org.apache.commons.configuration.reloading.ReloadingStrategy;
    38  import org.apache.commons.lang.StringUtils;
    39  import org.apache.commons.logging.LogFactory;
    40
    41  /***
    42   * <p>Partial implementation of the <code>FileConfiguration</code> interface.
    43   * Developpers of file based configuration may want to extend this class,
    44   * the two methods left to implement are <code>{@link FileConfiguration#load(Reader)}</code>
    45   * and <code>{@link FileConfiguration#save(Writer)}</code>.</p>
    46   * <p>This base class already implements a couple of ways to specify the location
    47   * of the file this configuration is based on. The following possibilities
    48   * exist:
    49   * <ul><li>URLs: With the method <code>setURL()</code> a full URL to the
    50   * configuration source can be specified. This is the most flexible way. Note
    51   * that the <code>save()</code> methods support only <em>file:</em> URLs.</li>
    52   * <li>Files: The <code>setFile()</code> method allows to specify the
    53   * configuration source as a file. This can be either a relative or an
    54   * absolute file. In the former case the file is resolved based on the current
    55   * directory.</li>
    56   * <li>As file paths in string form: With the <code>setPath()</code> method a
    57   * full path to a configuration file can be provided as a string.</li>
    58   * <li>Separated as base path and file name: This is the native form in which
    59   * the location is stored. The base path is a string defining either a local
    60   * directory or a URL. It can be set using the <code>setBasePath()</code>
    61   * method. The file name, non surprisingly, defines the name of the configuration
    62   * file.</li></ul></p>
    63   * <p>Note that the <code>load()</code> methods do not wipe out the configuration's
    64   * content before the new configuration file is loaded. Thus it is very easy to
    65   * construct a union configuration by simply loading multiple configuration
    66   * files, e.g.</p>
    67   * <p><pre>
    68   * config.load(configFile1);
    69   * config.load(configFile2);
    70   * </pre></p>
    71   * <p>After executing this code fragment, the resulting configuration will
    72   * contain both the properties of configFile1 and configFile2. On the other
    73   * hand, if the current configuration file is to be reloaded, <code>clear()</code>
    74   * should be called first. Otherwise the properties are doubled. This behavior
    75   * is analogous to the behavior of the <code>load(InputStream)</code> method
    76   * in <code>java.util.Properties</code>.</p>
    77   *
    78   * @author Emmanuel Bourg
    79   * @version $Revision: 545904 $, $Date: 2007-06-10 18:10:17 +0200 (So, 10 Jun 2007) $
    80   * @since 1.0-rc2
    81   */
    82  public abstract class AbstractFileConfiguration extends BaseConfiguration implements FileConfiguration
    83  {
    84      /*** Constant for the configuration reload event.*/
    85      public static final int EVENT_RELOAD = 20;
    86
    87      /*** Stores the file name.*/
    88      protected String fileName;
    89
    90      /*** Stores the base path.*/
    91      protected String basePath;
    92
    93      /*** The auto save flag.*/
    94      protected boolean autoSave;
    95
    96      /*** Holds a reference to the reloading strategy.*/
    97      protected ReloadingStrategy strategy;
    98
    99      /*** A lock object for protecting reload operations.*/
    100     private Object reloadLock = new Object();
    101
    102     /*** Stores the encoding of the configuration file.*/
    103     private String encoding;
    104
    105     /*** Stores the URL from which the configuration file was loaded.*/
    106     private URL sourceURL;
    107
    108     /*** A counter that prohibits reloading.*/
    109     private int noReload;
    110
    111     /***
    112      * Default constructor
    113      *
    114      * @since 1.1
    115      */
    116     public AbstractFileConfiguration()
    117     {
    118         initReloadingStrategy();
    119         setLogger(LogFactory.getLog(getClass()));
    120         addErrorLogListener();
    121     }
    122
    123     /***
    124      * Creates and loads the configuration from the specified file. The passed
    125      * in string must be a valid file name, either absolute or relativ.
    126      *
    127      * @param fileName The name of the file to load.
    128      *
    129      * @throws ConfigurationException Error while loading the file
    130      * @since 1.1
    131      */
    132     public AbstractFileConfiguration(String fileName) throws ConfigurationException
    133     {
    134         this();
    135
    136         // store the file name
    137         setFileName(fileName);
    138
    139         // load the file
    140         load();
    141     }
    142
    143     /***
    144      * Creates and loads the configuration from the specified file.
    145      *
    146      * @param file The file to load.
    147      * @throws ConfigurationException Error while loading the file
    148      * @since 1.1
    149      */
    150     public AbstractFileConfiguration(File file) throws ConfigurationException
    151     {
    152         this();
    153
    154         // set the file and update the url, the base path and the file name
    155         setFile(file);
    156
    157         // load the file
    158         if (file.exists())
    159         {
    160             load();
    161         }
    162     }
    163
    164     /***
    165      * Creates and loads the configuration from the specified URL.
    166      *
    167      * @param url The location of the file to load.
    168      * @throws ConfigurationException Error while loading the file
    169      * @since 1.1
    170      */
    171     public AbstractFileConfiguration(URL url) throws ConfigurationException
    172     {
    173         this();
    174
    175         // set the URL and update the base path and the file name
    176         setURL(url);
    177
    178         // load the file
    179         load();
    180     }
    181
    182     /***
    183      * Load the configuration from the underlying location.
    184      *
    185      * @throws ConfigurationException if loading of the configuration fails
    186      */
    187     public void load() throws ConfigurationException
    188     {
    189         if (sourceURL != null)
    190         {
    191             load(sourceURL);
    192         }
    193         else
    194         {
    195             load(getFileName());
    196         }
    197     }
    198
    199     /***
    200      * Locate the specified file and load the configuration. This does not
    201      * change the source of the configuration (i.e. the internally maintained file name).
    202      * Use one of the setter methods for this purpose.
    203      *
    204      * @param fileName the name of the file to be loaded
    205      * @throws ConfigurationException if an error occurs
    206      */
    207     public void load(String fileName) throws ConfigurationException
    208     {
    209         try
    210         {
    211             URL url = ConfigurationUtils.locate(basePath, fileName);
    212
    213             if (url == null)
    214             {
    215                 throw new ConfigurationException("Cannot locate configuration source " + fileName);
    216             }
    217             load(url);
    218         }
    219         catch (ConfigurationException e)
    220         {
    221             throw e;
    222         }
    223         catch (Exception e)
    224         {
    225             throw new ConfigurationException(e.getMessage(), e);
    226         }
    227     }
    228
    229     /***
    230      * Load the configuration from the specified file. This does not change
    231      * the source of the configuration (i.e. the internally maintained file
    232      * name). Use one of the setter methods for this purpose.
    233      *
    234      * @param file the file to load
    235      * @throws ConfigurationException if an error occurs
    236      */
    237     public void load(File file) throws ConfigurationException
    238     {
    239         try
    240         {
    241             load(file.toURL());
    242         }
    243         catch (ConfigurationException e)
    244         {
    245             throw e;
    246         }
    247         catch (Exception e)
    248         {
    249             throw new ConfigurationException(e.getMessage(), e);
    250         }
    251     }
    252
    253     /***
    254      * Load the configuration from the specified URL. This does not change the
    255      * source of the configuration (i.e. the internally maintained file name).
    256      * Use on of the setter methods for this purpose.
    257      *
    258      * @param url the URL of the file to be loaded
    259      * @throws ConfigurationException if an error occurs
    260      */
    261     public void load(URL url) throws ConfigurationException
    262     {
    263         if (sourceURL == null)
    264         {
    265             if (StringUtils.isEmpty(getBasePath()))
    266             {
    267                 // ensure that we have a valid base path
    268                 setBasePath(url.toString());
    269             }
    270             sourceURL = url;
    271         }
    272
    273         // throw an exception if the target URL is a directory
    274         File file = ConfigurationUtils.fileFromURL(url);
    275         if (file != null && file.isDirectory())
    276         {
    277             throw new ConfigurationException("Cannot load a configuration from a directory");
    278         }
    279
    280         InputStream in = null;
    281
    282         try
    283         {
    284             in = url.openStream();
    285             load(in);
    286         }
    287         catch (ConfigurationException e)
    288         {
    289             throw e;
    290         }
    291         catch (Exception e)
    292         {
    293             throw new ConfigurationException(e.getMessage(), e);
    294         }
    295         finally
    296         {
    297             // close the input stream
    298             try
    299             {
    300                 if (in != null)
    301                 {
    302                     in.close();
    303                 }
    304             }
    305             catch (IOException e)
    306             {
    307                 getLogger().warn("Could not close input stream", e);
    308             }
    309         }
    310     }
    311
    312     /***
    313      * Load the configuration from the specified stream, using the encoding
    314      * returned by {@link #getEncoding()}.
    315      *
    316      * @param in the input stream
    317      *
    318      * @throws ConfigurationException if an error occurs during the load operation
    319      */
    320     public void load(InputStream in) throws ConfigurationException
    321     {
    322         load(in, getEncoding());
    323     }
    324
    325     /***
    326      * Load the configuration from the specified stream, using the specified
    327      * encoding. If the encoding is null the default encoding is used.
    328      *
    329      * @param in the input stream
    330      * @param encoding the encoding used. <code>null</code> to use the default encoding
    331      *
    332      * @throws ConfigurationException if an error occurs during the load operation
    333      */
    334     public void load(InputStream in, String encoding) throws ConfigurationException
    335     {
    336         Reader reader = null;
    337
    338         if (encoding != null)
    339         {
    340             try
    341             {
    342                 reader = new InputStreamReader(in, encoding);
    343             }
    344             catch (UnsupportedEncodingException e)
    345             {
    346                 throw new ConfigurationException(
    347                         "The requested encoding is not supported, try the default encoding.", e);
    348             }
    349         }
    350
    351         if (reader == null)
    352         {
    353             reader = new InputStreamReader(in);
    354         }
    355
    356         load(reader);
    357     }
    358
    359     /***
    360      * Save the configuration. Before this method can be called a valid file
    361      * name must have been set.
    362      *
    363      * @throws ConfigurationException if an error occurs or no file name has
    364      * been set yet
    365      */
    366     public void save() throws ConfigurationException
    367     {
    368         if (getFileName() == null)
    369         {
    370             throw new ConfigurationException("No file name has been set!");
    371         }
    372
    373         if (sourceURL != null)
    374         {
    375             save(sourceURL);
    376         }
    377         else
    378         {
    379             save(fileName);
    380         }
    381         strategy.init();
    382     }
    383
    384     /***
    385      * Save the configuration to the specified file. This doesn't change the
    386      * source of the configuration, use setFileName() if you need it.
    387      *
    388      * @param fileName the file name
    389      *
    390      * @throws ConfigurationException if an error occurs during the save operation
    391      */
    392     public void save(String fileName) throws ConfigurationException
    393     {
    394         try
    395         {
    396             File file = ConfigurationUtils.getFile(basePath, fileName);
    397             if (file == null)
    398             {
    399                 throw new ConfigurationException("Invalid file name for save: " + fileName);
    400             }
    401             save(file);
    402         }
    403         catch (ConfigurationException e)
    404         {
    405             throw e;
    406         }
    407         catch (Exception e)
    408         {
    409             throw new ConfigurationException(e.getMessage(), e);
    410         }
    411     }
    412
    413     /***
    414      * Save the configuration to the specified URL.
    415      * This doesn't change the source of the configuration, use setURL()
    416      * if you need it.
    417      *
    418      * @param url the URL
    419      *
    420      * @throws ConfigurationException if an error occurs during the save operation
    421      */
    422     public void save(URL url) throws ConfigurationException
    423     {
    424         // file URLs have to be converted to Files since FileURLConnection is
    425         // read only (http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4191800)
    426         File file = ConfigurationUtils.fileFromURL(url);
    427         if (file != null)
    428         {
    429             save(file);
    430         }
    431         else
    432         {
    433             // for non file URLs save through an URLConnection
    434             OutputStream out = null;
    435             try
    436             {
    437                 URLConnection connection = url.openConnection();
    438                 connection.setDoOutput(true);
    439
    440                 // use the PUT method for http URLs
    441                 if (connection instanceof HttpURLConnection)
    442                 {
    443                     HttpURLConnection conn = (HttpURLConnection) connection;
    444                     conn.setRequestMethod("PUT");
    445                 }
    446
    447                 out = connection.getOutputStream();
    448                 save(out);
    449
    450                 // check the response code for http URLs and throw an exception if an error occured
    451                 if (connection instanceof HttpURLConnection)
    452                 {
    453                     HttpURLConnection conn = (HttpURLConnection) connection;
    454                     if (conn.getResponseCode() >= HttpURLConnection.HTTP_BAD_REQUEST)
    455                     {
    456                         throw new IOException("HTTP Error " + conn.getResponseCode() + " " + conn.getResponseMessage());
    457                     }
    458                 }
    459             }
    460             catch (IOException e)
    461             {
    462                 throw new ConfigurationException("Could not save to URL " + url, e);
    463             }
    464             finally
    465             {
    466                 closeSilent(out);
    467             }
    468         }
    469     }
    470
    471     /***
    472      * Save the configuration to the specified file. The file is created
    473      * automatically if it doesn't exist. This doesn't change the source
    474      * of the configuration, use {@link #setFile} if you need it.
    475      *
    476      * @param file the target file
    477      *
    478      * @throws ConfigurationException if an error occurs during the save operation
    479      */
    480     public void save(File file) throws ConfigurationException
    481     {
    482         OutputStream out = null;
    483
    484         try
    485         {
    486             // create the file if necessary
    487             createPath(file);
    488             out = new FileOutputStream(file);
    489             save(out);
    490         }
    491         catch (IOException e)
    492         {
    493             throw new ConfigurationException(e.getMessage(), e);
    494         }
    495         finally
    496         {
    497             closeSilent(out);
    498         }
    499     }
    500
    501     /***
    502      * Save the configuration to the specified stream, using the encoding
    503      * returned by {@link #getEncoding()}.
    504      *
    505      * @param out the output stream
    506      *
    507      * @throws ConfigurationException if an error occurs during the save operation
    508      */
    509     public void save(OutputStream out) throws ConfigurationException
    510     {
    511         save(out, getEncoding());
    512     }
    513
    514     /***
    515      * Save the configuration to the specified stream, using the specified
    516      * encoding. If the encoding is null the default encoding is used.
    517      *
    518      * @param out the output stream
    519      * @param encoding the encoding to use
    520      * @throws ConfigurationException if an error occurs during the save operation
    521      */
    522     public void save(OutputStream out, String encoding) throws ConfigurationException
    523     {
    524         Writer writer = null;
    525
    526         if (encoding != null)
    527         {
    528             try
    529             {
    530                 writer = new OutputStreamWriter(out, encoding);
    531             }
    532             catch (UnsupportedEncodingException e)
    533             {
    534                 throw new ConfigurationException(
    535                         "The requested encoding is not supported, try the default encoding.", e);
    536             }
    537         }
    538
    539         if (writer == null)
    540         {
    541             writer = new OutputStreamWriter(out);
    542         }
    543
    544         save(writer);
    545     }
    546
    547     /***
    548      * Return the name of the file.
    549      *
    550      * @return the file name
    551      */
    552     public String getFileName()
    553     {
    554         return fileName;
    555     }
    556
    557     /***
    558      * Set the name of the file. The passed in file name can contain a
    559      * relative path.
    560      * It must be used when referring files with relative paths from classpath.
    561      * Use <code>{@link AbstractFileConfiguration#setPath(String)
    562      * setPath()}</code> to set a full qualified file name.
    563      *
    564      * @param fileName the name of the file
    565      */
    566     public void setFileName(String fileName)
    567     {
    568         sourceURL = null;
    569         this.fileName = fileName;
    570     }
    571
    572     /***
    573      * Return the base path.
    574      *
    575      * @return the base path
    576      * @see FileConfiguration#getBasePath()
    577      */
    578     public String getBasePath()
    579     {
    580         return basePath;
    581     }
    582
    583     /***
    584      * Sets the base path. The base path is typically either a path to a
    585      * directory or a URL. Together with the value passed to the
    586      * <code>setFileName()</code> method it defines the location of the
    587      * configuration file to be loaded. The strategies for locating the file are
    588      * quite tolerant. For instance if the file name is already an absolute path
    589      * or a fully defined URL, the base path will be ignored. The base path can
    590      * also be a URL, in which case the file name is interpreted in this URL's
    591      * context. Because the base path is used by some of the derived classes for
    592      * resolving relative file names it should contain a meaningful value. If
    593      * other methods are used for determining the location of the configuration
    594      * file (e.g. <code>setFile()</code> or <code>setURL()</code>), the
    595      * base path is automatically set.
    596      *
    597      * @param basePath the base path.
    598      */
    599     public void setBasePath(String basePath)
    600     {
    601         sourceURL = null;
    602         this.basePath = basePath;
    603     }
    604
    605     /***
    606      * Return the file where the configuration is stored. If the base path is a
    607      * URL with a protocol different than &quot;file&quot;, or the configuration
    608      * file is within a compressed archive, the return value
    609      * will not point to a valid file object.
    610      *
    611      * @return the file where the configuration is stored; this can be <b>null</b>
    612      */
    613     public File getFile()
    614     {
    615         if (getFileName() == null && sourceURL == null)
    616         {
    617             return null;
    618         }
    619         else if (sourceURL != null)
    620         {
    621             return ConfigurationUtils.fileFromURL(sourceURL);
    622         }
    623         else
    624         {
    625             return ConfigurationUtils.getFile(getBasePath(), getFileName());
    626         }
    627     }
    628
    629     /***
    630      * Set the file where the configuration is stored. The passed in file is
    631      * made absolute if it is not yet. Then the file's path component becomes
    632      * the base path and its name component becomes the file name.
    633      *
    634      * @param file the file where the configuration is stored
    635      */
    636     public void setFile(File file)
    637     {
    638         sourceURL = null;
    639         setFileName(file.getName());
    640         setBasePath((file.getParentFile() != null) ? file.getParentFile()
    641                 .getAbsolutePath() : null);
    642     }
    643
    644     /***
    645      * Returns the full path to the file this configuration is based on. The
    646      * return value is a valid File path only if this configuration is based on
    647      * a file on the local disk.
    648      * If the configuration was loaded from a packed archive the returned value
    649      * is the string form of the URL from which the configuration was loaded.
    650      *
    651      * @return the full path to the configuration file
    652      */
    653     public String getPath()
    654     {
    655         String path = null;
    656         File file = getFile();
    657         // if resource was loaded from jar file may be null
    658         if (file != null)
    659         {
    660             path = file.getAbsolutePath();
    661         }
    662
    663         // try to see if file was loaded from a jar
    664         if (path == null)
    665         {
    666             if (sourceURL != null)
    667             {
    668                 path = sourceURL.getPath();
    669             }
    670             else
    671             {
    672                 try
    673                 {
    674                     path = ConfigurationUtils.getURL(getBasePath(), getFileName()).getPath();
    675                 }
    676                 catch (MalformedURLException e)
    677                 {
    678                     // simply ignore it and return null
    679                     ;
    680                 }
    681             }
    682         }
    683
    684         return path;
    685     }
    686
    687     /***
    688      * Sets the location of this configuration as a full or relative path name.
    689      * The passed in path should represent a valid file name on the file system.
    690      * It must not be used to specify relative paths for files that exist
    691      * in classpath, either plain file system or compressed archive,
    692      * because this method expands any relative path to an absolute one which
    693      * may end in an invalid absolute path for classpath references.
    694      *
    695      * @param path the full path name of the configuration file
    696      */
    697     public void setPath(String path)
    698     {
    699         setFile(new File(path));
    700     }
    701
    702     /***
    703      * Return the URL where the configuration is stored.
    704      *
    705      * @return the configuration's location as URL
    706      */
    707     public URL getURL()
    708     {
    709         return (sourceURL != null) ? sourceURL
    710                 : ConfigurationUtils.locate(getBasePath(), getFileName());
    711     }
    712
    713     /***
    714      * Set the location of this configuration as a URL. For loading this can be
    715      * an arbitrary URL with a supported protocol. If the configuration is to
    716      * be saved, too, a URL with the &quot;file&quot; protocol should be
    717      * provided.
    718      *
    719      * @param url the location of this configuration as URL
    720      */
    721     public void setURL(URL url)
    722     {
    723         setBasePath(ConfigurationUtils.getBasePath(url));
    724         setFileName(ConfigurationUtils.getFileName(url));
    725         sourceURL = url;
    726     }
    727
    728     public void setAutoSave(boolean autoSave)
    729     {
    730         this.autoSave = autoSave;
    731     }
    732
    733     public boolean isAutoSave()
    734     {
    735         return autoSave;
    736     }
    737
    738     /***
    739      * Save the configuration if the automatic persistence is enabled
    740      * and if a file is specified.
    741      */
    742     protected void possiblySave()
    743     {
    744         if (autoSave && fileName != null)
    745         {
    746             try
    747             {
    748                 save();
    749             }
    750             catch (ConfigurationException e)
    751             {
    752                 throw new ConfigurationRuntimeException("Failed to auto-save", e);
    753             }
    754         }
    755     }
    756
    757     /***
    758      * Adds a new property to this configuration. This implementation checks if
    759      * the auto save mode is enabled and saves the configuration if necessary.
    760      *
    761      * @param key the key of the new property
    762      * @param value the value
    763      */
    764     public void addProperty(String key, Object value)
    765     {
    766         super.addProperty(key, value);
    767         possiblySave();
    768     }
    769
    770     /***
    771      * Sets a new value for the specified property. This implementation checks
    772      * if the auto save mode is enabled and saves the configuration if
    773      * necessary.
    774      *
    775      * @param key the key of the affected property
    776      * @param value the value
    777      */
    778     public void setProperty(String key, Object value)
    779     {
    780         super.setProperty(key, value);
    781         possiblySave();
    782     }
    783
    784     public void clearProperty(String key)
    785     {
    786         super.clearProperty(key);
    787         possiblySave();
    788     }
    789
    790     public ReloadingStrategy getReloadingStrategy()
    791     {
    792         return strategy;
    793     }
    794
    795     public void setReloadingStrategy(ReloadingStrategy strategy)
    796     {
    797         this.strategy = strategy;
    798         strategy.setConfiguration(this);
    799         strategy.init();
    800     }
    801
    802     /***
    803      * Performs a reload operation if necessary. This method is called on each
    804      * access of this configuration. It asks the associated reloading strategy
    805      * whether a reload should be performed. If this is the case, the
    806      * configuration is cleared and loaded again from its source. If this
    807      * operation causes an exception, the registered error listeners will be
    808      * notified. The error event passed to the listeners is of type
    809      * <code>EVENT_RELOAD</code> and contains the exception that caused the
    810      * event.
    811      */
    812     public void reload()
    813     {
    814         synchronized (reloadLock)
    815         {
    816             if (noReload == 0)
    817             {
    818                 try
    819                 {
    820                     enterNoReload(); // avoid reentrant calls
    821
    822                     if (strategy.reloadingRequired())
    823                     {
    824                         if (getLogger().isInfoEnabled())
    825                         {
    826                             getLogger().info("Reloading configuration. URL is " + getURL());
    827                         }
    828                         fireEvent(EVENT_RELOAD, null, getURL(), true);
    829                         setDetailEvents(false);
    830                         boolean autoSaveBak = this.isAutoSave(); // save the current state
    831                         this.setAutoSave(false); // deactivate autoSave to prevent information loss
    832                         try
    833                         {
    834                             clear();
    835                             load();
    836                         }
    837                         finally
    838                         {
    839                             this.setAutoSave(autoSaveBak); // set autoSave to previous value
    840                             setDetailEvents(true);
    841                         }
    842                         fireEvent(EVENT_RELOAD, null, getURL(), false);
    843
    844                         // notify the strategy
    845                         strategy.reloadingPerformed();
    846                     }
    847                 }
    848                 catch (Exception e)
    849                 {
    850                     fireError(EVENT_RELOAD, null, null, e);
    851                     // todo rollback the changes if the file can't be reloaded
    852                 }
    853                 finally
    854                 {
    855                     exitNoReload();
    856                 }
    857             }
    858         }
    859     }
    860
    861     /***
    862      * Enters the &quot;No reloading mode&quot;. As long as this mode is active
    863      * no reloading will be performed. This is necessary for some
    864      * implementations of <code>save()</code> in derived classes, which may
    865      * cause a reload while accessing the properties to save. This may cause the
    866      * whole configuration to be erased. To avoid this, this method can be
    867      * called first. After a call to this method there always must be a
    868      * corresponding call of <code>{@link #exitNoReload()}</code> later! (If
    869      * necessary, <code>finally</code> blocks must be used to ensure this.
    870      */
    871     protected void enterNoReload()
    872     {
    873         synchronized (reloadLock)
    874         {
    875             noReload++;
    876         }
    877     }
    878
    879     /***
    880      * Leaves the &quot;No reloading mode&quot;.
    881      *
    882      * @see #enterNoReload()
    883      */
    884     protected void exitNoReload()
    885     {
    886         synchronized (reloadLock)
    887         {
    888             if (noReload > 0) // paranoia check
    889             {
    890                 noReload--;
    891             }
    892         }
    893     }
    894
    895     /***
    896      * Sends an event to all registered listeners. This implementation ensures
    897      * that no reloads are performed while the listeners are invoked. So
    898      * infinite loops can be avoided that can be caused by event listeners
    899      * accessing the configuration's properties when they are invoked.
    900      *
    901      * @param type the event type
    902      * @param propName the name of the property
    903      * @param propValue the value of the property
    904      * @param before the before update flag
    905      */
    906     protected void fireEvent(int type, String propName, Object propValue, boolean before)
    907     {
    908         enterNoReload();
    909         try
    910         {
    911             super.fireEvent(type, propName, propValue, before);
    912         }
    913         finally
    914         {
    915             exitNoReload();
    916         }
    917     }
    918
    919     public Object getProperty(String key)
    920     {
    921         reload();
    922         return super.getProperty(key);
    923     }
    924
    925     public boolean isEmpty()
    926     {
    927         reload();
    928         return super.isEmpty();
    929     }
    930
    931     public boolean containsKey(String key)
    932     {
    933         reload();
    934         return super.containsKey(key);
    935     }
    936
    937     public Iterator getKeys()
    938     {
    939         reload();
    940         return super.getKeys();
    941     }
    942
    943     /***
    944      * Create the path to the specified file.
    945      *
    946      * @param file the target file
    947      */
    948     private void createPath(File file)
    949     {
    950         if (file != null)
    951         {
    952             // create the path to the file if the file doesn't exist
    953             if (!file.exists())
    954             {
    955                 File parent = file.getParentFile();
    956                 if (parent != null && !parent.exists())
    957                 {
    958                     parent.mkdirs();
    959                 }
    960             }
    961         }
    962     }
    963
    964     public String getEncoding()
    965     {
    966         return encoding;
    967     }
    968
    969     public void setEncoding(String encoding)
    970     {
    971         this.encoding = encoding;
    972     }
    973
    974     /***
    975      * Creates a copy of this configuration. The new configuration object will
    976      * contain the same properties as the original, but it will lose any
    977      * connection to a source file (if one exists); this includes setting the
    978      * source URL, base path, and file name to <b>null</b>. This is done to
    979      * avoid race conditions if both the original and the copy are modified and
    980      * then saved.
    981      *
    982      * @return the copy
    983      * @since 1.3
    984      */
    985     public Object clone()
    986     {
    987         AbstractFileConfiguration copy = (AbstractFileConfiguration) super.clone();
    988         copy.setBasePath(null);
    989         copy.setFileName(null);
    990         copy.initReloadingStrategy();
    991         return copy;
    992     }
    993
    994     /***
    995      * Helper method for initializing the reloading strategy.
    996      */
    997     private void initReloadingStrategy()
    998     {
    999         setReloadingStrategy(new InvariantReloadingStrategy());
    1000     }
    1001
    1002     /***
    1003      * A helper method for closing an output stream. Occurring exceptions will
    1004      * be ignored.
    1005      *
    1006      * @param out the output stream to be closed (may be <b>null</b>)
    1007      * @since 1.5
    1008      */
    1009     private void closeSilent(OutputStream out)
    1010     {
    1011         try
    1012         {
    1013             if (out != null)
    1014             {
    1015                 out.close();
    1016             }
    1017         }
    1018         catch (IOException e)
    1019         {
    1020             getLogger().warn("Could not close output stream", e);
    1021         }
    1022     }
    1023 }