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 1785888 2017-03-07 21:05:35Z oheger $ 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(EventType<T> eventType, 079 EventListener<? super T> listener) 080 { 081 } 082 083 @Override 084 public <T extends Event> boolean removeEventListener( 085 EventType<T> eventType, 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(ImmutableConfiguration configuration, 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(Configuration configuration, 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(ImmutableConfiguration configuration, PrintWriter out) 134 { 135 for (Iterator<String> keys = configuration.getKeys(); keys.hasNext();) 136 { 137 String key = keys.next(); 138 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(Configuration configuration, 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(ImmutableConfiguration configuration) 173 { 174 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(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(ImmutableConfiguration source, Configuration target) 207 { 208 for (Iterator<String> keys = source.getKeys(); keys.hasNext();) 209 { 210 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(Configuration source, 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(ImmutableConfiguration source, Configuration target) 249 { 250 for (Iterator<String> keys = source.getKeys(); keys.hasNext();) 251 { 252 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(Configuration source, 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 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 Configuration conf, ExpressionEngine engine) 317 { 318 if (conf == null) 319 { 320 return null; 321 } 322 323 if (conf instanceof HierarchicalConfiguration) 324 { 325 HierarchicalConfiguration<?> hc = (HierarchicalConfiguration<?>) conf; 326 if (engine != null) 327 { 328 hc.setExpressionEngine(engine); 329 } 330 331 return hc; 332 } 333 else 334 { 335 BaseHierarchicalConfiguration hc = new BaseHierarchicalConfiguration(); 336 if (engine != null) 337 { 338 hc.setExpressionEngine(engine); 339 } 340 341 // Per default, a DisabledListDelimiterHandler is set. 342 // So list delimiters in property values are not an issue. 343 hc.copy(conf); 344 return hc; 345 } 346 } 347 348 /** 349 * Clones the given configuration object if this is possible. If the passed 350 * in configuration object implements the {@code Cloneable} 351 * interface, its {@code clone()} method will be invoked. Otherwise 352 * an exception will be thrown. 353 * 354 * @param config the configuration object to be cloned (can be <b>null</b>) 355 * @return the cloned configuration (<b>null</b> if the argument was 356 * <b>null</b>, too) 357 * @throws ConfigurationRuntimeException if cloning is not supported for 358 * this object 359 * @since 1.3 360 */ 361 public static Configuration cloneConfiguration(Configuration config) 362 throws ConfigurationRuntimeException 363 { 364 if (config == null) 365 { 366 return null; 367 } 368 else 369 { 370 try 371 { 372 return (Configuration) clone(config); 373 } 374 catch (CloneNotSupportedException cnex) 375 { 376 throw new ConfigurationRuntimeException(cnex); 377 } 378 } 379 } 380 381 /** 382 * Returns a clone of the passed in object if cloning is supported or the 383 * object itself if not. This method checks whether the passed in object 384 * implements the {@code Cloneable} interface. If this is the case, the 385 * {@code clone()} method is invoked. Otherwise, the object is directly 386 * returned. Errors that might occur during reflection calls are caught and 387 * also cause this method to return the original object. 388 * 389 * @param obj the object to be cloned 390 * @return the result of the cloning attempt 391 * @since 2.0 392 */ 393 public static Object cloneIfPossible(Object obj) 394 { 395 try 396 { 397 return clone(obj); 398 } 399 catch (Exception ex) 400 { 401 return obj; 402 } 403 } 404 405 /** 406 * An internally used helper method for cloning objects. This implementation 407 * is not very sophisticated nor efficient. Maybe it can be replaced by an 408 * implementation from Commons Lang later. The method checks whether the 409 * passed in object implements the {@code Cloneable} interface. If 410 * this is the case, the {@code clone()} method is invoked by 411 * reflection. Errors that occur during the cloning process are re-thrown as 412 * runtime exceptions. 413 * 414 * @param obj the object to be cloned 415 * @return the cloned object 416 * @throws CloneNotSupportedException if the object cannot be cloned 417 */ 418 static Object clone(Object obj) throws CloneNotSupportedException 419 { 420 if (obj instanceof Cloneable) 421 { 422 try 423 { 424 Method m = obj.getClass().getMethod(METHOD_CLONE); 425 return m.invoke(obj); 426 } 427 catch (NoSuchMethodException nmex) 428 { 429 throw new CloneNotSupportedException( 430 "No clone() method found for class" 431 + obj.getClass().getName()); 432 } 433 catch (IllegalAccessException iaex) 434 { 435 throw new ConfigurationRuntimeException(iaex); 436 } 437 catch (InvocationTargetException itex) 438 { 439 throw new ConfigurationRuntimeException(itex); 440 } 441 } 442 else 443 { 444 throw new CloneNotSupportedException(obj.getClass().getName() 445 + " does not implement Cloneable"); 446 } 447 } 448 449 /** 450 * Creates a clone of the specified {@code Synchronizer}. This method can be 451 * called by {@code clone()} implementations in configuration classes that 452 * also need to copy the {@code Synchronizer} object. This method can handle 453 * some well-known {@code Synchronizer} implementations directly. For other 454 * classes, it uses the following algorithm: 455 * <ul> 456 * <li>If the class of the {@code Synchronizer} has a standard constructor, 457 * a new instance is created using reflection.</li> 458 * <li>If this is not possible, it is tried whether the object can be 459 * cloned.</li> 460 * </ul> 461 * If all attempts fail, a {@code ConfigurationRuntimeException} is thrown. 462 * 463 * @param sync the {@code Synchronizer} object to be cloned 464 * @return the clone of this {@code Synchronizer} 465 * @throws ConfigurationRuntimeException if no clone can be created 466 * @throws IllegalArgumentException if <b>null</b> is passed in 467 */ 468 public static Synchronizer cloneSynchronizer(Synchronizer sync) 469 { 470 if (sync == null) 471 { 472 throw new IllegalArgumentException("Synchronizer must not be null!"); 473 } 474 if (NoOpSynchronizer.INSTANCE == sync) 475 { 476 return sync; 477 } 478 479 try 480 { 481 return sync.getClass().newInstance(); 482 } 483 catch (Exception ex) 484 { 485 LOG.info("Cannot create new instance of " + sync.getClass()); 486 } 487 488 try 489 { 490 return (Synchronizer) clone(sync); 491 } 492 catch (CloneNotSupportedException cnex) 493 { 494 throw new ConfigurationRuntimeException( 495 "Cannot clone Synchronizer " + sync); 496 } 497 } 498 499 /** 500 * Enables runtime exceptions for the specified configuration object. This 501 * method can be used for configuration implementations that may face errors 502 * on normal property access, e.g. {@code DatabaseConfiguration} or 503 * {@code JNDIConfiguration}. Per default such errors are simply 504 * logged and then ignored. This implementation will register a special 505 * {@link EventListener} that throws a runtime 506 * exception (namely a {@code ConfigurationRuntimeException}) on 507 * each received error event. 508 * 509 * @param src the configuration, for which runtime exceptions are to be 510 * enabled; this configuration must implement {@link EventSource} 511 */ 512 public static void enableRuntimeExceptions(Configuration src) 513 { 514 if (!(src instanceof EventSource)) 515 { 516 throw new IllegalArgumentException( 517 "Configuration must implement EventSource!"); 518 } 519 ((EventSource) src).addEventListener(ConfigurationErrorEvent.ANY, 520 new EventListener<ConfigurationErrorEvent>() 521 { 522 @Override 523 public void onEvent(ConfigurationErrorEvent event) 524 { 525 // Throw a runtime exception 526 throw new ConfigurationRuntimeException(event 527 .getCause()); 528 } 529 }); 530 } 531 532 /** 533 * Loads the class with the given name. This method is used whenever a class 534 * has to be loaded dynamically. It first tries the current thread's context 535 * class loader. If this fails, the class loader of this class is tried. 536 * 537 * @param clsName the name of the class to be loaded 538 * @return the loaded class 539 * @throws ClassNotFoundException if the class cannot be resolved 540 * @since 2.0 541 */ 542 public static Class<?> loadClass(String clsName) 543 throws ClassNotFoundException 544 { 545 if (LOG.isDebugEnabled()) 546 { 547 LOG.debug("Loading class " + clsName); 548 } 549 550 ClassLoader cl = Thread.currentThread().getContextClassLoader(); 551 try 552 { 553 if (cl != null) 554 { 555 return cl.loadClass(clsName); 556 } 557 } 558 catch (ClassNotFoundException cnfex) 559 { 560 LOG.info("Could not load class " + clsName 561 + " using CCL. Falling back to default CL.", cnfex); 562 } 563 564 return ConfigurationUtils.class.getClassLoader().loadClass(clsName); 565 } 566 567 /** 568 * Loads the class with the specified name re-throwing 569 * {@code ClassNotFoundException} exceptions as runtime exceptions. This 570 * method works like {@link #loadClass(String)}. However, checked exceptions 571 * are caught and re-thrown as {@code ConfigurationRuntimeException}. 572 * 573 * @param clsName the name of the class to be loaded 574 * @return the loaded class 575 * @throws ConfigurationRuntimeException if the class cannot be resolved 576 * @since 2.0 577 */ 578 public static Class<?> loadClassNoEx(String clsName) 579 { 580 try 581 { 582 return loadClass(clsName); 583 } 584 catch (ClassNotFoundException cnfex) 585 { 586 throw new ConfigurationRuntimeException("Cannot load class " 587 + clsName, cnfex); 588 } 589 } 590 591 /** 592 * Creates an {@code ImmutableConfiguration} from the given 593 * {@code Configuration} object. This method creates a proxy object wrapping 594 * the original configuration and making it available under the 595 * {@code ImmutableConfiguration} interface. Through this interface the 596 * configuration cannot be manipulated. It is also not possible to cast the 597 * returned object back to a {@code Configuration} instance to circumvent 598 * this protection. 599 * 600 * @param c the {@code Configuration} to be wrapped (must not be 601 * <b>null</b>) 602 * @return an {@code ImmutableConfiguration} view on the specified 603 * {@code Configuration} object 604 * @throws NullPointerException if the passed in {@code Configuration} is 605 * <b>null</b> 606 * @since 2.0 607 */ 608 public static ImmutableConfiguration unmodifiableConfiguration( 609 Configuration c) 610 { 611 return createUnmodifiableConfiguration(IMMUTABLE_CONFIG_IFCS, c); 612 } 613 614 /** 615 * Creates an {@code ImmutableHierarchicalConfiguration} from the given 616 * {@code HierarchicalConfiguration} object. This method works exactly like 617 * the method with the same name, but it operates on hierarchical 618 * configurations. 619 * 620 * @param c the {@code HierarchicalConfiguration} to be wrapped (must not be 621 * <b>null</b>) 622 * @return an {@code ImmutableHierarchicalConfiguration} view on the 623 * specified {@code HierarchicalConfiguration} object 624 * @throws NullPointerException if the passed in 625 * {@code HierarchicalConfiguration} is <b>null</b> 626 * @since 2.0 627 */ 628 public static ImmutableHierarchicalConfiguration unmodifiableConfiguration( 629 HierarchicalConfiguration<?> c) 630 { 631 return (ImmutableHierarchicalConfiguration) createUnmodifiableConfiguration( 632 IMMUTABLE_HIERARCHICAL_CONFIG_IFCS, c); 633 } 634 635 /** 636 * Helper method for creating a proxy for an unmodifiable configuration. The 637 * interfaces the proxy should implement are passed as argument. 638 * 639 * @param ifcs an array with the interface classes the proxy must implement 640 * @param c the configuration object to be wrapped 641 * @return a proxy object for an immutable configuration 642 * @throws NullPointerException if the configuration is <b>null</b> 643 */ 644 private static ImmutableConfiguration createUnmodifiableConfiguration( 645 Class<?>[] ifcs, Configuration c) 646 { 647 return (ImmutableConfiguration) Proxy.newProxyInstance( 648 ConfigurationUtils.class.getClassLoader(), ifcs, 649 new ImmutableConfigurationInvocationHandler(c)); 650 } 651 652 /** 653 * Casts the specified object to an {@code EventSource} if possible. The 654 * boolean argument determines the method's behavior if the object does not 655 * implement the {@code EventSource} event: if set to <b>false</b>, a 656 * {@code ConfigurationRuntimeException} is thrown; if set to <b>true</b>, a 657 * dummy {@code EventSource} is returned; on this object all methods can be 658 * called, but they do not have any effect. 659 * 660 * @param obj the object to be cast as {@code EventSource} 661 * @param mockIfUnsupported a flag whether a mock object should be returned 662 * if necessary 663 * @return an {@code EventSource} 664 * @throws ConfigurationRuntimeException if the object cannot be cast to 665 * {@code EventSource} and the mock flag is <b>false</b> 666 * @since 2.0 667 */ 668 public static EventSource asEventSource(Object obj, 669 boolean mockIfUnsupported) 670 { 671 if (obj instanceof EventSource) 672 { 673 return (EventSource) obj; 674 } 675 676 if (!mockIfUnsupported) 677 { 678 throw new ConfigurationRuntimeException( 679 "Cannot cast to EventSource: " + obj); 680 } 681 return DUMMY_EVENT_SOURCE; 682 } 683}