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