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