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 * @author <a href="mailto:herve.quiroz@esil.univ-mrs.fr">Herve Quiroz</a> 046 * @author Emmanuel Bourg 047 * @version $Id: ConfigurationUtils.java 1842194 2018-09-27 22:24:23Z ggregory $ 048 */ 049public final class ConfigurationUtils 050{ 051 /** Constant for the name of the clone() method.*/ 052 private static final String METHOD_CLONE = "clone"; 053 054 /** 055 * An array with interfaces to be implemented by a proxy for an immutable 056 * configuration. 057 */ 058 private static final Class<?>[] IMMUTABLE_CONFIG_IFCS = { 059 ImmutableConfiguration.class 060 }; 061 062 /** 063 * An array with interfaces to be implemented by a proxy for an immutable 064 * hierarchical configuration. 065 */ 066 private static final Class<?>[] IMMUTABLE_HIERARCHICAL_CONFIG_IFCS = { 067 ImmutableHierarchicalConfiguration.class 068 }; 069 /** 070 * A dummy event source that is returned by {@code asEventSource()} if a 071 * mock object has to be returned. It provides empty dummy implementations 072 * for all interface methods. 073 */ 074 private static final EventSource DUMMY_EVENT_SOURCE = new EventSource() 075 { 076 077 @Override 078 public <T extends Event> void addEventListener(final EventType<T> eventType, 079 final EventListener<? super T> listener) 080 { 081 } 082 083 @Override 084 public <T extends Event> boolean removeEventListener( 085 final EventType<T> eventType, final EventListener<? super T> listener) 086 { 087 return false; 088 } 089 }; 090 091 /** The logger.*/ 092 private static final Log LOG = LogFactory.getLog(ConfigurationUtils.class); 093 094 /** 095 * Private constructor. Prevents instances from being created. 096 */ 097 private ConfigurationUtils() 098 { 099 // to prevent instantiation... 100 } 101 102 /** 103 * Dump the configuration key/value mappings to some ouput stream. 104 * 105 * @param configuration the configuration 106 * @param out the output stream to dump the configuration to 107 * @since 2.2 108 */ 109 public static void dump(final ImmutableConfiguration configuration, final PrintStream out) 110 { 111 dump(configuration, new PrintWriter(out)); 112 } 113 114 /** 115 * Dump the configuration key/value mappings to some ouput stream. 116 * This version of the method exists only for backwards compatibility reason. 117 * 118 * @param configuration the configuration 119 * @param out the output stream to dump the configuration to 120 */ 121 public static void dump(final Configuration configuration, final PrintStream out) 122 { 123 dump((ImmutableConfiguration) configuration, out); 124 } 125 126 /** 127 * Dump the configuration key/value mappings to some writer. 128 * 129 * @param configuration the configuration 130 * @param out the writer to dump the configuration to 131 * @since 2.2 132 */ 133 public static void dump(final ImmutableConfiguration configuration, final PrintWriter out) 134 { 135 for (final Iterator<String> keys = configuration.getKeys(); keys.hasNext();) 136 { 137 final String key = keys.next(); 138 final Object value = configuration.getProperty(key); 139 out.print(key); 140 out.print("="); 141 out.print(value); 142 143 if (keys.hasNext()) 144 { 145 out.println(); 146 } 147 } 148 149 out.flush(); 150 } 151 152 /** 153 * Dump the configuration key/value mappings to some writer. 154 * This version of the method exists only for backwards compatibility reason. 155 * 156 * @param configuration the configuration 157 * @param out the writer to dump the configuration to 158 */ 159 public static void dump(final Configuration configuration, final PrintWriter out) 160 { 161 dump((ImmutableConfiguration) configuration, out); 162 } 163 164 /** 165 * Get a string representation of the key/value mappings of a 166 * configuration. 167 * 168 * @param configuration the configuration 169 * @return a string representation of the configuration 170 * @since 2.2 171 */ 172 public static String toString(final ImmutableConfiguration configuration) 173 { 174 final StringWriter writer = new StringWriter(); 175 dump(configuration, new PrintWriter(writer)); 176 return writer.toString(); 177 } 178 179 /** 180 * Get a string representation of the key/value mappings of a 181 * configuration. 182 * This version of the method exists only for backwards compatibility reason. 183 * 184 * @param configuration the configuration 185 * @return a string representation of the configuration 186 */ 187 public static String toString(final Configuration configuration) 188 { 189 return toString((ImmutableConfiguration) configuration); 190 } 191 192 /** 193 * <p>Copy all properties from the source configuration to the target 194 * configuration. Properties in the target configuration are replaced with 195 * the properties with the same key in the source configuration.</p> 196 * <p><em>Note:</em> This method is not able to handle some specifics of 197 * configurations derived from {@code AbstractConfiguration} (e.g. 198 * list delimiters). For a full support of all of these features the 199 * {@code copy()} method of {@code AbstractConfiguration} should 200 * be used. In a future release this method might become deprecated.</p> 201 * 202 * @param source the source configuration 203 * @param target the target configuration 204 * @since 2.2 205 */ 206 public static void copy(final ImmutableConfiguration source, final Configuration target) 207 { 208 for (final Iterator<String> keys = source.getKeys(); keys.hasNext();) 209 { 210 final String key = keys.next(); 211 target.setProperty(key, source.getProperty(key)); 212 } 213 } 214 215 /** 216 * <p>Copy all properties from the source configuration to the target 217 * configuration. Properties in the target configuration are replaced with 218 * the properties with the same key in the source configuration.</p> 219 * <p><em>Note:</em> This method is not able to handle some specifics of 220 * configurations derived from {@code AbstractConfiguration} (e.g. 221 * list delimiters). For a full support of all of these features the 222 * {@code copy()} method of {@code AbstractConfiguration} should 223 * be used. In a future release this method might become deprecated.</p> 224 * 225 * @param source the source configuration 226 * @param target the target configuration 227 * @since 1.1 228 */ 229 public static void copy(final Configuration source, final Configuration target) 230 { 231 copy((ImmutableConfiguration) source, target); 232 } 233 234 /** 235 * <p>Append all properties from the source configuration to the target 236 * configuration. Properties in the source configuration are appended to 237 * the properties with the same key in the target configuration.</p> 238 * <p><em>Note:</em> This method is not able to handle some specifics of 239 * configurations derived from {@code AbstractConfiguration} (e.g. 240 * list delimiters). For a full support of all of these features the 241 * {@code copy()} method of {@code AbstractConfiguration} should 242 * be used. In a future release this method might become deprecated.</p> 243 * 244 * @param source the source configuration 245 * @param target the target configuration 246 * @since 2.2 247 */ 248 public static void append(final ImmutableConfiguration source, final Configuration target) 249 { 250 for (final Iterator<String> keys = source.getKeys(); keys.hasNext();) 251 { 252 final String key = keys.next(); 253 target.addProperty(key, source.getProperty(key)); 254 } 255 } 256 257 /** 258 * <p>Append all properties from the source configuration to the target 259 * configuration. Properties in the source configuration are appended to 260 * the properties with the same key in the target configuration.</p> 261 * <p><em>Note:</em> This method is not able to handle some specifics of 262 * configurations derived from {@code AbstractConfiguration} (e.g. 263 * list delimiters). For a full support of all of these features the 264 * {@code copy()} method of {@code AbstractConfiguration} should 265 * be used. In a future release this method might become deprecated.</p> 266 * 267 * @param source the source configuration 268 * @param target the target configuration 269 * @since 1.1 270 */ 271 public static void append(final Configuration source, final Configuration target) 272 { 273 append((ImmutableConfiguration) source, target); 274 } 275 276 /** 277 * Converts the passed in configuration to a hierarchical one. If the 278 * configuration is already hierarchical, it is directly returned. Otherwise 279 * all properties are copied into a new hierarchical configuration. 280 * 281 * @param conf the configuration to convert 282 * @return the new hierarchical configuration (the result is <b>null</b> if 283 * and only if the passed in configuration is <b>null</b>) 284 * @since 1.3 285 */ 286 public static HierarchicalConfiguration<?> convertToHierarchical( 287 final Configuration conf) 288 { 289 return convertToHierarchical(conf, null); 290 } 291 292 /** 293 * Converts the passed in {@code Configuration} object to a 294 * hierarchical one using the specified {@code ExpressionEngine}. This 295 * conversion works by adding the keys found in the configuration to a newly 296 * created hierarchical configuration. When adding new keys to a 297 * hierarchical configuration the keys are interpreted by its 298 * {@code ExpressionEngine}. If they contain special characters (e.g. 299 * brackets) that are treated in a special way by the default expression 300 * engine, it may be necessary using a specific engine that can deal with 301 * such characters. Otherwise <b>null</b> can be passed in for the 302 * {@code ExpressionEngine}; then the default expression engine is 303 * used. If the passed in configuration is already hierarchical, it is 304 * directly returned. (However, the {@code ExpressionEngine} is set if 305 * it is not <b>null</b>.) Otherwise all properties are copied into a new 306 * hierarchical configuration. 307 * 308 * @param conf the configuration to convert 309 * @param engine the {@code ExpressionEngine} for the hierarchical 310 * configuration or <b>null</b> for the default 311 * @return the new hierarchical configuration (the result is <b>null</b> if 312 * and only if the passed in configuration is <b>null</b>) 313 * @since 1.6 314 */ 315 public static HierarchicalConfiguration<?> convertToHierarchical( 316 final Configuration conf, final ExpressionEngine engine) 317 { 318 if (conf == null) 319 { 320 return null; 321 } 322 323 if (conf instanceof HierarchicalConfiguration) 324 { 325 final HierarchicalConfiguration<?> hc = (HierarchicalConfiguration<?>) conf; 326 if (engine != null) 327 { 328 hc.setExpressionEngine(engine); 329 } 330 331 return hc; 332 } 333 final BaseHierarchicalConfiguration hc = new BaseHierarchicalConfiguration(); 334 if (engine != null) 335 { 336 hc.setExpressionEngine(engine); 337 } 338 339 // Per default, a DisabledListDelimiterHandler is set. 340 // So list delimiters in property values are not an issue. 341 hc.copy(conf); 342 return hc; 343 } 344 345 /** 346 * Clones the given configuration object if this is possible. If the passed 347 * in configuration object implements the {@code Cloneable} 348 * interface, its {@code clone()} method will be invoked. Otherwise 349 * an exception will be thrown. 350 * 351 * @param config the configuration object to be cloned (can be <b>null</b>) 352 * @return the cloned configuration (<b>null</b> if the argument was 353 * <b>null</b>, too) 354 * @throws ConfigurationRuntimeException if cloning is not supported for 355 * this object 356 * @since 1.3 357 */ 358 public static Configuration cloneConfiguration(final Configuration config) 359 throws ConfigurationRuntimeException 360 { 361 if (config == null) 362 { 363 return null; 364 } 365 try 366 { 367 return (Configuration) clone(config); 368 } 369 catch (final CloneNotSupportedException cnex) 370 { 371 throw new ConfigurationRuntimeException(cnex); 372 } 373 } 374 375 /** 376 * Returns a clone of the passed in object if cloning is supported or the 377 * object itself if not. This method checks whether the passed in object 378 * implements the {@code Cloneable} interface. If this is the case, the 379 * {@code clone()} method is invoked. Otherwise, the object is directly 380 * returned. Errors that might occur during reflection calls are caught and 381 * also cause this method to return the original object. 382 * 383 * @param obj the object to be cloned 384 * @return the result of the cloning attempt 385 * @since 2.0 386 */ 387 public static Object cloneIfPossible(final Object obj) 388 { 389 try 390 { 391 return clone(obj); 392 } 393 catch (final Exception ex) 394 { 395 return obj; 396 } 397 } 398 399 /** 400 * An internally used helper method for cloning objects. This implementation 401 * is not very sophisticated nor efficient. Maybe it can be replaced by an 402 * implementation from Commons Lang later. The method checks whether the 403 * passed in object implements the {@code Cloneable} interface. If 404 * this is the case, the {@code clone()} method is invoked by 405 * reflection. Errors that occur during the cloning process are re-thrown as 406 * runtime exceptions. 407 * 408 * @param obj the object to be cloned 409 * @return the cloned object 410 * @throws CloneNotSupportedException if the object cannot be cloned 411 */ 412 static Object clone(final Object obj) throws CloneNotSupportedException 413 { 414 if (obj instanceof Cloneable) 415 { 416 try 417 { 418 final Method m = obj.getClass().getMethod(METHOD_CLONE); 419 return m.invoke(obj); 420 } 421 catch (final NoSuchMethodException nmex) 422 { 423 throw new CloneNotSupportedException( 424 "No clone() method found for class" 425 + obj.getClass().getName()); 426 } 427 catch (final IllegalAccessException iaex) 428 { 429 throw new ConfigurationRuntimeException(iaex); 430 } 431 catch (final InvocationTargetException itex) 432 { 433 throw new ConfigurationRuntimeException(itex); 434 } 435 } 436 throw new CloneNotSupportedException(obj.getClass().getName() 437 + " does not implement Cloneable"); 438 } 439 440 /** 441 * Creates a clone of the specified {@code Synchronizer}. This method can be 442 * called by {@code clone()} implementations in configuration classes that 443 * also need to copy the {@code Synchronizer} object. This method can handle 444 * some well-known {@code Synchronizer} implementations directly. For other 445 * classes, it uses the following algorithm: 446 * <ul> 447 * <li>If the class of the {@code Synchronizer} has a standard constructor, 448 * a new instance is created using reflection.</li> 449 * <li>If this is not possible, it is tried whether the object can be 450 * cloned.</li> 451 * </ul> 452 * If all attempts fail, a {@code ConfigurationRuntimeException} is thrown. 453 * 454 * @param sync the {@code Synchronizer} object to be cloned 455 * @return the clone of this {@code Synchronizer} 456 * @throws ConfigurationRuntimeException if no clone can be created 457 * @throws IllegalArgumentException if <b>null</b> is passed in 458 */ 459 public static Synchronizer cloneSynchronizer(final Synchronizer sync) 460 { 461 if (sync == null) 462 { 463 throw new IllegalArgumentException("Synchronizer must not be null!"); 464 } 465 if (NoOpSynchronizer.INSTANCE == sync) 466 { 467 return sync; 468 } 469 470 try 471 { 472 return sync.getClass().newInstance(); 473 } 474 catch (final Exception ex) 475 { 476 LOG.info("Cannot create new instance of " + sync.getClass()); 477 } 478 479 try 480 { 481 return (Synchronizer) clone(sync); 482 } 483 catch (final CloneNotSupportedException cnex) 484 { 485 throw new ConfigurationRuntimeException( 486 "Cannot clone Synchronizer " + sync); 487 } 488 } 489 490 /** 491 * Enables runtime exceptions for the specified configuration object. This 492 * method can be used for configuration implementations that may face errors 493 * on normal property access, e.g. {@code DatabaseConfiguration} or 494 * {@code JNDIConfiguration}. Per default such errors are simply 495 * logged and then ignored. This implementation will register a special 496 * {@link EventListener} that throws a runtime 497 * exception (namely a {@code ConfigurationRuntimeException}) on 498 * each received error event. 499 * 500 * @param src the configuration, for which runtime exceptions are to be 501 * enabled; this configuration must implement {@link EventSource} 502 */ 503 public static void enableRuntimeExceptions(final Configuration src) 504 { 505 if (!(src instanceof EventSource)) 506 { 507 throw new IllegalArgumentException( 508 "Configuration must implement EventSource!"); 509 } 510 ((EventSource) src).addEventListener(ConfigurationErrorEvent.ANY, 511 new EventListener<ConfigurationErrorEvent>() 512 { 513 @Override 514 public void onEvent(final ConfigurationErrorEvent event) 515 { 516 // Throw a runtime exception 517 throw new ConfigurationRuntimeException(event 518 .getCause()); 519 } 520 }); 521 } 522 523 /** 524 * Loads the class with the given name. This method is used whenever a class 525 * has to be loaded dynamically. It first tries the current thread's context 526 * class loader. If this fails, the class loader of this class is tried. 527 * 528 * @param clsName the name of the class to be loaded 529 * @return the loaded class 530 * @throws ClassNotFoundException if the class cannot be resolved 531 * @since 2.0 532 */ 533 public static Class<?> loadClass(final String clsName) 534 throws ClassNotFoundException 535 { 536 if (LOG.isDebugEnabled()) 537 { 538 LOG.debug("Loading class " + clsName); 539 } 540 541 final ClassLoader cl = Thread.currentThread().getContextClassLoader(); 542 try 543 { 544 if (cl != null) 545 { 546 return cl.loadClass(clsName); 547 } 548 } 549 catch (final ClassNotFoundException cnfex) 550 { 551 LOG.info("Could not load class " + clsName 552 + " using CCL. Falling back to default CL.", cnfex); 553 } 554 555 return ConfigurationUtils.class.getClassLoader().loadClass(clsName); 556 } 557 558 /** 559 * Loads the class with the specified name re-throwing 560 * {@code ClassNotFoundException} exceptions as runtime exceptions. This 561 * method works like {@link #loadClass(String)}. However, checked exceptions 562 * are caught and re-thrown as {@code ConfigurationRuntimeException}. 563 * 564 * @param clsName the name of the class to be loaded 565 * @return the loaded class 566 * @throws ConfigurationRuntimeException if the class cannot be resolved 567 * @since 2.0 568 */ 569 public static Class<?> loadClassNoEx(final String clsName) 570 { 571 try 572 { 573 return loadClass(clsName); 574 } 575 catch (final ClassNotFoundException cnfex) 576 { 577 throw new ConfigurationRuntimeException("Cannot load class " 578 + clsName, cnfex); 579 } 580 } 581 582 /** 583 * Creates an {@code ImmutableConfiguration} from the given 584 * {@code Configuration} object. This method creates a proxy object wrapping 585 * the original configuration and making it available under the 586 * {@code ImmutableConfiguration} interface. Through this interface the 587 * configuration cannot be manipulated. It is also not possible to cast the 588 * returned object back to a {@code Configuration} instance to circumvent 589 * this protection. 590 * 591 * @param c the {@code Configuration} to be wrapped (must not be 592 * <b>null</b>) 593 * @return an {@code ImmutableConfiguration} view on the specified 594 * {@code Configuration} object 595 * @throws NullPointerException if the passed in {@code Configuration} is 596 * <b>null</b> 597 * @since 2.0 598 */ 599 public static ImmutableConfiguration unmodifiableConfiguration( 600 final Configuration c) 601 { 602 return createUnmodifiableConfiguration(IMMUTABLE_CONFIG_IFCS, c); 603 } 604 605 /** 606 * Creates an {@code ImmutableHierarchicalConfiguration} from the given 607 * {@code HierarchicalConfiguration} object. This method works exactly like 608 * the method with the same name, but it operates on hierarchical 609 * configurations. 610 * 611 * @param c the {@code HierarchicalConfiguration} to be wrapped (must not be 612 * <b>null</b>) 613 * @return an {@code ImmutableHierarchicalConfiguration} view on the 614 * specified {@code HierarchicalConfiguration} object 615 * @throws NullPointerException if the passed in 616 * {@code HierarchicalConfiguration} is <b>null</b> 617 * @since 2.0 618 */ 619 public static ImmutableHierarchicalConfiguration unmodifiableConfiguration( 620 final HierarchicalConfiguration<?> c) 621 { 622 return (ImmutableHierarchicalConfiguration) createUnmodifiableConfiguration( 623 IMMUTABLE_HIERARCHICAL_CONFIG_IFCS, c); 624 } 625 626 /** 627 * Helper method for creating a proxy for an unmodifiable configuration. The 628 * interfaces the proxy should implement are passed as argument. 629 * 630 * @param ifcs an array with the interface classes the proxy must implement 631 * @param c the configuration object to be wrapped 632 * @return a proxy object for an immutable configuration 633 * @throws NullPointerException if the configuration is <b>null</b> 634 */ 635 private static ImmutableConfiguration createUnmodifiableConfiguration( 636 final Class<?>[] ifcs, final Configuration c) 637 { 638 return (ImmutableConfiguration) Proxy.newProxyInstance( 639 ConfigurationUtils.class.getClassLoader(), ifcs, 640 new ImmutableConfigurationInvocationHandler(c)); 641 } 642 643 /** 644 * Casts the specified object to an {@code EventSource} if possible. The 645 * boolean argument determines the method's behavior if the object does not 646 * implement the {@code EventSource} event: if set to <b>false</b>, a 647 * {@code ConfigurationRuntimeException} is thrown; if set to <b>true</b>, a 648 * dummy {@code EventSource} is returned; on this object all methods can be 649 * called, but they do not have any effect. 650 * 651 * @param obj the object to be cast as {@code EventSource} 652 * @param mockIfUnsupported a flag whether a mock object should be returned 653 * if necessary 654 * @return an {@code EventSource} 655 * @throws ConfigurationRuntimeException if the object cannot be cast to 656 * {@code EventSource} and the mock flag is <b>false</b> 657 * @since 2.0 658 */ 659 public static EventSource asEventSource(final Object obj, 660 final boolean mockIfUnsupported) 661 { 662 if (obj instanceof EventSource) 663 { 664 return (EventSource) obj; 665 } 666 667 if (!mockIfUnsupported) 668 { 669 throw new ConfigurationRuntimeException( 670 "Cannot cast to EventSource: " + obj); 671 } 672 return DUMMY_EVENT_SOURCE; 673 } 674}