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