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.configuration2;
19  
20  import java.io.PrintStream;
21  import java.io.PrintWriter;
22  import java.io.StringWriter;
23  import java.lang.reflect.InvocationTargetException;
24  import java.lang.reflect.Method;
25  import java.lang.reflect.Proxy;
26  import java.util.Iterator;
27  
28  import org.apache.commons.configuration2.event.ConfigurationErrorEvent;
29  import org.apache.commons.configuration2.event.Event;
30  import org.apache.commons.configuration2.event.EventListener;
31  import org.apache.commons.configuration2.event.EventSource;
32  import org.apache.commons.configuration2.event.EventType;
33  import org.apache.commons.configuration2.ex.ConfigurationRuntimeException;
34  import org.apache.commons.configuration2.sync.NoOpSynchronizer;
35  import org.apache.commons.configuration2.sync.Synchronizer;
36  import org.apache.commons.configuration2.tree.ExpressionEngine;
37  import org.apache.commons.logging.Log;
38  import org.apache.commons.logging.LogFactory;
39  
40  /**
41   * Miscellaneous utility methods for configurations.
42   *
43   * @see ConfigurationConverter Utility methods to convert configurations.
44   */
45  public final class ConfigurationUtils {
46      /** Constant for the name of the clone() method. */
47      private static final String METHOD_CLONE = "clone";
48  
49      /**
50       * An array with interfaces to be implemented by a proxy for an immutable configuration.
51       */
52      private static final Class<?>[] IMMUTABLE_CONFIG_IFCS = {ImmutableConfiguration.class};
53  
54      /**
55       * An array with interfaces to be implemented by a proxy for an immutable hierarchical configuration.
56       */
57      private static final Class<?>[] IMMUTABLE_HIERARCHICAL_CONFIG_IFCS = {ImmutableHierarchicalConfiguration.class};
58      /**
59       * A dummy event source that is returned by {@code asEventSource()} if a mock object has to be returned. It provides
60       * empty dummy implementations for all interface methods.
61       */
62      private static final EventSource DUMMY_EVENT_SOURCE = new EventSource() {
63  
64          @Override
65          public <T extends Event> void addEventListener(final EventType<T> eventType, final EventListener<? super T> listener) {
66              // empty
67          }
68  
69          @Override
70          public <T extends Event> boolean removeEventListener(final EventType<T> eventType, final EventListener<? super T> listener) {
71              return false;
72          }
73      };
74  
75      /** The logger. */
76      private static final Log LOG = LogFactory.getLog(ConfigurationUtils.class);
77  
78      /**
79       * <p>
80       * Append all properties from the source configuration to the target configuration. Properties in the source
81       * configuration are appended to the properties with the same key in the target configuration.
82       * </p>
83       * <p>
84       * <em>Note:</em> This method is not able to handle some specifics of configurations derived from
85       * {@code AbstractConfiguration} (for example list delimiters). For a full support of all of these features the {@code copy()}
86       * method of {@code AbstractConfiguration} should be used. In a future release this method might become deprecated.
87       * </p>
88       *
89       * @param source the source configuration
90       * @param target the target configuration
91       * @since 1.1
92       */
93      public static void append(final Configuration source, final Configuration target) {
94          append((ImmutableConfiguration) source, target);
95      }
96  
97      /**
98       * <p>
99       * Append all properties from the source configuration to the target configuration. Properties in the source
100      * configuration are appended to the properties with the same key in the target configuration.
101      * </p>
102      * <p>
103      * <em>Note:</em> This method is not able to handle some specifics of configurations derived from
104      * {@code AbstractConfiguration} (for example list delimiters). For a full support of all of these features the {@code copy()}
105      * method of {@code AbstractConfiguration} should be used. In a future release this method might become deprecated.
106      * </p>
107      *
108      * @param source the source configuration
109      * @param target the target configuration
110      * @since 2.2
111      */
112     public static void append(final ImmutableConfiguration source, final Configuration target) {
113         source.getKeys().forEachRemaining(key -> target.addProperty(key, source.getProperty(key)));
114     }
115 
116     /**
117      * Casts the specified object to an {@code EventSource} if possible. The boolean argument determines the method's
118      * behavior if the object does not implement the {@code EventSource} event: if set to <strong>false</strong>, a
119      * {@code ConfigurationRuntimeException} is thrown; if set to <strong>true</strong>, a dummy {@code EventSource} is returned; on
120      * this object all methods can be called, but they do not have any effect.
121      *
122      * @param obj the object to be cast as {@code EventSource}
123      * @param mockIfUnsupported a flag whether a mock object should be returned if necessary
124      * @return an {@code EventSource}
125      * @throws ConfigurationRuntimeException if the object cannot be cast to {@code EventSource} and the mock flag is
126      *         <strong>false</strong>
127      * @since 2.0
128      */
129     public static EventSource asEventSource(final Object obj, final boolean mockIfUnsupported) {
130         if (obj instanceof EventSource) {
131             return (EventSource) obj;
132         }
133 
134         if (!mockIfUnsupported) {
135             throw new ConfigurationRuntimeException("Cannot cast to EventSource: " + obj);
136         }
137         return DUMMY_EVENT_SOURCE;
138     }
139 
140     /**
141      * An internally used helper method for cloning objects. This implementation is not very sophisticated nor efficient.
142      * Maybe it can be replaced by an implementation from Commons Lang later. The method checks whether the passed in object
143      * implements the {@code Cloneable} interface. If this is the case, the {@code clone()} method is invoked by reflection.
144      * Errors that occur during the cloning process are re-thrown as runtime exceptions.
145      *
146      * @param obj the object to be cloned
147      * @return the cloned object
148      * @throws CloneNotSupportedException if the object cannot be cloned
149      */
150     static Object clone(final Object obj) throws CloneNotSupportedException {
151         if (obj instanceof Cloneable) {
152             try {
153                 final Method m = obj.getClass().getMethod(METHOD_CLONE);
154                 return m.invoke(obj);
155             } catch (final NoSuchMethodException nmex) {
156                 throw new CloneNotSupportedException("No clone() method found for class" + obj.getClass().getName());
157             } catch (final IllegalAccessException | InvocationTargetException itex) {
158                 throw new ConfigurationRuntimeException(itex);
159             }
160         }
161         throw new CloneNotSupportedException(obj.getClass().getName() + " does not implement Cloneable");
162     }
163 
164     /**
165      * Clones the given configuration object if this is possible. If the passed in configuration object implements the
166      * {@code Cloneable} interface, its {@code clone()} method will be invoked. Otherwise an exception will be thrown.
167      *
168      * @param config the configuration object to be cloned (can be <strong>null</strong>)
169      * @return the cloned configuration (<strong>null</strong> if the argument was <strong>null</strong>, too)
170      * @throws ConfigurationRuntimeException if cloning is not supported for this object
171      * @since 1.3
172      */
173     public static Configuration cloneConfiguration(final Configuration config) throws ConfigurationRuntimeException {
174         if (config == null) {
175             return null;
176         }
177         try {
178             return (Configuration) clone(config);
179         } catch (final CloneNotSupportedException cnex) {
180             throw new ConfigurationRuntimeException(cnex);
181         }
182     }
183 
184     /**
185      * Returns a clone of the passed in object if cloning is supported or the object itself if not. This method checks
186      * whether the passed in object implements the {@code Cloneable} interface. If this is the case, the {@code clone()}
187      * method is invoked. Otherwise, the object is directly returned. Errors that might occur during reflection calls are
188      * caught and also cause this method to return the original object.
189      *
190      * @param obj the object to be cloned
191      * @return the result of the cloning attempt
192      * @since 2.0
193      */
194     public static Object cloneIfPossible(final Object obj) {
195         try {
196             return clone(obj);
197         } catch (final Exception ex) {
198             return obj;
199         }
200     }
201 
202     /**
203      * Creates a clone of the specified {@code Synchronizer}. This method can be called by {@code clone()} implementations
204      * in configuration classes that also need to copy the {@code Synchronizer} object. This method can handle some
205      * well-known {@code Synchronizer} implementations directly. For other classes, it uses the following algorithm:
206      * <ul>
207      * <li>If the class of the {@code Synchronizer} has a standard constructor, a new instance is created using
208      * reflection.</li>
209      * <li>If this is not possible, it is tried whether the object can be cloned.</li>
210      * </ul>
211      * If all attempts fail, a {@code ConfigurationRuntimeException} is thrown.
212      *
213      * @param sync the {@code Synchronizer} object to be cloned
214      * @return the clone of this {@code Synchronizer}
215      * @throws ConfigurationRuntimeException if no clone can be created
216      * @throws IllegalArgumentException if <strong>null</strong> is passed in
217      */
218     public static Synchronizer cloneSynchronizer(final Synchronizer sync) {
219         if (sync == null) {
220             throw new IllegalArgumentException("Synchronizer must not be null!");
221         }
222         if (NoOpSynchronizer.INSTANCE == sync) {
223             return sync;
224         }
225 
226         try {
227             return sync.getClass().getConstructor().newInstance();
228         } catch (final Exception ex) {
229             LOG.info("Cannot create new instance of " + sync.getClass());
230         }
231 
232         try {
233             return (Synchronizer) clone(sync);
234         } catch (final CloneNotSupportedException cnex) {
235             throw new ConfigurationRuntimeException("Cannot clone Synchronizer " + sync);
236         }
237     }
238 
239     /**
240      * Converts the passed in configuration to a hierarchical one. If the configuration is already hierarchical, it is
241      * directly returned. Otherwise all properties are copied into a new hierarchical configuration.
242      *
243      * @param conf the configuration to convert
244      * @return the new hierarchical configuration (the result is <strong>null</strong> if and only if the passed in configuration is
245      *         <strong>null</strong>)
246      * @since 1.3
247      */
248     public static HierarchicalConfiguration<?> convertToHierarchical(final Configuration conf) {
249         return convertToHierarchical(conf, null);
250     }
251 
252     /**
253      * Converts the passed in {@code Configuration} object to a hierarchical one using the specified
254      * {@code ExpressionEngine}. This conversion works by adding the keys found in the configuration to a newly created
255      * hierarchical configuration. When adding new keys to a hierarchical configuration the keys are interpreted by its
256      * {@code ExpressionEngine}. If they contain special characters (for example brackets) that are treated in a special way by the
257      * default expression engine, it may be necessary using a specific engine that can deal with such characters. Otherwise
258      * <strong>null</strong> can be passed in for the {@code ExpressionEngine}; then the default expression engine is used. If the
259      * passed in configuration is already hierarchical, it is directly returned. (However, the {@code ExpressionEngine} is
260      * set if it is not <strong>null</strong>.) Otherwise all properties are copied into a new hierarchical configuration.
261      *
262      * @param conf the configuration to convert
263      * @param engine the {@code ExpressionEngine} for the hierarchical configuration or <strong>null</strong> for the default
264      * @return the new hierarchical configuration (the result is <strong>null</strong> if and only if the passed in configuration is
265      *         <strong>null</strong>)
266      * @since 1.6
267      */
268     public static HierarchicalConfiguration<?> convertToHierarchical(final Configuration conf, final ExpressionEngine engine) {
269         if (conf == null) {
270             return null;
271         }
272 
273         if (conf instanceof HierarchicalConfiguration) {
274             final HierarchicalConfiguration<?> hc = (HierarchicalConfiguration<?>) conf;
275             if (engine != null) {
276                 hc.setExpressionEngine(engine);
277             }
278 
279             return hc;
280         }
281         final BaseHierarchicalConfiguration hc = new BaseHierarchicalConfiguration();
282         if (engine != null) {
283             hc.setExpressionEngine(engine);
284         }
285 
286         // Per default, a DisabledListDelimiterHandler is set.
287         // So list delimiters in property values are not an issue.
288         hc.copy(conf);
289         return hc;
290     }
291 
292     /**
293      * <p>
294      * Copy all properties from the source configuration to the target configuration. Properties in the target configuration
295      * are replaced with the properties with the same key in the source configuration.
296      * </p>
297      * <p>
298      * <em>Note:</em> This method is not able to handle some specifics of configurations derived from
299      * {@code AbstractConfiguration} (for example list delimiters). For a full support of all of these features the {@code copy()}
300      * method of {@code AbstractConfiguration} should be used. In a future release this method might become deprecated.
301      * </p>
302      *
303      * @param source the source configuration
304      * @param target the target configuration
305      * @since 1.1
306      */
307     public static void copy(final Configuration source, final Configuration target) {
308         copy((ImmutableConfiguration) source, target);
309     }
310 
311     /**
312      * <p>
313      * Copy all properties from the source configuration to the target configuration. Properties in the target configuration
314      * are replaced with the properties with the same key in the source configuration.
315      * </p>
316      * <p>
317      * <em>Note:</em> This method is not able to handle some specifics of configurations derived from
318      * {@code AbstractConfiguration} (for example list delimiters). For a full support of all of these features the {@code copy()}
319      * method of {@code AbstractConfiguration} should be used. In a future release this method might become deprecated.
320      * </p>
321      *
322      * @param source the source configuration
323      * @param target the target configuration
324      * @since 2.2
325      */
326     public static void copy(final ImmutableConfiguration source, final Configuration target) {
327         source.getKeys().forEachRemaining(key -> target.setProperty(key, source.getProperty(key)));
328     }
329 
330     /**
331      * Helper method for creating a proxy for an unmodifiable configuration. The interfaces the proxy should implement are
332      * passed as argument.
333      *
334      * @param ifcs an array with the interface classes the proxy must implement
335      * @param c the configuration object to be wrapped
336      * @return a proxy object for an immutable configuration
337      * @throws NullPointerException if the configuration is <strong>null</strong>
338      */
339     private static ImmutableConfiguration createUnmodifiableConfiguration(final Class<?>[] ifcs, final Configuration c) {
340         return (ImmutableConfiguration) Proxy.newProxyInstance(ConfigurationUtils.class.getClassLoader(), ifcs, new ImmutableConfigurationInvocationHandler(c));
341     }
342 
343     /**
344      * Dump the configuration key/value mappings to some ouput stream. This version of the method exists only for backwards
345      * compatibility reason.
346      *
347      * @param configuration the configuration
348      * @param out the output stream to dump the configuration to
349      */
350     public static void dump(final Configuration configuration, final PrintStream out) {
351         dump((ImmutableConfiguration) configuration, out);
352     }
353 
354     /**
355      * Dump the configuration key/value mappings to some writer. This version of the method exists only for backwards
356      * compatibility reason.
357      *
358      * @param configuration the configuration
359      * @param out the writer to dump the configuration to
360      */
361     public static void dump(final Configuration configuration, final PrintWriter out) {
362         dump((ImmutableConfiguration) configuration, out);
363     }
364 
365     /**
366      * Dump the configuration key/value mappings to some ouput stream.
367      *
368      * @param configuration the configuration
369      * @param out the output stream to dump the configuration to
370      * @since 2.2
371      */
372     public static void dump(final ImmutableConfiguration configuration, final PrintStream out) {
373         dump(configuration, new PrintWriter(out));
374     }
375 
376     /**
377      * Dump the configuration key/value mappings to some writer.
378      *
379      * @param configuration the configuration
380      * @param out the writer to dump the configuration to
381      * @since 2.2
382      */
383     public static void dump(final ImmutableConfiguration configuration, final PrintWriter out) {
384         for (final Iterator<String> keys = configuration.getKeys(); keys.hasNext();) {
385             final String key = keys.next();
386             final Object value = configuration.getProperty(key);
387             out.print(key);
388             out.print("=");
389             out.print(value);
390 
391             if (keys.hasNext()) {
392                 out.println();
393             }
394         }
395 
396         out.flush();
397     }
398 
399     /**
400      * Enables runtime exceptions for the specified configuration object. This method can be used for configuration
401      * implementations that may face errors on normal property access, for example {@code DatabaseConfiguration} or
402      * {@code JNDIConfiguration}. Per default such errors are simply logged and then ignored. This implementation will
403      * register a special {@link EventListener} that throws a runtime exception (namely a
404      * {@code ConfigurationRuntimeException}) on each received error event.
405      *
406      * @param src the configuration, for which runtime exceptions are to be enabled; this configuration must implement
407      *        {@link EventSource}
408      */
409     public static void enableRuntimeExceptions(final Configuration src) {
410         if (!(src instanceof EventSource)) {
411             throw new IllegalArgumentException("Configuration must implement EventSource!");
412         }
413         ((EventSource) src).addEventListener(ConfigurationErrorEvent.ANY, event -> {
414             // Throw a runtime exception
415             throw new ConfigurationRuntimeException(event.getCause());
416         });
417     }
418 
419     /**
420      * Loads the class with the given name. This method is used whenever a class has to be loaded dynamically. It first
421      * tries the current thread's context class loader. If this fails, the class loader of this class is tried.
422      *
423      * @param clsName the name of the class to be loaded
424      * @return the loaded class
425      * @throws ClassNotFoundException if the class cannot be resolved
426      * @since 2.0
427      */
428     public static Class<?> loadClass(final String clsName) throws ClassNotFoundException {
429         if (LOG.isDebugEnabled()) {
430             LOG.debug("Loading class " + clsName);
431         }
432 
433         final ClassLoader cl = Thread.currentThread().getContextClassLoader();
434         try {
435             if (cl != null) {
436                 return cl.loadClass(clsName);
437             }
438         } catch (final ClassNotFoundException cnfex) {
439             LOG.info("Could not load class " + clsName + " using CCL. Falling back to default CL.", cnfex);
440         }
441 
442         return ConfigurationUtils.class.getClassLoader().loadClass(clsName);
443     }
444 
445     /**
446      * Loads the class with the specified name re-throwing {@code ClassNotFoundException} exceptions as runtime exceptions.
447      * This method works like {@link #loadClass(String)}. However, checked exceptions are caught and re-thrown as
448      * {@code ConfigurationRuntimeException}.
449      *
450      * @param clsName the name of the class to be loaded
451      * @return the loaded class
452      * @throws ConfigurationRuntimeException if the class cannot be resolved
453      * @since 2.0
454      */
455     public static Class<?> loadClassNoEx(final String clsName) {
456         try {
457             return loadClass(clsName);
458         } catch (final ClassNotFoundException cnfex) {
459             throw new ConfigurationRuntimeException("Cannot load class " + clsName, cnfex);
460         }
461     }
462 
463     /**
464      * Gets a string representation of the key/value mappings of a configuration. This version of the method exists only for
465      * backwards compatibility reason.
466      *
467      * @param configuration the configuration
468      * @return a string representation of the configuration
469      */
470     public static String toString(final Configuration configuration) {
471         return toString((ImmutableConfiguration) configuration);
472     }
473 
474     /**
475      * Gets a string representation of the key/value mappings of a configuration.
476      *
477      * @param configuration the configuration
478      * @return a string representation of the configuration
479      * @since 2.2
480      */
481     public static String toString(final ImmutableConfiguration configuration) {
482         final StringWriter writer = new StringWriter();
483         dump(configuration, new PrintWriter(writer));
484         return writer.toString();
485     }
486 
487     /**
488      * Creates an {@code ImmutableConfiguration} from the given {@code Configuration} object. This method creates a proxy
489      * object wrapping the original configuration and making it available under the {@code ImmutableConfiguration}
490      * interface. Through this interface the configuration cannot be manipulated. It is also not possible to cast the
491      * returned object back to a {@code Configuration} instance to circumvent this protection.
492      *
493      * @param c the {@code Configuration} to be wrapped (must not be <strong>null</strong>)
494      * @return an {@code ImmutableConfiguration} view on the specified {@code Configuration} object
495      * @throws NullPointerException if the passed in {@code Configuration} is <strong>null</strong>
496      * @since 2.0
497      */
498     public static ImmutableConfiguration unmodifiableConfiguration(final Configuration c) {
499         return createUnmodifiableConfiguration(IMMUTABLE_CONFIG_IFCS, c);
500     }
501 
502     /**
503      * Creates an {@code ImmutableHierarchicalConfiguration} from the given {@code HierarchicalConfiguration} object. This
504      * method works exactly like the method with the same name, but it operates on hierarchical configurations.
505      *
506      * @param c the {@code HierarchicalConfiguration} to be wrapped (must not be <strong>null</strong>)
507      * @return an {@code ImmutableHierarchicalConfiguration} view on the specified {@code HierarchicalConfiguration} object
508      * @throws NullPointerException if the passed in {@code HierarchicalConfiguration} is <strong>null</strong>
509      * @since 2.0
510      */
511     public static ImmutableHierarchicalConfiguration unmodifiableConfiguration(final HierarchicalConfiguration<?> c) {
512         return (ImmutableHierarchicalConfiguration) createUnmodifiableConfiguration(IMMUTABLE_HIERARCHICAL_CONFIG_IFCS, c);
513     }
514 
515     /**
516      * Private constructor. Prevents instances from being created.
517      */
518     private ConfigurationUtils() {
519         // to prevent instantiation...
520     }
521 }