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 }