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 }