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