001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.configuration2; 019 020import java.io.PrintStream; 021import java.io.PrintWriter; 022import java.io.StringWriter; 023import java.lang.reflect.InvocationTargetException; 024import java.lang.reflect.Method; 025import java.lang.reflect.Proxy; 026import java.util.Iterator; 027 028import org.apache.commons.configuration2.event.ConfigurationErrorEvent; 029import org.apache.commons.configuration2.event.Event; 030import org.apache.commons.configuration2.event.EventListener; 031import org.apache.commons.configuration2.event.EventSource; 032import org.apache.commons.configuration2.event.EventType; 033import org.apache.commons.configuration2.ex.ConfigurationRuntimeException; 034import org.apache.commons.configuration2.sync.NoOpSynchronizer; 035import org.apache.commons.configuration2.sync.Synchronizer; 036import org.apache.commons.configuration2.tree.ExpressionEngine; 037import org.apache.commons.logging.Log; 038import org.apache.commons.logging.LogFactory; 039 040/** 041 * Miscellaneous utility methods for configurations. 042 * 043 * @see ConfigurationConverter Utility methods to convert configurations. 044 * 045 */ 046public final class ConfigurationUtils { 047 /** Constant for the name of the clone() method. */ 048 private static final String METHOD_CLONE = "clone"; 049 050 /** 051 * An array with interfaces to be implemented by a proxy for an immutable configuration. 052 */ 053 private static final Class<?>[] IMMUTABLE_CONFIG_IFCS = {ImmutableConfiguration.class}; 054 055 /** 056 * An array with interfaces to be implemented by a proxy for an immutable hierarchical configuration. 057 */ 058 private static final Class<?>[] IMMUTABLE_HIERARCHICAL_CONFIG_IFCS = {ImmutableHierarchicalConfiguration.class}; 059 /** 060 * A dummy event source that is returned by {@code asEventSource()} if a mock object has to be returned. It provides 061 * empty dummy implementations for all interface methods. 062 */ 063 private static final EventSource DUMMY_EVENT_SOURCE = new EventSource() { 064 065 @Override 066 public <T extends Event> void addEventListener(final EventType<T> eventType, final EventListener<? super T> listener) { 067 } 068 069 @Override 070 public <T extends Event> boolean removeEventListener(final EventType<T> eventType, final EventListener<? super T> listener) { 071 return false; 072 } 073 }; 074 075 /** The logger. */ 076 private static final Log LOG = LogFactory.getLog(ConfigurationUtils.class); 077 078 /** 079 * Private constructor. Prevents instances from being created. 080 */ 081 private ConfigurationUtils() { 082 // to prevent instantiation... 083 } 084 085 /** 086 * Dump the configuration key/value mappings to some ouput stream. 087 * 088 * @param configuration the configuration 089 * @param out the output stream to dump the configuration to 090 * @since 2.2 091 */ 092 public static void dump(final ImmutableConfiguration configuration, final PrintStream out) { 093 dump(configuration, new PrintWriter(out)); 094 } 095 096 /** 097 * Dump the configuration key/value mappings to some ouput stream. This version of the method exists only for backwards 098 * compatibility reason. 099 * 100 * @param configuration the configuration 101 * @param out the output stream to dump the configuration to 102 */ 103 public static void dump(final Configuration configuration, final PrintStream out) { 104 dump((ImmutableConfiguration) configuration, out); 105 } 106 107 /** 108 * Dump the configuration key/value mappings to some writer. 109 * 110 * @param configuration the configuration 111 * @param out the writer to dump the configuration to 112 * @since 2.2 113 */ 114 public static void dump(final ImmutableConfiguration configuration, final PrintWriter out) { 115 for (final Iterator<String> keys = configuration.getKeys(); keys.hasNext();) { 116 final String key = keys.next(); 117 final Object value = configuration.getProperty(key); 118 out.print(key); 119 out.print("="); 120 out.print(value); 121 122 if (keys.hasNext()) { 123 out.println(); 124 } 125 } 126 127 out.flush(); 128 } 129 130 /** 131 * Dump the configuration key/value mappings to some writer. This version of the method exists only for backwards 132 * compatibility reason. 133 * 134 * @param configuration the configuration 135 * @param out the writer to dump the configuration to 136 */ 137 public static void dump(final Configuration configuration, final PrintWriter out) { 138 dump((ImmutableConfiguration) configuration, out); 139 } 140 141 /** 142 * Get a string representation of the key/value mappings of a configuration. 143 * 144 * @param configuration the configuration 145 * @return a string representation of the configuration 146 * @since 2.2 147 */ 148 public static String toString(final ImmutableConfiguration configuration) { 149 final StringWriter writer = new StringWriter(); 150 dump(configuration, new PrintWriter(writer)); 151 return writer.toString(); 152 } 153 154 /** 155 * Get a string representation of the key/value mappings of a configuration. This version of the method exists only for 156 * backwards compatibility reason. 157 * 158 * @param configuration the configuration 159 * @return a string representation of the configuration 160 */ 161 public static String toString(final Configuration configuration) { 162 return toString((ImmutableConfiguration) configuration); 163 } 164 165 /** 166 * <p> 167 * Copy all properties from the source configuration to the target configuration. Properties in the target configuration 168 * are replaced with the properties with the same key in the source configuration. 169 * </p> 170 * <p> 171 * <em>Note:</em> This method is not able to handle some specifics of configurations derived from 172 * {@code AbstractConfiguration} (e.g. list delimiters). For a full support of all of these features the {@code copy()} 173 * method of {@code AbstractConfiguration} should be used. In a future release this method might become deprecated. 174 * </p> 175 * 176 * @param source the source configuration 177 * @param target the target configuration 178 * @since 2.2 179 */ 180 public static void copy(final ImmutableConfiguration source, final Configuration target) { 181 for (final Iterator<String> keys = source.getKeys(); keys.hasNext();) { 182 final String key = keys.next(); 183 target.setProperty(key, source.getProperty(key)); 184 } 185 } 186 187 /** 188 * <p> 189 * Copy all properties from the source configuration to the target configuration. Properties in the target configuration 190 * are replaced with the properties with the same key in the source configuration. 191 * </p> 192 * <p> 193 * <em>Note:</em> This method is not able to handle some specifics of configurations derived from 194 * {@code AbstractConfiguration} (e.g. list delimiters). For a full support of all of these features the {@code copy()} 195 * method of {@code AbstractConfiguration} should be used. In a future release this method might become deprecated. 196 * </p> 197 * 198 * @param source the source configuration 199 * @param target the target configuration 200 * @since 1.1 201 */ 202 public static void copy(final Configuration source, final Configuration target) { 203 copy((ImmutableConfiguration) source, target); 204 } 205 206 /** 207 * <p> 208 * Append all properties from the source configuration to the target configuration. Properties in the source 209 * configuration are appended to the properties with the same key in the target configuration. 210 * </p> 211 * <p> 212 * <em>Note:</em> This method is not able to handle some specifics of configurations derived from 213 * {@code AbstractConfiguration} (e.g. list delimiters). For a full support of all of these features the {@code copy()} 214 * method of {@code AbstractConfiguration} should be used. In a future release this method might become deprecated. 215 * </p> 216 * 217 * @param source the source configuration 218 * @param target the target configuration 219 * @since 2.2 220 */ 221 public static void append(final ImmutableConfiguration source, final Configuration target) { 222 for (final Iterator<String> keys = source.getKeys(); keys.hasNext();) { 223 final String key = keys.next(); 224 target.addProperty(key, source.getProperty(key)); 225 } 226 } 227 228 /** 229 * <p> 230 * Append all properties from the source configuration to the target configuration. Properties in the source 231 * configuration are appended to the properties with the same key in the target configuration. 232 * </p> 233 * <p> 234 * <em>Note:</em> This method is not able to handle some specifics of configurations derived from 235 * {@code AbstractConfiguration} (e.g. list delimiters). For a full support of all of these features the {@code copy()} 236 * method of {@code AbstractConfiguration} should be used. In a future release this method might become deprecated. 237 * </p> 238 * 239 * @param source the source configuration 240 * @param target the target configuration 241 * @since 1.1 242 */ 243 public static void append(final Configuration source, final Configuration target) { 244 append((ImmutableConfiguration) source, target); 245 } 246 247 /** 248 * Converts the passed in configuration to a hierarchical one. If the configuration is already hierarchical, it is 249 * directly returned. Otherwise all properties are copied into a new hierarchical configuration. 250 * 251 * @param conf the configuration to convert 252 * @return the new hierarchical configuration (the result is <b>null</b> if and only if the passed in configuration is 253 * <b>null</b>) 254 * @since 1.3 255 */ 256 public static HierarchicalConfiguration<?> convertToHierarchical(final Configuration conf) { 257 return convertToHierarchical(conf, null); 258 } 259 260 /** 261 * Converts the passed in {@code Configuration} object to a hierarchical one using the specified 262 * {@code ExpressionEngine}. This conversion works by adding the keys found in the configuration to a newly created 263 * hierarchical configuration. When adding new keys to a hierarchical configuration the keys are interpreted by its 264 * {@code ExpressionEngine}. If they contain special characters (e.g. brackets) that are treated in a special way by the 265 * default expression engine, it may be necessary using a specific engine that can deal with such characters. Otherwise 266 * <b>null</b> can be passed in for the {@code ExpressionEngine}; then the default expression engine is used. If the 267 * passed in configuration is already hierarchical, it is directly returned. (However, the {@code ExpressionEngine} is 268 * set if it is not <b>null</b>.) Otherwise all properties are copied into a new hierarchical configuration. 269 * 270 * @param conf the configuration to convert 271 * @param engine the {@code ExpressionEngine} for the hierarchical configuration or <b>null</b> for the default 272 * @return the new hierarchical configuration (the result is <b>null</b> if and only if the passed in configuration is 273 * <b>null</b>) 274 * @since 1.6 275 */ 276 public static HierarchicalConfiguration<?> convertToHierarchical(final Configuration conf, final ExpressionEngine engine) { 277 if (conf == null) { 278 return null; 279 } 280 281 if (conf instanceof HierarchicalConfiguration) { 282 final HierarchicalConfiguration<?> hc = (HierarchicalConfiguration<?>) conf; 283 if (engine != null) { 284 hc.setExpressionEngine(engine); 285 } 286 287 return hc; 288 } 289 final BaseHierarchicalConfiguration hc = new BaseHierarchicalConfiguration(); 290 if (engine != null) { 291 hc.setExpressionEngine(engine); 292 } 293 294 // Per default, a DisabledListDelimiterHandler is set. 295 // So list delimiters in property values are not an issue. 296 hc.copy(conf); 297 return hc; 298 } 299 300 /** 301 * Clones the given configuration object if this is possible. If the passed in configuration object implements the 302 * {@code Cloneable} interface, its {@code clone()} method will be invoked. Otherwise an exception will be thrown. 303 * 304 * @param config the configuration object to be cloned (can be <b>null</b>) 305 * @return the cloned configuration (<b>null</b> if the argument was <b>null</b>, too) 306 * @throws ConfigurationRuntimeException if cloning is not supported for this object 307 * @since 1.3 308 */ 309 public static Configuration cloneConfiguration(final Configuration config) throws ConfigurationRuntimeException { 310 if (config == null) { 311 return null; 312 } 313 try { 314 return (Configuration) clone(config); 315 } catch (final CloneNotSupportedException cnex) { 316 throw new ConfigurationRuntimeException(cnex); 317 } 318 } 319 320 /** 321 * Returns a clone of the passed in object if cloning is supported or the object itself if not. This method checks 322 * whether the passed in object implements the {@code Cloneable} interface. If this is the case, the {@code clone()} 323 * method is invoked. Otherwise, the object is directly returned. Errors that might occur during reflection calls are 324 * caught and also cause this method to return the original object. 325 * 326 * @param obj the object to be cloned 327 * @return the result of the cloning attempt 328 * @since 2.0 329 */ 330 public static Object cloneIfPossible(final Object obj) { 331 try { 332 return clone(obj); 333 } catch (final Exception ex) { 334 return obj; 335 } 336 } 337 338 /** 339 * An internally used helper method for cloning objects. This implementation is not very sophisticated nor efficient. 340 * Maybe it can be replaced by an implementation from Commons Lang later. The method checks whether the passed in object 341 * implements the {@code Cloneable} interface. If this is the case, the {@code clone()} method is invoked by reflection. 342 * Errors that occur during the cloning process are re-thrown as runtime exceptions. 343 * 344 * @param obj the object to be cloned 345 * @return the cloned object 346 * @throws CloneNotSupportedException if the object cannot be cloned 347 */ 348 static Object clone(final Object obj) throws CloneNotSupportedException { 349 if (obj instanceof Cloneable) { 350 try { 351 final Method m = obj.getClass().getMethod(METHOD_CLONE); 352 return m.invoke(obj); 353 } catch (final NoSuchMethodException nmex) { 354 throw new CloneNotSupportedException("No clone() method found for class" + obj.getClass().getName()); 355 } catch (final IllegalAccessException | InvocationTargetException itex) { 356 throw new ConfigurationRuntimeException(itex); 357 } 358 } 359 throw new CloneNotSupportedException(obj.getClass().getName() + " does not implement Cloneable"); 360 } 361 362 /** 363 * Creates a clone of the specified {@code Synchronizer}. This method can be called by {@code clone()} implementations 364 * in configuration classes that also need to copy the {@code Synchronizer} object. This method can handle some 365 * well-known {@code Synchronizer} implementations directly. For other classes, it uses the following algorithm: 366 * <ul> 367 * <li>If the class of the {@code Synchronizer} has a standard constructor, a new instance is created using 368 * reflection.</li> 369 * <li>If this is not possible, it is tried whether the object can be cloned.</li> 370 * </ul> 371 * If all attempts fail, a {@code ConfigurationRuntimeException} is thrown. 372 * 373 * @param sync the {@code Synchronizer} object to be cloned 374 * @return the clone of this {@code Synchronizer} 375 * @throws ConfigurationRuntimeException if no clone can be created 376 * @throws IllegalArgumentException if <b>null</b> is passed in 377 */ 378 public static Synchronizer cloneSynchronizer(final Synchronizer sync) { 379 if (sync == null) { 380 throw new IllegalArgumentException("Synchronizer must not be null!"); 381 } 382 if (NoOpSynchronizer.INSTANCE == sync) { 383 return sync; 384 } 385 386 try { 387 return sync.getClass().newInstance(); 388 } catch (final Exception ex) { 389 LOG.info("Cannot create new instance of " + sync.getClass()); 390 } 391 392 try { 393 return (Synchronizer) clone(sync); 394 } catch (final CloneNotSupportedException cnex) { 395 throw new ConfigurationRuntimeException("Cannot clone Synchronizer " + sync); 396 } 397 } 398 399 /** 400 * Enables runtime exceptions for the specified configuration object. This method can be used for configuration 401 * implementations that may face errors on normal property access, e.g. {@code DatabaseConfiguration} or 402 * {@code JNDIConfiguration}. Per default such errors are simply logged and then ignored. This implementation will 403 * register a special {@link EventListener} that throws a runtime exception (namely a 404 * {@code ConfigurationRuntimeException}) on each received error event. 405 * 406 * @param src the configuration, for which runtime exceptions are to be enabled; this configuration must implement 407 * {@link EventSource} 408 */ 409 public static void enableRuntimeExceptions(final Configuration src) { 410 if (!(src instanceof EventSource)) { 411 throw new IllegalArgumentException("Configuration must implement EventSource!"); 412 } 413 ((EventSource) src).addEventListener(ConfigurationErrorEvent.ANY, event -> { 414 // Throw a runtime exception 415 throw new ConfigurationRuntimeException(event.getCause()); 416 }); 417 } 418 419 /** 420 * Loads the class with the given name. This method is used whenever a class has to be loaded dynamically. It first 421 * tries the current thread's context class loader. If this fails, the class loader of this class is tried. 422 * 423 * @param clsName the name of the class to be loaded 424 * @return the loaded class 425 * @throws ClassNotFoundException if the class cannot be resolved 426 * @since 2.0 427 */ 428 public static Class<?> loadClass(final String clsName) throws ClassNotFoundException { 429 if (LOG.isDebugEnabled()) { 430 LOG.debug("Loading class " + clsName); 431 } 432 433 final ClassLoader cl = Thread.currentThread().getContextClassLoader(); 434 try { 435 if (cl != null) { 436 return cl.loadClass(clsName); 437 } 438 } catch (final ClassNotFoundException cnfex) { 439 LOG.info("Could not load class " + clsName + " using CCL. Falling back to default CL.", cnfex); 440 } 441 442 return ConfigurationUtils.class.getClassLoader().loadClass(clsName); 443 } 444 445 /** 446 * Loads the class with the specified name re-throwing {@code ClassNotFoundException} exceptions as runtime exceptions. 447 * This method works like {@link #loadClass(String)}. However, checked exceptions are caught and re-thrown as 448 * {@code ConfigurationRuntimeException}. 449 * 450 * @param clsName the name of the class to be loaded 451 * @return the loaded class 452 * @throws ConfigurationRuntimeException if the class cannot be resolved 453 * @since 2.0 454 */ 455 public static Class<?> loadClassNoEx(final String clsName) { 456 try { 457 return loadClass(clsName); 458 } catch (final ClassNotFoundException cnfex) { 459 throw new ConfigurationRuntimeException("Cannot load class " + clsName, cnfex); 460 } 461 } 462 463 /** 464 * Creates an {@code ImmutableConfiguration} from the given {@code Configuration} object. This method creates a proxy 465 * object wrapping the original configuration and making it available under the {@code ImmutableConfiguration} 466 * interface. Through this interface the configuration cannot be manipulated. It is also not possible to cast the 467 * returned object back to a {@code Configuration} instance to circumvent this protection. 468 * 469 * @param c the {@code Configuration} to be wrapped (must not be <b>null</b>) 470 * @return an {@code ImmutableConfiguration} view on the specified {@code Configuration} object 471 * @throws NullPointerException if the passed in {@code Configuration} is <b>null</b> 472 * @since 2.0 473 */ 474 public static ImmutableConfiguration unmodifiableConfiguration(final Configuration c) { 475 return createUnmodifiableConfiguration(IMMUTABLE_CONFIG_IFCS, c); 476 } 477 478 /** 479 * Creates an {@code ImmutableHierarchicalConfiguration} from the given {@code HierarchicalConfiguration} object. This 480 * method works exactly like the method with the same name, but it operates on hierarchical configurations. 481 * 482 * @param c the {@code HierarchicalConfiguration} to be wrapped (must not be <b>null</b>) 483 * @return an {@code ImmutableHierarchicalConfiguration} view on the specified {@code HierarchicalConfiguration} object 484 * @throws NullPointerException if the passed in {@code HierarchicalConfiguration} is <b>null</b> 485 * @since 2.0 486 */ 487 public static ImmutableHierarchicalConfiguration unmodifiableConfiguration(final HierarchicalConfiguration<?> c) { 488 return (ImmutableHierarchicalConfiguration) createUnmodifiableConfiguration(IMMUTABLE_HIERARCHICAL_CONFIG_IFCS, c); 489 } 490 491 /** 492 * Helper method for creating a proxy for an unmodifiable configuration. The interfaces the proxy should implement are 493 * passed as argument. 494 * 495 * @param ifcs an array with the interface classes the proxy must implement 496 * @param c the configuration object to be wrapped 497 * @return a proxy object for an immutable configuration 498 * @throws NullPointerException if the configuration is <b>null</b> 499 */ 500 private static ImmutableConfiguration createUnmodifiableConfiguration(final Class<?>[] ifcs, final Configuration c) { 501 return (ImmutableConfiguration) Proxy.newProxyInstance(ConfigurationUtils.class.getClassLoader(), ifcs, new ImmutableConfigurationInvocationHandler(c)); 502 } 503 504 /** 505 * Casts the specified object to an {@code EventSource} if possible. The boolean argument determines the method's 506 * behavior if the object does not implement the {@code EventSource} event: if set to <b>false</b>, a 507 * {@code ConfigurationRuntimeException} is thrown; if set to <b>true</b>, a dummy {@code EventSource} is returned; on 508 * this object all methods can be called, but they do not have any effect. 509 * 510 * @param obj the object to be cast as {@code EventSource} 511 * @param mockIfUnsupported a flag whether a mock object should be returned if necessary 512 * @return an {@code EventSource} 513 * @throws ConfigurationRuntimeException if the object cannot be cast to {@code EventSource} and the mock flag is 514 * <b>false</b> 515 * @since 2.0 516 */ 517 public static EventSource asEventSource(final Object obj, final boolean mockIfUnsupported) { 518 if (obj instanceof EventSource) { 519 return (EventSource) obj; 520 } 521 522 if (!mockIfUnsupported) { 523 throw new ConfigurationRuntimeException("Cannot cast to EventSource: " + obj); 524 } 525 return DUMMY_EVENT_SOURCE; 526 } 527}