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     */
017    package org.apache.commons.configuration.beanutils;
018    
019    import static org.junit.Assert.assertEquals;
020    import static org.junit.Assert.assertNotNull;
021    import static org.junit.Assert.assertNull;
022    import static org.junit.Assert.assertSame;
023    import static org.junit.Assert.assertTrue;
024    
025    import java.util.HashMap;
026    import java.util.Map;
027    
028    import org.apache.commons.configuration.ConfigurationRuntimeException;
029    import org.junit.After;
030    import org.junit.Before;
031    import org.junit.Test;
032    
033    /**
034     * Test class for BeanHelper.
035     *
036     * @since 1.3
037     * @author <a
038     * href="http://commons.apache.org/configuration/team-list.html">Commons
039     * Configuration team</a>
040     * @version $Id: TestBeanHelper.java 1225344 2011-12-28 21:28:23Z oheger $
041     */
042    public class TestBeanHelper
043    {
044        /** Constant for the name of the test bean factory. */
045        private static final String TEST_FACTORY = "testFactory";
046    
047        /**
048         * Stores the default bean factory. Because this is a static field in
049         * BeanHelper it is temporarily stored and reset after the tests.
050         */
051        private BeanFactory tempDefaultBeanFactory;
052    
053        @Before
054        public void setUp() throws Exception
055        {
056            tempDefaultBeanFactory = BeanHelper.getDefaultBeanFactory();
057            deregisterFactories();
058        }
059    
060        @After
061        public void tearDown() throws Exception
062        {
063            deregisterFactories();
064    
065            // Reset old default bean factory
066            BeanHelper.setDefaultBeanFactory(tempDefaultBeanFactory);
067        }
068    
069        /**
070         * Removes all bean factories that might have been registered during a test.
071         */
072        private void deregisterFactories()
073        {
074            for (String name : BeanHelper.registeredFactoryNames())
075            {
076                BeanHelper.deregisterBeanFactory(name);
077            }
078            assertTrue("Remaining registered bean factories", BeanHelper
079                    .registeredFactoryNames().isEmpty());
080        }
081    
082        /**
083         * Tests registering a new bean factory.
084         */
085        @Test
086        public void testRegisterBeanFactory()
087        {
088            assertTrue("List of registered factories is not empty", BeanHelper
089                    .registeredFactoryNames().isEmpty());
090            BeanHelper.registerBeanFactory(TEST_FACTORY, new TestBeanFactory());
091            assertEquals("Wrong number of registered factories", 1, BeanHelper
092                    .registeredFactoryNames().size());
093            assertTrue("Test factory is not contained", BeanHelper
094                    .registeredFactoryNames().contains(TEST_FACTORY));
095        }
096    
097        /**
098         * Tries to register a null factory. This should cause an exception.
099         */
100        @Test(expected = IllegalArgumentException.class)
101        public void testRegisterBeanFactoryNull()
102        {
103            BeanHelper.registerBeanFactory(TEST_FACTORY, null);
104        }
105    
106        /**
107         * Tries to register a bean factory with a null name. This should cause an
108         * exception.
109         */
110        @Test(expected = IllegalArgumentException.class)
111        public void testRegisterBeanFactoryNullName()
112        {
113            BeanHelper.registerBeanFactory(null, new TestBeanFactory());
114        }
115    
116        /**
117         * Tests to deregister a bean factory.
118         */
119        @Test
120        public void testDeregisterBeanFactory()
121        {
122            assertNull("deregistering non existing factory", BeanHelper
123                    .deregisterBeanFactory(TEST_FACTORY));
124            assertNull("deregistering null factory", BeanHelper
125                    .deregisterBeanFactory(null));
126            BeanFactory factory = new TestBeanFactory();
127            BeanHelper.registerBeanFactory(TEST_FACTORY, factory);
128            assertSame("Could not deregister factory", factory, BeanHelper
129                    .deregisterBeanFactory(TEST_FACTORY));
130            assertTrue("List of factories is not empty", BeanHelper
131                    .registeredFactoryNames().isEmpty());
132        }
133    
134        /**
135         * Tests whether the default bean factory is correctly initialized.
136         */
137        @Test
138        public void testGetDefaultBeanFactory()
139        {
140            assertSame("Incorrect default bean factory",
141                    DefaultBeanFactory.INSTANCE, tempDefaultBeanFactory);
142        }
143    
144        /**
145         * Tests setting the default bean factory to null. This should caus an
146         * exception.
147         */
148        @Test(expected = IllegalArgumentException.class)
149        public void testSetDefaultBeanFactoryNull()
150        {
151            BeanHelper.setDefaultBeanFactory(null);
152        }
153    
154        /**
155         * Tests initializing a bean.
156         */
157        @Test
158        public void testInitBean()
159        {
160            BeanHelper.setDefaultBeanFactory(new TestBeanFactory());
161            TestBeanDeclaration data = setUpBeanDeclaration();
162            TestBean bean = new TestBean();
163            BeanHelper.initBean(bean, data);
164            checkBean(bean);
165        }
166    
167        /**
168         * Tests initializing a bean when the bean declaration does not contain any
169         * data.
170         */
171        @Test
172        public void testInitBeanWithNoData()
173        {
174            TestBeanDeclaration data = new TestBeanDeclaration();
175            TestBean bean = new TestBean();
176            BeanHelper.initBean(bean, data);
177            assertNull("Wrong string property", bean.getStringValue());
178            assertEquals("Wrong int property", 0, bean.getIntValue());
179            assertNull("Buddy was set", bean.getBuddy());
180        }
181    
182        /**
183         * Tries to initialize a bean with a bean declaration that contains an
184         * invalid property value. This should cause an exception.
185         */
186        @Test(expected = ConfigurationRuntimeException.class)
187        public void testInitBeanWithInvalidProperty()
188        {
189            TestBeanDeclaration data = setUpBeanDeclaration();
190            data.getBeanProperties().put("nonExistingProperty", Boolean.TRUE);
191            BeanHelper.initBean(new TestBean(), data);
192        }
193    
194        /**
195         * Tests creating a bean. All necessary information is stored in the bean
196         * declaration.
197         */
198        @Test
199        public void testCreateBean()
200        {
201            TestBeanFactory factory = new TestBeanFactory();
202            BeanHelper.registerBeanFactory(TEST_FACTORY, factory);
203            TestBeanDeclaration data = setUpBeanDeclaration();
204            data.setBeanFactoryName(TEST_FACTORY);
205            data.setBeanClassName(TestBean.class.getName());
206            checkBean((TestBean) BeanHelper.createBean(data, null));
207            assertNull("A parameter was passed", factory.parameter);
208        }
209    
210        /**
211         * Tests creating a bean when no bean declaration is provided. This should
212         * cause an exception.
213         */
214        @Test(expected = IllegalArgumentException.class)
215        public void testCreateBeanWithNullDeclaration()
216        {
217            BeanHelper.createBean(null);
218        }
219    
220        /**
221         * Tests creating a bean. The bean's class is specified as the default class
222         * argument.
223         */
224        @Test
225        public void testCreateBeanWithDefaultClass()
226        {
227            BeanHelper.registerBeanFactory(TEST_FACTORY, new TestBeanFactory());
228            TestBeanDeclaration data = setUpBeanDeclaration();
229            data.setBeanFactoryName(TEST_FACTORY);
230            checkBean((TestBean) BeanHelper.createBean(data, TestBean.class));
231        }
232    
233        /**
234         * Tests creating a bean when the bean's class is specified as the default
235         * class of the bean factory.
236         */
237        @Test
238        public void testCreateBeanWithFactoryDefaultClass()
239        {
240            TestBeanFactory factory = new TestBeanFactory();
241            factory.supportsDefaultClass = true;
242            BeanHelper.registerBeanFactory(TEST_FACTORY, factory);
243            TestBeanDeclaration data = setUpBeanDeclaration();
244            data.setBeanFactoryName(TEST_FACTORY);
245            checkBean((TestBean) BeanHelper.createBean(data, null));
246        }
247    
248        /**
249         * Tries to create a bean when no class is provided. This should cause an
250         * exception.
251         */
252        @Test(expected = ConfigurationRuntimeException.class)
253        public void testCreateBeanWithNoClass()
254        {
255            BeanHelper.registerBeanFactory(TEST_FACTORY, new TestBeanFactory());
256            TestBeanDeclaration data = setUpBeanDeclaration();
257            data.setBeanFactoryName(TEST_FACTORY);
258            BeanHelper.createBean(data, null);
259        }
260    
261        /**
262         * Tries to create a bean with a non existing class. This should cause an
263         * exception.
264         */
265        @Test(expected = ConfigurationRuntimeException.class)
266        public void testCreateBeanWithInvalidClass()
267        {
268            BeanHelper.registerBeanFactory(TEST_FACTORY, new TestBeanFactory());
269            TestBeanDeclaration data = setUpBeanDeclaration();
270            data.setBeanFactoryName(TEST_FACTORY);
271            data.setBeanClassName("non.existing.ClassName");
272            BeanHelper.createBean(data, null);
273        }
274    
275        /**
276         * Tests creating a bean using the default bean factory.
277         */
278        @Test
279        public void testCreateBeanWithDefaultFactory()
280        {
281            BeanHelper.setDefaultBeanFactory(new TestBeanFactory());
282            TestBeanDeclaration data = setUpBeanDeclaration();
283            data.setBeanClassName(TestBean.class.getName());
284            checkBean((TestBean) BeanHelper.createBean(data, null));
285        }
286    
287        /**
288         * Tests creating a bean using a non registered factory.
289         */
290        @Test(expected = ConfigurationRuntimeException.class)
291        public void testCreateBeanWithUnknownFactory()
292        {
293            TestBeanDeclaration data = setUpBeanDeclaration();
294            data.setBeanFactoryName(TEST_FACTORY);
295            data.setBeanClassName(TestBean.class.getName());
296            BeanHelper.createBean(data, null);
297        }
298    
299        /**
300         * Tests creating a bean when the factory throws an exception.
301         */
302        @Test(expected = ConfigurationRuntimeException.class)
303        public void testCreateBeanWithException()
304        {
305            BeanHelper.registerBeanFactory(TEST_FACTORY, new TestBeanFactory());
306            TestBeanDeclaration data = setUpBeanDeclaration();
307            data.setBeanFactoryName(TEST_FACTORY);
308            data.setBeanClassName(getClass().getName());
309            BeanHelper.createBean(data, null);
310        }
311    
312        /**
313         * Tests if a parameter is correctly passed to the bean factory.
314         */
315        @Test
316        public void testCreateBeanWithParameter()
317        {
318            Object param = new Integer(42);
319            TestBeanFactory factory = new TestBeanFactory();
320            BeanHelper.registerBeanFactory(TEST_FACTORY, factory);
321            TestBeanDeclaration data = setUpBeanDeclaration();
322            data.setBeanFactoryName(TEST_FACTORY);
323            data.setBeanClassName(TestBean.class.getName());
324            checkBean((TestBean) BeanHelper.createBean(data, null, param));
325            assertSame("Wrong parameter", param, factory.parameter);
326        }
327    
328        /**
329         * Returns an initialized bean declaration.
330         *
331         * @return the bean declaration
332         */
333        private TestBeanDeclaration setUpBeanDeclaration()
334        {
335            TestBeanDeclaration data = new TestBeanDeclaration();
336            Map<String, Object> properties = new HashMap<String, Object>();
337            properties.put("stringValue", "testString");
338            properties.put("intValue", "42");
339            data.setBeanProperties(properties);
340            TestBeanDeclaration buddyData = new TestBeanDeclaration();
341            Map<String, Object> properties2 = new HashMap<String, Object>();
342            properties2.put("stringValue", "Another test string");
343            properties2.put("intValue", new Integer(100));
344            buddyData.setBeanProperties(properties2);
345            buddyData.setBeanClassName(TestBean.class.getName());
346            if (BeanHelper.getDefaultBeanFactory() == null)
347            {
348                buddyData.setBeanFactoryName(TEST_FACTORY);
349            }
350            Map<String, Object> nested = new HashMap<String, Object>();
351            nested.put("buddy", buddyData);
352            data.setNestedBeanDeclarations(nested);
353            return data;
354        }
355    
356        /**
357         * Tests if the bean was correctly initialized from the data of the test
358         * bean declaration.
359         *
360         * @param bean the bean to be checked
361         */
362        private void checkBean(TestBean bean)
363        {
364            assertEquals("Wrong string property", "testString", bean
365                    .getStringValue());
366            assertEquals("Wrong int property", 42, bean.getIntValue());
367            TestBean buddy = bean.getBuddy();
368            assertNotNull("Buddy was not set", buddy);
369            assertEquals("Wrong string property in buddy", "Another test string",
370                    buddy.getStringValue());
371            assertEquals("Wrong int property in buddy", 100, buddy.getIntValue());
372        }
373    
374        /**
375         * A simple bean class used for testing creation operations.
376         */
377        public static class TestBean
378        {
379            private String stringValue;
380    
381            private int intValue;
382    
383            private TestBean buddy;
384    
385            public TestBean getBuddy()
386            {
387                return buddy;
388            }
389    
390            public void setBuddy(TestBean buddy)
391            {
392                this.buddy = buddy;
393            }
394    
395            public int getIntValue()
396            {
397                return intValue;
398            }
399    
400            public void setIntValue(int intValue)
401            {
402                this.intValue = intValue;
403            }
404    
405            public String getStringValue()
406            {
407                return stringValue;
408            }
409    
410            public void setStringValue(String stringValue)
411            {
412                this.stringValue = stringValue;
413            }
414        }
415    
416        /**
417         * An implementation of the BeanFactory interface used for testing. This
418         * implementation is really simple: If the TestBean class is provided, a new
419         * instance will be created. Otherwise an exception is thrown.
420         */
421        static class TestBeanFactory implements BeanFactory
422        {
423            Object parameter;
424    
425            boolean supportsDefaultClass;
426    
427            public Object createBean(Class<?> beanClass, BeanDeclaration data, Object param)
428                    throws Exception
429            {
430                parameter = param;
431                if (TestBean.class.equals(beanClass))
432                {
433                    TestBean bean = new TestBean();
434                    BeanHelper.initBean(bean, data);
435                    return bean;
436                }
437                else
438                {
439                    throw new IllegalArgumentException("Unsupported class: "
440                            + beanClass);
441                }
442            }
443    
444            /**
445             * Returns the default class, but only if the supportsDefaultClass flag
446             * is set.
447             */
448            public Class<?> getDefaultBeanClass()
449            {
450                return supportsDefaultClass ? TestBean.class : null;
451            }
452        }
453    
454        /**
455         * A test implementation of the BeanDeclaration interface. This
456         * implementation allows to set the values directly, which should be
457         * returned by the methods required by the BeanDeclaration interface.
458         */
459        static class TestBeanDeclaration implements BeanDeclaration
460        {
461            private String beanClassName;
462    
463            private String beanFactoryName;
464    
465            private Object beanFactoryParameter;
466    
467            private Map<String, Object> beanProperties;
468    
469            private Map<String, Object> nestedBeanDeclarations;
470    
471            public String getBeanClassName()
472            {
473                return beanClassName;
474            }
475    
476            public void setBeanClassName(String beanClassName)
477            {
478                this.beanClassName = beanClassName;
479            }
480    
481            public String getBeanFactoryName()
482            {
483                return beanFactoryName;
484            }
485    
486            public void setBeanFactoryName(String beanFactoryName)
487            {
488                this.beanFactoryName = beanFactoryName;
489            }
490    
491            public Object getBeanFactoryParameter()
492            {
493                return beanFactoryParameter;
494            }
495    
496            public void setBeanFactoryParameter(Object beanFactoryParameter)
497            {
498                this.beanFactoryParameter = beanFactoryParameter;
499            }
500    
501            public Map<String, Object> getBeanProperties()
502            {
503                return beanProperties;
504            }
505    
506            public void setBeanProperties(Map<String, Object> beanProperties)
507            {
508                this.beanProperties = beanProperties;
509            }
510    
511            public Map<String, Object> getNestedBeanDeclarations()
512            {
513                return nestedBeanDeclarations;
514            }
515    
516            public void setNestedBeanDeclarations(Map<String, Object> nestedBeanDeclarations)
517            {
518                this.nestedBeanDeclarations = nestedBeanDeclarations;
519            }
520        }
521    }