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