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);
+ }
}