From 9e60248dfc5676a2efce46b997d9c2fcabdaaf3c Mon Sep 17 00:00:00 2001 From: dsyer Date: Mon, 22 Jun 2009 07:22:13 +0000 Subject: [PATCH] OPEN BATCH-1303: Support Spring 3.0 EL. --- .../batch/core/scope/StepScope.java | 100 +++++++++++++++--- .../scope/util/PlaceholderTargetSource.java | 2 +- .../PlaceholderTargetSourceErrorTests.java | 18 ++-- ...ncPlaceholderTargetSourceTests-context.xml | 2 +- ...xtPlaceholderTargetSourceTests-context.xml | 4 +- .../PlaceholderTargetSourceTests-context.xml | 26 ++--- ...lePlaceholderTargetSourceTests-context.xml | 2 +- 7 files changed, 112 insertions(+), 42 deletions(-) diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/scope/StepScope.java b/spring-batch-core/src/main/java/org/springframework/batch/core/scope/StepScope.java index ff02ce18f..8dde3ca72 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/scope/StepScope.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/scope/StepScope.java @@ -103,7 +103,7 @@ public class StepScope implements Scope, BeanFactoryPostProcessor, Ordered { /** * Flag to indicate that proxies should use dynamic subclassing. This allows - * classes with no interface to be proxied. Defaults to false. + * classes with no interface to be proxied. Defaults to false. * * @param proxyTargetClass set to true to have proxies created using dynamic * subclasses @@ -196,14 +196,15 @@ public class StepScope implements Scope, BeanFactoryPostProcessor, Ordered { "BeanFactory was not a BeanDefinitionRegistry, so StepScope cannot be used."); BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; - Scopifier scopifier = new Scopifier(registry, name, proxyTargetClass); - for (String beanName : beanFactory.getBeanDefinitionNames()) { BeanDefinition definition = beanFactory.getBeanDefinition(beanName); // Replace this or any of its inner beans with scoped proxy if it // has this scope + boolean scoped = name.equals(definition.getScope()); + Scopifier scopifier = new Scopifier(registry, name, proxyTargetClass, scoped); scopifier.visitBeanDefinition(definition); - if (name.equals(definition.getScope())) { + if (scoped) { + new ExpressionHider(name, scoped).visitBeanDefinition(definition); createScopedProxy(beanName, definition, registry, proxyTargetClass); } } @@ -267,7 +268,9 @@ public class StepScope implements Scope, BeanFactoryPostProcessor, Ordered { private final String scope; - public Scopifier(BeanDefinitionRegistry registry, String scope, boolean proxyTargetClass) { + private final boolean scoped; + + public Scopifier(BeanDefinitionRegistry registry, String scope, boolean proxyTargetClass, boolean scoped) { super(new StringValueResolver() { public String resolveStringValue(String value) { return value; @@ -276,32 +279,99 @@ public class StepScope implements Scope, BeanFactoryPostProcessor, Ordered { this.registry = registry; this.proxyTargetClass = proxyTargetClass; this.scope = scope; + this.scoped = scoped; } @Override protected Object resolveValue(Object value) { + + BeanDefinition definition = null; + String beanName = null; if (value instanceof BeanDefinition) { - BeanDefinition definition = (BeanDefinition) value; - if (scope.equals(definition.getScope())) { - String beanName = BeanDefinitionReaderUtils.generateBeanName(definition, registry); - // Exit here so that nested inner bean definitions are not - // analysed - return createScopedProxy(beanName, definition, registry, proxyTargetClass); - } + definition = (BeanDefinition) value; + beanName = BeanDefinitionReaderUtils.generateBeanName(definition, registry); } else if (value instanceof BeanDefinitionHolder) { BeanDefinitionHolder holder = (BeanDefinitionHolder) value; - BeanDefinition definition = holder.getBeanDefinition(); - if (scope.equals(definition.getScope())) { + definition = holder.getBeanDefinition(); + beanName = holder.getBeanName(); + } + + if (definition != null) { + boolean nestedScoped = scope.equals(definition.getScope()); + boolean scopeChangeRequiresProxy = !scoped && nestedScoped; + new ExpressionHider(scope, nestedScoped).visitBeanDefinition(definition); + if (scopeChangeRequiresProxy) { // Exit here so that nested inner bean definitions are not // analysed - return createScopedProxy(holder.getBeanName(), definition, registry, proxyTargetClass); + return createScopedProxy(beanName, definition, registry, proxyTargetClass); + } + } + + // Nested inner bean definitions are recursively analysed here + value = super.resolveValue(value); + return value; + + } + + } + + /** + * Helper class to scan a bean definition hierarchy and hide placeholders + * from Spring EL. + * + * @author Dave Syer + * + */ + private static class ExpressionHider extends BeanDefinitionVisitor { + + private static final String PLACEHOLDER_PREFIX = "#{"; + + private static final String PLACEHOLDER_SUFFIX = "}"; + + private static final String REPLACEMENT_PREFIX = "%{"; + + private final String scope; + + private final boolean scoped; + + private ExpressionHider(String scope, final boolean scoped) { + super(new StringValueResolver() { + public String resolveStringValue(String value) { + if (scoped && value.contains(PLACEHOLDER_PREFIX) && value.contains(PLACEHOLDER_SUFFIX)) { + value = value.replace(PLACEHOLDER_PREFIX, REPLACEMENT_PREFIX); + } + return value; + } + }); + this.scope = scope; + this.scoped = scoped; + } + + @Override + protected Object resolveValue(Object value) { + BeanDefinition definition = null; + if (value instanceof BeanDefinition) { + definition = (BeanDefinition) value; + } + else if (value instanceof BeanDefinitionHolder) { + BeanDefinitionHolder holder = (BeanDefinitionHolder) value; + definition = holder.getBeanDefinition(); + } + if (definition != null) { + boolean scopeChange = !scope.equals(definition.getScope()); + if (scopeChange) { + new ExpressionHider(definition.getScope(), !scoped).visitBeanDefinition(definition); + // Exit here so that nested inner bean definitions are not + // analysed by both vistors + return value; } } // Nested inner bean definitions are recursively analysed here value = super.resolveValue(value); return value; } + } } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/scope/util/PlaceholderTargetSource.java b/spring-batch-core/src/main/java/org/springframework/batch/core/scope/util/PlaceholderTargetSource.java index d4a750196..451695fac 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/scope/util/PlaceholderTargetSource.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/scope/util/PlaceholderTargetSource.java @@ -57,7 +57,7 @@ public class PlaceholderTargetSource extends SimpleBeanTargetSource implements I /** * Key for placeholders to be replaced from the properties provided. */ - private static final String PLACEHOLDER_PREFIX = "#{"; + private static final String PLACEHOLDER_PREFIX = "%{"; private static final String PLACEHOLDER_SUFFIX = "}"; diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/scope/util/PlaceholderTargetSourceErrorTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/scope/util/PlaceholderTargetSourceErrorTests.java index 316bab2db..619e8b3bd 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/scope/util/PlaceholderTargetSourceErrorTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/scope/util/PlaceholderTargetSourceErrorTests.java @@ -68,26 +68,26 @@ public class PlaceholderTargetSourceErrorTests extends ContextFactorySupport { @Test public void testPartialReplaceSunnyDay() throws Exception { - Node target = (Node) createValue("name", "#{foo}-bar").getTarget(); + Node target = (Node) createValue("name", "%{foo}-bar").getTarget(); assertEquals("bar-bar", target.getName()); } @Test public void testPartialReplaceMissingProperty() throws Exception { - Node target = (Node) createValue("name", "#{garbage}-bar").getTarget(); - assertEquals("#{garbage}-bar", target.getName()); + Node target = (Node) createValue("name", "%{garbage}-bar").getTarget(); + assertEquals("%{garbage}-bar", target.getName()); } @Test public void testFullReplaceSunnyDay() throws Exception { - Node target = (Node) createValue("name", "#{foo}").getTarget(); + Node target = (Node) createValue("name", "%{foo}").getTarget(); assertEquals("bar", target.getName()); } @Test public void testFullReplaceMissingProperty() throws Exception { try { - Node target = (Node) createValue("name", "#{garbage}").getTarget(); + Node target = (Node) createValue("name", "%{garbage}").getTarget(); assertEquals("bar", target.getName()); fail("Expected IllegalStateException"); } @@ -99,26 +99,26 @@ public class PlaceholderTargetSourceErrorTests extends ContextFactorySupport { @Test public void testPartialReplaceIntegerToString() throws Exception { - Node target = (Node) createValue("name", "foo-#{integer}").getTarget(); + Node target = (Node) createValue("name", "foo-%{integer}").getTarget(); assertEquals("foo-4321", target.getName()); } @Test public void testFullReplaceIntegerToString() throws Exception { - Node target = (Node) createValue("name", "#{integer}").getTarget(); + Node target = (Node) createValue("name", "%{integer}").getTarget(); assertEquals("4321", target.getName()); } @Test public void testFullReplaceIntegerToLong() throws Exception { - Node target = (Node) createValue("value", "#{integer}").getTarget(); + Node target = (Node) createValue("value", "%{integer}").getTarget(); assertEquals(4321L, target.getValue()); } @Test public void testFullReplaceIntegerToNode() throws Exception { try { - Node target = (Node) createValue("parent", "#{integer}").getTarget(); + Node target = (Node) createValue("parent", "%{integer}").getTarget(); assertEquals("4321", target.getParent()); fail("Expected IllegalArgumentException"); } diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/scope/util/AsyncPlaceholderTargetSourceTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/scope/util/AsyncPlaceholderTargetSourceTests-context.xml index 8416f6b4b..939c63553 100644 --- a/spring-batch-core/src/test/resources/org/springframework/batch/core/scope/util/AsyncPlaceholderTargetSourceTests-context.xml +++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/scope/util/AsyncPlaceholderTargetSourceTests-context.xml @@ -19,7 +19,7 @@ - + - + - + - + - + - + - + - + - + - #{foo} - foo-#{integer} - bar-#{integer} + %{foo} + foo-%{integer} + bar-%{integer} @@ -138,25 +138,25 @@ - + - + - + - + diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/scope/util/SimplePlaceholderTargetSourceTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/scope/util/SimplePlaceholderTargetSourceTests-context.xml index 041ca0b77..593bb3c30 100644 --- a/spring-batch-core/src/test/resources/org/springframework/batch/core/scope/util/SimplePlaceholderTargetSourceTests-context.xml +++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/scope/util/SimplePlaceholderTargetSourceTests-context.xml @@ -21,7 +21,7 @@ - +