ConfigurationUtils.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. package org.apache.commons.configuration2;

  18. import java.io.PrintStream;
  19. import java.io.PrintWriter;
  20. import java.io.StringWriter;
  21. import java.lang.reflect.InvocationTargetException;
  22. import java.lang.reflect.Method;
  23. import java.lang.reflect.Proxy;
  24. import java.util.Iterator;

  25. import org.apache.commons.configuration2.event.ConfigurationErrorEvent;
  26. import org.apache.commons.configuration2.event.Event;
  27. import org.apache.commons.configuration2.event.EventListener;
  28. import org.apache.commons.configuration2.event.EventSource;
  29. import org.apache.commons.configuration2.event.EventType;
  30. import org.apache.commons.configuration2.ex.ConfigurationRuntimeException;
  31. import org.apache.commons.configuration2.sync.NoOpSynchronizer;
  32. import org.apache.commons.configuration2.sync.Synchronizer;
  33. import org.apache.commons.configuration2.tree.ExpressionEngine;
  34. import org.apache.commons.logging.Log;
  35. import org.apache.commons.logging.LogFactory;

  36. /**
  37.  * Miscellaneous utility methods for configurations.
  38.  *
  39.  * @see ConfigurationConverter Utility methods to convert configurations.
  40.  */
  41. public final class ConfigurationUtils {
  42.     /** Constant for the name of the clone() method. */
  43.     private static final String METHOD_CLONE = "clone";

  44.     /**
  45.      * An array with interfaces to be implemented by a proxy for an immutable configuration.
  46.      */
  47.     private static final Class<?>[] IMMUTABLE_CONFIG_IFCS = {ImmutableConfiguration.class};

  48.     /**
  49.      * An array with interfaces to be implemented by a proxy for an immutable hierarchical configuration.
  50.      */
  51.     private static final Class<?>[] IMMUTABLE_HIERARCHICAL_CONFIG_IFCS = {ImmutableHierarchicalConfiguration.class};
  52.     /**
  53.      * A dummy event source that is returned by {@code asEventSource()} if a mock object has to be returned. It provides
  54.      * empty dummy implementations for all interface methods.
  55.      */
  56.     private static final EventSource DUMMY_EVENT_SOURCE = new EventSource() {

  57.         @Override
  58.         public <T extends Event> void addEventListener(final EventType<T> eventType, final EventListener<? super T> listener) {
  59.             // empty
  60.         }

  61.         @Override
  62.         public <T extends Event> boolean removeEventListener(final EventType<T> eventType, final EventListener<? super T> listener) {
  63.             return false;
  64.         }
  65.     };

  66.     /** The logger. */
  67.     private static final Log LOG = LogFactory.getLog(ConfigurationUtils.class);

  68.     /**
  69.      * <p>
  70.      * Append all properties from the source configuration to the target configuration. Properties in the source
  71.      * configuration are appended to the properties with the same key in the target configuration.
  72.      * </p>
  73.      * <p>
  74.      * <em>Note:</em> This method is not able to handle some specifics of configurations derived from
  75.      * {@code AbstractConfiguration} (for example list delimiters). For a full support of all of these features the {@code copy()}
  76.      * method of {@code AbstractConfiguration} should be used. In a future release this method might become deprecated.
  77.      * </p>
  78.      *
  79.      * @param source the source configuration
  80.      * @param target the target configuration
  81.      * @since 1.1
  82.      */
  83.     public static void append(final Configuration source, final Configuration target) {
  84.         append((ImmutableConfiguration) source, target);
  85.     }

  86.     /**
  87.      * <p>
  88.      * Append all properties from the source configuration to the target configuration. Properties in the source
  89.      * configuration are appended to the properties with the same key in the target configuration.
  90.      * </p>
  91.      * <p>
  92.      * <em>Note:</em> This method is not able to handle some specifics of configurations derived from
  93.      * {@code AbstractConfiguration} (for example list delimiters). For a full support of all of these features the {@code copy()}
  94.      * method of {@code AbstractConfiguration} should be used. In a future release this method might become deprecated.
  95.      * </p>
  96.      *
  97.      * @param source the source configuration
  98.      * @param target the target configuration
  99.      * @since 2.2
  100.      */
  101.     public static void append(final ImmutableConfiguration source, final Configuration target) {
  102.         source.getKeys().forEachRemaining(key -> target.addProperty(key, source.getProperty(key)));
  103.     }

  104.     /**
  105.      * Casts the specified object to an {@code EventSource} if possible. The boolean argument determines the method's
  106.      * behavior if the object does not implement the {@code EventSource} event: if set to <strong>false</strong>, a
  107.      * {@code ConfigurationRuntimeException} is thrown; if set to <strong>true</strong>, a dummy {@code EventSource} is returned; on
  108.      * this object all methods can be called, but they do not have any effect.
  109.      *
  110.      * @param obj the object to be cast as {@code EventSource}
  111.      * @param mockIfUnsupported a flag whether a mock object should be returned if necessary
  112.      * @return an {@code EventSource}
  113.      * @throws ConfigurationRuntimeException if the object cannot be cast to {@code EventSource} and the mock flag is
  114.      *         <strong>false</strong>
  115.      * @since 2.0
  116.      */
  117.     public static EventSource asEventSource(final Object obj, final boolean mockIfUnsupported) {
  118.         if (obj instanceof EventSource) {
  119.             return (EventSource) obj;
  120.         }

  121.         if (!mockIfUnsupported) {
  122.             throw new ConfigurationRuntimeException("Cannot cast to EventSource: " + obj);
  123.         }
  124.         return DUMMY_EVENT_SOURCE;
  125.     }

  126.     /**
  127.      * An internally used helper method for cloning objects. This implementation is not very sophisticated nor efficient.
  128.      * Maybe it can be replaced by an implementation from Commons Lang later. The method checks whether the passed in object
  129.      * implements the {@code Cloneable} interface. If this is the case, the {@code clone()} method is invoked by reflection.
  130.      * Errors that occur during the cloning process are re-thrown as runtime exceptions.
  131.      *
  132.      * @param obj the object to be cloned
  133.      * @return the cloned object
  134.      * @throws CloneNotSupportedException if the object cannot be cloned
  135.      */
  136.     static Object clone(final Object obj) throws CloneNotSupportedException {
  137.         if (obj instanceof Cloneable) {
  138.             try {
  139.                 final Method m = obj.getClass().getMethod(METHOD_CLONE);
  140.                 return m.invoke(obj);
  141.             } catch (final NoSuchMethodException nmex) {
  142.                 throw new CloneNotSupportedException("No clone() method found for class" + obj.getClass().getName());
  143.             } catch (final IllegalAccessException | InvocationTargetException itex) {
  144.                 throw new ConfigurationRuntimeException(itex);
  145.             }
  146.         }
  147.         throw new CloneNotSupportedException(obj.getClass().getName() + " does not implement Cloneable");
  148.     }

  149.     /**
  150.      * Clones the given configuration object if this is possible. If the passed in configuration object implements the
  151.      * {@code Cloneable} interface, its {@code clone()} method will be invoked. Otherwise an exception will be thrown.
  152.      *
  153.      * @param config the configuration object to be cloned (can be <strong>null</strong>)
  154.      * @return the cloned configuration (<strong>null</strong> if the argument was <strong>null</strong>, too)
  155.      * @throws ConfigurationRuntimeException if cloning is not supported for this object
  156.      * @since 1.3
  157.      */
  158.     public static Configuration cloneConfiguration(final Configuration config) throws ConfigurationRuntimeException {
  159.         if (config == null) {
  160.             return null;
  161.         }
  162.         try {
  163.             return (Configuration) clone(config);
  164.         } catch (final CloneNotSupportedException cnex) {
  165.             throw new ConfigurationRuntimeException(cnex);
  166.         }
  167.     }

  168.     /**
  169.      * Returns a clone of the passed in object if cloning is supported or the object itself if not. This method checks
  170.      * whether the passed in object implements the {@code Cloneable} interface. If this is the case, the {@code clone()}
  171.      * method is invoked. Otherwise, the object is directly returned. Errors that might occur during reflection calls are
  172.      * caught and also cause this method to return the original object.
  173.      *
  174.      * @param obj the object to be cloned
  175.      * @return the result of the cloning attempt
  176.      * @since 2.0
  177.      */
  178.     public static Object cloneIfPossible(final Object obj) {
  179.         try {
  180.             return clone(obj);
  181.         } catch (final Exception ex) {
  182.             return obj;
  183.         }
  184.     }

  185.     /**
  186.      * Creates a clone of the specified {@code Synchronizer}. This method can be called by {@code clone()} implementations
  187.      * in configuration classes that also need to copy the {@code Synchronizer} object. This method can handle some
  188.      * well-known {@code Synchronizer} implementations directly. For other classes, it uses the following algorithm:
  189.      * <ul>
  190.      * <li>If the class of the {@code Synchronizer} has a standard constructor, a new instance is created using
  191.      * reflection.</li>
  192.      * <li>If this is not possible, it is tried whether the object can be cloned.</li>
  193.      * </ul>
  194.      * If all attempts fail, a {@code ConfigurationRuntimeException} is thrown.
  195.      *
  196.      * @param sync the {@code Synchronizer} object to be cloned
  197.      * @return the clone of this {@code Synchronizer}
  198.      * @throws ConfigurationRuntimeException if no clone can be created
  199.      * @throws IllegalArgumentException if <strong>null</strong> is passed in
  200.      */
  201.     public static Synchronizer cloneSynchronizer(final Synchronizer sync) {
  202.         if (sync == null) {
  203.             throw new IllegalArgumentException("Synchronizer must not be null!");
  204.         }
  205.         if (NoOpSynchronizer.INSTANCE == sync) {
  206.             return sync;
  207.         }

  208.         try {
  209.             return sync.getClass().getConstructor().newInstance();
  210.         } catch (final Exception ex) {
  211.             LOG.info("Cannot create new instance of " + sync.getClass());
  212.         }

  213.         try {
  214.             return (Synchronizer) clone(sync);
  215.         } catch (final CloneNotSupportedException cnex) {
  216.             throw new ConfigurationRuntimeException("Cannot clone Synchronizer " + sync);
  217.         }
  218.     }

  219.     /**
  220.      * Converts the passed in configuration to a hierarchical one. If the configuration is already hierarchical, it is
  221.      * directly returned. Otherwise all properties are copied into a new hierarchical configuration.
  222.      *
  223.      * @param conf the configuration to convert
  224.      * @return the new hierarchical configuration (the result is <strong>null</strong> if and only if the passed in configuration is
  225.      *         <strong>null</strong>)
  226.      * @since 1.3
  227.      */
  228.     public static HierarchicalConfiguration<?> convertToHierarchical(final Configuration conf) {
  229.         return convertToHierarchical(conf, null);
  230.     }

  231.     /**
  232.      * Converts the passed in {@code Configuration} object to a hierarchical one using the specified
  233.      * {@code ExpressionEngine}. This conversion works by adding the keys found in the configuration to a newly created
  234.      * hierarchical configuration. When adding new keys to a hierarchical configuration the keys are interpreted by its
  235.      * {@code ExpressionEngine}. If they contain special characters (for example brackets) that are treated in a special way by the
  236.      * default expression engine, it may be necessary using a specific engine that can deal with such characters. Otherwise
  237.      * <strong>null</strong> can be passed in for the {@code ExpressionEngine}; then the default expression engine is used. If the
  238.      * passed in configuration is already hierarchical, it is directly returned. (However, the {@code ExpressionEngine} is
  239.      * set if it is not <strong>null</strong>.) Otherwise all properties are copied into a new hierarchical configuration.
  240.      *
  241.      * @param conf the configuration to convert
  242.      * @param engine the {@code ExpressionEngine} for the hierarchical configuration or <strong>null</strong> for the default
  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.6
  246.      */
  247.     public static HierarchicalConfiguration<?> convertToHierarchical(final Configuration conf, final ExpressionEngine engine) {
  248.         if (conf == null) {
  249.             return null;
  250.         }

  251.         if (conf instanceof HierarchicalConfiguration) {
  252.             final HierarchicalConfiguration<?> hc = (HierarchicalConfiguration<?>) conf;
  253.             if (engine != null) {
  254.                 hc.setExpressionEngine(engine);
  255.             }

  256.             return hc;
  257.         }
  258.         final BaseHierarchicalConfiguration hc = new BaseHierarchicalConfiguration();
  259.         if (engine != null) {
  260.             hc.setExpressionEngine(engine);
  261.         }

  262.         // Per default, a DisabledListDelimiterHandler is set.
  263.         // So list delimiters in property values are not an issue.
  264.         hc.copy(conf);
  265.         return hc;
  266.     }

  267.     /**
  268.      * <p>
  269.      * Copy all properties from the source configuration to the target configuration. Properties in the target configuration
  270.      * are replaced with the properties with the same key in the source configuration.
  271.      * </p>
  272.      * <p>
  273.      * <em>Note:</em> This method is not able to handle some specifics of configurations derived from
  274.      * {@code AbstractConfiguration} (for example list delimiters). For a full support of all of these features the {@code copy()}
  275.      * method of {@code AbstractConfiguration} should be used. In a future release this method might become deprecated.
  276.      * </p>
  277.      *
  278.      * @param source the source configuration
  279.      * @param target the target configuration
  280.      * @since 1.1
  281.      */
  282.     public static void copy(final Configuration source, final Configuration target) {
  283.         copy((ImmutableConfiguration) source, target);
  284.     }

  285.     /**
  286.      * <p>
  287.      * Copy all properties from the source configuration to the target configuration. Properties in the target configuration
  288.      * are replaced with the properties with the same key in the source configuration.
  289.      * </p>
  290.      * <p>
  291.      * <em>Note:</em> This method is not able to handle some specifics of configurations derived from
  292.      * {@code AbstractConfiguration} (for example list delimiters). For a full support of all of these features the {@code copy()}
  293.      * method of {@code AbstractConfiguration} should be used. In a future release this method might become deprecated.
  294.      * </p>
  295.      *
  296.      * @param source the source configuration
  297.      * @param target the target configuration
  298.      * @since 2.2
  299.      */
  300.     public static void copy(final ImmutableConfiguration source, final Configuration target) {
  301.         source.getKeys().forEachRemaining(key -> target.setProperty(key, source.getProperty(key)));
  302.     }

  303.     /**
  304.      * Helper method for creating a proxy for an unmodifiable configuration. The interfaces the proxy should implement are
  305.      * passed as argument.
  306.      *
  307.      * @param ifcs an array with the interface classes the proxy must implement
  308.      * @param c the configuration object to be wrapped
  309.      * @return a proxy object for an immutable configuration
  310.      * @throws NullPointerException if the configuration is <strong>null</strong>
  311.      */
  312.     private static ImmutableConfiguration createUnmodifiableConfiguration(final Class<?>[] ifcs, final Configuration c) {
  313.         return (ImmutableConfiguration) Proxy.newProxyInstance(ConfigurationUtils.class.getClassLoader(), ifcs, new ImmutableConfigurationInvocationHandler(c));
  314.     }

  315.     /**
  316.      * Dump the configuration key/value mappings to some ouput stream. This version of the method exists only for backwards
  317.      * compatibility reason.
  318.      *
  319.      * @param configuration the configuration
  320.      * @param out the output stream to dump the configuration to
  321.      */
  322.     public static void dump(final Configuration configuration, final PrintStream out) {
  323.         dump((ImmutableConfiguration) configuration, out);
  324.     }

  325.     /**
  326.      * Dump the configuration key/value mappings to some writer. This version of the method exists only for backwards
  327.      * compatibility reason.
  328.      *
  329.      * @param configuration the configuration
  330.      * @param out the writer to dump the configuration to
  331.      */
  332.     public static void dump(final Configuration configuration, final PrintWriter out) {
  333.         dump((ImmutableConfiguration) configuration, out);
  334.     }

  335.     /**
  336.      * Dump the configuration key/value mappings to some ouput stream.
  337.      *
  338.      * @param configuration the configuration
  339.      * @param out the output stream to dump the configuration to
  340.      * @since 2.2
  341.      */
  342.     public static void dump(final ImmutableConfiguration configuration, final PrintStream out) {
  343.         dump(configuration, new PrintWriter(out));
  344.     }

  345.     /**
  346.      * Dump the configuration key/value mappings to some writer.
  347.      *
  348.      * @param configuration the configuration
  349.      * @param out the writer to dump the configuration to
  350.      * @since 2.2
  351.      */
  352.     public static void dump(final ImmutableConfiguration configuration, final PrintWriter out) {
  353.         for (final Iterator<String> keys = configuration.getKeys(); keys.hasNext();) {
  354.             final String key = keys.next();
  355.             final Object value = configuration.getProperty(key);
  356.             out.print(key);
  357.             out.print("=");
  358.             out.print(value);

  359.             if (keys.hasNext()) {
  360.                 out.println();
  361.             }
  362.         }

  363.         out.flush();
  364.     }

  365.     /**
  366.      * Enables runtime exceptions for the specified configuration object. This method can be used for configuration
  367.      * implementations that may face errors on normal property access, for example {@code DatabaseConfiguration} or
  368.      * {@code JNDIConfiguration}. Per default such errors are simply logged and then ignored. This implementation will
  369.      * register a special {@link EventListener} that throws a runtime exception (namely a
  370.      * {@code ConfigurationRuntimeException}) on each received error event.
  371.      *
  372.      * @param src the configuration, for which runtime exceptions are to be enabled; this configuration must implement
  373.      *        {@link EventSource}
  374.      */
  375.     public static void enableRuntimeExceptions(final Configuration src) {
  376.         if (!(src instanceof EventSource)) {
  377.             throw new IllegalArgumentException("Configuration must implement EventSource!");
  378.         }
  379.         ((EventSource) src).addEventListener(ConfigurationErrorEvent.ANY, event -> {
  380.             // Throw a runtime exception
  381.             throw new ConfigurationRuntimeException(event.getCause());
  382.         });
  383.     }

  384.     /**
  385.      * Loads the class with the given name. This method is used whenever a class has to be loaded dynamically. It first
  386.      * tries the current thread's context class loader. If this fails, the class loader of this class is tried.
  387.      *
  388.      * @param clsName the name of the class to be loaded
  389.      * @return the loaded class
  390.      * @throws ClassNotFoundException if the class cannot be resolved
  391.      * @since 2.0
  392.      */
  393.     public static Class<?> loadClass(final String clsName) throws ClassNotFoundException {
  394.         if (LOG.isDebugEnabled()) {
  395.             LOG.debug("Loading class " + clsName);
  396.         }

  397.         final ClassLoader cl = Thread.currentThread().getContextClassLoader();
  398.         try {
  399.             if (cl != null) {
  400.                 return cl.loadClass(clsName);
  401.             }
  402.         } catch (final ClassNotFoundException cnfex) {
  403.             LOG.info("Could not load class " + clsName + " using CCL. Falling back to default CL.", cnfex);
  404.         }

  405.         return ConfigurationUtils.class.getClassLoader().loadClass(clsName);
  406.     }

  407.     /**
  408.      * Loads the class with the specified name re-throwing {@code ClassNotFoundException} exceptions as runtime exceptions.
  409.      * This method works like {@link #loadClass(String)}. However, checked exceptions are caught and re-thrown as
  410.      * {@code ConfigurationRuntimeException}.
  411.      *
  412.      * @param clsName the name of the class to be loaded
  413.      * @return the loaded class
  414.      * @throws ConfigurationRuntimeException if the class cannot be resolved
  415.      * @since 2.0
  416.      */
  417.     public static Class<?> loadClassNoEx(final String clsName) {
  418.         try {
  419.             return loadClass(clsName);
  420.         } catch (final ClassNotFoundException cnfex) {
  421.             throw new ConfigurationRuntimeException("Cannot load class " + clsName, cnfex);
  422.         }
  423.     }

  424.     /**
  425.      * Gets a string representation of the key/value mappings of a configuration. This version of the method exists only for
  426.      * backwards compatibility reason.
  427.      *
  428.      * @param configuration the configuration
  429.      * @return a string representation of the configuration
  430.      */
  431.     public static String toString(final Configuration configuration) {
  432.         return toString((ImmutableConfiguration) configuration);
  433.     }

  434.     /**
  435.      * Gets a string representation of the key/value mappings of a configuration.
  436.      *
  437.      * @param configuration the configuration
  438.      * @return a string representation of the configuration
  439.      * @since 2.2
  440.      */
  441.     public static String toString(final ImmutableConfiguration configuration) {
  442.         final StringWriter writer = new StringWriter();
  443.         dump(configuration, new PrintWriter(writer));
  444.         return writer.toString();
  445.     }

  446.     /**
  447.      * Creates an {@code ImmutableConfiguration} from the given {@code Configuration} object. This method creates a proxy
  448.      * object wrapping the original configuration and making it available under the {@code ImmutableConfiguration}
  449.      * interface. Through this interface the configuration cannot be manipulated. It is also not possible to cast the
  450.      * returned object back to a {@code Configuration} instance to circumvent this protection.
  451.      *
  452.      * @param c the {@code Configuration} to be wrapped (must not be <strong>null</strong>)
  453.      * @return an {@code ImmutableConfiguration} view on the specified {@code Configuration} object
  454.      * @throws NullPointerException if the passed in {@code Configuration} is <strong>null</strong>
  455.      * @since 2.0
  456.      */
  457.     public static ImmutableConfiguration unmodifiableConfiguration(final Configuration c) {
  458.         return createUnmodifiableConfiguration(IMMUTABLE_CONFIG_IFCS, c);
  459.     }

  460.     /**
  461.      * Creates an {@code ImmutableHierarchicalConfiguration} from the given {@code HierarchicalConfiguration} object. This
  462.      * method works exactly like the method with the same name, but it operates on hierarchical configurations.
  463.      *
  464.      * @param c the {@code HierarchicalConfiguration} to be wrapped (must not be <strong>null</strong>)
  465.      * @return an {@code ImmutableHierarchicalConfiguration} view on the specified {@code HierarchicalConfiguration} object
  466.      * @throws NullPointerException if the passed in {@code HierarchicalConfiguration} is <strong>null</strong>
  467.      * @since 2.0
  468.      */
  469.     public static ImmutableHierarchicalConfiguration unmodifiableConfiguration(final HierarchicalConfiguration<?> c) {
  470.         return (ImmutableHierarchicalConfiguration) createUnmodifiableConfiguration(IMMUTABLE_HIERARCHICAL_CONFIG_IFCS, c);
  471.     }

  472.     /**
  473.      * Private constructor. Prevents instances from being created.
  474.      */
  475.     private ConfigurationUtils() {
  476.         // to prevent instantiation...
  477.     }
  478. }