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