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