You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shiro.apache.org by lh...@apache.org on 2012/01/07 04:36:58 UTC

svn commit: r1228564 - in /shiro/trunk/core/src: main/java/org/apache/shiro/config/ test/groovy/org/apache/shiro/config/ test/java/org/apache/shiro/config/

Author: lhazlewood
Date: Sat Jan  7 03:36:57 2012
New Revision: 1228564

URL: http://svn.apache.org/viewvc?rev=1228564&view=rev
Log:
SHIRO-305: INI config now supports setting map/array referenced values, e.g. bean.aMapProperty[mapKey] = mapValue

Added:
    shiro/trunk/core/src/test/groovy/org/apache/shiro/config/
    shiro/trunk/core/src/test/groovy/org/apache/shiro/config/ReflectionBuilderTest.groovy
Removed:
    shiro/trunk/core/src/test/java/org/apache/shiro/config/ReflectionBuilderTest.java
Modified:
    shiro/trunk/core/src/main/java/org/apache/shiro/config/ReflectionBuilder.java
    shiro/trunk/core/src/test/java/org/apache/shiro/config/CompositeBean.java
    shiro/trunk/core/src/test/java/org/apache/shiro/config/SimpleBean.java

Modified: shiro/trunk/core/src/main/java/org/apache/shiro/config/ReflectionBuilder.java
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/main/java/org/apache/shiro/config/ReflectionBuilder.java?rev=1228564&r1=1228563&r2=1228564&view=diff
==============================================================================
--- shiro/trunk/core/src/main/java/org/apache/shiro/config/ReflectionBuilder.java (original)
+++ shiro/trunk/core/src/main/java/org/apache/shiro/config/ReflectionBuilder.java Sat Jan  7 03:36:57 2012
@@ -54,6 +54,8 @@ public class ReflectionBuilder {
     private static final String NULL_VALUE_TOKEN = "null";
     private static final String EMPTY_STRING_VALUE_TOKEN = "\"\"";
     private static final char STRING_VALUE_DELIMETER = '"';
+    private static final char MAP_PROPERTY_BEGIN_TOKEN = '[';
+    private static final char MAP_PROPERTY_END_TOKEN = ']';
 
     private Map<String, ?> objects;
 
@@ -361,6 +363,119 @@ public class ReflectionBuilder {
             return stringValue;
         }
     }
+    
+    protected void applyProperty(Object object, String propertyPath, Object value) {
+
+        int mapBegin = propertyPath.indexOf(MAP_PROPERTY_BEGIN_TOKEN);
+        int mapEnd = -1;
+        String mapPropertyPath = null;
+        String keyString = null;
+
+        String remaining = null;
+        
+        if (mapBegin >= 0) {
+            //a map is being referenced in the overall property path.  Find just the map's path:
+            mapPropertyPath = propertyPath.substring(0, mapBegin);
+            //find the end of the map reference:
+            mapEnd = propertyPath.indexOf(MAP_PROPERTY_END_TOKEN, mapBegin);
+            //find the token in between the [ and the ] (the map/array key or index):
+            keyString = propertyPath.substring(mapBegin+1, mapEnd);
+
+            //find out if there is more path reference to follow.  If not, we're at a terminal of the OGNL expression
+            if (propertyPath.length() > (mapEnd+1)) {
+                remaining = propertyPath.substring(mapEnd+1);
+                if (remaining.startsWith(".")) {
+                    remaining = StringUtils.clean(remaining.substring(1));
+                }
+            }
+        }
+        
+        if (remaining == null) {
+            //we've terminated the OGNL expression.  Check to see if we're assigning a property or a map entry:
+            if (keyString == null) {
+                //not a map or array value assignment - assign the property directly:
+                setProperty(object, propertyPath, value);
+            } else {
+                //we're assigning a map or array entry.  Check to see which we should call:
+                if (isTypedProperty(object, mapPropertyPath, Map.class)) {
+                    Map map = (Map)getProperty(object, mapPropertyPath);
+                    Object mapKey = resolveValue(keyString);
+                    //noinspection unchecked
+                    map.put(mapKey, value);
+                } else {
+                    //must be an array property.  Convert the key string to an index:
+                    int index = Integer.valueOf(keyString);
+                    setIndexedProperty(object, mapPropertyPath, index, value);
+                }
+            }
+        } else {
+            //property is being referenced as part of a nested path.  Find the referenced map/array entry and
+            //recursively call this method with the remaining property path
+            Object referencedValue = null;
+            if (isTypedProperty(object, mapPropertyPath, Map.class)) {
+                Map map = (Map)getProperty(object, mapPropertyPath);
+                Object mapKey = resolveValue(keyString);
+                referencedValue = map.get(mapKey);
+            } else {
+                //must be an array property:
+                int index = Integer.valueOf(keyString);
+                referencedValue = getIndexedProperty(object, mapPropertyPath, index);
+            }
+
+            if (referencedValue == null) {
+                throw new ConfigurationException("Referenced map/array value '" + mapPropertyPath + "[" +
+                keyString + "]' does not exist.");
+            }
+
+            applyProperty(referencedValue, remaining, value);
+        }
+    }
+    
+    private void setProperty(Object object, String propertyPath, Object value) {
+        try {
+            if (log.isTraceEnabled()) {
+                log.trace("Applying property [{}] value [{}] on object of type [{}]",
+                        new Object[]{propertyPath, value, object.getClass().getName()});
+            }
+            BeanUtils.setProperty(object, propertyPath, value);
+        } catch (Exception e) {
+            String msg = "Unable to set property '" + propertyPath + "' with value [" + value + "] on object " +
+                    "of type " + (object != null ? object.getClass().getName() : null) + ".  If " +
+                    "'" + value + "' is a reference to another (previously defined) object, prefix it with " +
+                    "'" + OBJECT_REFERENCE_BEGIN_TOKEN + "' to indicate that the referenced " +
+                    "object should be used as the actual value.  " +
+                    "For example, " + OBJECT_REFERENCE_BEGIN_TOKEN + value;
+            throw new ConfigurationException(msg, e);
+        }
+    }
+    
+    private Object getProperty(Object object, String propertyPath) {
+        try {
+            return PropertyUtils.getProperty(object, propertyPath);
+        } catch (Exception e) {
+            throw new ConfigurationException("Unable to access property '" + propertyPath + "'", e);
+        }
+    }
+    
+    private void setIndexedProperty(Object object, String propertyPath, int index, Object value) {
+        try {
+            PropertyUtils.setIndexedProperty(object, propertyPath, index, value);
+        } catch (Exception e) {
+            throw new ConfigurationException("Unable to set array property '" + propertyPath + "'", e);
+        }
+    }
+    
+    private Object getIndexedProperty(Object object, String propertyPath, int index) {
+        try {
+            return PropertyUtils.getIndexedProperty(object, propertyPath, index);
+        } catch (Exception e) {
+            throw new ConfigurationException("Unable to acquire array property '" + propertyPath + "'", e);
+        }
+    }
+    
+    protected boolean isIndexedPropertyAssignment(String propertyPath) {
+        return propertyPath.endsWith("" + MAP_PROPERTY_END_TOKEN);
+    }
 
     protected void applyProperty(Object object, String propertyName, String stringValue) {
 
@@ -370,6 +485,9 @@ public class ReflectionBuilder {
             value = null;
         } else if (EMPTY_STRING_VALUE_TOKEN.equals(stringValue)) {
             value = StringUtils.EMPTY_STRING;
+        } else if (isIndexedPropertyAssignment(propertyName)) {
+            String checked = checkForNullOrEmptyLiteral(stringValue);
+            value = resolveValue(checked);
         } else if (isTypedProperty(object, propertyName, Set.class)) {
             value = toSet(stringValue);
         } else if (isTypedProperty(object, propertyName, Map.class)) {
@@ -387,21 +505,7 @@ public class ReflectionBuilder {
             value = resolveValue(checked);
         }
 
-        try {
-            if (log.isTraceEnabled()) {
-                log.trace("Applying property [{}] value [{}] on object of type [{}]",
-                        new Object[]{propertyName, value, object.getClass().getName()});
-            }
-            BeanUtils.setProperty(object, propertyName, value);
-        } catch (Exception e) {
-            String msg = "Unable to set property '" + propertyName + "' with value [" + stringValue + "] on object " +
-                    "of type " + (object != null ? object.getClass().getName() : null) + ".  If " +
-                    "'" + stringValue + "' is a reference to another (previously defined) object, prefix it with " +
-                    "'" + OBJECT_REFERENCE_BEGIN_TOKEN + "' to indicate that the referenced " +
-                    "object should be used as the actual value.  " +
-                    "For example, " + OBJECT_REFERENCE_BEGIN_TOKEN + stringValue;
-            throw new ConfigurationException(msg, e);
-        }
+        applyProperty(object, propertyName, value);
     }
 
 }

Added: shiro/trunk/core/src/test/groovy/org/apache/shiro/config/ReflectionBuilderTest.groovy
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/test/groovy/org/apache/shiro/config/ReflectionBuilderTest.groovy?rev=1228564&view=auto
==============================================================================
--- shiro/trunk/core/src/test/groovy/org/apache/shiro/config/ReflectionBuilderTest.groovy (added)
+++ shiro/trunk/core/src/test/groovy/org/apache/shiro/config/ReflectionBuilderTest.groovy Sat Jan  7 03:36:57 2012
@@ -0,0 +1,392 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.config
+
+import org.apache.shiro.codec.Base64
+import org.apache.shiro.codec.CodecSupport
+import org.apache.shiro.codec.Hex
+import org.apache.shiro.util.CollectionUtils
+
+/**
+ * Unit tests for the {@link ReflectionBuilder} implementation.
+ */
+class ReflectionBuilderTest extends GroovyTestCase {
+
+    void testStandardPropertyAssignment() {
+        ReflectionBuilder builder = new ReflectionBuilder();
+
+        CompositeBean cBean = new CompositeBean();
+        builder.applyProperty(cBean, 'stringProp', 'hello world')
+        builder.applyProperty(cBean, 'booleanProp', true)
+        builder.applyProperty(cBean, 'intProp', 42)
+        builder.applyProperty(cBean, 'simpleBean', new SimpleBean())
+        
+        assertTrue cBean.stringProp == 'hello world'
+        assertTrue cBean.booleanProp
+        assertTrue cBean.intProp == 42
+        assertTrue cBean.simpleBean instanceof SimpleBean
+    }
+    
+    void testMapEntryAssignment() {
+        ReflectionBuilder builder = new ReflectionBuilder();
+
+        CompositeBean cBean = new CompositeBean();
+        cBean.simpleBeanMap = ['simpleBean1': new SimpleBean()]
+        
+        builder.applyProperty(cBean, 'simpleBeanMap[simpleBean2]', new SimpleBean())
+        
+        assertTrue cBean.simpleBeanMap['simpleBean2'] instanceof SimpleBean
+    }
+
+    void testArrayEntryAssignment() {
+        ReflectionBuilder builder = new ReflectionBuilder();
+
+        CompositeBean cBean = new CompositeBean();
+        cBean.compositeBeanArray = new CompositeBean[1];
+
+        builder.applyProperty(cBean, 'compositeBeanArray[0]', new CompositeBean())
+
+        assertTrue cBean.compositeBeanArray[0] instanceof CompositeBean
+    }
+
+    void testNestedPathAssignment() {
+        ReflectionBuilder builder = new ReflectionBuilder();
+
+        CompositeBean cbean1 = new CompositeBean('cbean1');
+        cbean1.compositeBeanMap = ['cbean2': new CompositeBean('cbean2')]
+        cbean1.compositeBeanMap['cbean2'].compositeBeanArray = new CompositeBean[2];
+        
+        builder.applyProperty(cbean1, "compositeBeanMap[cbean2].compositeBeanArray[0]", new CompositeBean('cbean3'))
+        builder.applyProperty(cbean1, "compositeBeanMap[cbean2].compositeBeanArray[0].simpleBean", new SimpleBean('sbean1'))
+
+        assertTrue cbean1.compositeBeanMap['cbean2'].compositeBeanArray[0].name == 'cbean3'
+        assertTrue cbean1.compositeBeanMap['cbean2'].compositeBeanArray[0].simpleBean.name == 'sbean1'
+    }
+
+    //asserts SHIRO-305: https://issues.apache.org/jira/browse/SHIRO-305
+    void testNestedMapAssignmentWithPeriodDelimitedKeys() {
+        def ini = new Ini()
+        ini.load('''
+            ldapRealm = org.apache.shiro.realm.ldap.JndiLdapRealm
+            ldapRealm.contextFactory.environment[java.naming.security.protocol] = ssl
+            ldapRealm.contextFactory.environment[com.sun.jndi.ldap.connect.pool.protocol] = plain ssl 
+            ldapRealm.contextFactory.environment[com.sun.jndi.ldap.connect.pool] = true 
+        ''')
+        def builder = new ReflectionBuilder()
+        def objects = builder.buildObjects(ini.getSections().iterator().next())
+        
+        assertFalse objects.isEmpty()
+        assertEquals 'ssl', objects['ldapRealm'].contextFactory.environment['java.naming.security.protocol']
+    }
+
+    void testSimpleConfig() {
+        Map<String, String> defs = new LinkedHashMap<String, String>();
+        defs.put("compositeBean", "org.apache.shiro.config.CompositeBean");
+        defs.put("compositeBean.stringProp", "blah");
+        defs.put("compositeBean.booleanProp", "true");
+        defs.put("compositeBean.intProp", "42");
+
+        ReflectionBuilder builder = new ReflectionBuilder();
+        Map beans = builder.buildObjects(defs);
+
+        CompositeBean compositeBean = (CompositeBean) beans.get("compositeBean");
+        assertNotNull(compositeBean);
+        assertEquals(compositeBean.getStringProp(), "blah");
+        assertTrue(compositeBean.isBooleanProp());
+        assertEquals(compositeBean.getIntProp(), 42);
+    }
+
+    void testWithConfiguredNullValue() {
+        Map<String,Object> defaults = new LinkedHashMap<String,Object>();
+        CompositeBean cBean = new CompositeBean();
+        cBean.setSimpleBean(new SimpleBean());
+        defaults.put("compositeBean", cBean);
+
+        Map<String, String> defs = new LinkedHashMap<String, String>();
+        defs.put("compositeBean.intProp", "42");
+        defs.put("compositeBean.booleanProp", "true");
+        defs.put("compositeBean.stringProp", "test");
+        defs.put("compositeBean.simpleBean", "null");
+
+        ReflectionBuilder builder = new ReflectionBuilder(defaults);
+        Map beans = builder.buildObjects(defs);
+
+        CompositeBean compositeBean = (CompositeBean) beans.get("compositeBean");
+        assertNotNull(compositeBean);
+        assertTrue(compositeBean.isBooleanProp());
+        assertEquals(compositeBean.getIntProp(), 42);
+        assertEquals("test", compositeBean.getStringProp());
+        assertNull(compositeBean.getSimpleBean());
+    }
+
+    void testWithConfiguredNullLiteralValue() {
+        Map<String, String> defs = new LinkedHashMap<String, String>();
+        defs.put("compositeBean", "org.apache.shiro.config.CompositeBean");
+        defs.put("compositeBean.intProp", "42");
+        defs.put("compositeBean.booleanProp", "true");
+        defs.put("compositeBean.stringProp", "\"null\"");
+
+        ReflectionBuilder builder = new ReflectionBuilder();
+        Map beans = builder.buildObjects(defs);
+
+        CompositeBean compositeBean = (CompositeBean) beans.get("compositeBean");
+        assertNotNull(compositeBean);
+        assertTrue(compositeBean.isBooleanProp());
+        assertEquals(compositeBean.getIntProp(), 42);
+        assertEquals("null", compositeBean.getStringProp());
+    }
+
+    void testWithConfiguredEmptyStringValue() {
+        Map<String, String> defs = new LinkedHashMap<String, String>();
+        defs.put("compositeBean", "org.apache.shiro.config.CompositeBean");
+        defs.put("compositeBean.intProp", "42");
+        defs.put("compositeBean.booleanProp", "true");
+        defs.put("compositeBean.stringProp", "\"\"");
+
+        ReflectionBuilder builder = new ReflectionBuilder();
+        Map beans = builder.buildObjects(defs);
+
+        CompositeBean compositeBean = (CompositeBean) beans.get("compositeBean");
+        assertNotNull(compositeBean);
+        assertTrue(compositeBean.isBooleanProp());
+        assertEquals(compositeBean.getIntProp(), 42);
+        assertEquals("", compositeBean.getStringProp());
+    }
+
+    void testWithConfiguredEmptyStringLiteralValue() {
+        Map<String, String> defs = new LinkedHashMap<String, String>();
+        defs.put("compositeBean", "org.apache.shiro.config.CompositeBean");
+        defs.put("compositeBean.intProp", "42");
+        defs.put("compositeBean.booleanProp", "true");
+        defs.put("compositeBean.stringProp", "\"\"\"\"");
+
+        ReflectionBuilder builder = new ReflectionBuilder();
+        Map beans = builder.buildObjects(defs);
+
+        CompositeBean compositeBean = (CompositeBean) beans.get("compositeBean");
+        assertNotNull(compositeBean);
+        assertTrue(compositeBean.isBooleanProp());
+        assertEquals(compositeBean.getIntProp(), 42);
+        assertEquals("\"\"", compositeBean.getStringProp());
+    }
+
+    void testSimpleConfigWithDollarSignStringValue() {
+        Map<String, String> defs = new LinkedHashMap<String, String>();
+        defs.put("compositeBean", "org.apache.shiro.config.CompositeBean");
+        defs.put("compositeBean.stringProp", '\\$500');
+
+        ReflectionBuilder builder = new ReflectionBuilder();
+        Map beans = builder.buildObjects(defs);
+
+        CompositeBean compositeBean = (CompositeBean) beans.get("compositeBean");
+        assertEquals(compositeBean.getStringProp(), '$500');
+    }
+
+    void testObjectReferenceConfig() {
+        Map<String, String> defs = new LinkedHashMap<String, String>();
+        defs.put("simpleBean", "org.apache.shiro.config.SimpleBean");
+        defs.put("simpleBean.intProp", "101");
+        defs.put("compositeBean", "org.apache.shiro.config.CompositeBean");
+        defs.put("compositeBean.stringProp", "blah");
+        defs.put("compositeBean.simpleBean", '$simpleBean');
+
+        ReflectionBuilder builder = new ReflectionBuilder();
+        Map beans = builder.buildObjects(defs);
+
+        CompositeBean compositeBean = (CompositeBean) beans.get("compositeBean");
+        assertNotNull(compositeBean);
+        assertEquals(compositeBean.getStringProp(), "blah");
+        SimpleBean simpleBean = (SimpleBean) beans.get("simpleBean");
+        assertNotNull(simpleBean);
+        assertNotNull(compositeBean.getSimpleBean());
+        assertEquals(simpleBean, compositeBean.getSimpleBean());
+        assertEquals(simpleBean.getIntProp(), 101);
+    }
+
+    void testObjectReferenceConfigWithTypeMismatch() {
+        Map<String, String> defs = new LinkedHashMap<String, String>();
+        defs.put("simpleBean", "org.apache.shiro.config.SimpleBean");
+        defs.put("compositeBean", "org.apache.shiro.config.CompositeBean");
+        defs.put("compositeBean.simpleBean", "simpleBean");
+        ReflectionBuilder builder = new ReflectionBuilder();
+        try {
+            builder.buildObjects(defs);
+            "Should have encountered an " + ConfigurationException.class.name
+        } catch (ConfigurationException expected) {
+        }
+    }
+
+    void testObjectReferenceConfigWithInvalidReference() {
+        Map<String, String> defs = new LinkedHashMap<String, String>();
+        defs.put("simpleBean", "org.apache.shiro.config.SimpleBean");
+        defs.put("compositeBean", "org.apache.shiro.config.CompositeBean");
+        defs.put("compositeBean.simpleBean", '$foo');
+        ReflectionBuilder builder = new ReflectionBuilder();
+        try {
+            builder.buildObjects(defs);
+            fail "should have encountered an " + UnresolveableReferenceException.class.name
+        } catch (UnresolveableReferenceException expected) {
+        }
+    }
+
+    void testSetProperty() {
+        Map<String, String> defs = new LinkedHashMap<String, String>();
+        defs.put("simpleBean1", "org.apache.shiro.config.SimpleBean");
+        defs.put("simpleBean2", "org.apache.shiro.config.SimpleBean");
+        defs.put("compositeBean", "org.apache.shiro.config.CompositeBean");
+        defs.put("compositeBean.simpleBeanSet", '$simpleBean1, $simpleBean2, $simpleBean2');
+        ReflectionBuilder builder = new ReflectionBuilder();
+        Map objects = builder.buildObjects(defs);
+        assertFalse(CollectionUtils.isEmpty(objects));
+        CompositeBean cBean = (CompositeBean) objects.get("compositeBean");
+        assertNotNull(cBean);
+        Set<SimpleBean> simpleBeans = cBean.getSimpleBeanSet();
+        assertNotNull(simpleBeans);
+        assertEquals(2, simpleBeans.size());
+    }
+
+    void testListProperty() {
+        Map<String, String> defs = new LinkedHashMap<String, String>();
+        defs.put("simpleBean1", "org.apache.shiro.config.SimpleBean");
+        defs.put("simpleBean2", "org.apache.shiro.config.SimpleBean");
+        defs.put("compositeBean", "org.apache.shiro.config.CompositeBean");
+        defs.put("compositeBean.simpleBeanList", '$simpleBean1, $simpleBean2, $simpleBean2');
+        ReflectionBuilder builder = new ReflectionBuilder();
+        Map objects = builder.buildObjects(defs);
+        assertFalse(CollectionUtils.isEmpty(objects));
+        CompositeBean cBean = (CompositeBean) objects.get("compositeBean");
+        assertNotNull(cBean);
+        List<SimpleBean> simpleBeans = cBean.getSimpleBeanList();
+        assertNotNull(simpleBeans);
+        assertEquals(3, simpleBeans.size());
+    }
+
+    void testCollectionProperty() {
+        Map<String, String> defs = new LinkedHashMap<String, String>();
+        defs.put("simpleBean1", "org.apache.shiro.config.SimpleBean");
+        defs.put("simpleBean2", "org.apache.shiro.config.SimpleBean");
+        defs.put("compositeBean", "org.apache.shiro.config.CompositeBean");
+        defs.put("compositeBean.simpleBeanCollection", '$simpleBean1, $simpleBean2, $simpleBean2');
+        ReflectionBuilder builder = new ReflectionBuilder();
+        Map objects = builder.buildObjects(defs);
+        assertFalse(CollectionUtils.isEmpty(objects));
+        CompositeBean cBean = (CompositeBean) objects.get("compositeBean");
+        assertNotNull(cBean);
+        Collection<SimpleBean> simpleBeans = cBean.getSimpleBeanCollection();
+        assertNotNull(simpleBeans);
+        assertTrue(simpleBeans instanceof List);
+        assertEquals(3, simpleBeans.size());
+    }
+
+    void testByteArrayHexProperty() {
+        String source = "Hello, world.";
+        byte[] bytes = CodecSupport.toBytes(source);
+        String hex = Hex.encodeToString(bytes);
+        String hexValue = "0x" + hex;
+
+        Map<String, String> defs = new LinkedHashMap<String, String>();
+        defs.put("simpleBean", "org.apache.shiro.config.SimpleBean");
+        defs.put("simpleBean.byteArrayProp", hexValue);
+        ReflectionBuilder builder = new ReflectionBuilder();
+        Map objects = builder.buildObjects(defs);
+        assertFalse(CollectionUtils.isEmpty(objects));
+        SimpleBean bean = (SimpleBean) objects.get("simpleBean");
+        assertNotNull(bean);
+        byte[] beanBytes = bean.getByteArrayProp();
+        assertNotNull(beanBytes);
+        String reconstituted = CodecSupport.toString(beanBytes);
+        assertEquals(source, reconstituted);
+    }
+
+    void testByteArrayBase64Property() {
+        String source = "Hello, world.";
+        byte[] bytes = CodecSupport.toBytes(source);
+        String base64 = Base64.encodeToString(bytes);
+
+        Map<String, String> defs = new LinkedHashMap<String, String>();
+        defs.put("simpleBean", "org.apache.shiro.config.SimpleBean");
+        defs.put("simpleBean.byteArrayProp", base64);
+        ReflectionBuilder builder = new ReflectionBuilder();
+        Map objects = builder.buildObjects(defs);
+        SimpleBean bean = (SimpleBean) objects.get("simpleBean");
+        byte[] beanBytes = bean.getByteArrayProp();
+        assertNotNull(beanBytes);
+        assertTrue(Arrays.equals(beanBytes, bytes));
+        String reconstituted = CodecSupport.toString(beanBytes);
+        assertEquals(reconstituted, source);
+    }
+
+    void testMapProperty() {
+        Map<String, String> defs = new LinkedHashMap<String, String>();
+        defs.put("simpleBean1", "org.apache.shiro.config.SimpleBean");
+        defs.put("simpleBean2", "org.apache.shiro.config.SimpleBean");
+        defs.put("compositeBean", "org.apache.shiro.config.CompositeBean");
+        defs.put("compositeBean.simpleBeanMap", 'simpleBean1:$simpleBean1, simpleBean2:$simpleBean2');
+        ReflectionBuilder builder = new ReflectionBuilder();
+        Map objects = builder.buildObjects(defs);
+        assertFalse(CollectionUtils.isEmpty(objects));
+        CompositeBean cBean = (CompositeBean) objects.get("compositeBean");
+        assertNotNull(cBean);
+        Map map = cBean.getSimpleBeanMap();
+        assertNotNull(map);
+        assertEquals(2, map.size());
+        Object value = map.get("simpleBean1");
+        assertTrue(value instanceof SimpleBean);
+        value = map.get("simpleBean2");
+        assertTrue(value instanceof SimpleBean);
+    }
+
+    void testNestedListProperty() {
+        Map<String, String> defs = new LinkedHashMap<String, String>();
+        defs.put("simpleBean1", "org.apache.shiro.config.SimpleBean");
+        defs.put("simpleBean2", "org.apache.shiro.config.SimpleBean");
+        defs.put("simpleBean3", "org.apache.shiro.config.SimpleBean");
+        defs.put("compositeBean", "org.apache.shiro.config.CompositeBean");
+        defs.put("compositeBean.simpleBean", '$simpleBean1');
+        defs.put("compositeBean.simpleBean.simpleBeans", '$simpleBean2, $simpleBean3');
+        ReflectionBuilder builder = new ReflectionBuilder();
+        Map objects = builder.buildObjects(defs);
+        assertFalse(CollectionUtils.isEmpty(objects));
+        CompositeBean cBean = (CompositeBean) objects.get("compositeBean");
+        assertNotNull(cBean);
+        SimpleBean nested = cBean.getSimpleBean();
+        assertNotNull(nested);
+        List<SimpleBean> children = nested.getSimpleBeans();
+        assertNotNull(children);
+        assertEquals(2, children.size());
+    }
+
+    void testFactoryInstantiation() {
+        Map<String, String> defs = new LinkedHashMap<String, String>();
+        defs.put("simpleBeanFactory", "org.apache.shiro.config.SimpleBeanFactory");
+        defs.put("simpleBeanFactory.factoryInt", "5");
+        defs.put("simpleBeanFactory.factoryString", "someString");
+        defs.put("compositeBean", "org.apache.shiro.config.CompositeBean");
+        defs.put("compositeBean.simpleBean", '$simpleBeanFactory');
+
+        ReflectionBuilder builder = new ReflectionBuilder();
+        Map objects = builder.buildObjects(defs);
+        assertFalse(CollectionUtils.isEmpty(objects));
+        CompositeBean compositeBean = (CompositeBean) objects.get("compositeBean");
+        SimpleBean bean = compositeBean.getSimpleBean();
+        assertNotNull(bean);
+        assertEquals(5, bean.getIntProp());
+        assertEquals("someString", bean.getStringProp());
+    }
+}

Modified: shiro/trunk/core/src/test/java/org/apache/shiro/config/CompositeBean.java
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/test/java/org/apache/shiro/config/CompositeBean.java?rev=1228564&r1=1228563&r2=1228564&view=diff
==============================================================================
--- shiro/trunk/core/src/test/java/org/apache/shiro/config/CompositeBean.java (original)
+++ shiro/trunk/core/src/test/java/org/apache/shiro/config/CompositeBean.java Sat Jan  7 03:36:57 2012
@@ -28,6 +28,8 @@ import java.util.Set;
  */
 @SuppressWarnings({"UnusedDeclaration"})
 public class CompositeBean {
+    
+    private String name;
 
     private String stringProp;
     private boolean booleanProp;
@@ -38,9 +40,23 @@ public class CompositeBean {
     private List<SimpleBean> simpleBeanList;
     private Collection<SimpleBean> simpleBeanCollection;
     private Map<String, SimpleBean> simpleBeanMap;
+    private Map<String, CompositeBean> compositeBeanMap;
+    private CompositeBean[] compositeBeanArray;
 
     public CompositeBean() {
     }
+    
+    public CompositeBean(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
 
     public String getStringProp() {
         return stringProp;
@@ -105,4 +121,20 @@ public class CompositeBean {
     public void setSimpleBeanMap(Map<String, SimpleBean> simpleBeanMap) {
         this.simpleBeanMap = simpleBeanMap;
     }
+
+    public Map<String, CompositeBean> getCompositeBeanMap() {
+        return compositeBeanMap;
+    }
+
+    public void setCompositeBeanMap(Map<String, CompositeBean> compositeBeanMap) {
+        this.compositeBeanMap = compositeBeanMap;
+    }
+
+    public CompositeBean[] getCompositeBeanArray() {
+        return compositeBeanArray;
+    }
+
+    public void setCompositeBeanArray(CompositeBean[] compositeBeanArray) {
+        this.compositeBeanArray = compositeBeanArray;
+    }
 }

Modified: shiro/trunk/core/src/test/java/org/apache/shiro/config/SimpleBean.java
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/test/java/org/apache/shiro/config/SimpleBean.java?rev=1228564&r1=1228563&r2=1228564&view=diff
==============================================================================
--- shiro/trunk/core/src/test/java/org/apache/shiro/config/SimpleBean.java (original)
+++ shiro/trunk/core/src/test/java/org/apache/shiro/config/SimpleBean.java Sat Jan  7 03:36:57 2012
@@ -24,6 +24,8 @@ import java.util.List;
  * @since 1.0
  */
 public class SimpleBean {
+    
+    private String name;
 
     private String stringProp = null;
     private int intProp;
@@ -33,6 +35,18 @@ public class SimpleBean {
 
     public SimpleBean() {
     }
+    
+    public SimpleBean(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
 
     public String getStringProp() {
         return stringProp;