From eb1631f458a822fa4da7b51ea44388344bc55d6c Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 19 Feb 2009 16:17:35 +0000 Subject: [PATCH] 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) --- .../factory/config/BeanDefinitionVisitor.java | 6 +- .../PropertyResourceConfigurerTests.java | 62 +++++++++++++++---- .../xml/UtilNamespaceHandlerTests.java | 21 +++---- .../beans/factory/xml/testUtilNamespace.xml | 5 +- .../core/SimpleAliasRegistry.java | 7 ++- .../org/springframework/util/ClassUtils.java | 21 ++++++- 6 files changed, 91 insertions(+), 31 deletions(-) diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/config/BeanDefinitionVisitor.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/config/BeanDefinitionVisitor.java index aea944f01b..ee9b5b5bcd 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/config/BeanDefinitionVisitor.java +++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/config/BeanDefinitionVisitor.java @@ -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); } } diff --git a/org.springframework.beans/src/test/java/org/springframework/beans/factory/config/PropertyResourceConfigurerTests.java b/org.springframework.beans/src/test/java/org/springframework/beans/factory/config/PropertyResourceConfigurerTests.java index 0dcecbb591..b026e36e96 100644 --- a/org.springframework.beans/src/test/java/org/springframework/beans/factory/config/PropertyResourceConfigurerTests.java +++ b/org.springframework.beans/src/test/java/org/springframework/beans/factory/config/PropertyResourceConfigurerTests.java @@ -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; } diff --git a/org.springframework.beans/src/test/java/org/springframework/beans/factory/xml/UtilNamespaceHandlerTests.java b/org.springframework.beans/src/test/java/org/springframework/beans/factory/xml/UtilNamespaceHandlerTests.java index e1861ee825..bff9bd544e 100644 --- a/org.springframework.beans/src/test/java/org/springframework/beans/factory/xml/UtilNamespaceHandlerTests.java +++ b/org.springframework.beans/src/test/java/org/springframework/beans/factory/xml/UtilNamespaceHandlerTests.java @@ -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 { diff --git a/org.springframework.beans/src/test/resources/org/springframework/beans/factory/xml/testUtilNamespace.xml b/org.springframework.beans/src/test/resources/org/springframework/beans/factory/xml/testUtilNamespace.xml index 54aafa5c67..e99c99d72e 100644 --- a/org.springframework.beans/src/test/resources/org/springframework/beans/factory/xml/testUtilNamespace.xml +++ b/org.springframework.beans/src/test/resources/org/springframework/beans/factory/xml/testUtilNamespace.xml @@ -97,14 +97,15 @@ - + + min - + diff --git a/org.springframework.core/src/main/java/org/springframework/core/SimpleAliasRegistry.java b/org.springframework.core/src/main/java/org/springframework/core/SimpleAliasRegistry.java index 31583f9589..3c520241ee 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/SimpleAliasRegistry.java +++ b/org.springframework.core/src/main/java/org/springframework/core/SimpleAliasRegistry.java @@ -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); diff --git a/org.springframework.core/src/main/java/org/springframework/util/ClassUtils.java b/org.springframework.core/src/main/java/org/springframework/util/ClassUtils.java index d62a294a21..ac69a88c0a 100644 --- a/org.springframework.core/src/main/java/org/springframework/util/ClassUtils.java +++ b/org.springframework.core/src/main/java/org/springframework/util/ClassUtils.java @@ -201,7 +201,9 @@ public abstract class ClassUtils { /** * Replacement for Class.forName() 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 null, 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; + } } /**