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.beanutils; 018 019import java.lang.reflect.Constructor; 020import java.util.Collection; 021import java.util.Collections; 022import java.util.LinkedList; 023import java.util.List; 024 025import org.apache.commons.configuration2.convert.ConversionHandler; 026import org.apache.commons.configuration2.convert.DefaultConversionHandler; 027import org.apache.commons.configuration2.ex.ConfigurationRuntimeException; 028 029/** 030 * <p> 031 * The default implementation of the {@code BeanFactory} interface. 032 * </p> 033 * <p> 034 * This class creates beans of arbitrary types using reflection. Each time the {@code createBean()} method is invoked, a 035 * new bean instance is created. A default bean class is not supported. 036 * </p> 037 * <p> 038 * For data type conversions (which may be needed before invoking methods through reflection to ensure that the current 039 * parameters match their declared types) a {@link ConversionHandler} object is used. An instance of this class can be 040 * passed to the constructor. Alternatively, a default {@code ConversionHandler} instance is used. 041 * </p> 042 * <p> 043 * An instance of this factory class will be set as the default bean factory for the {@link BeanHelper} class. This 044 * means that if not bean factory is specified in a {@link BeanDeclaration}, this default instance will be used. 045 * </p> 046 * 047 * @since 1.3 048 */ 049public class DefaultBeanFactory implements BeanFactory { 050 051 /** Stores the default instance of this class. */ 052 public static final DefaultBeanFactory INSTANCE = new DefaultBeanFactory(); 053 054 /** A format string for generating error messages for constructor matching. */ 055 private static final String FMT_CTOR_ERROR = "%s! Bean class = %s, constructor arguments = %s"; 056 057 /** 058 * Checks whether exactly one matching constructor was found. Throws a meaningful exception if there 059 * is not a single matching constructor. 060 * 061 * @param beanClass the bean class 062 * @param data the bean declaration 063 * @param matchingConstructors the list with matching constructors 064 * @throws ConfigurationRuntimeException if there is not exactly one match 065 */ 066 private static <T> void checkSingleMatchingConstructor(final Class<T> beanClass, final BeanDeclaration data, 067 final List<Constructor<T>> matchingConstructors) { 068 if (matchingConstructors.isEmpty()) { 069 throw constructorMatchingException(beanClass, data, "No matching constructor found"); 070 } 071 if (matchingConstructors.size() > 1) { 072 throw constructorMatchingException(beanClass, data, "Multiple matching constructors found"); 073 } 074 } 075 076 /** 077 * Constructs an exception if no single matching constructor was found with a meaningful error message. 078 * 079 * @param beanClass the affected bean class 080 * @param data the bean declaration 081 * @param msg an error message 082 * @return the exception with the error message 083 */ 084 private static ConfigurationRuntimeException constructorMatchingException(final Class<?> beanClass, final BeanDeclaration data, final String msg) { 085 return new ConfigurationRuntimeException(FMT_CTOR_ERROR, msg, beanClass.getName(), getConstructorArgs(data).toString()); 086 } 087 088 /** 089 * Evaluates constructor arguments in the specified {@code BeanDeclaration} and tries to find a unique matching 090 * constructor. If this is not possible, an exception is thrown. Note: This method is intended to be used by concrete 091 * {@link BeanFactory} implementations and not by client code. 092 * 093 * @param beanClass the class of the bean to be created 094 * @param data the current {@code BeanDeclaration} 095 * @param <T> the type of the bean to be created 096 * @return the single matching constructor 097 * @throws ConfigurationRuntimeException if no single matching constructor can be found 098 * @throws NullPointerException if the bean class or bean declaration are <b>null</b> 099 */ 100 protected static <T> Constructor<T> findMatchingConstructor(final Class<T> beanClass, final BeanDeclaration data) { 101 final List<Constructor<T>> matchingConstructors = findMatchingConstructors(beanClass, data); 102 checkSingleMatchingConstructor(beanClass, data, matchingConstructors); 103 return matchingConstructors.get(0); 104 } 105 106 /** 107 * Returns a list with all constructors which are compatible with the constructor arguments specified by the given 108 * {@code BeanDeclaration}. 109 * 110 * @param beanClass the bean class to be instantiated 111 * @param data the current {@code BeanDeclaration} 112 * @return a list with all matching constructors 113 */ 114 private static <T> List<Constructor<T>> findMatchingConstructors(final Class<T> beanClass, final BeanDeclaration data) { 115 final List<Constructor<T>> result = new LinkedList<>(); 116 final Collection<ConstructorArg> args = getConstructorArgs(data); 117 for (final Constructor<?> ctor : beanClass.getConstructors()) { 118 if (matchesConstructor(ctor, args)) { 119 // cast should be okay according to the Javadocs of 120 // getConstructors() 121 @SuppressWarnings("unchecked") 122 final Constructor<T> match = (Constructor<T>) ctor; 123 result.add(match); 124 } 125 } 126 return result; 127 } 128 129 /** 130 * Gets constructor arguments from a bean declaration. Deals with <b>null</b> values. 131 * 132 * @param data the bean declaration 133 * @return the collection with constructor arguments (never <b>null</b>) 134 */ 135 private static Collection<ConstructorArg> getConstructorArgs(final BeanDeclaration data) { 136 Collection<ConstructorArg> args = data.getConstructorArgs(); 137 if (args == null) { 138 args = Collections.emptySet(); 139 } 140 return args; 141 } 142 143 /** 144 * Checks whether the given constructor is compatible with the given list of arguments. 145 * 146 * @param ctor the constructor to be checked 147 * @param args the collection of constructor arguments 148 * @return a flag whether this constructor is compatible with the given arguments 149 */ 150 private static boolean matchesConstructor(final Constructor<?> ctor, final Collection<ConstructorArg> args) { 151 final Class<?>[] types = ctor.getParameterTypes(); 152 if (types.length != args.size()) { 153 return false; 154 } 155 156 int idx = 0; 157 for (final ConstructorArg arg : args) { 158 if (!arg.matches(types[idx++])) { 159 return false; 160 } 161 } 162 163 return true; 164 } 165 166 /** 167 * Fetches constructor arguments from the given bean declaration. Handles <b>null</b> values safely. 168 * 169 * @param data the bean declaration 170 * @return the collection with constructor arguments (never <b>null</b>) 171 */ 172 private static Collection<ConstructorArg> nullSafeConstructorArgs(final BeanDeclaration data) { 173 Collection<ConstructorArg> args = data.getConstructorArgs(); 174 if (args == null) { 175 args = Collections.emptySet(); 176 } 177 return args; 178 } 179 180 /** The conversion handler used by this instance. */ 181 private final ConversionHandler conversionHandler; 182 183 /** 184 * Constructs a new instance of {@code DefaultBeanFactory} using a default {@code ConversionHandler}. 185 */ 186 public DefaultBeanFactory() { 187 this(null); 188 } 189 190 /** 191 * Constructs a new instance of {@code DefaultBeanFactory} using the specified {@code ConversionHandler} for data type 192 * conversions. 193 * 194 * @param convHandler the {@code ConversionHandler}; can be <b>null</b>, then a default handler is used 195 * @since 2.0 196 */ 197 public DefaultBeanFactory(final ConversionHandler convHandler) { 198 conversionHandler = convHandler != null ? convHandler : DefaultConversionHandler.INSTANCE; 199 } 200 201 /** 202 * Creates a new bean instance. This implementation delegates to the protected methods {@code createBeanInstance()} and 203 * {@code initBeanInstance()} for creating and initializing the bean. This makes it easier for derived classes that need 204 * to change specific functionality of the base class. 205 * 206 * @param bcc the context object defining the bean to be created 207 * @return the new bean instance 208 * @throws Exception if an error occurs 209 */ 210 @Override 211 public Object createBean(final BeanCreationContext bcc) throws Exception { 212 final Object result = createBeanInstance(bcc); 213 initBeanInstance(result, bcc); 214 return result; 215 } 216 217 /** 218 * Creates the bean instance. This method is called by {@code createBean()}. It uses reflection to create a new instance 219 * of the specified class. 220 * 221 * @param bcc the context object defining the bean to be created 222 * @return the new bean instance 223 * @throws Exception if an error occurs 224 */ 225 protected Object createBeanInstance(final BeanCreationContext bcc) throws Exception { 226 final Constructor<?> ctor = findMatchingConstructor(bcc.getBeanClass(), bcc.getBeanDeclaration()); 227 final Object[] args = fetchConstructorArgs(ctor, bcc); 228 return ctor.newInstance(args); 229 } 230 231 /** 232 * Obtains the arguments for a constructor call to create a bean. This method resolves nested bean declarations and 233 * performs necessary type conversions. 234 * 235 * @param ctor the constructor to be invoked 236 * @param bcc the context object defining the bean to be created 237 * @return an array with constructor arguments 238 */ 239 private Object[] fetchConstructorArgs(final Constructor<?> ctor, final BeanCreationContext bcc) { 240 final Class<?>[] types = ctor.getParameterTypes(); 241 assert types.length == nullSafeConstructorArgs(bcc.getBeanDeclaration()).size() : "Wrong number of constructor arguments!"; 242 final Object[] args = new Object[types.length]; 243 int idx = 0; 244 245 for (final ConstructorArg arg : nullSafeConstructorArgs(bcc.getBeanDeclaration())) { 246 final Object val = arg.isNestedBeanDeclaration() ? bcc.createBean(arg.getBeanDeclaration()) : arg.getValue(); 247 args[idx] = getConversionHandler().to(val, types[idx], null); 248 idx++; 249 } 250 251 return args; 252 } 253 254 /** 255 * Gets the {@code ConversionHandler} used by this object. 256 * 257 * @return the {@code ConversionHandler} 258 * @since 2.0 259 */ 260 public ConversionHandler getConversionHandler() { 261 return conversionHandler; 262 } 263 264 /** 265 * Gets the default bean class used by this factory. This is always <b>null</b> for this implementation. 266 * 267 * @return the default bean class 268 */ 269 @Override 270 public Class<?> getDefaultBeanClass() { 271 return null; 272 } 273 274 /** 275 * Initializes the newly created bean instance. This method is called by {@code createBean()}. It calls the 276 * {@code initBean()} method of the context object for performing the initialization. 277 * 278 * @param bean the newly created bean instance 279 * @param bcc the context object defining the bean to be created 280 * @throws Exception if an error occurs 281 */ 282 protected void initBeanInstance(final Object bean, final BeanCreationContext bcc) throws Exception { 283 bcc.initBean(bean, bcc.getBeanDeclaration()); 284 } 285}