##### Coverage Report - org.apache.commons.configuration.beanutils.BeanHelper

Classes in this File | Line Coverage | Branch Coverage | Complexity
BeanHelper |
90% | 60/67
94% | 15/16
| 3.3333333333333335;3,333

1 |  |
     /*
2 |  |
      * Licensed to the Apache Software Foundation (ASF) under one or more
3 |  |
      * contributor license agreements.  See the NOTICE file distributed with
4 |  |
      * this work for additional information regarding copyright ownership.
5 |  |
      * The ASF licenses this file to You under the Apache License, Version 2.0
6 |  |
      * (the "License"); you may not use this file except in compliance with
7 |  |
      * the License.  You may obtain a copy of the License at
8 |  |
      *
9 |  |
      *     http://www.apache.org/licenses/LICENSE-2.0
10 |  |
      *
11 |  |
      * Unless required by applicable law or agreed to in writing, software
12 |  |
      * distributed under the License is distributed on an "AS IS" BASIS,
13 |  |
      * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 |  |
      * See the License for the specific language governing permissions and
15 |  |
      * limitations under the License.
16 |  |
      */
17 |  |
     package org.apache.commons.configuration.beanutils;
18 |  |
19 |  |
     import java.lang.reflect.InvocationTargetException;
20 |  |
     import java.util.HashMap;
21 |  |
     import java.util.Iterator;
22 |  |
     import java.util.Map;
23 |  |
     import java.util.Set;
24 |  |
25 |  |
     import org.apache.commons.beanutils.BeanUtils;
26 |  |
     import org.apache.commons.beanutils.PropertyUtils;
27 |  |
     import org.apache.commons.configuration.ConfigurationRuntimeException;
28 |  |
29 |  |
     /**
30 |  |
      * <p>
31 |  |
      * A helper class for creating bean instances that are defined in configuration
32 |  |
      * files.
33 |  |
      * </p>
34 |  |
      * <p>
35 |  |
      * This class provides static utility methods related to bean creation
36 |  |
      * operations. These methods simplify such operations because a client need not
37 |  |
      * deal with all involved interfaces. Usually, if a bean declaration has already
38 |  |
      * been obtained, a single method call is necessary to create a new bean
39 |  |
      * instance.
40 |  |
      * </p>
41 |  |
      * <p>
42 |  |
      * This class also supports the registration of custom bean factories.
43 |  |
      * Implementations of the <code>{@link BeanFactory}</code> interface can be
44 |  |
      * registered under a symbolic name using the <code>registerBeanFactory()</code>
45 |  |
      * method. In the configuration file the name of the bean factory can be
46 |  |
      * specified in the bean declaration. Then this factory will be used to create
47 |  |
      * the bean.
48 |  |
      * </p>
49 |  |
      *
50 |  |
      * @since 1.3
51 |  |
      * @author Oliver Heger
52 |  |
      * @version $Id: BeanHelper.java 439648 2006-09-02 20:42:10Z oheger $
53 |  |
      */
54 |  |
     public class BeanHelper
55 |  |
     {
56 |  |
         /** Stores a map with the registered bean factories. */
57 | 8 |
         private static Map beanFactories = new HashMap();
58 |  |
59 |  |
         /**
60 |  |
          * Stores the default bean factory, which will be used if no other factory
61 |  |
          * is provided.
62 |  |
          */
63 | 4 |
         private static BeanFactory defaultBeanFactory = DefaultBeanFactory.INSTANCE;
64 |  |
65 |  |
         /**
66 |  |
          * Private constructor, so no instances can be created.
67 |  |
          */
68 |  |
         private BeanHelper()
69 | 0 |
         {
70 | 0 |
         }
71 |  |
72 |  |
         /**
73 |  |
          * Register a bean factory under a symbolic name. This factory object can
74 |  |
          * then be specified in bean declarations with the effect that this factory
75 |  |
          * will be used to obtain an instance for the corresponding bean
76 |  |
          * declaration.
77 |  |
          *
78 |  |
          * @param name the name of the factory
79 |  |
          * @param factory the factory to be registered
80 |  |
          */
81 |  |
         public static void registerBeanFactory(String name, BeanFactory factory)
82 |  |
         {
83 | 13 |
             if (name == null)
84 |  |
             {
85 | 1 |
                 throw new IllegalArgumentException(
86 |  |
                         "Name for bean factory must not be null!");
87 |  |
             }
88 | 12 |
             if (factory == null)
89 |  |
             {
90 | 1 |
                 throw new IllegalArgumentException("Bean factory must not be null!");
91 |  |
             }
92 |  |
93 | 11 |
             beanFactories.put(name, factory);
94 | 11 |
         }
95 |  |
96 |  |
         /**
97 |  |
          * Deregisters the bean factory with the given name. After that this factory
98 |  |
          * cannot be used any longer.
99 |  |
          *
100 |  |
          * @param name the name of the factory to be deregistered
101 |  |
          * @return the factory that was registered under this name; <b>null</b> if
102 |  |
          * there was no such factory
103 |  |
          */
104 |  |
         public static BeanFactory deregisterBeanFactory(String name)
105 |  |
         {
106 | 11 |
             return (BeanFactory) beanFactories.remove(name);
107 |  |
         }
108 |  |
109 |  |
         /**
110 |  |
          * Returns a set with the names of all currently registered bean factories.
111 |  |
          *
112 |  |
          * @return a set with the names of the registered bean factories
113 |  |
          */
114 |  |
         public static Set registeredFactoryNames()
115 |  |
         {
116 | 42 |
             return beanFactories.keySet();
117 |  |
         }
118 |  |
119 |  |
         /**
120 |  |
          * Returns the default bean factory.
121 |  |
          *
122 |  |
          * @return the default bean factory
123 |  |
          */
124 |  |
         public static BeanFactory getDefaultBeanFactory()
125 |  |
         {
126 | 66 |
             return defaultBeanFactory;
127 |  |
         }
128 |  |
129 |  |
         /**
130 |  |
          * Sets the default bean factory. This factory will be used for all create
131 |  |
          * operations, for which no special factory is provided in the bean
132 |  |
          * declaration.
133 |  |
          *
134 |  |
          * @param factory the default bean factory (must not be <b>null</b>)
135 |  |
          */
136 |  |
         public static void setDefaultBeanFactory(BeanFactory factory)
137 |  |
         {
138 | 22 |
             if (factory == null)
139 |  |
             {
140 | 1 |
                 throw new IllegalArgumentException(
141 |  |
                         "Default bean factory must not be null!");
142 |  |
             }
143 | 21 |
             defaultBeanFactory = factory;
144 | 21 |
         }
145 |  |
146 |  |
         /**
147 |  |
          * Initializes the passed in bean. This method will obtain all the bean's
148 |  |
          * properties that are defined in the passed in bean declaration. These
149 |  |
          * properties will be set on the bean. If necessary, further beans will be
150 |  |
          * created recursively.
151 |  |
          *
152 |  |
          * @param bean the bean to be initialized
153 |  |
          * @param data the bean declaration
154 |  |
          * @throws ConfigurationRuntimeException if a property cannot be set
155 |  |
          */
156 |  |
         public static void initBean(Object bean, BeanDeclaration data)
157 |  |
                 throws ConfigurationRuntimeException
158 |  |
         {
159 | 102 |
             Map properties = data.getBeanProperties();
160 | 102 |
             if (properties != null)
161 |  |
             {
162 | 299 |
                 for (Iterator it = properties.keySet().iterator(); it.hasNext();)
163 |  |
                 {
164 | 98 |
                     String propName = (String) it.next();
165 | 98 |
                     initProperty(bean, propName, properties.get(propName));
166 |  |
                 }
167 |  |
             }
168 |  |
169 | 101 |
             Map nestedBeans = data.getNestedBeanDeclarations();
170 | 101 |
             if (nestedBeans != null)
171 |  |
             {
172 | 204 |
                 for (Iterator it = nestedBeans.keySet().iterator(); it.hasNext();)
173 |  |
                 {
174 | 18 |
                     String propName = (String) it.next();
175 | 18 |
                     initProperty(bean, propName, createBean(
176 |  |
                             (BeanDeclaration) nestedBeans.get(propName), null));
177 |  |
                 }
178 |  |
             }
179 | 101 |
         }
180 |  |
181 |  |
         /**
182 |  |
          * Sets a property on the given bean using Common Beanutils.
183 |  |
          *
184 |  |
          * @param bean the bean
185 |  |
          * @param propName the name of the property
186 |  |
          * @param value the property's value
187 |  |
          * @throws ConfigurationRuntimeException if the property is not writeable or
188 |  |
          * an error occurred
189 |  |
          */
190 |  |
         private static void initProperty(Object bean, String propName, Object value)
191 |  |
                 throws ConfigurationRuntimeException
192 |  |
         {
193 | 116 |
             if (!PropertyUtils.isWriteable(bean, propName))
194 |  |
             {
195 | 1 |
                 throw new ConfigurationRuntimeException("Property " + propName
196 |  |
                         + " cannot be set!");
197 |  |
             }
198 |  |
199 |  |
             try
200 |  |
             {
201 | 115 |
                 BeanUtils.setProperty(bean, propName, value);
202 | 115 |
             }
203 |  |
             catch (IllegalAccessException iaex)
204 |  |
             {
205 | 0 |
                 throw new ConfigurationRuntimeException(iaex);
206 |  |
             }
207 |  |
             catch (InvocationTargetException itex)
208 |  |
             {
209 | 0 |
                 throw new ConfigurationRuntimeException(itex);
210 |  |
             }
211 | 115 |
         }
212 |  |
213 |  |
         /**
214 |  |
          * The main method for creating and initializing beans from a configuration.
215 |  |
          * This method will return an initialized instance of the bean class
216 |  |
          * specified in the passed in bean declaration. If this declaration does not
217 |  |
          * contain the class of the bean, the passed in default class will be used.
218 |  |
          * From the bean declaration the factory to be used for creating the bean is
219 |  |
          * queried. The declaration may here return <b>null</b>, then a default
220 |  |
          * factory is used. This factory is then invoked to perform the create
221 |  |
          * operation.
222 |  |
          *
223 |  |
          * @param data the bean declaration
224 |  |
          * @param defaultClass the default class to use
225 |  |
          * @param param an additional parameter that will be passed to the bean
226 |  |
          * factory; some factories may support parameters and behave different
227 |  |
          * depending on the value passed in here
228 |  |
          * @return the new bean
229 |  |
          * @throws ConfigurationRuntimeException if an error occurs
230 |  |
          */
231 |  |
         public static Object createBean(BeanDeclaration data, Class defaultClass,
232 |  |
                 Object param) throws ConfigurationRuntimeException
233 |  |
         {
234 | 104 |
             if (data == null)
235 |  |
             {
236 | 1 |
                 throw new IllegalArgumentException(
237 |  |
                         "Bean declaration must not be null!");
238 |  |
             }
239 |  |
240 | 103 |
             BeanFactory factory = fetchBeanFactory(data);
241 |  |
             try
242 |  |
             {
243 | 102 |
                 return factory.createBean(fetchBeanClass(data, defaultClass,
244 |  |
                         factory), data, param);
245 |  |
             }
246 |  |
             catch (Exception ex)
247 |  |
             {
248 | 5 |
                 throw new ConfigurationRuntimeException(ex);
249 |  |
             }
250 |  |
         }
251 |  |
252 |  |
         /**
253 |  |
          * Returns a bean instance for the specified declaration. This method is a
254 |  |
          * short cut for <code>createBean(data, null, null);</code>.
255 |  |
          *
256 |  |
          * @param data the bean declaration
257 |  |
          * @param defaultClass the class to be used when in the declation no class
258 |  |
          * is specified
259 |  |
          * @return the new bean
260 |  |
          * @throws ConfigurationRuntimeException if an error occurs
261 |  |
          */
262 |  |
         public static Object createBean(BeanDeclaration data, Class defaultClass)
263 |  |
                 throws ConfigurationRuntimeException
264 |  |
         {
265 | 103 |
             return createBean(data, defaultClass, null);
266 |  |
         }
267 |  |
268 |  |
         /**
269 |  |
          * Returns a bean instance for the specified declaration. This method is a
270 |  |
          * short cut for <code>createBean(data, null);</code>.
271 |  |
          *
272 |  |
          * @param data the bean declaration
273 |  |
          * @return the new bean
274 |  |
          * @throws ConfigurationRuntimeException if an error occurs
275 |  |
          */
276 |  |
         public static Object createBean(BeanDeclaration data)
277 |  |
                 throws ConfigurationRuntimeException
278 |  |
         {
279 | 60 |
             return createBean(data, null);
280 |  |
         }
281 |  |
282 |  |
         /**
283 |  |
          * Returns a <code>java.lang.Class</code> object for the specified name.
284 |  |
          * This method and the helper method it invokes are very similar to code
285 |  |
          * extracted from the <code>ClassLoaderUtils</code> class of Commons
286 |  |
          * Jelly. It should be replaced if Commons Lang provides a generic version.
287 |  |
          *
288 |  |
          * @param name the name of the class to be loaded
289 |  |
          * @param callingClass the calling class
290 |  |
          * @return the class object for the specified name
291 |  |
          * @throws ClassNotFoundException if the class cannot be loaded
292 |  |
          */
293 |  |
         static Class loadClass(String name, Class callingClass)
294 |  |
                 throws ClassNotFoundException
295 |  |
         {
296 | 23 |
             ClassLoader loader = findClassLoader(callingClass);
297 | 23 |
             return Class.forName(name, true, loader);
298 |  |
         }
299 |  |
300 |  |
         /**
301 |  |
          * Determines which class loader should be used in the context of the given
302 |  |
          * class.
303 |  |
          *
304 |  |
          * @param callingClass the calling class
305 |  |
          * @return the class loader to be used
306 |  |
          */
307 |  |
         private static ClassLoader findClassLoader(Class callingClass)
308 |  |
         {
309 | 23 |
             ClassLoader loader = Thread.currentThread().getContextClassLoader();
310 | 23 |
             if (loader == null)
311 |  |
             {
312 | 0 |
                 loader = callingClass.getClassLoader();
313 | 0 |
                 if (loader == null)
314 |  |
                 {
315 | 0 |
                     loader = ClassLoader.getSystemClassLoader();
316 |  |
                 }
317 |  |
             }
318 | 23 |
             return loader;
319 |  |
         }
320 |  |
321 |  |
         /**
322 |  |
          * Determines the class of the bean to be created. If the bean declaration
323 |  |
          * contains a class name, this class is used. Otherwise it is checked
324 |  |
          * whether a default class is provided. If this is not the case, the
325 |  |
          * factory's default class is used. If this class is undefined, too, an
326 |  |
          * exception is thrown.
327 |  |
          *
328 |  |
          * @param data the bean declaration
329 |  |
          * @param defaultClass the default class
330 |  |
          * @param factory the bean factory to use
331 |  |
          * @return the class of the bean to be created
332 |  |
          * @throws ConfigurationRuntimeException if the class cannot be determined
333 |  |
          */
334 |  |
         private static Class fetchBeanClass(BeanDeclaration data,
335 |  |
                 Class defaultClass, BeanFactory factory)
336 |  |
                 throws ConfigurationRuntimeException
337 |  |
         {
338 | 102 |
             String clsName = data.getBeanClassName();
339 | 102 |
             if (clsName != null)
340 |  |
             {
341 |  |
                 try
342 |  |
                 {
343 | 23 |
                     return loadClass(clsName, factory.getClass());
344 |  |
                 }
345 |  |
                 catch (ClassNotFoundException cex)
346 |  |
                 {
347 | 1 |
                     throw new ConfigurationRuntimeException(cex);
348 |  |
                 }
349 |  |
             }
350 |  |
351 | 79 |
             if (defaultClass != null)
352 |  |
             {
353 | 18 |
                 return defaultClass;
354 |  |
             }
355 |  |
356 | 61 |
             Class clazz = factory.getDefaultBeanClass();
357 | 61 |
             if (clazz == null)
358 |  |
             {
359 | 1 |
                 throw new ConfigurationRuntimeException(
360 |  |
                         "Bean class is not specified!");
361 |  |
             }
362 | 60 |
             return clazz;
363 |  |
         }
364 |  |
365 |  |
         /**
366 |  |
          * Obtains the bean factory to use for creating the specified bean. This
367 |  |
          * method will check whether a factory is specified in the bean declaration.
368 |  |
          * If this is not the case, the default bean factory will be used.
369 |  |
          *
370 |  |
          * @param data the bean declaration
371 |  |
          * @return the bean factory to use
372 |  |
          * @throws ConfigurationRuntimeException if the factory cannot be determined
373 |  |
          */
374 |  |
         private static BeanFactory fetchBeanFactory(BeanDeclaration data)
375 |  |
                 throws ConfigurationRuntimeException
376 |  |
         {
377 | 103 |
             String factoryName = data.getBeanFactoryName();
378 | 103 |
             if (factoryName != null)
379 |  |
             {
380 | 67 |
                 BeanFactory factory = (BeanFactory) beanFactories.get(factoryName);
381 | 67 |
                 if (factory == null)
382 |  |
                 {
383 | 1 |
                     throw new ConfigurationRuntimeException(
384 |  |
                             "Unknown bean factory: " + factoryName);
385 |  |
                 }
386 |  |
                 else
387 |  |
                 {
388 | 66 |
                     return factory;
389 |  |
                 }
390 |  |
             }
391 |  |
             else
392 |  |
             {
393 | 36 |
                 return getDefaultBeanFactory();
394 |  |
             }
395 |  |
         }
396 |  |
     }