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.fluent; 018 019import java.lang.reflect.InvocationHandler; 020import java.lang.reflect.Method; 021import java.lang.reflect.Proxy; 022 023import org.apache.commons.configuration2.builder.BasicBuilderParameters; 024import org.apache.commons.configuration2.builder.BuilderParameters; 025import org.apache.commons.configuration2.builder.DatabaseBuilderParametersImpl; 026import org.apache.commons.configuration2.builder.DefaultParametersHandler; 027import org.apache.commons.configuration2.builder.DefaultParametersManager; 028import org.apache.commons.configuration2.builder.FileBasedBuilderParametersImpl; 029import org.apache.commons.configuration2.builder.HierarchicalBuilderParametersImpl; 030import org.apache.commons.configuration2.builder.INIBuilderParametersImpl; 031import org.apache.commons.configuration2.builder.JndiBuilderParametersImpl; 032import org.apache.commons.configuration2.builder.PropertiesBuilderParametersImpl; 033import org.apache.commons.configuration2.builder.XMLBuilderParametersImpl; 034import org.apache.commons.configuration2.builder.combined.CombinedBuilderParametersImpl; 035import org.apache.commons.configuration2.builder.combined.MultiFileBuilderParametersImpl; 036 037//@formatter:off 038/** 039 * A convenience class for creating parameter objects for initializing configuration builder objects. 040 * <p> 041 * For setting initialization properties of new configuration objects, a number of specialized parameter classes exists. 042 * These classes use inheritance to organize the properties they support in a logic way. For instance, parameters for 043 * file-based configurations also support the basic properties common to all configuration implementations, parameters 044 * for XML configurations also include file-based and basic properties, etc. 045 * </p> 046 * <p> 047 * When constructing a configuration builder, an easy-to-use fluent API is desired to define specific properties for the 048 * configuration to be created. However, the inheritance structure of the parameter classes makes it surprisingly 049 * difficult to provide such an API. This class comes to rescue by defining a set of methods for the creation of 050 * interface-based parameter objects offering a truly fluent API. The methods provided can be called directly when 051 * setting up a configuration builder as shown in the following example code fragment: 052 * </p> 053 * <pre> 054 * Parameters params = new Parameters(); 055 * configurationBuilder.configure(params.fileBased() 056 * .setThrowExceptionOnMissing(true) 057 * .setEncoding("UTF-8") 058 * .setListDelimiter('#') 059 * .setFileName("test.xml")); 060 * </pre> 061 * <p> 062 * Using this class it is not only possible to create new parameters objects but also to initialize the newly created 063 * objects with default values. This is via the associated {@link DefaultParametersManager} object. Such an object can 064 * be passed to the constructor, or a new (uninitialized) instance is created. There are convenience methods for 065 * interacting with the associated {@code DefaultParametersManager}, namely to register or remove 066 * {@link DefaultParametersHandler} objects. On all newly created parameters objects the handlers registered at the 067 * associated {@code DefaultParametersHandler} are automatically applied. 068 * </p> 069 * <p> 070 * Implementation note: This class is thread-safe. 071 * </p> 072 * 073 * @since 2.0 074 */ 075//@formatter:off 076public final class Parameters { 077 /** 078 * A specialized {@code InvocationHandler} implementation which maps the methods of a parameters interface to an 079 * implementation of the corresponding property interfaces. The parameters interface is a union of multiple property 080 * interfaces. The wrapped object implements all of these, but not the union interface. Therefore, a reflection-based 081 * approach is required. A special handling is required for the method of the {@code BuilderParameters} interface 082 * because here no fluent return value is used. 083 */ 084 private static final class ParametersIfcInvocationHandler implements InvocationHandler { 085 /** 086 * Checks whether the specified method belongs to an interface which requires fluent result values. 087 * 088 * @param method the method to be checked 089 * @return a flag whether the method's result should be handled as a fluent result value 090 */ 091 private static boolean isFluentResult(final Method method) { 092 final Class<?> declaringClass = method.getDeclaringClass(); 093 return declaringClass.isInterface() && !declaringClass.equals(BuilderParameters.class); 094 } 095 096 /** The target object of reflection calls. */ 097 private final Object target; 098 099 /** 100 * Creates a new instance of {@code ParametersIfcInvocationHandler} and sets the wrapped parameters object. 101 * 102 * @param targetObj the target object for reflection calls 103 */ 104 public ParametersIfcInvocationHandler(final Object targetObj) { 105 target = targetObj; 106 } 107 108 /** 109 * {@inheritDoc} This implementation delegates method invocations to the target object and handles the return value 110 * correctly. 111 */ 112 @Override 113 public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { 114 final Object result = method.invoke(target, args); 115 return isFluentResult(method) ? proxy : result; 116 } 117 } 118 119 /** The manager for default handlers. */ 120 private final DefaultParametersManager defaultParametersManager; 121 122 /** 123 * Creates a new instance of {@code Parameters}. A new, uninitialized {@link DefaultParametersManager} is created. 124 */ 125 public Parameters() { 126 this(null); 127 } 128 129 /** 130 * Creates a new instance of {@code Parameters} and initializes it with the given {@code DefaultParametersManager}. 131 * Because {@code DefaultParametersManager} is thread-safe, it makes sense to share a single instance between multiple 132 * {@code Parameters} objects; that way the same initialization is performed on newly created parameters objects. 133 * 134 * @param manager the {@code DefaultParametersHandler} (may be <b>null</b>, then a new default instance is created) 135 */ 136 public Parameters(final DefaultParametersManager manager) { 137 defaultParametersManager = manager != null ? manager : new DefaultParametersManager(); 138 } 139 140 /** 141 * Creates a new instance of a parameters object for basic configuration properties. 142 * 143 * @return the new parameters object 144 */ 145 public BasicBuilderParameters basic() { 146 return new BasicBuilderParameters(); 147 } 148 149 /** 150 * Creates a new instance of a parameters object for combined configuration builder properties. 151 * 152 * @return the new parameters object 153 */ 154 public CombinedBuilderParameters combined() { 155 return createParametersProxy(new CombinedBuilderParametersImpl(), CombinedBuilderParameters.class); 156 } 157 158 /** 159 * Creates a proxy object for a given parameters interface based on the given implementation object. The newly created 160 * object is initialized with default values if there are matching {@link DefaultParametersHandler} objects. 161 * 162 * @param <T> the type of the parameters interface 163 * @param target the implementing target object 164 * @param ifcClass the interface class 165 * @param superIfcs an array with additional interface classes to be implemented 166 * @return the proxy object 167 */ 168 private <T> T createParametersProxy(final Object target, final Class<T> ifcClass, final Class<?>... superIfcs) { 169 final Class<?>[] ifcClasses = new Class<?>[1 + superIfcs.length]; 170 ifcClasses[0] = ifcClass; 171 System.arraycopy(superIfcs, 0, ifcClasses, 1, superIfcs.length); 172 final Object obj = Proxy.newProxyInstance(Parameters.class.getClassLoader(), ifcClasses, new ParametersIfcInvocationHandler(target)); 173 getDefaultParametersManager().initializeParameters((BuilderParameters) obj); 174 return ifcClass.cast(obj); 175 } 176 177 /** 178 * Creates a new instance of a parameters object for database configurations. 179 * 180 * @return the new parameters object 181 */ 182 public DatabaseBuilderParameters database() { 183 return createParametersProxy(new DatabaseBuilderParametersImpl(), DatabaseBuilderParameters.class); 184 } 185 186 /** 187 * Creates a new instance of a parameters object for file-based configuration properties. 188 * 189 * @return the new parameters object 190 */ 191 public FileBasedBuilderParameters fileBased() { 192 return createParametersProxy(new FileBasedBuilderParametersImpl(), FileBasedBuilderParameters.class); 193 } 194 195 /** 196 * Gets the {@code DefaultParametersManager} associated with this object. 197 * 198 * @return the {@code DefaultParametersManager} 199 */ 200 public DefaultParametersManager getDefaultParametersManager() { 201 return defaultParametersManager; 202 } 203 204 /** 205 * Creates a new instance of a parameters object for hierarchical configurations. 206 * 207 * @return the new parameters object 208 */ 209 public HierarchicalBuilderParameters hierarchical() { 210 return createParametersProxy(new HierarchicalBuilderParametersImpl(), HierarchicalBuilderParameters.class, FileBasedBuilderParameters.class); 211 } 212 213 /** 214 * Creates a new instance of a parameters object for INI configurations. 215 * 216 * @return the new parameters object 217 */ 218 public INIBuilderParameters ini() { 219 return createParametersProxy(new INIBuilderParametersImpl(), INIBuilderParameters.class, FileBasedBuilderParameters.class, 220 HierarchicalBuilderParameters.class); 221 } 222 223 /** 224 * Creates a new instance of a parameters object for JNDI configurations. 225 * 226 * @return the new parameters object 227 */ 228 public JndiBuilderParameters jndi() { 229 return createParametersProxy(new JndiBuilderParametersImpl(), JndiBuilderParameters.class); 230 } 231 232 /** 233 * Creates a new instance of a parameters object for a builder for multiple file-based configurations. 234 * 235 * @return the new parameters object 236 */ 237 public MultiFileBuilderParameters multiFile() { 238 return createParametersProxy(new MultiFileBuilderParametersImpl(), MultiFileBuilderParameters.class); 239 } 240 241 /** 242 * Creates a new instance of a parameters object for properties configurations. 243 * 244 * @return the new parameters object 245 */ 246 public PropertiesBuilderParameters properties() { 247 return createParametersProxy(new PropertiesBuilderParametersImpl(), PropertiesBuilderParameters.class, FileBasedBuilderParameters.class); 248 } 249 250 /** 251 * Registers the specified {@code DefaultParametersHandler} object for the given parameters class. This is a convenience 252 * method which just delegates to the associated {@code DefaultParametersManager}. 253 * 254 * @param <T> the type of the parameters supported by this handler 255 * @param paramsClass the parameters class supported by this handler (must not be <b>null</b>) 256 * @param handler the {@code DefaultParametersHandler} to be registered (must not be <b>null</b>) 257 * @throws IllegalArgumentException if a required parameter is missing 258 * @see DefaultParametersManager 259 */ 260 public <T> void registerDefaultsHandler(final Class<T> paramsClass, final DefaultParametersHandler<? super T> handler) { 261 getDefaultParametersManager().registerDefaultsHandler(paramsClass, handler); 262 } 263 264 /** 265 * Registers the specified {@code DefaultParametersHandler} object for the given parameters class and start class in the 266 * inheritance hierarchy. This is a convenience method which just delegates to the associated 267 * {@code DefaultParametersManager}. 268 * 269 * @param <T> the type of the parameters supported by this handler 270 * @param paramsClass the parameters class supported by this handler (must not be <b>null</b>) 271 * @param handler the {@code DefaultParametersHandler} to be registered (must not be <b>null</b>) 272 * @param startClass an optional start class in the hierarchy of parameter objects for which this handler should be 273 * applied 274 * @throws IllegalArgumentException if a required parameter is missing 275 */ 276 public <T> void registerDefaultsHandler(final Class<T> paramsClass, final DefaultParametersHandler<? super T> handler, final Class<?> startClass) { 277 getDefaultParametersManager().registerDefaultsHandler(paramsClass, handler, startClass); 278 } 279 280 /** 281 * Creates a new instance of a parameters object for XML configurations. 282 * 283 * @return the new parameters object 284 */ 285 public XMLBuilderParameters xml() { 286 return createParametersProxy(new XMLBuilderParametersImpl(), XMLBuilderParameters.class, FileBasedBuilderParameters.class, 287 HierarchicalBuilderParameters.class); 288 } 289}