JSEC-119 - cleaned up primitive and object reference reflection/injection.  Objects now can now be referenced via $beanId notation

git-svn-id: https://svn.apache.org/repos/asf/incubator/jsecurity/trunk@711069 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/changes.txt b/changes.txt
index b93a931..06b1a47 100644
--- a/changes.txt
+++ b/changes.txt
@@ -7,5 +7,11 @@
 * ReflectionBuilder (used in JSecurity .ini and web.xml based text configuration) now handles setting

   primitive attributes correctly, performing proper string-to-primitive conversion (BeanUtils does this automatically).

 

+* ReflectionBuilder (used for JSecurity .ini and web.xml configuration) now supports setting other previously configured

+  objects by '$' reference. For example:

+  bar = some.package.class.Bar

+  foo = some.package.class.Foo

+  foo.bar = $bar

+

 

 

diff --git a/src/org/jsecurity/config/ReflectionBuilder.java b/src/org/jsecurity/config/ReflectionBuilder.java
index 5ceb76a..ce7951f 100644
--- a/src/org/jsecurity/config/ReflectionBuilder.java
+++ b/src/org/jsecurity/config/ReflectionBuilder.java
@@ -44,7 +44,11 @@
 

     private static final Log log = LogFactory.getLog(ReflectionBuilder.class);

 

+    private static final String OBJECT_REFERENCE_BEGIN_TOKEN = "$";

+    private static final String ESCAPED_OBJECT_REFERENCE_BEGIN_TOKEN = "\\$";

     private static final String GLOBAL_PROPERTY_PREFIX = "jsecurity";

+

+

     protected Map objects;

 

     public ReflectionBuilder() {

@@ -169,24 +173,58 @@
         }

     }

 

-    protected void applyProperty(Object object, String propertyName, String value) {

+    protected boolean isReference(String value) {

+        return value != null && value.startsWith(OBJECT_REFERENCE_BEGIN_TOKEN);

+    }

+

+    protected String getId(String referenceToken) {

+        return referenceToken.substring(OBJECT_REFERENCE_BEGIN_TOKEN.length());

+    }

+

+    protected Object getReferencedObject(String id) {

+        Object o = objects != null && !objects.isEmpty() ? objects.get(id) : null;

+        if (o == null) {

+            String msg = "The object with id [" + id + "] has not yet been defined and therefore cannot be " +

+                    "referenced.  Please ensure objects are defined in the order in which they should be " +

+                    "created and made avaialable for future reference.";

+            throw new UnresolveableReferenceException(msg);

+        }

+        return o;

+    }

+

+    protected String unescapeIfNecessary(String value) {

+        if (value != null && value.startsWith(ESCAPED_OBJECT_REFERENCE_BEGIN_TOKEN)) {

+            return value.substring(ESCAPED_OBJECT_REFERENCE_BEGIN_TOKEN.length() - 1);

+        }

+        return value;

+    }

+

+    protected void applyProperty(Object object, String propertyName, String stringValue) {

+

+        Object value;

+

+        if (isReference(stringValue)) {

+            String id = getId(stringValue);

+            if (log.isDebugEnabled()) {

+                log.debug("Encountered object reference [" + stringValue + "].  Looking up object " +

+                        "with id [" + id + "]");

+            }

+            value = getReferencedObject(id);

+        } else {

+            value = unescapeIfNecessary(stringValue);

+        }

+

         try {

             if (log.isTraceEnabled()) {

                 log.trace("Applying property [" + propertyName + "] value [" + value + "] on object of type [" + object.getClass().getName() + "]");

             }

             BeanUtils.setProperty(object, propertyName, value);

         } catch (Exception e) {

-            //perhaps the value was a reference to an object already defined:

-            Object o = (objects != null && !objects.isEmpty() ? objects.get(value) : null);

-            if (o != null) {

-                try {

-                    BeanUtils.setProperty(object, propertyName, o);

-                    return;

-                } catch (Exception ignored) {

-                }

-            }

-

-            String msg = "Unable to set property [" + propertyName + "] with value [" + value + "]";

+            String msg = "Unable to set property [" + propertyName + "] with value [" + stringValue + "].  If " +

+                    "'" + stringValue + "' is a reference to another (previously defined) object, please 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);

         }

     }

diff --git a/src/org/jsecurity/config/UnresolveableReferenceException.java b/src/org/jsecurity/config/UnresolveableReferenceException.java
new file mode 100644
index 0000000..998033c
--- /dev/null
+++ b/src/org/jsecurity/config/UnresolveableReferenceException.java
@@ -0,0 +1,45 @@
+/*

+ * 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.jsecurity.config;

+

+/**

+ * Exception thrown when a reference to an object is made, but that object cannot be found.  This is most likely

+ * thrown due to a configuration line that references an object that hasn't been defined yet.

+ *

+ * @author Les Hazlewood

+ * @since 0.9 RC2

+ */

+public class UnresolveableReferenceException extends ConfigurationException {

+

+    public UnresolveableReferenceException() {

+        super();

+    }

+

+    public UnresolveableReferenceException(String message) {

+        super(message);

+    }

+

+    public UnresolveableReferenceException(Throwable cause) {

+        super(cause);

+    }

+

+    public UnresolveableReferenceException(String message, Throwable cause) {

+        super(message, cause);

+    }

+}

diff --git a/test/org/jsecurity/config/ReflectionBuilderTest.java b/test/org/jsecurity/config/ReflectionBuilderTest.java
index a441014..b7c9e97 100644
--- a/test/org/jsecurity/config/ReflectionBuilderTest.java
+++ b/test/org/jsecurity/config/ReflectionBuilderTest.java
@@ -55,7 +55,7 @@
         defs.put("otherTestBean.intProp", "101");

         defs.put("testBean", "org.jsecurity.config.TestBean");

         defs.put("testBean.stringProp", "blah");

-        defs.put("testBean.otherTestBean", "otherTestBean");

+        defs.put("testBean.otherTestBean", "$otherTestBean");

 

         ReflectionBuilder builder = new ReflectionBuilder();

         Map beans = builder.buildObjects(defs);

@@ -69,4 +69,24 @@
         assertEquals(otherTestBean, testBean.getOtherTestBean());

         assertEquals(otherTestBean.getIntProp(), 101);

     }

+

+    @Test(expected = ConfigurationException.class)

+    public void testObjectReferenceConfigWithTypeMismatch() {

+        Map<String, String> defs = new LinkedHashMap<String, String>();

+        defs.put("otherTestBean", "org.jsecurity.config.OtherTestBean");

+        defs.put("testBean", "org.jsecurity.config.TestBean");

+        defs.put("testBean.otherTestBean", "otherTestBean");

+        ReflectionBuilder builder = new ReflectionBuilder();

+        builder.buildObjects(defs);

+    }

+

+    @Test(expected = UnresolveableReferenceException.class)

+    public void testObjectReferenceConfigWithInvalidReference() {

+        Map<String, String> defs = new LinkedHashMap<String, String>();

+        defs.put("otherTestBean", "org.jsecurity.config.OtherTestBean");

+        defs.put("testBean", "org.jsecurity.config.TestBean");

+        defs.put("testBean.otherTestBean", "$foo");

+        ReflectionBuilder builder = new ReflectionBuilder();

+        builder.buildObjects(defs);

+    }

 }