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