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.IOException;
    22  import java.io.InputStream;
    23  import java.io.PrintStream;
    24  import java.io.PrintWriter;
    25  import java.io.StringWriter;
    26  import java.lang.reflect.InvocationTargetException;
    27  import java.lang.reflect.Method;
    28  import java.net.MalformedURLException;
    29  import java.net.URL;
    30  import java.net.URLDecoder;
    31  import java.util.Iterator;
    32
    33  import org.apache.commons.lang.StringUtils;
    34  import org.apache.commons.logging.Log;
    35  import org.apache.commons.logging.LogFactory;
    36
    37  /***
    38   * Miscellaneous utility methods for configurations.
    39   *
    40   * @see ConfigurationConverter Utility methods to convert configurations.
    41   *
    42   * @author <a href="mailto:herve.quiroz@esil.univ-mrs.fr">Herve Quiroz</a>
    43   * @author <a href="mailto:oliver.heger@t-online.de">Oliver Heger</a>
    44   * @author Emmanuel Bourg
    45   * @version $Revision: 439648 $, $Date: 2006-09-02 22:42:10 +0200 (Sa, 02 Sep 2006) $
    46   */
    47  public final class ConfigurationUtils
    48  {
    49      /*** Constant for the file URL protocol.*/
    50      static final String PROTOCOL_FILE = "file";
    51
    52      /*** Constant for the resource path separator.*/
    53      static final String RESOURCE_PATH_SEPARATOR = "/";
    54
    55      /*** Constant for the name of the clone() method.*/
    56      private static final String METHOD_CLONE = "clone";
    57
    58      /*** The logger.*/
    59      private static Log log = LogFactory.getLog(ConfigurationUtils.class);
    60
    61      /***
    62       * Private constructor. Prevents instances from being created.
    63       */
    64      private ConfigurationUtils()
    65      {
    66          // to prevent instanciation...
    67      }
    68
    69      /***
    70       * Dump the configuration key/value mappings to some ouput stream.
    71       *
    72       * @param configuration the configuration
    73       * @param out the output stream to dump the configuration to
    74       */
    75      public static void dump(Configuration configuration, PrintStream out)
    76      {
    77          dump(configuration, new PrintWriter(out));
    78      }
    79
    80      /***
    81       * Dump the configuration key/value mappings to some writer.
    82       *
    83       * @param configuration the configuration
    84       * @param out the writer to dump the configuration to
    85       */
    86      public static void dump(Configuration configuration, PrintWriter out)
    87      {
    88          Iterator keys = configuration.getKeys();
    89          while (keys.hasNext())
    90          {
    91              String key = (String) keys.next();
    92              Object value = configuration.getProperty(key);
    93              out.print(key);
    94              out.print("=");
    95              out.print(value);
    96
    97              if (keys.hasNext())
    98              {
    99                  out.println();
    100             }
    101         }
    102
    103         out.flush();
    104     }
    105
    106     /***
    107      * Get a string representation of the key/value mappings of a
    108      * configuration.
    109      *
    110      * @param configuration the configuration
    111      * @return a string representation of the configuration
    112      */
    113     public static String toString(Configuration configuration)
    114     {
    115         StringWriter writer = new StringWriter();
    116         dump(configuration, new PrintWriter(writer));
    117         return writer.toString();
    118     }
    119
    120     /***
    121      * Copy all properties from the source configuration to the target
    122      * configuration. Properties in the target configuration are replaced with
    123      * the properties with the same key in the source configuration.
    124      * <em>Note:</em> This method won't work well on hierarchical configurations
    125      * because it is not able to copy information about the properties'
    126      * structure. So when dealing with hierarchical configuration objects their
    127      * <code>{@link HierarchicalConfiguration#clone() clone()}</code> methods
    128      * should be used.
    129      *
    130      * @param source the source configuration
    131      * @param target the target configuration
    132      * @since 1.1
    133      */
    134     public static void copy(Configuration source, Configuration target)
    135     {
    136         Iterator keys = source.getKeys();
    137         while (keys.hasNext())
    138         {
    139             String key = (String) keys.next();
    140             target.setProperty(key, source.getProperty(key));
    141         }
    142     }
    143
    144     /***
    145      * Append all properties from the source configuration to the target
    146      * configuration. Properties in the source configuration are appended to
    147      * the properties with the same key in the target configuration.
    148      *
    149      * @param source the source configuration
    150      * @param target the target configuration
    151      * @since 1.1
    152      */
    153     public static void append(Configuration source, Configuration target)
    154     {
    155         Iterator keys = source.getKeys();
    156         while (keys.hasNext())
    157         {
    158             String key = (String) keys.next();
    159             target.addProperty(key, source.getProperty(key));
    160         }
    161     }
    162
    163     /***
    164      * Converts the passed in configuration to a hierarchical one. If the
    165      * configuration is already hierarchical, it is directly returned. Otherwise
    166      * all properties are copied into a new hierarchical configuration.
    167      *
    168      * @param conf the configuration to convert
    169      * @return the new hierarchical configuration (the result is <b>null</b> if
    170      * and only if the passed in configuration is <b>null</b>)
    171      * @since 1.3
    172      */
    173     public static HierarchicalConfiguration convertToHierarchical(
    174             Configuration conf)
    175     {
    176         if (conf == null)
    177         {
    178             return null;
    179         }
    180
    181         if (conf instanceof HierarchicalConfiguration)
    182         {
    183             return (HierarchicalConfiguration) conf;
    184         }
    185         else
    186         {
    187             HierarchicalConfiguration hc = new HierarchicalConfiguration();
    188             ConfigurationUtils.copy(conf, hc);
    189             return hc;
    190         }
    191     }
    192
    193     /***
    194      * Clones the given configuration object if this is possible. If the passed
    195      * in configuration object implements the <code>Cloneable</code>
    196      * interface, its <code>clone()</code> method will be invoked. Otherwise
    197      * an exception will be thrown.
    198      *
    199      * @param config the configuration object to be cloned (can be <b>null</b>)
    200      * @return the cloned configuration (<b>null</b> if the argument was
    201      * <b>null</b>, too)
    202      * @throws ConfigurationRuntimeException if cloning is not supported for
    203      * this object
    204      * @since 1.3
    205      */
    206     public static Configuration cloneConfiguration(Configuration config)
    207             throws ConfigurationRuntimeException
    208     {
    209         if (config == null)
    210         {
    211             return null;
    212         }
    213         else
    214         {
    215             try
    216             {
    217                 return (Configuration) clone(config);
    218             }
    219             catch (CloneNotSupportedException cnex)
    220             {
    221                 throw new ConfigurationRuntimeException(cnex);
    222             }
    223         }
    224     }
    225
    226     /***
    227      * An internally used helper method for cloning objects. This implementation
    228      * is not very sophisticated nor efficient. Maybe it can be replaced by an
    229      * implementation from Commons Lang later. The method checks whether the
    230      * passed in object implements the <code>Cloneable</code> interface. If
    231      * this is the case, the <code>clone()</code> method is invoked by
    232      * reflection. Errors that occur during the cloning process are re-thrown as
    233      * runtime exceptions.
    234      *
    235      * @param obj the object to be cloned
    236      * @return the cloned object
    237      * @throws CloneNotSupportedException if the object cannot be cloned
    238      */
    239     static Object clone(Object obj) throws CloneNotSupportedException
    240     {
    241         if (obj instanceof Cloneable)
    242         {
    243             try
    244             {
    245                 Method m = obj.getClass().getMethod(METHOD_CLONE, null);
    246                 return m.invoke(obj, null);
    247             }
    248             catch (NoSuchMethodException nmex)
    249             {
    250                 throw new CloneNotSupportedException(
    251                         "No clone() method found for class"
    252                                 + obj.getClass().getName());
    253             }
    254             catch (IllegalAccessException iaex)
    255             {
    256                 throw new ConfigurationRuntimeException(iaex);
    257             }
    258             catch (InvocationTargetException itex)
    259             {
    260                 throw new ConfigurationRuntimeException(itex);
    261             }
    262         }
    263         else
    264         {
    265             throw new CloneNotSupportedException(obj.getClass().getName()
    266                     + " does not implement Cloneable");
    267         }
    268     }
    269
    270     /***
    271      * Constructs a URL from a base path and a file name. The file name can
    272      * be absolute, relative or a full URL. If necessary the base path URL is
    273      * applied.
    274      *
    275      * @param basePath the base path URL (can be <b>null</b>)
    276      * @param file the file name
    277      * @return the resulting URL
    278      * @throws MalformedURLException if URLs are invalid
    279      */
    280     public static URL getURL(String basePath, String file) throws MalformedURLException
    281     {
    282         File f = new File(file);
    283         if (f.isAbsolute()) // already absolute?
    284         {
    285             return f.toURL();
    286         }
    287
    288         try
    289         {
    290             if (basePath == null)
    291             {
    292                 return new URL(file);
    293             }
    294             else
    295             {
    296                 URL base = new URL(basePath);
    297                 return new URL(base, file);
    298             }
    299         }
    300         catch (MalformedURLException uex)
    301         {
    302             return constructFile(basePath, file).toURL();
    303         }
    304     }
    305
    306     /***
    307      * Helper method for constructing a file object from a base path and a
    308      * file name. This method is called if the base path passed to
    309      * <code>getURL()</code> does not seem to be a valid URL.
    310      *
    311      * @param basePath the base path
    312      * @param fileName the file name
    313      * @return the resulting file
    314      */
    315     static File constructFile(String basePath, String fileName)
    316     {
    317         File file = null;
    318
    319         File absolute = null;
    320         if (fileName != null)
    321         {
    322             absolute = new File(fileName);
    323         }
    324
    325         if (StringUtils.isEmpty(basePath) || (absolute != null && absolute.isAbsolute()))
    326         {
    327             file = new File(fileName);
    328         }
    329         else
    330         {
    331             StringBuffer fName = new StringBuffer();
    332             fName.append(basePath);
    333
    334             // My best friend. Paranoia.
    335             if (!basePath.endsWith(File.separator))
    336             {
    337                 fName.append(File.separator);
    338             }
    339
    340             //
    341             // We have a relative path, and we have
    342             // two possible forms here. If we have the
    343             // "./" form then just strip that off first
    344             // before continuing.
    345             //
    346             if (fileName.startsWith("." + File.separator))
    347             {
    348                 fName.append(fileName.substring(2));
    349             }
    350             else
    351             {
    352                 fName.append(fileName);
    353             }
    354
    355             file = new File(fName.toString());
    356         }
    357
    358         return file;
    359     }
    360
    361     /***
    362      * Return the location of the specified resource by searching the user home
    363      * directory, the current classpath and the system classpath.
    364      *
    365      * @param name the name of the resource
    366      *
    367      * @return the location of the resource
    368      */
    369     public static URL locate(String name)
    370     {
    371         return locate(null, name);
    372     }
    373
    374     /***
    375      * Return the location of the specified resource by searching the user home
    376      * directory, the current classpath and the system classpath.
    377      *
    378      * @param base the base path of the resource
    379      * @param name the name of the resource
    380      *
    381      * @return the location of the resource
    382      */
    383     public static URL locate(String base, String name)
    384     {
    385         if (name == null)
    386         {
    387             // undefined, always return null
    388             return null;
    389         }
    390
    391         URL url = null;
    392
    393         // attempt to create an URL directly
    394         try
    395         {
    396             if (base == null)
    397             {
    398                 url = new URL(name);
    399             }
    400             else
    401             {
    402                 URL baseURL = new URL(base);
    403                 url = new URL(baseURL, name);
    404
    405                 // check if the file exists
    406                 InputStream in = null;
    407                 try
    408                 {
    409                     in = url.openStream();
    410                 }
    411                 finally
    412                 {
    413                     if (in != null)
    414                     {
    415                         in.close();
    416                     }
    417                 }
    418             }
    419
    420             log.debug("Configuration loaded from the URL " + url);
    421         }
    422         catch (IOException e)
    423         {
    424             url = null;
    425         }
    426
    427         // attempt to load from an absolute path
    428         if (url == null)
    429         {
    430             File file = new File(name);
    431             if (file.isAbsolute() && file.exists()) // already absolute?
    432             {
    433                 try
    434                 {
    435                     url = file.toURL();
    436                     log.debug("Configuration loaded from the absolute path " + name);
    437                 }
    438                 catch (MalformedURLException e)
    439                 {
    440                     e.printStackTrace();
    441                 }
    442             }
    443         }
    444
    445         // attempt to load from the base directory
    446         if (url == null)
    447         {
    448             try
    449             {
    450                 File file = constructFile(base, name);
    451                 if (file != null && file.exists())
    452                 {
    453                     url = file.toURL();
    454                 }
    455
    456                 if (url != null)
    457                 {
    458                     log.debug("Configuration loaded from the base path " + name);
    459                 }
    460             }
    461             catch (IOException e)
    462             {
    463                 e.printStackTrace();
    464             }
    465         }
    466
    467         // attempt to load from the user home directory
    468         if (url == null)
    469         {
    470             try
    471             {
    472                 File file = constructFile(System.getProperty("user.home"), name);
    473                 if (file != null && file.exists())
    474                 {
    475                     url = file.toURL();
    476                 }
    477
    478                 if (url != null)
    479                 {
    480                     log.debug("Configuration loaded from the home path " + name);
    481                 }
    482
    483             }
    484             catch (IOException e)
    485             {
    486                 e.printStackTrace();
    487             }
    488         }
    489
    490         // attempt to load from classpath
    491         if (url == null)
    492         {
    493             url = locateFromClasspath(name);
    494         }
    495         return url;
    496     }
    497
    498     /***
    499      * Tries to find a resource with the given name in the classpath.
    500      * @param resourceName the name of the resource
    501      * @return the URL to the found resource or <b>null</b> if the resource
    502      * cannot be found
    503      */
    504     static URL locateFromClasspath(String resourceName)
    505     {
    506         URL url = null;
    507         // attempt to load from the context classpath
    508         ClassLoader loader = Thread.currentThread().getContextClassLoader();
    509         if (loader != null)
    510         {
    511             url = loader.getResource(resourceName);
    512
    513             if (url != null)
    514             {
    515                 log.debug("Configuration loaded from the context classpath (" + resourceName + ")");
    516             }
    517         }
    518
    519         // attempt to load from the system classpath
    520         if (url == null)
    521         {
    522             url = ClassLoader.getSystemResource(resourceName);
    523
    524             if (url != null)
    525             {
    526                 log.debug("Configuration loaded from the system classpath (" + resourceName + ")");
    527             }
    528         }
    529         return url;
    530     }
    531
    532     /***
    533      * Return the path without the file name, for example http://xyz.net/foo/bar.xml
    534      * results in http://xyz.net/foo/
    535      *
    536      * @param url the URL from which to extract the path
    537      * @return the path component of the passed in URL
    538      */
    539     static String getBasePath(URL url)
    540     {
    541         if (url == null)
    542         {
    543             return null;
    544         }
    545
    546         String s = url.toString();
    547
    548         if (s.endsWith("/") || StringUtils.isEmpty(url.getPath()))
    549         {
    550             return s;
    551         }
    552         else
    553         {
    554             return s.substring(0, s.lastIndexOf("/") + 1);
    555         }
    556     }
    557
    558     /***
    559      * Extract the file name from the specified URL.
    560      *
    561      * @param url the URL from which to extract the file name
    562      * @return the extracted file name
    563      */
    564     static String getFileName(URL url)
    565     {
    566         if (url == null)
    567         {
    568             return null;
    569         }
    570
    571         String path = url.getPath();
    572
    573         if (path.endsWith("/") || StringUtils.isEmpty(path))
    574         {
    575             return null;
    576         }
    577         else
    578         {
    579             return path.substring(path.lastIndexOf("/") + 1);
    580         }
    581     }
    582
    583     /***
    584      * Tries to convert the specified base path and file name into a file object.
    585      * This method is called e.g. by the save() methods of file based
    586      * configurations. The parameter strings can be relative files, absolute
    587      * files and URLs as well.
    588      *
    589      * @param basePath the base path
    590      * @param fileName the file name
    591      * @return the file object (<b>null</b> if no file can be obtained)
    592      */
    593     public static File getFile(String basePath, String fileName)
    594     {
    595         // Check if URLs are involved
    596         URL url;
    597         try
    598         {
    599             url = new URL(new URL(basePath), fileName);
    600         }
    601         catch (MalformedURLException mex1)
    602         {
    603             try
    604             {
    605                 url = new URL(fileName);
    606             }
    607             catch (MalformedURLException mex2)
    608             {
    609                 url = null;
    610             }
    611         }
    612
    613         if (url != null)
    614         {
    615             return fileFromURL(url);
    616         }
    617
    618         return constructFile(basePath, fileName);
    619     }
    620
    621     /***
    622      * Tries to convert the specified URL to a file object. If this fails,
    623      * <b>null</b> is returned.
    624      *
    625      * @param url the URL
    626      * @return the resulting file object
    627      */
    628     public static File fileFromURL(URL url)
    629     {
    630         if (PROTOCOL_FILE.equals(url.getProtocol()))
    631         {
    632             return new File(URLDecoder.decode(url.getPath()));
    633         }
    634         else
    635         {
    636             return null;
    637         }
    638     }
    639 }