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");
* 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 " +
"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");
* you may not use this file except in compliance with the License.
@ -16,23 +16,26 @@
package org.springframework.beans.factory.config;
import static org.junit.Assert.*;
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.genericBeanDefinition;
import static test.util.TestResourceUtils.qualifiedResource;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.prefs.Preferences;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Ignore;
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.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanInitializationException;
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.DefaultListableBeanFactory;
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.core.io.Resource;
import test.beans.IndexedTestBean;
import test.beans.TestBean;
/**
* Unit tests for various {@link PropertyResourceConfigurer} implementations including:
* {@link PropertyPlaceholderConfigurer}, {@link PropertyOverrideConfigurer} and
@ -296,7 +296,7 @@ public final class PropertyResourceConfigurerTests {
poc.postProcessBeanFactory(factory);
} catch (BeanInitializationException ex) {
// 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) {
Map singletonMap = Collections.singletonMap("myKey", "myValue");
if (parentChildSeparation) {
MutablePropertyValues pvs1 = new MutablePropertyValues();
pvs1.addPropertyValue("age", "${age}");
MutablePropertyValues pvs2 = new MutablePropertyValues();
pvs2.addPropertyValue("name", "name${var}${var}${");
pvs2.addPropertyValue("spouse", new RuntimeBeanReference("${ref}"));
pvs2.addPropertyValue("someMap", singletonMap);
RootBeanDefinition parent = new RootBeanDefinition(TestBean.class, pvs1);
ChildBeanDefinition bd = new ChildBeanDefinition("${parent}", pvs2);
factory.registerBeanDefinition("parent1", parent);
@ -362,6 +363,7 @@ public final class PropertyResourceConfigurerTests {
pvs.addPropertyValue("age", "${age}");
pvs.addPropertyValue("name", "name${var}${var}${");
pvs.addPropertyValue("spouse", new RuntimeBeanReference("${ref}"));
pvs.addPropertyValue("someMap", singletonMap);
RootBeanDefinition bd = new RootBeanDefinition(TestBean.class, pvs);
factory.registerBeanDefinition("tb1", bd);
}
@ -415,6 +417,8 @@ public final class PropertyResourceConfigurerTests {
assertEquals("namemyvarmyvar${", tb1.getName());
assertEquals("myvarname98", tb2.getName());
assertEquals(tb2, tb1.getSpouse());
assertEquals(1, tb1.getSomeMap().size());
assertEquals("myValue", tb1.getSomeMap().get("myKey"));
assertEquals(2, tb2.getFriends().size());
assertEquals("na98me", tb2.getFriends().iterator().next());
assertEquals(tb2, tb2.getFriends().toArray()[1]);
@ -498,7 +502,7 @@ public final class PropertyResourceConfigurerTests {
}
catch (BeanDefinitionStoreException ex) {
// 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) {
// expected
assertTrue(ex.getMessage().indexOf("ref") != -1);
assertTrue(ex.getMessage().contains("ref"));
}
}
@ -582,6 +586,39 @@ public final class PropertyResourceConfigurerTests {
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
public void testPropertyPlaceholderConfigurerWithCircularReference() {
factory.registerBeanDefinition("tb",
@ -760,6 +797,7 @@ public final class PropertyResourceConfigurerTests {
private static class ConvertingOverrideConfigurer extends PropertyOverrideConfigurer {
protected String convertPropertyValue(String 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");
* 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 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.support.AbstractBeanDefinition;
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 test.beans.TestBean;
/**
* @author Rob Harrop
* @author Juergen Hoeller
@ -163,19 +162,19 @@ public class UtilNamespaceHandlerTests extends TestCase {
public void testNestedInCollections() throws Exception {
TestBean bean = (TestBean) this.beanFactory.getBean("nestedCustomTagBean");
Integer min = new Integer(Integer.MIN_VALUE);
List list = bean.getSomeList();
assertEquals(1, list.size());
assertEquals(min, list.get(0));
assertEquals(Integer.MIN_VALUE, list.get(0));
Set set = bean.getSomeSet();
assertEquals(1, set.size());
assertTrue(set.contains(min));
assertEquals(2, set.size());
assertTrue(set.contains(Thread.State.NEW));
assertTrue(set.contains(Thread.State.RUNNABLE));
Map map = bean.getSomeMap();
assertEquals(1, map.size());
assertEquals(min, map.get("min"));
assertEquals(CustomEnum.VALUE_1, map.get("min"));
}
public void testCircularCollections() throws Exception {

View File

@ -97,14 +97,15 @@
</property>
<property name="someSet">
<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>
</property>
<property name="someMap">
<map>
<entry>
<key><value>min</value></key>
<util:constant static-field="java.lang.Integer.MIN_VALUE"/>
<util:constant static-field="test.beans.CustomEnum.VALUE_1"/>
</entry>
</map>
</property>

View File

@ -117,7 +117,10 @@ public class SimpleAliasRegistry implements AliasRegistry {
String registeredName = aliasCopy.get(alias);
String resolvedAlias = valueResolver.resolveStringValue(alias);
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);
if (existingName != null && !existingName.equals(resolvedName)) {
throw new IllegalStateException(
@ -125,8 +128,8 @@ public class SimpleAliasRegistry implements AliasRegistry {
"') for name '" + resolvedName + "': It is already registered for name '" +
registeredName + "'.");
}
this.aliasMap.put(resolvedAlias, resolvedName);
this.aliasMap.remove(alias);
this.aliasMap.put(resolvedAlias, resolvedName);
}
else if (!registeredName.equals(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
* 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 classLoader the class loader to use
* (may be <code>null</code>, which indicates the default class loader)
@ -246,7 +248,22 @@ public abstract class ClassUtils {
if (classLoaderToUse == null) {
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;
}
}
/**