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 /** 050 * A specialized {@code InvocationHandler} implementation for wrapper configurations. Here the logic of accessing a 051 * wrapped builder is implemented. 052 */ 053 private static final class BuilderConfigurationWrapperInvocationHandler implements InvocationHandler { 054 055 /** The wrapped builder. */ 056 private final ConfigurationBuilder<? extends ImmutableConfiguration> builder; 057 058 /** The level of {@code EventSource} support. */ 059 private final EventSourceSupport eventSourceSupport; 060 061 /** 062 * Creates a new instance of {@code BuilderConfigurationWrapperInvocationHandler}. 063 * 064 * @param wrappedBuilder the wrapped builder 065 * @param evSrcSupport the level of {@code EventSource} support 066 */ 067 public BuilderConfigurationWrapperInvocationHandler(final ConfigurationBuilder<? extends ImmutableConfiguration> wrappedBuilder, 068 final EventSourceSupport evSrcSupport) { 069 builder = wrappedBuilder; 070 eventSourceSupport = evSrcSupport; 071 } 072 073 /** 074 * Handles a method invocation on the associated builder's configuration object. 075 * 076 * @param method the method to be invoked 077 * @param args method arguments 078 * @return the return value of the method 079 * @throws Exception if an error occurs 080 */ 081 private Object handleConfigurationInvocation(final Method method, final Object[] args) throws ReflectiveOperationException, ConfigurationException { 082 return method.invoke(builder.getConfiguration(), args); 083 } 084 085 /** 086 * Handles a method invocation on the {@code EventSource} interface. This method evaluates the current 087 * {@code EventSourceSupport} object in order to find the appropriate target for the invocation. 088 * 089 * @param method the method to be invoked 090 * @param args method arguments 091 * @return the return value of the method 092 * @throws ReflectiveOperationException if an error occurs 093 */ 094 private Object handleEventSourceInvocation(final Method method, final Object... args) throws ReflectiveOperationException { 095 return method.invoke(EventSourceSupport.DUMMY == eventSourceSupport ? ConfigurationUtils.asEventSource(this, true) : builder, args); 096 } 097 098 /** 099 * 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 <b>null</b> 162 * @param builder the wrapped {@code ConfigurationBuilder} (must not be <b>null</b>) 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 <b>null</b> 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 <b>null</b> 223 * @param builder the wrapped {@code ConfigurationBuilder} (must not be <b>null</b>) 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}