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 19 import java.lang.reflect.InvocationHandler; 20 import java.lang.reflect.Method; 21 import java.lang.reflect.Proxy; 22 23 import org.apache.commons.configuration2.ConfigurationUtils; 24 import org.apache.commons.configuration2.ImmutableConfiguration; 25 import org.apache.commons.configuration2.event.EventSource; 26 import org.apache.commons.configuration2.ex.ConfigurationException; 27 28 /** 29 * <p> 30 * A class that allows the creation of configuration objects wrapping a {@link ConfigurationBuilder}. 31 * </p> 32 * <p> 33 * Using this class special {@code ImmutableConfiguration} proxies can be created that delegate all method invocations 34 * to another {@code ImmutableConfiguration} obtained from a {@code ConfigurationBuilder}. For instance, if there is a 35 * configuration {@code c} wrapping the builder {@code builder}, the call {@code c.getString(myKey)} is transformed to 36 * {@code builder.getConfiguration().getString(myKey)}. 37 * </p> 38 * <p> 39 * There are multiple use cases for such a constellation. One example is that client code can continue working with 40 * {@code ImmutableConfiguration} objects while under the hood builders are used. Another example is that dynamic 41 * configurations can be realized in a transparent way: a client holds a single configuration (proxy) object, but the 42 * underlying builder may return a different data object on each call. 43 * </p> 44 * 45 * @since 2.0 46 */ 47 public class BuilderConfigurationWrapperFactory { 48 49 /** 50 * A specialized {@code InvocationHandler} implementation for wrapper configurations. Here the logic of accessing a 51 * wrapped builder is implemented. 52 */ 53 private static final class BuilderConfigurationWrapperInvocationHandler implements InvocationHandler { 54 55 /** The wrapped builder. */ 56 private final ConfigurationBuilder<? extends ImmutableConfiguration> builder; 57 58 /** The level of {@code EventSource} support. */ 59 private final EventSourceSupport eventSourceSupport; 60 61 /** 62 * Creates a new instance of {@code BuilderConfigurationWrapperInvocationHandler}. 63 * 64 * @param wrappedBuilder the wrapped builder 65 * @param evSrcSupport the level of {@code EventSource} support 66 */ 67 public BuilderConfigurationWrapperInvocationHandler(final ConfigurationBuilder<? extends ImmutableConfiguration> wrappedBuilder, 68 final EventSourceSupport evSrcSupport) { 69 builder = wrappedBuilder; 70 eventSourceSupport = evSrcSupport; 71 } 72 73 /** 74 * Handles a method invocation on the associated builder's configuration object. 75 * 76 * @param method the method to be invoked 77 * @param args method arguments 78 * @return the return value of the method 79 * @throws Exception if an error occurs 80 */ 81 private Object handleConfigurationInvocation(final Method method, final Object[] args) throws ReflectiveOperationException, ConfigurationException { 82 return method.invoke(builder.getConfiguration(), args); 83 } 84 85 /** 86 * Handles a method invocation on the {@code EventSource} interface. This method evaluates the current 87 * {@code EventSourceSupport} object in order to find the appropriate target for the invocation. 88 * 89 * @param method the method to be invoked 90 * @param args method arguments 91 * @return the return value of the method 92 * @throws ReflectiveOperationException if an error occurs 93 */ 94 private Object handleEventSourceInvocation(final Method method, final Object... args) throws ReflectiveOperationException { 95 return method.invoke(EventSourceSupport.DUMMY == eventSourceSupport ? ConfigurationUtils.asEventSource(this, true) : builder, args); 96 } 97 98 /** 99 * Handles method invocations. This implementation handles methods of two different interfaces: 100 * <ul> 101 * <li>Methods from the {@code EventSource} interface are handled according to the current support level.</li> 102 * <li>Other method calls are delegated to the builder's configuration object.</li> 103 * </ul> 104 * 105 * @param proxy the proxy object 106 * @param method the method to be invoked 107 * @param args method arguments 108 * @return the return value of the method 109 * @throws ReflectiveOperationException if an error occurs 110 * @throws ConfigurationException if an error occurs 111 */ 112 @Override 113 public Object invoke(final Object proxy, final Method method, final Object[] args) throws ReflectiveOperationException, ConfigurationException { 114 return EventSource.class.equals(method.getDeclaringClass()) ? handleEventSourceInvocation(method, args) 115 : handleConfigurationInvocation(method, args); 116 } 117 } 118 119 /** 120 * <p> 121 * An enumeration class with different options for supporting the {@code EventSource} interface in generated 122 * {@code ImmutableConfiguration} proxies. 123 * </p> 124 * <p> 125 * Using literals of this class it is possible to specify that a {@code ImmutableConfiguration} object returned by 126 * {@code BuilderConfigurationWrapperFactory} also implements the {@code EventSource} interface and how this 127 * implementation should work. See the documentation of the single constants for more details. 128 * </p> 129 */ 130 public enum EventSourceSupport { 131 /** 132 * No support of the {@code EventSource} interface. If this option is set, {@code ImmutableConfiguration} objects 133 * generated by {@code BuilderConfigurationWrapperFactory} do not implement the {@code EventSource} interface. 134 */ 135 NONE, 136 137 /** 138 * Dummy support of the {@code EventSource} interface. This option causes {@code ImmutableConfiguration} objects 139 * generated by {@code BuilderConfigurationWrapperFactory} to implement the {@code EventSource} interface, however, this 140 * implementation consists only of empty dummy methods without real functionality. 141 */ 142 DUMMY, 143 144 /** 145 * {@code EventSource} support is implemented by delegating to the associated {@code ConfigurationBuilder} object. If 146 * this option is used, generated {@code ImmutableConfiguration} objects provide a fully functional implementation of 147 * {@code EventSource} by delegating to the builder. Because the {@code ConfigurationBuilder} interface extends 148 * {@code EventSource} this delegation is always possible. 149 */ 150 BUILDER 151 } 152 153 /** 154 * Creates a {@code ImmutableConfiguration} object which wraps the specified {@code ConfigurationBuilder}. Each access 155 * of the configuration is delegated to a corresponding call on the {@code ImmutableConfiguration} object managed by the 156 * builder. This is a convenience method which allows creating wrapper configurations without having to instantiate this 157 * class. 158 * 159 * @param <T> the type of the configuration objects returned by this method 160 * @param ifcClass the class of the configuration objects returned by this method; this must be an interface class and 161 * must not be <strong>null</strong> 162 * @param builder the wrapped {@code ConfigurationBuilder} (must not be <strong>null</strong>) 163 * @param evSrcSupport the level of {@code EventSource} support 164 * @return the wrapper configuration 165 * @throws IllegalArgumentException if a required parameter is missing 166 * @throws org.apache.commons.configuration2.ex.ConfigurationRuntimeException if an error occurs when creating the 167 * result {@code ImmutableConfiguration} 168 */ 169 public static <T extends ImmutableConfiguration> T createBuilderConfigurationWrapper(final Class<T> ifcClass, 170 final ConfigurationBuilder<? extends T> builder, final EventSourceSupport evSrcSupport) { 171 if (ifcClass == null) { 172 throw new IllegalArgumentException("Interface class must not be null!"); 173 } 174 if (builder == null) { 175 throw new IllegalArgumentException("Builder must not be null!"); 176 } 177 178 return ifcClass.cast(Proxy.newProxyInstance(BuilderConfigurationWrapperFactory.class.getClassLoader(), getSupportedInterfaces(ifcClass, evSrcSupport), 179 new BuilderConfigurationWrapperInvocationHandler(builder, evSrcSupport))); 180 } 181 182 /** 183 * Gets an array with the classes the generated proxy has to support. 184 * 185 * @param ifcClass the class of the configuration objects returned by this method; this must be an interface class and 186 * must not be <strong>null</strong> 187 * @param evSrcSupport the level of {@code EventSource} support 188 * @return an array with the interface classes to implement 189 */ 190 private static Class<?>[] getSupportedInterfaces(final Class<?> ifcClass, final EventSourceSupport evSrcSupport) { 191 return EventSourceSupport.NONE == evSrcSupport ? new Class<?>[] {ifcClass} : new Class<?>[] {EventSource.class, ifcClass}; 192 } 193 194 /** The current {@code EventSourceSupport} value. */ 195 private final EventSourceSupport eventSourceSupport; 196 197 /** 198 * Creates a new instance of {@code BuilderConfigurationWrapperFactory} setting the default {@code EventSourceSupport} 199 * <em>NONE</em>. 200 */ 201 public BuilderConfigurationWrapperFactory() { 202 this(EventSourceSupport.NONE); 203 } 204 205 /** 206 * Creates a new instance of {@code BuilderConfigurationWrapperFactory} and sets the property for supporting the 207 * {@code EventSource} interface. 208 * 209 * @param evSrcSupport the level of {@code EventSource} support 210 */ 211 public BuilderConfigurationWrapperFactory(final EventSourceSupport evSrcSupport) { 212 eventSourceSupport = evSrcSupport; 213 } 214 215 /** 216 * Creates a wrapper {@code ImmutableConfiguration} on top of the specified {@code ConfigurationBuilder}. This 217 * implementation delegates to 218 * {@link #createBuilderConfigurationWrapper(Class, ConfigurationBuilder, EventSourceSupport)}. 219 * 220 * @param <T> the type of the configuration objects returned by this method 221 * @param ifcClass the class of the configuration objects returned by this method; this must be an interface class and 222 * must not be <strong>null</strong> 223 * @param builder the wrapped {@code ConfigurationBuilder} (must not be <strong>null</strong>) 224 * @return the wrapper configuration 225 * @throws IllegalArgumentException if a required parameter is missing 226 * @throws org.apache.commons.configuration2.ex.ConfigurationRuntimeException if an error occurs when creating the 227 * result {@code ImmutableConfiguration} 228 */ 229 public <T extends ImmutableConfiguration> T createBuilderConfigurationWrapper(final Class<T> ifcClass, final ConfigurationBuilder<? extends T> builder) { 230 return createBuilderConfigurationWrapper(ifcClass, builder, getEventSourceSupport()); 231 } 232 233 /** 234 * Gets the level of {@code EventSource} support used when generating {@code ImmutableConfiguration} objects. 235 * 236 * @return the level of {@code EventSource} support 237 */ 238 public EventSourceSupport getEventSourceSupport() { 239 return eventSourceSupport; 240 } 241 }