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