BuilderConfigurationWrapperFactory.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.builder;

  18. import java.lang.reflect.InvocationHandler;
  19. import java.lang.reflect.Method;
  20. import java.lang.reflect.Proxy;

  21. import org.apache.commons.configuration2.ConfigurationUtils;
  22. import org.apache.commons.configuration2.ImmutableConfiguration;
  23. import org.apache.commons.configuration2.event.EventSource;
  24. import org.apache.commons.configuration2.ex.ConfigurationException;

  25. /**
  26.  * <p>
  27.  * A class that allows the creation of configuration objects wrapping a {@link ConfigurationBuilder}.
  28.  * </p>
  29.  * <p>
  30.  * Using this class special {@code ImmutableConfiguration} proxies can be created that delegate all method invocations
  31.  * to another {@code ImmutableConfiguration} obtained from a {@code ConfigurationBuilder}. For instance, if there is a
  32.  * configuration {@code c} wrapping the builder {@code builder}, the call {@code c.getString(myKey)} is transformed to
  33.  * {@code builder.getConfiguration().getString(myKey)}.
  34.  * </p>
  35.  * <p>
  36.  * There are multiple use cases for such a constellation. One example is that client code can continue working with
  37.  * {@code ImmutableConfiguration} objects while under the hood builders are used. Another example is that dynamic
  38.  * configurations can be realized in a transparent way: a client holds a single configuration (proxy) object, but the
  39.  * underlying builder may return a different data object on each call.
  40.  * </p>
  41.  *
  42.  * @since 2.0
  43.  */
  44. public class BuilderConfigurationWrapperFactory {

  45.     /**
  46.      * A specialized {@code InvocationHandler} implementation for wrapper configurations. Here the logic of accessing a
  47.      * wrapped builder is implemented.
  48.      */
  49.     private static final class BuilderConfigurationWrapperInvocationHandler implements InvocationHandler {

  50.         /** The wrapped builder. */
  51.         private final ConfigurationBuilder<? extends ImmutableConfiguration> builder;

  52.         /** The level of {@code EventSource} support. */
  53.         private final EventSourceSupport eventSourceSupport;

  54.         /**
  55.          * Creates a new instance of {@code BuilderConfigurationWrapperInvocationHandler}.
  56.          *
  57.          * @param wrappedBuilder the wrapped builder
  58.          * @param evSrcSupport the level of {@code EventSource} support
  59.          */
  60.         public BuilderConfigurationWrapperInvocationHandler(final ConfigurationBuilder<? extends ImmutableConfiguration> wrappedBuilder,
  61.             final EventSourceSupport evSrcSupport) {
  62.             builder = wrappedBuilder;
  63.             eventSourceSupport = evSrcSupport;
  64.         }

  65.         /**
  66.          * Handles a method invocation on the associated builder's configuration object.
  67.          *
  68.          * @param method the method to be invoked
  69.          * @param args method arguments
  70.          * @return the return value of the method
  71.          * @throws Exception if an error occurs
  72.          */
  73.         private Object handleConfigurationInvocation(final Method method, final Object[] args) throws ReflectiveOperationException, ConfigurationException {
  74.             return method.invoke(builder.getConfiguration(), args);
  75.         }

  76.         /**
  77.          * Handles a method invocation on the {@code EventSource} interface. This method evaluates the current
  78.          * {@code EventSourceSupport} object in order to find the appropriate target for the invocation.
  79.          *
  80.          * @param method the method to be invoked
  81.          * @param args method arguments
  82.          * @return the return value of the method
  83.          * @throws ReflectiveOperationException if an error occurs
  84.          */
  85.         private Object handleEventSourceInvocation(final Method method, final Object... args) throws ReflectiveOperationException {
  86.             return method.invoke(EventSourceSupport.DUMMY == eventSourceSupport ? ConfigurationUtils.asEventSource(this, true) : builder, args);
  87.         }

  88.         /**
  89.          * Handles method invocations. This implementation handles methods of two different interfaces:
  90.          * <ul>
  91.          * <li>Methods from the {@code EventSource} interface are handled according to the current support level.</li>
  92.          * <li>Other method calls are delegated to the builder's configuration object.</li>
  93.          * </ul>
  94.          *
  95.          * @param proxy the proxy object
  96.          * @param method the method to be invoked
  97.          * @param args method arguments
  98.          * @return the return value of the method
  99.          * @throws ReflectiveOperationException if an error occurs
  100.          * @throws ConfigurationException if an error occurs
  101.          */
  102.         @Override
  103.         public Object invoke(final Object proxy, final Method method, final Object[] args) throws ReflectiveOperationException, ConfigurationException {
  104.             return EventSource.class.equals(method.getDeclaringClass()) ? handleEventSourceInvocation(method, args)
  105.                 : handleConfigurationInvocation(method, args);
  106.         }
  107.     }

  108.     /**
  109.      * <p>
  110.      * An enumeration class with different options for supporting the {@code EventSource} interface in generated
  111.      * {@code ImmutableConfiguration} proxies.
  112.      * </p>
  113.      * <p>
  114.      * Using literals of this class it is possible to specify that a {@code ImmutableConfiguration} object returned by
  115.      * {@code BuilderConfigurationWrapperFactory} also implements the {@code EventSource} interface and how this
  116.      * implementation should work. See the documentation of the single constants for more details.
  117.      * </p>
  118.      */
  119.     public enum EventSourceSupport {
  120.         /**
  121.          * No support of the {@code EventSource} interface. If this option is set, {@code ImmutableConfiguration} objects
  122.          * generated by {@code BuilderConfigurationWrapperFactory} do not implement the {@code EventSource} interface.
  123.          */
  124.         NONE,

  125.         /**
  126.          * Dummy support of the {@code EventSource} interface. This option causes {@code ImmutableConfiguration} objects
  127.          * generated by {@code BuilderConfigurationWrapperFactory} to implement the {@code EventSource} interface, however, this
  128.          * implementation consists only of empty dummy methods without real functionality.
  129.          */
  130.         DUMMY,

  131.         /**
  132.          * {@code EventSource} support is implemented by delegating to the associated {@code ConfigurationBuilder} object. If
  133.          * this option is used, generated {@code ImmutableConfiguration} objects provide a fully functional implementation of
  134.          * {@code EventSource} by delegating to the builder. Because the {@code ConfigurationBuilder} interface extends
  135.          * {@code EventSource} this delegation is always possible.
  136.          */
  137.         BUILDER
  138.     }

  139.     /**
  140.      * Creates a {@code ImmutableConfiguration} object which wraps the specified {@code ConfigurationBuilder}. Each access
  141.      * of the configuration is delegated to a corresponding call on the {@code ImmutableConfiguration} object managed by the
  142.      * builder. This is a convenience method which allows creating wrapper configurations without having to instantiate this
  143.      * class.
  144.      *
  145.      * @param <T> the type of the configuration objects returned by this method
  146.      * @param ifcClass the class of the configuration objects returned by this method; this must be an interface class and
  147.      *        must not be <strong>null</strong>
  148.      * @param builder the wrapped {@code ConfigurationBuilder} (must not be <strong>null</strong>)
  149.      * @param evSrcSupport the level of {@code EventSource} support
  150.      * @return the wrapper configuration
  151.      * @throws IllegalArgumentException if a required parameter is missing
  152.      * @throws org.apache.commons.configuration2.ex.ConfigurationRuntimeException if an error occurs when creating the
  153.      *         result {@code ImmutableConfiguration}
  154.      */
  155.     public static <T extends ImmutableConfiguration> T createBuilderConfigurationWrapper(final Class<T> ifcClass,
  156.         final ConfigurationBuilder<? extends T> builder, final EventSourceSupport evSrcSupport) {
  157.         if (ifcClass == null) {
  158.             throw new IllegalArgumentException("Interface class must not be null!");
  159.         }
  160.         if (builder == null) {
  161.             throw new IllegalArgumentException("Builder must not be null!");
  162.         }

  163.         return ifcClass.cast(Proxy.newProxyInstance(BuilderConfigurationWrapperFactory.class.getClassLoader(), getSupportedInterfaces(ifcClass, evSrcSupport),
  164.             new BuilderConfigurationWrapperInvocationHandler(builder, evSrcSupport)));
  165.     }

  166.     /**
  167.      * Gets an array with the classes the generated proxy has to support.
  168.      *
  169.      * @param ifcClass the class of the configuration objects returned by this method; this must be an interface class and
  170.      *        must not be <strong>null</strong>
  171.      * @param evSrcSupport the level of {@code EventSource} support
  172.      * @return an array with the interface classes to implement
  173.      */
  174.     private static Class<?>[] getSupportedInterfaces(final Class<?> ifcClass, final EventSourceSupport evSrcSupport) {
  175.         return EventSourceSupport.NONE == evSrcSupport ? new Class<?>[] {ifcClass} : new Class<?>[] {EventSource.class, ifcClass};
  176.     }

  177.     /** The current {@code EventSourceSupport} value. */
  178.     private final EventSourceSupport eventSourceSupport;

  179.     /**
  180.      * Creates a new instance of {@code BuilderConfigurationWrapperFactory} setting the default {@code EventSourceSupport}
  181.      * <em>NONE</em>.
  182.      */
  183.     public BuilderConfigurationWrapperFactory() {
  184.         this(EventSourceSupport.NONE);
  185.     }

  186.     /**
  187.      * Creates a new instance of {@code BuilderConfigurationWrapperFactory} and sets the property for supporting the
  188.      * {@code EventSource} interface.
  189.      *
  190.      * @param evSrcSupport the level of {@code EventSource} support
  191.      */
  192.     public BuilderConfigurationWrapperFactory(final EventSourceSupport evSrcSupport) {
  193.         eventSourceSupport = evSrcSupport;
  194.     }

  195.     /**
  196.      * Creates a wrapper {@code ImmutableConfiguration} on top of the specified {@code ConfigurationBuilder}. This
  197.      * implementation delegates to
  198.      * {@link #createBuilderConfigurationWrapper(Class, ConfigurationBuilder, EventSourceSupport)}.
  199.      *
  200.      * @param <T> the type of the configuration objects returned by this method
  201.      * @param ifcClass the class of the configuration objects returned by this method; this must be an interface class and
  202.      *        must not be <strong>null</strong>
  203.      * @param builder the wrapped {@code ConfigurationBuilder} (must not be <strong>null</strong>)
  204.      * @return the wrapper configuration
  205.      * @throws IllegalArgumentException if a required parameter is missing
  206.      * @throws org.apache.commons.configuration2.ex.ConfigurationRuntimeException if an error occurs when creating the
  207.      *         result {@code ImmutableConfiguration}
  208.      */
  209.     public <T extends ImmutableConfiguration> T createBuilderConfigurationWrapper(final Class<T> ifcClass, final ConfigurationBuilder<? extends T> builder) {
  210.         return createBuilderConfigurationWrapper(ifcClass, builder, getEventSourceSupport());
  211.     }

  212.     /**
  213.      * Gets the level of {@code EventSource} support used when generating {@code ImmutableConfiguration} objects.
  214.      *
  215.      * @return the level of {@code EventSource} support
  216.      */
  217.     public EventSourceSupport getEventSourceSupport() {
  218.         return eventSourceSupport;
  219.     }
  220. }