From bd43ca100703e92cb8cbd6069ae1dfb852d4b3ec Mon Sep 17 00:00:00 2001 From: Stephan Freund Date: Thu, 10 Jun 2021 00:12:41 +0200 Subject: [PATCH] Fix TARGET_NAME_PREFIX in StepScope Resolves #3936 --- .../batch/core/scope/StepScope.java | 352 +++++++++--------- .../StepScopeConfigurationTests.java | 36 +- ...singNamespaceAutoregisterBeans-context.xml | 30 ++ 3 files changed, 240 insertions(+), 178 deletions(-) create mode 100644 spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/annotation/StepScopeConfigurationTestsUsingNamespaceAutoregisterBeans-context.xml 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 f014bf10e..d7e687a98 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 @@ -1,176 +1,176 @@ -/* - * Copyright 2006-2013 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. - * You may obtain a copy of the License at - * - * https://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.springframework.batch.core.scope; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.batch.core.scope.context.StepContext; -import org.springframework.batch.core.scope.context.StepSynchronizationManager; -import org.springframework.beans.BeanWrapper; -import org.springframework.beans.BeanWrapperImpl; -import org.springframework.beans.factory.ObjectFactory; -import org.springframework.beans.factory.config.Scope; - -/** - * Scope for step context. Objects in this scope use the Spring container as an - * object factory, so there is only one instance of such a bean per executing - * step. All objects in this scope are <aop:scoped-proxy/> (no need to - * decorate the bean definitions).
- *
- * - * In addition, support is provided for late binding of references accessible - * from the {@link StepContext} using #{..} placeholders. Using this feature, - * bean properties can be pulled from the step or job execution context and the - * job parameters. E.g. - * - *
- * <bean id="..." class="..." scope="step">
- * 	<property name="parent" ref="#{stepExecutionContext[helper]}" />
- * </bean>
- *
- * <bean id="..." class="..." scope="step">
- * 	<property name="name" value="#{stepExecutionContext['input.name']}" />
- * </bean>
- *
- * <bean id="..." class="..." scope="step">
- * 	<property name="name" value="#{jobParameters[input]}" />
- * </bean>
- *
- * <bean id="..." class="..." scope="step">
- * 	<property name="name" value="#{jobExecutionContext['input.stem']}.txt" />
- * </bean>
- * 
- * - * The {@link StepContext} is referenced using standard bean property paths (as - * per {@link BeanWrapper}). The examples above all show the use of the Map - * accessors provided as a convenience for step and job attributes. - * - * @author Dave Syer - * @author Michael Minella - * @since 2.0 - */ -public class StepScope extends BatchScopeSupport { - - private static final String TARGET_NAME_PREFIX = "stepScopedTarget."; - - private Log logger = LogFactory.getLog(getClass()); - - private final Object mutex = new Object(); - - /** - * Context key for clients to use for conversation identifier. - */ - public static final String ID_KEY = "STEP_IDENTIFIER"; - - public StepScope() { - super(); - setName("step"); - } - - /** - * This will be used to resolve expressions in step-scoped beans. - */ - @Override - public Object resolveContextualObject(String key) { - StepContext context = getContext(); - // TODO: support for attributes as well maybe (setters not exposed yet - // so not urgent). - return new BeanWrapperImpl(context).getPropertyValue(key); - } - - /** - * @see Scope#get(String, ObjectFactory) - */ - @Override - public Object get(String name, ObjectFactory objectFactory) { - StepContext context = getContext(); - Object scopedObject = context.getAttribute(name); - - if (scopedObject == null) { - - synchronized (mutex) { - scopedObject = context.getAttribute(name); - if (scopedObject == null) { - - if (logger.isDebugEnabled()) { - logger.debug(String.format("Creating object in scope=%s, name=%s", this.getName(), name)); - } - - - scopedObject = objectFactory.getObject(); - context.setAttribute(name, scopedObject); - - } - - } - - } - return scopedObject; - } - - /** - * @see Scope#getConversationId() - */ - @Override - public String getConversationId() { - StepContext context = getContext(); - return context.getId(); - } - - /** - * @see Scope#registerDestructionCallback(String, Runnable) - */ - @Override - public void registerDestructionCallback(String name, Runnable callback) { - StepContext context = getContext(); - if (logger.isDebugEnabled()) { - logger.debug(String.format("Registered destruction callback in scope=%s, name=%s", this.getName(), name)); - } - context.registerDestructionCallback(name, callback); - } - - /** - * @see Scope#remove(String) - */ - @Override - public Object remove(String name) { - StepContext context = getContext(); - if (logger.isDebugEnabled()) { - logger.debug(String.format("Removing from scope=%s, name=%s", this.getName(), name)); - } - return context.removeAttribute(name); - } - - /** - * Get an attribute accessor in the form of a {@link StepContext} that can - * be used to store scoped bean instances. - * - * @return the current step context which we can use as a scope storage - * medium - */ - private StepContext getContext() { - StepContext context = StepSynchronizationManager.getContext(); - if (context == null) { - throw new IllegalStateException("No context holder available for step scope"); - } - return context; - } - - @Override - public String getTargetNamePrefix() { - return TARGET_NAME_PREFIX; - } -} +/* + * Copyright 2006-2023 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. + * You may obtain a copy of the License at + * + * https://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.springframework.batch.core.scope; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.batch.core.scope.context.StepContext; +import org.springframework.batch.core.scope.context.StepSynchronizationManager; +import org.springframework.beans.BeanWrapper; +import org.springframework.beans.BeanWrapperImpl; +import org.springframework.beans.factory.ObjectFactory; +import org.springframework.beans.factory.config.Scope; + +/** + * Scope for step context. Objects in this scope use the Spring container as an + * object factory, so there is only one instance of such a bean per executing + * step. All objects in this scope are <aop:scoped-proxy/> (no need to + * decorate the bean definitions).
+ *
+ * + * In addition, support is provided for late binding of references accessible + * from the {@link StepContext} using #{..} placeholders. Using this feature, + * bean properties can be pulled from the step or job execution context and the + * job parameters. E.g. + * + *
+ * <bean id="..." class="..." scope="step">
+ * 	<property name="parent" ref="#{stepExecutionContext[helper]}" />
+ * </bean>
+ *
+ * <bean id="..." class="..." scope="step">
+ * 	<property name="name" value="#{stepExecutionContext['input.name']}" />
+ * </bean>
+ *
+ * <bean id="..." class="..." scope="step">
+ * 	<property name="name" value="#{jobParameters[input]}" />
+ * </bean>
+ *
+ * <bean id="..." class="..." scope="step">
+ * 	<property name="name" value="#{jobExecutionContext['input.stem']}.txt" />
+ * </bean>
+ * 
+ * + * The {@link StepContext} is referenced using standard bean property paths (as + * per {@link BeanWrapper}). The examples above all show the use of the Map + * accessors provided as a convenience for step and job attributes. + * + * @author Dave Syer + * @author Michael Minella + * @since 2.0 + */ +public class StepScope extends BatchScopeSupport { + + private static final String TARGET_NAME_PREFIX = "scopedTarget."; + + private Log logger = LogFactory.getLog(getClass()); + + private final Object mutex = new Object(); + + /** + * Context key for clients to use for conversation identifier. + */ + public static final String ID_KEY = "STEP_IDENTIFIER"; + + public StepScope() { + super(); + setName("step"); + } + + /** + * This will be used to resolve expressions in step-scoped beans. + */ + @Override + public Object resolveContextualObject(String key) { + StepContext context = getContext(); + // TODO: support for attributes as well maybe (setters not exposed yet + // so not urgent). + return new BeanWrapperImpl(context).getPropertyValue(key); + } + + /** + * @see Scope#get(String, ObjectFactory) + */ + @Override + public Object get(String name, ObjectFactory objectFactory) { + StepContext context = getContext(); + Object scopedObject = context.getAttribute(name); + + if (scopedObject == null) { + + synchronized (mutex) { + scopedObject = context.getAttribute(name); + if (scopedObject == null) { + + if (logger.isDebugEnabled()) { + logger.debug(String.format("Creating object in scope=%s, name=%s", this.getName(), name)); + } + + + scopedObject = objectFactory.getObject(); + context.setAttribute(name, scopedObject); + + } + + } + + } + return scopedObject; + } + + /** + * @see Scope#getConversationId() + */ + @Override + public String getConversationId() { + StepContext context = getContext(); + return context.getId(); + } + + /** + * @see Scope#registerDestructionCallback(String, Runnable) + */ + @Override + public void registerDestructionCallback(String name, Runnable callback) { + StepContext context = getContext(); + if (logger.isDebugEnabled()) { + logger.debug(String.format("Registered destruction callback in scope=%s, name=%s", this.getName(), name)); + } + context.registerDestructionCallback(name, callback); + } + + /** + * @see Scope#remove(String) + */ + @Override + public Object remove(String name) { + StepContext context = getContext(); + if (logger.isDebugEnabled()) { + logger.debug(String.format("Removing from scope=%s, name=%s", this.getName(), name)); + } + return context.removeAttribute(name); + } + + /** + * Get an attribute accessor in the form of a {@link StepContext} that can + * be used to store scoped bean instances. + * + * @return the current step context which we can use as a scope storage + * medium + */ + private StepContext getContext() { + StepContext context = StepSynchronizationManager.getContext(); + if (context == null) { + throw new IllegalStateException("No context holder available for step scope"); + } + return context; + } + + @Override + public String getTargetNamePrefix() { + return TARGET_NAME_PREFIX; + } +} diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/annotation/StepScopeConfigurationTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/annotation/StepScopeConfigurationTests.java index 2af2dc03b..82d081bcd 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/annotation/StepScopeConfigurationTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/annotation/StepScopeConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2006-2019 the original author or authors. + * Copyright 2006-2023 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. @@ -103,6 +103,19 @@ public class StepScopeConfigurationTests { assertEquals("STEP", value.call()); } + /** + * @see org.springframework.batch.core.configuration.xml.CoreNamespaceUtils#autoregisterBeansForNamespace + */ + @Test + public void testStepScopeUsingNamespaceAutoregisterBeans() throws Exception { + init(StepScopeConfigurationTestsUsingNamespaceAutoregisterBeans.class); + + ISimpleHolder value = (ISimpleHolder) context.getBean("xmlValue"); + assertEquals("STEP", value.call()); + value = (ISimpleHolder) context.getBean("javaValue"); + assertEquals("STEP", value.call()); + } + @Test public void testStepScopeWithProxyTargetClassInjected() throws Exception { init(StepScopeConfigurationInjectingProxy.class); @@ -187,7 +200,14 @@ public class StepScopeConfigurationTests { } } - public static class SimpleHolder { + public static interface ISimpleHolder { + + String call() throws Exception; + + } + + public static class SimpleHolder implements ISimpleHolder { + private final String value; protected SimpleHolder() { @@ -222,6 +242,18 @@ public class StepScopeConfigurationTests { @EnableBatchProcessing public static class StepScopeConfigurationXmlImportUsingNamespace { + @Bean + @StepScope + protected SimpleHolder javaValue(@Value("#{stepExecution.stepName}") final String value) { + return new SimpleHolder(value); + } + + } + + @Configuration + @ImportResource("org/springframework/batch/core/configuration/annotation/StepScopeConfigurationTestsUsingNamespaceAutoregisterBeans-context.xml") + public static class StepScopeConfigurationTestsUsingNamespaceAutoregisterBeans { + @Bean @StepScope protected SimpleHolder javaValue(@Value("#{stepExecution.stepName}") diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/annotation/StepScopeConfigurationTestsUsingNamespaceAutoregisterBeans-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/annotation/StepScopeConfigurationTestsUsingNamespaceAutoregisterBeans-context.xml new file mode 100644 index 000000000..00b9a7e65 --- /dev/null +++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/annotation/StepScopeConfigurationTestsUsingNamespaceAutoregisterBeans-context.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + +