SimpleAliasRegistry detects resolved aliases that loop back to the original name (SPR-5419); PropertyPlaceholderConfigurer does not modify Map in case of equal String keys (SPR-5318); inner class names in Java source style ("java.lang.Thread.State") supported as well (SPR-5210)

This commit is contained in:
Juergen Hoeller 2009-02-19 16:17:35 +00:00
parent ff8e7a1289
commit eb1631f458
6 changed files with 91 additions and 31 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2008 the original author or authors. * Copyright 2002-2009 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -264,7 +264,9 @@ public class BeanDefinitionVisitor {
throw new IllegalStateException("No StringValueResolver specified - pass a resolver " + throw new IllegalStateException("No StringValueResolver specified - pass a resolver " +
"object into the constructor or override the 'resolveStringValue' method"); "object into the constructor or override the 'resolveStringValue' method");
} }
return this.valueResolver.resolveStringValue(strVal); String resolvedValue = this.valueResolver.resolveStringValue(strVal);
// Return original String if not modified.
return (strVal.equals(resolvedValue) ? strVal : resolvedValue);
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2008 the original author or authors. * Copyright 2002-2009 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,23 +16,26 @@
package org.springframework.beans.factory.config; package org.springframework.beans.factory.config;
import static org.junit.Assert.*; import java.util.Collections;
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.genericBeanDefinition;
import static test.util.TestResourceUtils.qualifiedResource;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.Set; import java.util.Set;
import java.util.prefs.Preferences; import java.util.prefs.Preferences;
import static org.junit.Assert.*;
import org.junit.Before; import org.junit.Before;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import test.beans.IndexedTestBean;
import test.beans.TestBean;
import static test.util.TestResourceUtils.*;
import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanInitializationException; import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.*;
import org.springframework.beans.factory.support.ChildBeanDefinition; import org.springframework.beans.factory.support.ChildBeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.ManagedList; import org.springframework.beans.factory.support.ManagedList;
@ -41,9 +44,6 @@ import org.springframework.beans.factory.support.ManagedSet;
import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import test.beans.IndexedTestBean;
import test.beans.TestBean;
/** /**
* Unit tests for various {@link PropertyResourceConfigurer} implementations including: * Unit tests for various {@link PropertyResourceConfigurer} implementations including:
* {@link PropertyPlaceholderConfigurer}, {@link PropertyOverrideConfigurer} and * {@link PropertyPlaceholderConfigurer}, {@link PropertyOverrideConfigurer} and
@ -296,7 +296,7 @@ public final class PropertyResourceConfigurerTests {
poc.postProcessBeanFactory(factory); poc.postProcessBeanFactory(factory);
} catch (BeanInitializationException ex) { } catch (BeanInitializationException ex) {
// prove that the processor chokes on the invalid key // prove that the processor chokes on the invalid key
assertTrue(ex.getMessage().toLowerCase().indexOf("argh") != -1); assertTrue(ex.getMessage().toLowerCase().contains("argh"));
} }
} }
} }
@ -345,13 +345,14 @@ public final class PropertyResourceConfigurerTests {
} }
private void doTestPropertyPlaceholderConfigurer(boolean parentChildSeparation) { private void doTestPropertyPlaceholderConfigurer(boolean parentChildSeparation) {
Map singletonMap = Collections.singletonMap("myKey", "myValue");
if (parentChildSeparation) { if (parentChildSeparation) {
MutablePropertyValues pvs1 = new MutablePropertyValues(); MutablePropertyValues pvs1 = new MutablePropertyValues();
pvs1.addPropertyValue("age", "${age}"); pvs1.addPropertyValue("age", "${age}");
MutablePropertyValues pvs2 = new MutablePropertyValues(); MutablePropertyValues pvs2 = new MutablePropertyValues();
pvs2.addPropertyValue("name", "name${var}${var}${"); pvs2.addPropertyValue("name", "name${var}${var}${");
pvs2.addPropertyValue("spouse", new RuntimeBeanReference("${ref}")); pvs2.addPropertyValue("spouse", new RuntimeBeanReference("${ref}"));
pvs2.addPropertyValue("someMap", singletonMap);
RootBeanDefinition parent = new RootBeanDefinition(TestBean.class, pvs1); RootBeanDefinition parent = new RootBeanDefinition(TestBean.class, pvs1);
ChildBeanDefinition bd = new ChildBeanDefinition("${parent}", pvs2); ChildBeanDefinition bd = new ChildBeanDefinition("${parent}", pvs2);
factory.registerBeanDefinition("parent1", parent); factory.registerBeanDefinition("parent1", parent);
@ -362,6 +363,7 @@ public final class PropertyResourceConfigurerTests {
pvs.addPropertyValue("age", "${age}"); pvs.addPropertyValue("age", "${age}");
pvs.addPropertyValue("name", "name${var}${var}${"); pvs.addPropertyValue("name", "name${var}${var}${");
pvs.addPropertyValue("spouse", new RuntimeBeanReference("${ref}")); pvs.addPropertyValue("spouse", new RuntimeBeanReference("${ref}"));
pvs.addPropertyValue("someMap", singletonMap);
RootBeanDefinition bd = new RootBeanDefinition(TestBean.class, pvs); RootBeanDefinition bd = new RootBeanDefinition(TestBean.class, pvs);
factory.registerBeanDefinition("tb1", bd); factory.registerBeanDefinition("tb1", bd);
} }
@ -415,6 +417,8 @@ public final class PropertyResourceConfigurerTests {
assertEquals("namemyvarmyvar${", tb1.getName()); assertEquals("namemyvarmyvar${", tb1.getName());
assertEquals("myvarname98", tb2.getName()); assertEquals("myvarname98", tb2.getName());
assertEquals(tb2, tb1.getSpouse()); assertEquals(tb2, tb1.getSpouse());
assertEquals(1, tb1.getSomeMap().size());
assertEquals("myValue", tb1.getSomeMap().get("myKey"));
assertEquals(2, tb2.getFriends().size()); assertEquals(2, tb2.getFriends().size());
assertEquals("na98me", tb2.getFriends().iterator().next()); assertEquals("na98me", tb2.getFriends().iterator().next());
assertEquals(tb2, tb2.getFriends().toArray()[1]); assertEquals(tb2, tb2.getFriends().toArray()[1]);
@ -498,7 +502,7 @@ public final class PropertyResourceConfigurerTests {
} }
catch (BeanDefinitionStoreException ex) { catch (BeanDefinitionStoreException ex) {
// expected // expected
assertTrue(ex.getMessage().indexOf("user.dir") != -1); assertTrue(ex.getMessage().contains("user.dir"));
} }
} }
@ -516,7 +520,7 @@ public final class PropertyResourceConfigurerTests {
} }
catch (BeanDefinitionStoreException ex) { catch (BeanDefinitionStoreException ex) {
// expected // expected
assertTrue(ex.getMessage().indexOf("ref") != -1); assertTrue(ex.getMessage().contains("ref"));
} }
} }
@ -582,6 +586,39 @@ public final class PropertyResourceConfigurerTests {
assertEquals("myname", tb.getName()); assertEquals("myname", tb.getName());
} }
@Test
public void testPropertyPlaceholderConfigurerWithPlaceholderInAlias() {
factory.registerBeanDefinition("tb",
genericBeanDefinition(TestBean.class).getBeanDefinition());
factory.registerAlias("tb", "${alias}");
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
Properties props = new Properties();
props.put("alias", "tb2");
ppc.setProperties(props);
ppc.postProcessBeanFactory(factory);
TestBean tb = (TestBean) factory.getBean("tb");
TestBean tb2 = (TestBean) factory.getBean("tb2");
assertSame(tb, tb2);
}
@Test
public void testPropertyPlaceholderConfigurerWithSelfReferencingPlaceholderInAlias() {
factory.registerBeanDefinition("tb",
genericBeanDefinition(TestBean.class).getBeanDefinition());
factory.registerAlias("tb", "${alias}");
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
Properties props = new Properties();
props.put("alias", "tb");
ppc.setProperties(props);
ppc.postProcessBeanFactory(factory);
TestBean tb = (TestBean) factory.getBean("tb");
assertEquals(0, factory.getAliases("tb").length);
}
@Test @Test
public void testPropertyPlaceholderConfigurerWithCircularReference() { public void testPropertyPlaceholderConfigurerWithCircularReference() {
factory.registerBeanDefinition("tb", factory.registerBeanDefinition("tb",
@ -760,6 +797,7 @@ public final class PropertyResourceConfigurerTests {
private static class ConvertingOverrideConfigurer extends PropertyOverrideConfigurer { private static class ConvertingOverrideConfigurer extends PropertyOverrideConfigurer {
protected String convertPropertyValue(String originalValue) { protected String convertPropertyValue(String originalValue) {
return "X" + originalValue; return "X" + originalValue;
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2008 the original author or authors. * Copyright 2002-2009 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -24,17 +24,16 @@ import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
import junit.framework.TestCase; import junit.framework.TestCase;
import test.beans.CustomEnum;
import test.beans.TestBean;
import org.springframework.beans.factory.config.FieldRetrievingFactoryBean;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.beans.factory.parsing.ComponentDefinition; import org.springframework.beans.factory.parsing.ComponentDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.beans.factory.config.FieldRetrievingFactoryBean;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
import test.beans.TestBean;
/** /**
* @author Rob Harrop * @author Rob Harrop
* @author Juergen Hoeller * @author Juergen Hoeller
@ -163,19 +162,19 @@ public class UtilNamespaceHandlerTests extends TestCase {
public void testNestedInCollections() throws Exception { public void testNestedInCollections() throws Exception {
TestBean bean = (TestBean) this.beanFactory.getBean("nestedCustomTagBean"); TestBean bean = (TestBean) this.beanFactory.getBean("nestedCustomTagBean");
Integer min = new Integer(Integer.MIN_VALUE);
List list = bean.getSomeList(); List list = bean.getSomeList();
assertEquals(1, list.size()); assertEquals(1, list.size());
assertEquals(min, list.get(0)); assertEquals(Integer.MIN_VALUE, list.get(0));
Set set = bean.getSomeSet(); Set set = bean.getSomeSet();
assertEquals(1, set.size()); assertEquals(2, set.size());
assertTrue(set.contains(min)); assertTrue(set.contains(Thread.State.NEW));
assertTrue(set.contains(Thread.State.RUNNABLE));
Map map = bean.getSomeMap(); Map map = bean.getSomeMap();
assertEquals(1, map.size()); assertEquals(1, map.size());
assertEquals(min, map.get("min")); assertEquals(CustomEnum.VALUE_1, map.get("min"));
} }
public void testCircularCollections() throws Exception { public void testCircularCollections() throws Exception {

View File

@ -97,14 +97,15 @@
</property> </property>
<property name="someSet"> <property name="someSet">
<set> <set>
<util:constant static-field="java.lang.Integer.MIN_VALUE"/> <util:constant static-field="java.lang.Thread$State.NEW"/>
<util:constant static-field="java.lang.Thread.State.RUNNABLE"/>
</set> </set>
</property> </property>
<property name="someMap"> <property name="someMap">
<map> <map>
<entry> <entry>
<key><value>min</value></key> <key><value>min</value></key>
<util:constant static-field="java.lang.Integer.MIN_VALUE"/> <util:constant static-field="test.beans.CustomEnum.VALUE_1"/>
</entry> </entry>
</map> </map>
</property> </property>

View File

@ -117,7 +117,10 @@ public class SimpleAliasRegistry implements AliasRegistry {
String registeredName = aliasCopy.get(alias); String registeredName = aliasCopy.get(alias);
String resolvedAlias = valueResolver.resolveStringValue(alias); String resolvedAlias = valueResolver.resolveStringValue(alias);
String resolvedName = valueResolver.resolveStringValue(registeredName); String resolvedName = valueResolver.resolveStringValue(registeredName);
if (!resolvedAlias.equals(alias)) { if (resolvedAlias.equals(resolvedName)) {
this.aliasMap.remove(alias);
}
else if (!resolvedAlias.equals(alias)) {
String existingName = this.aliasMap.get(resolvedAlias); String existingName = this.aliasMap.get(resolvedAlias);
if (existingName != null && !existingName.equals(resolvedName)) { if (existingName != null && !existingName.equals(resolvedName)) {
throw new IllegalStateException( throw new IllegalStateException(
@ -125,8 +128,8 @@ public class SimpleAliasRegistry implements AliasRegistry {
"') for name '" + resolvedName + "': It is already registered for name '" + "') for name '" + resolvedName + "': It is already registered for name '" +
registeredName + "'."); registeredName + "'.");
} }
this.aliasMap.put(resolvedAlias, resolvedName);
this.aliasMap.remove(alias); this.aliasMap.remove(alias);
this.aliasMap.put(resolvedAlias, resolvedName);
} }
else if (!registeredName.equals(resolvedName)) { else if (!registeredName.equals(resolvedName)) {
this.aliasMap.put(alias, resolvedName); this.aliasMap.put(alias, resolvedName);

View File

@ -201,7 +201,9 @@ public abstract class ClassUtils {
/** /**
* Replacement for <code>Class.forName()</code> that also returns Class instances * Replacement for <code>Class.forName()</code> that also returns Class instances
* for primitives (like "int") and array class names (like "String[]"). * for primitives (e.g."int") and array class names (e.g. "String[]").
* Furthermore, it is also capable of resolving inner class names in Java source
* style (e.g. "java.lang.Thread.State" instead of "java.lang.Thread$State").
* @param name the name of the Class * @param name the name of the Class
* @param classLoader the class loader to use * @param classLoader the class loader to use
* (may be <code>null</code>, which indicates the default class loader) * (may be <code>null</code>, which indicates the default class loader)
@ -246,7 +248,22 @@ public abstract class ClassUtils {
if (classLoaderToUse == null) { if (classLoaderToUse == null) {
classLoaderToUse = getDefaultClassLoader(); classLoaderToUse = getDefaultClassLoader();
} }
return classLoaderToUse.loadClass(name); try {
return classLoaderToUse.loadClass(name);
}
catch (ClassNotFoundException ex) {
int lastDotIndex = name.lastIndexOf('.');
if (lastDotIndex != -1) {
String innerClassName = name.substring(0, lastDotIndex) + '$' + name.substring(lastDotIndex + 1);
try {
return classLoaderToUse.loadClass(innerClassName);
}
catch (ClassNotFoundException ex2) {
// swallow - let original exception get through
}
}
throw ex;
}
} }
/** /**