diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/job/AbstractJob.java b/spring-batch-core/src/main/java/org/springframework/batch/core/job/AbstractJob.java index a8984d09c..7468b77a8 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/job/AbstractJob.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/job/AbstractJob.java @@ -335,9 +335,9 @@ InitializingBean { if (execution.getStatus().isLessThanOrEqualTo(BatchStatus.STOPPED) && execution.getStepExecutions().isEmpty()) { ExitStatus exitStatus = execution.getExitStatus(); - execution - .setExitStatus(exitStatus.and(ExitStatus.NOOP - .addExitDescription("All steps already completed or no steps configured for this job."))); + ExitStatus newExitStatus = + ExitStatus.NOOP.addExitDescription("All steps already completed or no steps configured for this job."); + execution.setExitStatus(exitStatus.and(newExitStatus)); } execution.setEndTime(new Date()); diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/scope/BatchScopeSupport.java b/spring-batch-core/src/main/java/org/springframework/batch/core/scope/BatchScopeSupport.java new file mode 100644 index 000000000..f65d9dc57 --- /dev/null +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/scope/BatchScopeSupport.java @@ -0,0 +1,226 @@ +/* + * Copyright 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 + * + * http://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.springframework.aop.scope.ScopedProxyUtils; +import org.springframework.batch.core.scope.context.StepContext; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.BeanDefinitionHolder; +import org.springframework.beans.factory.config.BeanDefinitionVisitor; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.config.Scope; +import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.core.Ordered; +import org.springframework.util.Assert; +import org.springframework.util.StringValueResolver; + +/** + * ScopeSupport. + * + * @author Michael Minella + * @since 3.0 + */ +public abstract class BatchScopeSupport implements Scope, BeanFactoryPostProcessor, Ordered { + + private boolean autoProxy = true; + + private boolean proxyTargetClass = false; + + private String name; + + private int order = Ordered.LOWEST_PRECEDENCE; + + /** + * @param order the order value to set priority of callback execution for + * the {@link BeanFactoryPostProcessor} part of this scope bean. + */ + public void setOrder(int order) { + this.order = order; + } + + @Override + public int getOrder() { + return order; + } + + public String getName() { + return this.name; + } + + /** + * Public setter for the name property. This can then be used as a bean + * definition attribute, e.g. scope="job". + * + * @param name the name to set for this scope. + */ + public void setName(String name) { + this.name = name; + } + + /** + * Flag to indicate that proxies should use dynamic subclassing. This allows + * classes with no interface to be proxied. Defaults to false. + * + * @param proxyTargetClass set to true to have proxies created using dynamic + * subclasses + */ + public void setProxyTargetClass(boolean proxyTargetClass) { + this.proxyTargetClass = proxyTargetClass; + } + + /** + * Flag to indicate that bean definitions need not be auto proxied. This gives control back to the declarer of the + * bean definition (e.g. in an @Configuration class). + * + * @param autoProxy the flag value to set (default true) + */ + public void setAutoProxy(boolean autoProxy) { + this.autoProxy = autoProxy; + } + + public abstract String getTargetNamePrefix(); + + /** + * Register this scope with the enclosing BeanFactory. + * + * @see BeanFactoryPostProcessor#postProcessBeanFactory(ConfigurableListableBeanFactory) + * + * @param beanFactory the BeanFactory to register with + * @throws BeansException if there is a problem. + */ + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + + beanFactory.registerScope(name, this); + + if(!autoProxy) { + return; + } + + Assert.state(beanFactory instanceof BeanDefinitionRegistry, + "BeanFactory was not a BeanDefinitionRegistry, so JobScope cannot be used."); + BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; + + for (String beanName : beanFactory.getBeanDefinitionNames()) { + if (!beanName.startsWith(getTargetNamePrefix())) { + 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 (scoped && !definition.isAbstract()) { + createScopedProxy(beanName, definition, registry, proxyTargetClass); + } + } + } + + } + + /** + * Wrap a target bean definition in a proxy that defers initialization until + * after the {@link StepContext} is available. Amounts to adding + * <aop-auto-proxy/> to a step scoped bean. + * + * @param beanName the bean name to replace + * @param definition the bean definition to replace + * @param registry the enclosing {@link BeanDefinitionRegistry} + * @param proxyTargetClass true if we need to force use of dynamic + * subclasses + * @return a {@link BeanDefinitionHolder} for the new representation of the + * target. Caller should register it if needed to be visible at top level in + * bean factory. + */ + protected static BeanDefinitionHolder createScopedProxy(String beanName, BeanDefinition definition, + BeanDefinitionRegistry registry, boolean proxyTargetClass) { + + BeanDefinitionHolder proxyHolder; + + proxyHolder = ScopedProxyUtils.createScopedProxy(new BeanDefinitionHolder(definition, beanName), registry, + proxyTargetClass); + + registry.registerBeanDefinition(beanName, proxyHolder.getBeanDefinition()); + + return proxyHolder; + + } + + /** + * Helper class to scan a bean definition hierarchy and force the use of + * auto-proxy for step scoped beans. + * + * @author Dave Syer + * + */ + protected static class Scopifier extends BeanDefinitionVisitor { + + private final boolean proxyTargetClass; + + private final BeanDefinitionRegistry registry; + + private final String scope; + + private final boolean scoped; + + public Scopifier(BeanDefinitionRegistry registry, String scope, boolean proxyTargetClass, boolean scoped) { + super(new StringValueResolver() { + @Override + public String resolveStringValue(String value) { + return value; + } + }); + 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) { + definition = (BeanDefinition) value; + beanName = BeanDefinitionReaderUtils.generateBeanName(definition, registry); + } + else if (value instanceof BeanDefinitionHolder) { + BeanDefinitionHolder holder = (BeanDefinitionHolder) value; + definition = holder.getBeanDefinition(); + beanName = holder.getBeanName(); + } + + if (definition != null) { + boolean nestedScoped = scope.equals(definition.getScope()); + boolean scopeChangeRequiresProxy = !scoped && nestedScoped; + if (scopeChangeRequiresProxy) { + // Exit here so that nested inner bean definitions are not + // analysed + return createScopedProxy(beanName, definition, registry, proxyTargetClass); + } + } + + // 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/JobScope.java b/spring-batch-core/src/main/java/org/springframework/batch/core/scope/JobScope.java index e8de653a8..d49667a5f 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/scope/JobScope.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/scope/JobScope.java @@ -1,5 +1,5 @@ /* - * Copyright 2006-2007 the original author or authors. + * 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. @@ -19,8 +19,6 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.batch.core.scope.context.JobContext; import org.springframework.batch.core.scope.context.JobSynchronizationManager; -import org.springframework.batch.core.scope.util.ContextFactory; -import org.springframework.batch.core.scope.util.JobContextFactory; import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeanWrapperImpl; import org.springframework.beans.factory.ObjectFactory; @@ -32,31 +30,34 @@ import org.springframework.beans.factory.config.Scope; * job. 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 JobContext} using #{..} placeholders. Using this feature, * bean properties can be pulled from the job or job execution context and the * job parameters. E.g. - * + * *
  * <bean id="..." class="..." scope="job">
  * 	<property name="name" value="#{jobParameters[input]}" />
  * </bean>
- * 
+ *
  * <bean id="..." class="..." scope="job">
  * 	<property name="name" value="#{jobExecutionContext['input.stem']}.txt" />
  * </bean>
  * 
- * + * * The {@link JobContext} 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 job attributes. - * + * * @author Dave Syer * @author Jimmy Praet (create JobScope based on {@link StepScope}) - * @since 2.0 + * @author Michael Minella + * @since 3.0 */ -public class JobScope extends ScopeSupport { +public class JobScope extends BatchScopeSupport { + + private static final String TARGET_NAME_PREFIX = "jobScopedTarget."; private Log logger = LogFactory.getLog(getClass()); @@ -67,20 +68,15 @@ public class JobScope extends ScopeSupport { */ public static final String ID_KEY = "JOB_IDENTIFIER"; - /** - * The ContextFactory. - */ - private static final ContextFactory CONTEXT_FACTORY = new JobContextFactory(); - public JobScope() { - super("job", CONTEXT_FACTORY); + super(); + setName("job"); } /** - * If Spring 3.0 is available, this will be used to resolve expressions in - * job-scoped beans. This method is part of the Scope SPI in Spring 3.0, - * but should just be ignored by earlier versions of Spring. + * This will be used to resolve expressions in job-scoped beans. */ + @Override public Object resolveContextualObject(String key) { JobContext context = getContext(); // TODO: support for attributes as well maybe (setters not exposed yet @@ -91,8 +87,9 @@ public class JobScope extends ScopeSupport { /** * @see Scope#get(String, ObjectFactory) */ + @SuppressWarnings("rawtypes") + @Override public Object get(String name, ObjectFactory objectFactory) { - JobContext context = getContext(); Object scopedObject = context.getAttribute(name); @@ -118,6 +115,7 @@ public class JobScope extends ScopeSupport { /** * @see Scope#getConversationId() */ + @Override public String getConversationId() { JobContext context = getContext(); return context.getId(); @@ -126,6 +124,7 @@ public class JobScope extends ScopeSupport { /** * @see Scope#registerDestructionCallback(String, Runnable) */ + @Override public void registerDestructionCallback(String name, Runnable callback) { JobContext context = getContext(); logger.debug(String.format("Registered destruction callback in scope=%s, name=%s", this.getName(), name)); @@ -135,6 +134,7 @@ public class JobScope extends ScopeSupport { /** * @see Scope#remove(String) */ + @Override public Object remove(String name) { JobContext context = getContext(); logger.debug(String.format("Removing from scope=%s, name=%s", this.getName(), name)); @@ -144,7 +144,7 @@ public class JobScope extends ScopeSupport { /** * Get an attribute accessor in the form of a {@link JobContext} that can * be used to store scoped bean instances. - * + * * @return the current job context which we can use as a scope storage * medium */ @@ -156,4 +156,8 @@ public class JobScope extends ScopeSupport { return context; } -} \ No newline at end of file + @Override + public String getTargetNamePrefix() { + return TARGET_NAME_PREFIX; + } +} diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/scope/ScopeSupport.java b/spring-batch-core/src/main/java/org/springframework/batch/core/scope/ScopeSupport.java deleted file mode 100644 index ded838484..000000000 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/scope/ScopeSupport.java +++ /dev/null @@ -1,316 +0,0 @@ -/* - * Copyright 2006-2007 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 - * - * http://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.springframework.aop.scope.ScopedProxyUtils; -import org.springframework.batch.core.scope.util.ContextFactory; -import org.springframework.batch.core.scope.util.PlaceholderProxyFactoryBean; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.BeanDefinitionHolder; -import org.springframework.beans.factory.config.BeanDefinitionVisitor; -import org.springframework.beans.factory.config.BeanFactoryPostProcessor; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.config.Scope; -import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.core.Ordered; -import org.springframework.util.Assert; -import org.springframework.util.ReflectionUtils; -import org.springframework.util.StringValueResolver; - -/** - * ScopeSupport. - * - * @author Dave Syer - */ -public abstract class ScopeSupport implements Scope, BeanFactoryPostProcessor, Ordered { - - private static boolean springThreeDetected; - - private static boolean cachedSpringThreeResult; - - private int order = Ordered.LOWEST_PRECEDENCE; - - private String name; - - private boolean proxyTargetClass = false; - - private ContextFactory contextFactory; - - /** - * ScopeSupport constructor. - * - * @param defaultScopeName the default scope name - * @param contextFactory the ContextFactory - */ - protected ScopeSupport(String defaultScopeName, ContextFactory contextFactory) { - this.name = defaultScopeName; - this.contextFactory = contextFactory; - } - - /** - * Flag to indicate that proxies should use dynamic subclassing. This allows - * classes with no interface to be proxied. Defaults to false. - * - * @param proxyTargetClass set to true to have proxies created using dynamic - * subclasses - */ - public void setProxyTargetClass(boolean proxyTargetClass) { - this.proxyTargetClass = proxyTargetClass; - } - - /** - * @param order the order value to set priority of callback execution for - * the {@link BeanFactoryPostProcessor} part of this scope bean. - */ - public void setOrder(int order) { - this.order = order; - } - - public int getOrder() { - return order; - } - - /** - * Public setter for the name property. This can then be used as a bean - * definition attribute, e.g. scope="step". - * - * @param name the name to set for this scope. - */ - public void setName(String name) { - this.name = name; - } - - /** - * Return the scope name. - * - * @return the scope name - */ - public String getName() { - return name; - } - - /** - * Register this scope with the enclosing BeanFactory. - * - * @see BeanFactoryPostProcessor#postProcessBeanFactory(ConfigurableListableBeanFactory) - * - * @param beanFactory the BeanFactory to register with - * @throws BeansException if there is a problem. - */ - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { - - beanFactory.registerScope(name, this); - - Assert.state(beanFactory instanceof BeanDefinitionRegistry, - "BeanFactory was not a BeanDefinitionRegistry, so scope cannot be used."); - BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; - - 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, contextFactory); - scopifier.visitBeanDefinition(definition); - if (scoped) { - if (!isSpringThree()) { - new ExpressionHider(name, scoped).visitBeanDefinition(definition); - } - createScopedProxy(beanName, definition, registry, proxyTargetClass, contextFactory); - } - } - - } - - private static boolean isSpringThree() { - if (!cachedSpringThreeResult) { - springThreeDetected = ReflectionUtils.findMethod(Scope.class, "resolveContextualObject", - new Class[] { String.class }) != null; - cachedSpringThreeResult = true; - } - return springThreeDetected; - } - - /** - * Wrap a target bean definition in a proxy that defers initialization until - * after the context is available. Amounts to adding - * <aop-auto-proxy/> to a scoped bean. Also if Spring EL is not - * available will enable a weak version of late binding as described in the - * class-level docs. - * - * @param beanName the bean name to replace - * @param definition the bean definition to replace - * @param registry the enclosing {@link BeanDefinitionRegistry} - * @param proxyTargetClass true if we need to force use of dynamic - * subclasses - * @param contextFactory the ContextFactory - * @return a {@link BeanDefinitionHolder} for the new representation of the - * target. Caller should register it if needed to be visible at top level in - * bean factory. - */ - private static BeanDefinitionHolder createScopedProxy(String beanName, BeanDefinition definition, - BeanDefinitionRegistry registry, boolean proxyTargetClass, ContextFactory contextFactory) { - - BeanDefinitionHolder proxyHolder; - - if (isSpringThree()) { - proxyHolder = ScopedProxyUtils.createScopedProxy(new BeanDefinitionHolder(definition, beanName), registry, - proxyTargetClass); - } - else { - // Create the scoped proxy... - proxyHolder = PlaceholderProxyFactoryBean.createScopedProxy(new BeanDefinitionHolder(definition, beanName), - registry, proxyTargetClass, contextFactory); - // ...and register it under the original target name - } - registry.registerBeanDefinition(beanName, proxyHolder.getBeanDefinition()); - - return proxyHolder; - - } - - /** - * Helper class to scan a bean definition hierarchy and force the use of - * auto-proxy for scoped beans. - * - * @author Dave Syer - * - */ - private static class Scopifier extends BeanDefinitionVisitor { - - private final boolean proxyTargetClass; - - private final BeanDefinitionRegistry registry; - - private final String scope; - - private final boolean scoped; - - private final ContextFactory contextFactory; - - public Scopifier(BeanDefinitionRegistry registry, String scope, boolean proxyTargetClass, boolean scoped, - ContextFactory contextFactory) { - super(new StringValueResolver() { - public String resolveStringValue(String value) { - return value; - } - }); - this.registry = registry; - this.proxyTargetClass = proxyTargetClass; - this.scope = scope; - this.scoped = scoped; - this.contextFactory = contextFactory; - } - - @Override - protected Object resolveValue(Object value) { - - BeanDefinition definition = null; - String beanName = null; - if (value instanceof BeanDefinition) { - definition = (BeanDefinition) value; - beanName = BeanDefinitionReaderUtils.generateBeanName(definition, registry); - } - else if (value instanceof BeanDefinitionHolder) { - BeanDefinitionHolder holder = (BeanDefinitionHolder) value; - definition = holder.getBeanDefinition(); - beanName = holder.getBeanName(); - } - - if (definition != null) { - boolean nestedScoped = scope.equals(definition.getScope()); - boolean scopeChangeRequiresProxy = !scoped && nestedScoped; - if (!isSpringThree()) { - new ExpressionHider(scope, nestedScoped).visitBeanDefinition(definition); - } - if (scopeChangeRequiresProxy) { - // Exit here so that nested inner bean definitions are not - // analysed - return createScopedProxy(beanName, definition, registry, proxyTargetClass, contextFactory); - } - } - - // 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) { - String otherScope = definition.getScope(); - boolean scopeChange = !scope.equals(otherScope); - if (scopeChange) { - new ExpressionHider(otherScope == null ? scope : otherScope, !scoped) - .visitBeanDefinition(definition); - // Exit here so that nested inner bean definitions are not - // analysed by both visitors - 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/StepScope.java b/spring-batch-core/src/main/java/org/springframework/batch/core/scope/StepScope.java index 3be3bce90..0959d4d70 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,5 +1,5 @@ /* - * Copyright 2006-2007 the original author or authors. + * 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. @@ -19,8 +19,6 @@ 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.batch.core.scope.util.ContextFactory; -import org.springframework.batch.core.scope.util.StepContextFactory; import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeanWrapperImpl; import org.springframework.beans.factory.ObjectFactory; @@ -32,38 +30,41 @@ import org.springframework.beans.factory.config.Scope; * 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 ScopeSupport { +public class StepScope extends BatchScopeSupport { + + private static final String TARGET_NAME_PREFIX = "stepScopedTarget."; private Log logger = LogFactory.getLog(getClass()); @@ -74,20 +75,15 @@ public class StepScope extends ScopeSupport { */ public static final String ID_KEY = "STEP_IDENTIFIER"; - /** - * The ContextFactory. - */ - private static final ContextFactory CONTEXT_FACTORY = new StepContextFactory(); - public StepScope() { - super("step", CONTEXT_FACTORY); + super(); + setName("step"); } /** - * If Spring 3.0 is available, this will be used to resolve expressions in - * step-scoped beans. This method is part of the Scope SPI in Spring 3.0, - * but should just be ignored by earlier versions of Spring. + * 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 @@ -98,8 +94,9 @@ public class StepScope extends ScopeSupport { /** * @see Scope#get(String, ObjectFactory) */ + @SuppressWarnings("rawtypes") + @Override public Object get(String name, ObjectFactory objectFactory) { - StepContext context = getContext(); Object scopedObject = context.getAttribute(name); @@ -125,6 +122,7 @@ public class StepScope extends ScopeSupport { /** * @see Scope#getConversationId() */ + @Override public String getConversationId() { StepContext context = getContext(); return context.getId(); @@ -133,6 +131,7 @@ public class StepScope extends ScopeSupport { /** * @see Scope#registerDestructionCallback(String, Runnable) */ + @Override public void registerDestructionCallback(String name, Runnable callback) { StepContext context = getContext(); logger.debug(String.format("Registered destruction callback in scope=%s, name=%s", this.getName(), name)); @@ -142,6 +141,7 @@ public class StepScope extends ScopeSupport { /** * @see Scope#remove(String) */ + @Override public Object remove(String name) { StepContext context = getContext(); logger.debug(String.format("Removing from scope=%s, name=%s", this.getName(), name)); @@ -151,7 +151,7 @@ public class StepScope extends ScopeSupport { /** * 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 */ @@ -163,4 +163,8 @@ public class StepScope extends ScopeSupport { return context; } + @Override + public String getTargetNamePrefix() { + return TARGET_NAME_PREFIX; + } } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/scope/context/JobContext.java b/spring-batch-core/src/main/java/org/springframework/batch/core/scope/context/JobContext.java index acdd5e0b1..b838d0485 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/scope/context/JobContext.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/scope/context/JobContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2006-2007 the original author or authors. + * 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. @@ -41,9 +41,10 @@ import org.springframework.util.Assert; * based on bean paths. Has public getters for the job execution and * convenience methods for accessing commonly used properties like the {@link ExecutionContext} associated with the job * execution. - * + * * @author Dave Syer * @author Jimmy Praet (create JobContext based on {@link StepContext}) + * @since 3.0 */ public class JobContext extends SynchronizedAttributeAccessor { @@ -59,19 +60,19 @@ public class JobContext extends SynchronizedAttributeAccessor { /** * Convenient accessor for current job name identifier. - * + * * @return the job name identifier of the enclosing {@link JobInstance} associated with the current * {@link JobExecution} */ public String getJobName() { - Assert.state(jobExecution.getJobInstance() != null, "StepExecution does not have a JobInstance"); + Assert.state(jobExecution.getJobInstance() != null, "JobExecution does not have a JobInstance"); return jobExecution.getJobInstance().getJobName(); } /** * Convenient accessor for System properties to make it easy to access them * from placeholder expressions. - * + * * @return the current System properties */ public Properties getSystemProperties() { @@ -94,7 +95,7 @@ public class JobContext extends SynchronizedAttributeAccessor { */ public Map getJobParameters() { Map result = new HashMap(); - for (Entry entry : jobExecution.getJobInstance().getJobParameters().getParameters() + for (Entry entry : jobExecution.getJobParameters().getParameters() .entrySet()) { result.put(entry.getKey(), entry.getValue().getValue()); } @@ -103,7 +104,7 @@ public class JobContext extends SynchronizedAttributeAccessor { /** * Allow clients to register callbacks for clean up on close. - * + * * @param name * the callback id (unique attribute key in this context) * @param callback @@ -129,7 +130,7 @@ public class JobContext extends SynchronizedAttributeAccessor { /** * Override base class behaviour to ensure destruction callbacks are * unregistered as well as the default behaviour. - * + * * @see SynchronizedAttributeAccessor#removeAttribute(String) */ @Override @@ -182,7 +183,7 @@ public class JobContext extends SynchronizedAttributeAccessor { /** * The current {@link JobExecution} that is active in this context. - * + * * @return the current {@link JobExecution} */ public JobExecution getJobExecution() { @@ -204,10 +205,12 @@ public class JobContext extends SynchronizedAttributeAccessor { */ @Override public boolean equals(Object other) { - if (!(other instanceof JobContext)) + if (!(other instanceof JobContext)) { return false; - if (other == this) + } + if (other == this) { return true; + } JobContext context = (JobContext) other; if (context.jobExecution == jobExecution) { return true; diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/scope/context/JobScopeManager.java b/spring-batch-core/src/main/java/org/springframework/batch/core/scope/context/JobScopeManager.java index 1007c18e5..9f8d45619 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/scope/context/JobScopeManager.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/scope/context/JobScopeManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2006-2007 the original author or authors. + * Copyright 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. @@ -25,9 +25,10 @@ import org.springframework.batch.core.JobExecution; * Convenient aspect to wrap a single threaded job execution, where the * implementation of the {@link Job} is not job scope aware (i.e. not the ones * provided by the framework). - * + * * @author Dave Syer * @author Jimmy Praet + * @since 3.0 */ @Aspect public class JobScopeManager { diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/scope/context/JobSynchronizationManager.java b/spring-batch-core/src/main/java/org/springframework/batch/core/scope/context/JobSynchronizationManager.java index 3d056aa08..b3a35a323 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/scope/context/JobSynchronizationManager.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/scope/context/JobSynchronizationManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2006-2007 the original author or authors. + * Copyright 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. @@ -17,6 +17,7 @@ package org.springframework.batch.core.scope.context; import org.springframework.batch.core.Job; import org.springframework.batch.core.JobExecution; +import org.springframework.batch.core.jsr.configuration.support.BatchPropertyContext; /** * Central convenience class for framework use in managing the job scope @@ -24,71 +25,68 @@ import org.springframework.batch.core.JobExecution; * it is the responsibility of every {@link Job} implementation to ensure that * a {@link JobContext} is available on every thread that might be involved in * a job execution, including worker threads from a pool. - * + * * @author Dave Syer * @author Jimmy Praet + * @since 3.0 */ public class JobSynchronizationManager { - private static final SynchronizationManagerSupport synchronizationManager = - new SynchronizationManagerSupport() { + private static final SynchronizationManagerSupport manager = new SynchronizationManagerSupport() { - @Override - protected JobContext createNewContext(JobExecution execution) { - return new JobContext(execution); - } + @Override + protected JobContext createNewContext(JobExecution execution, BatchPropertyContext args) { + return new JobContext(execution); + } - @Override - protected void close(JobContext context) { - context.close(); - } - }; - - /* - * We have to deal with single and multi-threaded execution, with a single - * and with multiple job execution instances. That's 2x2 = 4 scenarios. - */ + @Override + protected void close(JobContext context) { + context.close(); + } + }; /** * Getter for the current context if there is one, otherwise returns null. - * + * * @return the current {@link JobContext} or null if there is none (if one - * has not been registered for this thread). + * has not been registered for this thread). */ public static JobContext getContext() { - return synchronizationManager.getContext(); + return manager.getContext(); } /** - * Register a context with the current thread - always put a matching {@link #close()} call in a finally block to - * ensure that the correct context is available in the enclosing block. - * - * @param jobExecution the job context to register - * @return a new {@link JobContext} or the current one if it has the same {@link JobExecution} + * Register a context with the current thread - always put a matching + * {@link #close()} call in a finally block to ensure that the correct + * context is available in the enclosing block. + * + * @param JobExecution the step context to register + * @return a new {@link JobContext} or the current one if it has the same + * {@link JobExecution} */ - public static JobContext register(JobExecution jobExecution) { - return synchronizationManager.register(jobExecution); + public static JobContext register(JobExecution JobExecution) { + return manager.register(JobExecution); } /** * Method for de-registering the current context - should always and only be - * used by in conjunction with a matching {@link #register(JobExecution)} to ensure that {@link #getContext()} - * always returns the correct value. + * used by in conjunction with a matching {@link #register(JobExecution)} + * to ensure that {@link #getContext()} always returns the correct value. * Does not call {@link JobContext#close()} - that is left up to the caller * because he has a reference to the context (having registered it) and only - * he has knowledge of when the job actually ended. + * he has knowledge of when the step actually ended. */ public static void close() { - synchronizationManager.close(); + manager.close(); } /** - * A convenient "deep" close operation. Call this instead of {@link #close()} if the job execution for the current - * context is ending. - * Delegates to {@link JobContext#close()} and then ensures that {@link #close()} is also called in a finally block. + * A convenient "deep" close operation. Call this instead of + * {@link #close()} if the step execution for the current context is ending. + * Delegates to {@link JobContext#close()} and then ensures that + * {@link #close()} is also called in a finally block. */ public static void release() { - synchronizationManager.release(); + manager.release(); } - } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/scope/context/StepSynchronizationManager.java b/spring-batch-core/src/main/java/org/springframework/batch/core/scope/context/StepSynchronizationManager.java index 11f25bf09..a1c16c295 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/scope/context/StepSynchronizationManager.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/scope/context/StepSynchronizationManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2006-2007 the original author or authors. + * 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. @@ -32,24 +32,27 @@ import org.springframework.batch.core.jsr.configuration.support.BatchPropertyCon */ public class StepSynchronizationManager { - private static final SynchronizationManagerSupport synchronizationManager = + private static final SynchronizationManagerSupport manager = new SynchronizationManagerSupport() { - @Override - protected StepContext createNewContext(StepExecution execution) { - return new StepContext(execution); - } + @Override + protected StepContext createNewContext(StepExecution execution, BatchPropertyContext propertyContext) { + StepContext context; - @Override - protected void close(StepContext context) { - context.close(); - } - }; + if(propertyContext != null) { + context = new StepContext(execution, propertyContext); + } else { + context = new StepContext(execution); + } - /* - * We have to deal with single and multi-threaded execution, with a single - * and with multiple step execution instances. That's 2x2 = 4 scenarios. - */ + return context; + } + + @Override + protected void close(StepContext context) { + context.close(); + } + }; /** * Getter for the current context if there is one, otherwise returns null. @@ -58,7 +61,7 @@ public class StepSynchronizationManager { * has not been registered for this thread). */ public static StepContext getContext() { - return synchronizationManager.getContext(); + return manager.getContext(); } /** @@ -71,7 +74,7 @@ public class StepSynchronizationManager { * {@link StepExecution} */ public static StepContext register(StepExecution stepExecution) { - return synchronizationManager.register(stepExecution); + return manager.register(stepExecution); } /** @@ -84,20 +87,7 @@ public class StepSynchronizationManager { * {@link StepExecution} */ public static StepContext register(StepExecution stepExecution, BatchPropertyContext propertyContext) { - if (stepExecution == null) { - return null; - } - getCurrent().push(stepExecution); - StepContext context; - synchronized (contexts) { - context = contexts.get(stepExecution); - if (context == null) { - context = new StepContext(stepExecution, propertyContext); - contexts.put(stepExecution, context); - } - } - increment(); - return context; + return manager.register(stepExecution, propertyContext); } /** @@ -109,7 +99,7 @@ public class StepSynchronizationManager { * he has knowledge of when the step actually ended. */ public static void close() { - synchronizationManager.close(); + manager.close(); } /** @@ -119,7 +109,6 @@ public class StepSynchronizationManager { * {@link #close()} is also called in a finally block. */ public static void release() { - synchronizationManager.release(); + manager.release(); } - } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/scope/context/SynchronizationManagerSupport.java b/spring-batch-core/src/main/java/org/springframework/batch/core/scope/context/SynchronizationManagerSupport.java index 647c01e25..d7cf71feb 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/scope/context/SynchronizationManagerSupport.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/scope/context/SynchronizationManagerSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2006-2007 the original author or authors. + * Copyright 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. @@ -20,12 +20,16 @@ import java.util.Map; import java.util.Stack; import java.util.concurrent.atomic.AtomicInteger; +import org.springframework.batch.core.jsr.configuration.support.BatchPropertyContext; + + /** * Central convenience class for framework use in managing the scope * context. - * + * * @author Dave Syer * @author Jimmy Praet + * @since 3.0 */ public abstract class SynchronizationManagerSupport { @@ -58,7 +62,7 @@ public abstract class SynchronizationManagerSupport { /** * Getter for the current context if there is one, otherwise returns null. - * + * * @return the current context or null if there is none (if one * has not been registered for this thread). */ @@ -75,7 +79,7 @@ public abstract class SynchronizationManagerSupport { * Register a context with the current thread - always put a matching {@link #close()} call in a finally block to * ensure that the correct * context is available in the enclosing block. - * + * * @param execution the execution to register * @return a new context or the current one if it has the same * execution @@ -89,7 +93,33 @@ public abstract class SynchronizationManagerSupport { synchronized (contexts) { context = contexts.get(execution); if (context == null) { - context = createNewContext(execution); + context = createNewContext(execution, null); + contexts.put(execution, context); + } + } + increment(); + return context; + } + + /** + * Register a context with the current thread - always put a matching {@link #close()} call in a finally block to + * ensure that the correct + * context is available in the enclosing block. + * + * @param execution the execution to register + * @return a new context or the current one if it has the same + * execution + */ + public C register(E execution, BatchPropertyContext propertyContext) { + if (execution == null) { + return null; + } + getCurrent().push(execution); + C context; + synchronized (contexts) { + context = contexts.get(execution); + if (context == null) { + context = createNewContext(execution, propertyContext); contexts.put(execution, context); } } @@ -99,7 +129,7 @@ public abstract class SynchronizationManagerSupport { /** * Method for de-registering the current context - should always and only be - * used by in conjunction with a matching {@link #register(E)} to ensure that {@link #getContext()} always returns + * used by in conjunction with a matching {@link #register(Object)} to ensure that {@link #getContext()} always returns * the correct value. * Does not call close on the context - that is left up to the caller * because he has a reference to the context (having registered it) and only @@ -126,7 +156,7 @@ public abstract class SynchronizationManagerSupport { } } - private void increment() { + public void increment() { E current = getCurrent().peek(); if (current != null) { AtomicInteger count; @@ -141,7 +171,7 @@ public abstract class SynchronizationManagerSupport { } } - private Stack getCurrent() { + public Stack getCurrent() { if (executionHolder.get() == null) { executionHolder.set(new Stack()); } @@ -151,7 +181,7 @@ public abstract class SynchronizationManagerSupport { /** * A convenient "deep" close operation. Call this instead of {@link #close()} if the execution for the current * context is ending. - * Delegates to {@link context#close()} and then ensures that {@link #close()} is also called in a finally block. + * Delegates to {@link #close(Object)} and then ensures that {@link #close()} is also called in a finally block. */ public void release() { C context = getContext(); @@ -166,6 +196,6 @@ public abstract class SynchronizationManagerSupport { protected abstract void close(C context); - protected abstract C createNewContext(E execution); + protected abstract C createNewContext(E execution, BatchPropertyContext propertyContext); } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/scope/util/JobContextFactory.java b/spring-batch-core/src/main/java/org/springframework/batch/core/scope/util/JobContextFactory.java deleted file mode 100644 index 7e5fe1fcc..000000000 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/scope/util/JobContextFactory.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2006-2007 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 - * - * http://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.util; - -import org.springframework.batch.core.scope.context.JobContext; -import org.springframework.batch.core.scope.context.JobSynchronizationManager; - -/** - * Implementation of {@link ContextFactory} that provides the current - * {@link JobContext} as a context object. - * - * @author Dave Syer - * @author Jimmy Praet - */ -public class JobContextFactory implements ContextFactory { - - public Object getContext() { - return JobSynchronizationManager.getContext(); - } - - public String getContextId() { - JobContext context = JobSynchronizationManager.getContext(); - return context!=null ? (String) context.getId() : "sysinit"; - } - -} diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/AutoRegisteringJobScopeTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/AutoRegisteringJobScopeTests.java index 7bd8fe1f1..1be49773c 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/AutoRegisteringJobScopeTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/AutoRegisteringJobScopeTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2006-2009 the original author or authors. + * Copyright 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. @@ -30,10 +30,10 @@ import org.springframework.context.support.ClassPathXmlApplicationContext; * @author Jimmy Praet */ public class AutoRegisteringJobScopeTests { - + @Test public void testJobElement() throws Exception { - ConfigurableApplicationContext ctx = + ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext( "org/springframework/batch/core/configuration/xml/AutoRegisteringJobScopeForJobElementTests-context.xml"); @SuppressWarnings("unchecked") @@ -43,7 +43,7 @@ public class AutoRegisteringJobScopeTests { @Test public void testStepElement() throws Exception { - ConfigurableApplicationContext ctx = + ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext( "org/springframework/batch/core/configuration/xml/AutoRegisteringJobScopeForStepElementTests-context.xml"); @SuppressWarnings("unchecked") diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/scope/JobScopeIntegrationTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/scope/JobScopeIntegrationTests.java index cffe4eafe..085d6f198 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/scope/JobScopeIntegrationTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/scope/JobScopeIntegrationTests.java @@ -22,6 +22,8 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) public class JobScopeIntegrationTests { + private static final String PROXY_TO_STRING_REGEX = "class .*\\$Proxy\\d+"; + @Autowired @Qualifier("vanilla") private Job vanilla; @@ -64,7 +66,7 @@ public class JobScopeIntegrationTests { assertNotNull(collaborator); assertEquals("bar", collaborator); assertTrue("Scoped proxy not created", ((String) TestJob.getContext().getAttribute("collaborator.class")) - .startsWith("class $Proxy")); + .matches(PROXY_TO_STRING_REGEX)); } @Test @@ -78,7 +80,7 @@ public class JobScopeIntegrationTests { assertNotNull(parent); assertEquals("bar", parent); assertTrue("Scoped proxy not created", ((String) TestJob.getContext().getAttribute("parent.class")) - .startsWith("class $Proxy")); + .matches(PROXY_TO_STRING_REGEX)); } @Test diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/scope/StepScopePerformanceTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/scope/StepScopePerformanceTests.java index e24b20453..2002eadbc 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/scope/StepScopePerformanceTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/scope/StepScopePerformanceTests.java @@ -37,10 +37,10 @@ public class StepScopePerformanceTests implements ApplicationContextAware { public void start() throws Exception { int count = doTest("vanilla", "warmup"); logger.info("Item count: "+count); + StepSynchronizationManager.close(); StepSynchronizationManager.register(new StepExecution("step", new JobExecution(0L),1L)); } - @Before @After public void cleanup() { StepSynchronizationManager.close(); diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/scope/context/JobContextTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/scope/context/JobContextTests.java index ed50c3e0b..a1efdcb6e 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/scope/context/JobContextTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/scope/context/JobContextTests.java @@ -24,6 +24,7 @@ import static org.junit.Assert.fail; import java.util.ArrayList; import java.util.List; +import org.junit.Before; import org.junit.Test; import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.JobInstance; @@ -37,11 +38,20 @@ import org.springframework.batch.item.ExecutionContext; */ public class JobContextTests { - private List list = new ArrayList(); + private List list; - private JobExecution jobExecution = new JobExecution(new JobInstance(2L, null, "job"), 1L); + private JobExecution jobExecution; - private JobContext context = new JobContext(jobExecution); + private JobContext context; + + @Before + public void setUp() { + jobExecution = new JobExecution(1l); + JobInstance jobInstance = new JobInstance(2l, "job"); + jobExecution.setJobInstance(jobInstance); + context = new JobContext(jobExecution); + list = new ArrayList(); + } @Test public void testGetJobExecution() { @@ -79,6 +89,7 @@ public class JobContextTests { public void testDestructionCallbackSunnyDay() throws Exception { context.setAttribute("foo", "FOO"); context.registerDestructionCallback("foo", new Runnable() { + @Override public void run() { list.add("bar"); } @@ -91,6 +102,7 @@ public class JobContextTests { @Test public void testDestructionCallbackMissingAttribute() throws Exception { context.registerDestructionCallback("foo", new Runnable() { + @Override public void run() { list.add("bar"); } @@ -106,12 +118,14 @@ public class JobContextTests { context.setAttribute("foo", "FOO"); context.setAttribute("bar", "BAR"); context.registerDestructionCallback("bar", new Runnable() { + @Override public void run() { list.add("spam"); throw new RuntimeException("fail!"); } }); context.registerDestructionCallback("foo", new Runnable() { + @Override public void run() { list.add("bar"); throw new RuntimeException("fail!"); @@ -152,8 +166,10 @@ public class JobContextTests { @Test public void testJobParameters() throws Exception { JobParameters jobParameters = new JobParametersBuilder().addString("foo", "bar").toJobParameters(); - JobInstance jobInstance = new JobInstance(0L, jobParameters, "foo"); + JobInstance jobInstance = new JobInstance(0L, "foo"); + jobExecution = new JobExecution(5l, jobParameters); jobExecution.setJobInstance(jobInstance); + context = new JobContext(jobExecution); assertEquals("bar", context.getJobParameters().get("foo")); } @@ -164,9 +180,7 @@ public class JobContextTests { @Test(expected = IllegalStateException.class) public void testIllegalContextId() throws Exception { - JobParameters jobParameters = new JobParametersBuilder().addString("foo", "bar").toJobParameters(); - JobInstance jobInstance = new JobInstance(0L, jobParameters, "foo"); - context = new JobContext(new JobExecution(jobInstance)); + context = new JobContext(new JobExecution(null)); context.getId(); } diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/scope/context/JobSynchronizationManagerTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/scope/context/JobSynchronizationManagerTests.java index 7ab4d6008..e67f396c3 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/scope/context/JobSynchronizationManagerTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/scope/context/JobSynchronizationManagerTests.java @@ -4,10 +4,8 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; -import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -18,11 +16,10 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.batch.core.JobExecution; -import org.springframework.util.ReflectionUtils; /** * JobSynchronizationManagerTests. - * + * * @author Jimmy Praet */ public class JobSynchronizationManagerTests { @@ -49,6 +46,7 @@ public class JobSynchronizationManagerTests { final List list = new ArrayList(); JobContext context = JobSynchronizationManager.register(jobExecution); context.registerDestructionCallback("foo", new Runnable() { + @Override public void run() { list.add("foo"); } @@ -56,26 +54,14 @@ public class JobSynchronizationManagerTests { JobSynchronizationManager.close(); assertNull(JobSynchronizationManager.getContext()); assertEquals(0, list.size()); - // check for possible memory leak - assertEquals(0, extractStaticMap("counts").size()); - assertEquals(0, extractStaticMap("contexts").size()); } - private Map extractStaticMap(String name) throws IllegalAccessException { - Field field = ReflectionUtils.findField(JobSynchronizationManager.class, "synchronizationManager"); - ReflectionUtils.makeAccessible(field); - SynchronizationManagerSupport synchronizationManager = - (SynchronizationManagerSupport) field.get(JobSynchronizationManager.class); - field = ReflectionUtils.findField(SynchronizationManagerSupport.class, name); - ReflectionUtils.makeAccessible(field); - Map map = (Map) field.get(synchronizationManager); - return map; - } @Test public void testMultithreaded() throws Exception { JobContext context = JobSynchronizationManager.register(jobExecution); ExecutorService executorService = Executors.newFixedThreadPool(2); FutureTask task = new FutureTask(new Callable() { + @Override public JobContext call() throws Exception { try { JobSynchronizationManager.register(jobExecution); @@ -100,6 +86,7 @@ public class JobSynchronizationManagerTests { JobContext context = JobSynchronizationManager.register(jobExecution); final List list = new ArrayList(); context.registerDestructionCallback("foo", new Runnable() { + @Override public void run() { list.add("foo"); } @@ -130,4 +117,4 @@ public class JobSynchronizationManagerTests { assertNull(JobSynchronizationManager.getContext()); } -} \ No newline at end of file +} diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/scope/context/StepSynchronizationManagerTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/scope/context/StepSynchronizationManagerTests.java index 5c277bf5d..5ceed8754 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/scope/context/StepSynchronizationManagerTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/scope/context/StepSynchronizationManagerTests.java @@ -19,10 +19,8 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; -import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -35,7 +33,6 @@ import org.junit.Test; import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.StepExecution; import org.springframework.batch.core.jsr.configuration.support.BatchPropertyContext; -import org.springframework.util.ReflectionUtils; public class StepSynchronizationManagerTests { @@ -80,20 +77,6 @@ public class StepSynchronizationManagerTests { StepSynchronizationManager.close(); assertNull(StepSynchronizationManager.getContext()); assertEquals(0, list.size()); - // check for possible memory leak - assertEquals(0, extractStaticMap("counts").size()); - assertEquals(0, extractStaticMap("contexts").size()); - } - - private Map extractStaticMap(String name) throws IllegalAccessException { - Field field = ReflectionUtils.findField(StepSynchronizationManager.class, "synchronizationManager"); - ReflectionUtils.makeAccessible(field); - SynchronizationManagerSupport synchronizationManager = - (SynchronizationManagerSupport) field.get(StepSynchronizationManager.class); - field = ReflectionUtils.findField(SynchronizationManagerSupport.class, name); - ReflectionUtils.makeAccessible(field); - Map map = (Map) field.get(synchronizationManager); - return map; } @Test diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/scope/util/JobContextFactoryTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/scope/util/JobContextFactoryTests.java deleted file mode 100644 index 97622691d..000000000 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/scope/util/JobContextFactoryTests.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.springframework.batch.core.scope.util; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; - -import org.junit.After; -import org.junit.Test; -import org.springframework.batch.core.JobExecution; -import org.springframework.batch.core.scope.context.JobContext; -import org.springframework.batch.core.scope.context.JobSynchronizationManager; - -public class JobContextFactoryTests { - - private JobContextFactory factory = new JobContextFactory(); - - @After - public void cleanUp() { - JobSynchronizationManager.close(); - JobSynchronizationManager.close(); - } - - @Test - public void testGetContext() { - JobExecution jobExecution = new JobExecution(11L); - JobContext context = JobSynchronizationManager.register(jobExecution); - assertEquals(context, factory.getContext()); - } - - @Test - public void testGetContextId() { - JobSynchronizationManager.register(new JobExecution(11L)); - Object id1 = factory.getContextId(); - JobSynchronizationManager.register(new JobExecution(12L)); - Object id2 = factory.getContextId(); - assertFalse(id2.equals(id1)); - } - -} diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/AutoRegisteringJobScopeForJobElementTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/AutoRegisteringJobScopeForJobElementTests-context.xml index 093b8dfd0..89d4d0352 100644 --- a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/AutoRegisteringJobScopeForJobElementTests-context.xml +++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/AutoRegisteringJobScopeForJobElementTests-context.xml @@ -1,17 +1,17 @@ - + - + - \ No newline at end of file + diff --git a/spring-batch-test/src/test/resources/org/springframework/batch/test/JobScopeTestExecutionListenerIntegrationTests-context.xml b/spring-batch-test/src/test/resources/org/springframework/batch/test/JobScopeTestExecutionListenerIntegrationTests-context.xml index 3323c84a5..9a2e9ef1f 100644 --- a/spring-batch-test/src/test/resources/org/springframework/batch/test/JobScopeTestExecutionListenerIntegrationTests-context.xml +++ b/spring-batch-test/src/test/resources/org/springframework/batch/test/JobScopeTestExecutionListenerIntegrationTests-context.xml @@ -1,12 +1,12 @@ - + http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch.xsd"> + - + - +