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 * http://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 */ 017package org.apache.commons.configuration2.builder; 018 019import java.lang.reflect.InvocationHandler; 020import java.lang.reflect.Method; 021import java.lang.reflect.Proxy; 022 023import org.apache.commons.configuration2.ConfigurationUtils; 024import org.apache.commons.configuration2.ImmutableConfiguration; 025import org.apache.commons.configuration2.event.EventSource; 026 027/** 028 * <p> 029 * A class that allows the creation of configuration objects wrapping a 030 * {@link ConfigurationBuilder}. 031 * </p> 032 * <p> 033 * Using this class special {@code ImmutableConfiguration} proxies can be created that 034 * delegate all method invocations to another {@code ImmutableConfiguration} obtained 035 * from a {@code ConfigurationBuilder}. For instance, if there is a 036 * configuration {@code c} wrapping the builder {@code builder}, the call 037 * {@code c.getString(myKey)} is transformed to 038 * {@code builder.getConfiguration().getString(myKey)}. 039 * </p> 040 * <p> 041 * There are multiple use cases for such a constellation. One example is that 042 * client code can continue working with {@code ImmutableConfiguration} objects while 043 * under the hood builders are used. Another example is that dynamic 044 * configurations can be realized in a transparent way: a client holds a single 045 * configuration (proxy) object, but the underlying builder may return a 046 * different data object on each call. 047 * </p> 048 * 049 * @version $Id: BuilderConfigurationWrapperFactory.java 1706911 2015-10-05 20:01:32Z oheger $ 050 * @since 2.0 051 */ 052public class BuilderConfigurationWrapperFactory 053{ 054 /** The current {@code EventSourceSupport} value. */ 055 private final EventSourceSupport eventSourceSupport; 056 057 /** 058 * Creates a new instance of {@code BuilderConfigurationWrapperFactory} and 059 * sets the property for supporting the {@code EventSource} interface. 060 * 061 * @param evSrcSupport the level of {@code EventSource} support 062 */ 063 public BuilderConfigurationWrapperFactory(EventSourceSupport evSrcSupport) 064 { 065 eventSourceSupport = evSrcSupport; 066 } 067 068 /** 069 * Creates a new instance of {@code BuilderConfigurationWrapperFactory} 070 * setting the default {@code EventSourceSupport} <em>NONE</em>. 071 */ 072 public BuilderConfigurationWrapperFactory() 073 { 074 this(EventSourceSupport.NONE); 075 } 076 077 /** 078 * Creates a wrapper {@code ImmutableConfiguration} on top of the specified 079 * {@code ConfigurationBuilder}. This implementation delegates to 080 * {@link #createBuilderConfigurationWrapper(Class, ConfigurationBuilder, EventSourceSupport)} 081 * . 082 * 083 * @param <T> the type of the configuration objects returned by this method 084 * @param ifcClass the class of the configuration objects returned by this 085 * method; this must be an interface class and must not be 086 * <b>null</b> 087 * @param builder the wrapped {@code ConfigurationBuilder} (must not be 088 * <b>null</b>) 089 * @return the wrapper configuration 090 * @throws IllegalArgumentException if a required parameter is missing 091 * @throws org.apache.commons.configuration2.ex.ConfigurationRuntimeException if an error 092 * occurs when creating the result {@code ImmutableConfiguration} 093 */ 094 public <T extends ImmutableConfiguration> T createBuilderConfigurationWrapper( 095 Class<T> ifcClass, ConfigurationBuilder<? extends T> builder) 096 { 097 return createBuilderConfigurationWrapper(ifcClass, builder, 098 getEventSourceSupport()); 099 } 100 101 /** 102 * Returns the level of {@code EventSource} support used when generating 103 * {@code ImmutableConfiguration} objects. 104 * 105 * @return the level of {@code EventSource} support 106 */ 107 public EventSourceSupport getEventSourceSupport() 108 { 109 return eventSourceSupport; 110 } 111 112 /** 113 * Returns a {@code ImmutableConfiguration} object which wraps the specified 114 * {@code ConfigurationBuilder}. Each access of the configuration is 115 * delegated to a corresponding call on the {@code ImmutableConfiguration} object 116 * managed by the builder. This is a convenience method which allows 117 * creating wrapper configurations without having to instantiate this class. 118 * 119 * @param <T> the type of the configuration objects returned by this method 120 * @param ifcClass the class of the configuration objects returned by this 121 * method; this must be an interface class and must not be 122 * <b>null</b> 123 * @param builder the wrapped {@code ConfigurationBuilder} (must not be 124 * <b>null</b>) 125 * @param evSrcSupport the level of {@code EventSource} support 126 * @return the wrapper configuration 127 * @throws IllegalArgumentException if a required parameter is missing 128 * @throws org.apache.commons.configuration2.ex.ConfigurationRuntimeException if an error 129 * occurs when creating the result {@code ImmutableConfiguration} 130 */ 131 public static <T extends ImmutableConfiguration> T createBuilderConfigurationWrapper( 132 Class<T> ifcClass, ConfigurationBuilder<? extends T> builder, 133 EventSourceSupport evSrcSupport) 134 { 135 if (ifcClass == null) 136 { 137 throw new IllegalArgumentException( 138 "Interface class must not be null!"); 139 } 140 if (builder == null) 141 { 142 throw new IllegalArgumentException("Builder must not be null!"); 143 } 144 145 return ifcClass.cast(Proxy.newProxyInstance( 146 BuilderConfigurationWrapperFactory.class.getClassLoader(), 147 fetchSupportedInterfaces(ifcClass, evSrcSupport), 148 new BuilderConfigurationWrapperInvocationHandler(builder, 149 evSrcSupport))); 150 } 151 152 /** 153 * Returns an array with the classes the generated proxy has to support. 154 * 155 * @param ifcClass the class of the configuration objects returned by this 156 * method; this must be an interface class and must not be 157 * <b>null</b> 158 * @param evSrcSupport the level of {@code EventSource} support 159 * @return an array with the interface classes to implement 160 */ 161 private static Class<?>[] fetchSupportedInterfaces(Class<?> ifcClass, 162 EventSourceSupport evSrcSupport) 163 { 164 if (EventSourceSupport.NONE == evSrcSupport) 165 { 166 return new Class<?>[] { 167 ifcClass 168 }; 169 } 170 171 Class<?>[] result = new Class<?>[2]; 172 result[0] = EventSource.class; 173 result[1] = ifcClass; 174 return result; 175 } 176 177 /** 178 * <p> 179 * An enumeration class with different options for supporting the 180 * {@code EventSource} interface in generated {@code ImmutableConfiguration} proxies. 181 * </p> 182 * <p> 183 * Using literals of this class it is possible to specify that a 184 * {@code ImmutableConfiguration} object returned by 185 * {@code BuilderConfigurationWrapperFactory} also implements the 186 * {@code EventSource} interface and how this implementation should work. 187 * See the documentation of the single constants for more details. 188 * </p> 189 */ 190 public enum EventSourceSupport 191 { 192 /** 193 * No support of the {@code EventSource} interface. If this option is 194 * set, {@code ImmutableConfiguration} objects generated by 195 * {@code BuilderConfigurationWrapperFactory} do not implement the 196 * {@code EventSource} interface. 197 */ 198 NONE, 199 200 /** 201 * Dummy support of the {@code EventSource} interface. This option 202 * causes {@code ImmutableConfiguration} objects generated by 203 * {@code BuilderConfigurationWrapperFactory} to implement the 204 * {@code EventSource} interface, however, this implementation consists 205 * only of empty dummy methods without real functionality. 206 */ 207 DUMMY, 208 209 /** 210 * {@code EventSource} support is implemented by delegating to the 211 * associated {@code ConfigurationBuilder} object. If this option is 212 * used, generated {@code ImmutableConfiguration} objects provide a fully 213 * functional implementation of {@code EventSource} by delegating to the 214 * builder. Because the {@code ConfigurationBuilder} interface extends 215 * {@code EventSource} this delegation is always possible. 216 */ 217 BUILDER 218 } 219 220 /** 221 * A specialized {@code InvocationHandler} implementation for wrapper 222 * configurations. Here the logic of accessing a wrapped builder is 223 * implemented. 224 */ 225 private static class BuilderConfigurationWrapperInvocationHandler implements 226 InvocationHandler 227 { 228 /** The wrapped builder. */ 229 private final ConfigurationBuilder<? extends ImmutableConfiguration> builder; 230 231 /** The level of {@code EventSource} support. */ 232 private final EventSourceSupport eventSourceSupport; 233 234 /** 235 * Creates a new instance of 236 * {@code BuilderConfigurationWrapperInvocationHandler}. 237 * 238 * @param wrappedBuilder the wrapped builder 239 * @param evSrcSupport the level of {@code EventSource} support 240 */ 241 public BuilderConfigurationWrapperInvocationHandler( 242 ConfigurationBuilder<? extends ImmutableConfiguration> wrappedBuilder, 243 EventSourceSupport evSrcSupport) 244 { 245 builder = wrappedBuilder; 246 eventSourceSupport = evSrcSupport; 247 } 248 249 /** 250 * Handles method invocations. This implementation handles methods of 251 * two different interfaces: 252 * <ul> 253 * <li>Methods from the {@code EventSource} interface are handled 254 * according to the current support level.</li> 255 * <li>Other method calls are delegated to the builder's configuration 256 * object.</li> 257 * </ul> 258 * 259 * @param proxy the proxy object 260 * @param method the method to be invoked 261 * @param args method arguments 262 * @return the return value of the method 263 * @throws Throwable if an error occurs 264 */ 265 @Override 266 public Object invoke(Object proxy, Method method, Object[] args) 267 throws Throwable 268 { 269 if (EventSource.class.equals(method.getDeclaringClass())) 270 { 271 return handleEventSourceInvocation(method, args); 272 } 273 else 274 { 275 return handleConfigurationInvocation(method, args); 276 } 277 } 278 279 /** 280 * Handles a method invocation on the associated builder's configuration 281 * object. 282 * 283 * @param method the method to be invoked 284 * @param args method arguments 285 * @return the return value of the method 286 * @throws Exception if an error occurs 287 */ 288 private Object handleConfigurationInvocation(Method method, 289 Object[] args) throws Exception 290 { 291 return method.invoke(builder.getConfiguration(), args); 292 } 293 294 /** 295 * Handles a method invocation on the {@code EventSource} interface. 296 * This method evaluates the current {@code EventSourceSupport} object 297 * in order to find the appropriate target for the invocation. 298 * 299 * @param method the method to be invoked 300 * @param args method arguments 301 * @return the return value of the method 302 * @throws Exception if an error occurs 303 */ 304 private Object handleEventSourceInvocation(Method method, Object[] args) 305 throws Exception 306 { 307 Object target = 308 (EventSourceSupport.DUMMY == eventSourceSupport) ? ConfigurationUtils 309 .asEventSource(this, true) : builder; 310 return method.invoke(target, args); 311 } 312 } 313}