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