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 }