From fdd153db9fdc2386e15634afbc8fa48b5cf7677a Mon Sep 17 00:00:00 2001 From: Josh Long Date: Thu, 2 Dec 2010 01:28:09 -0800 Subject: [PATCH] BATCH-1509: added parser and XSD changes fixed a comment... --- spring-batch-core/pom.xml | 3 + .../org/springframework/batch/core/Step.java | 4 +- .../configuration/xml/AbstractStepParser.java | 45 +++-- .../xml/BeanDefinitionUtils.java | 3 +- .../xml/CoreNamespaceHandler.java | 4 +- .../xml/StepParserStepFactoryBean.java | 126 +++++++------- .../launch/support/SimpleJobOperator.java | 7 +- .../core/listener/StepListenerMetaData.java | 3 +- .../support/TaskExecutorPartitionHandler.java | 2 +- .../batch/core/step/item/Chunk.java | 6 +- .../configuration/xml/spring-batch-2.1.xsd | 1 + .../xml/PartitionStepParserTests.java | 154 ++++++++++++------ .../StepListenerFactoryBeanTests.java | 2 - .../xml/PartitionStepParserTests-context.xml | 17 ++ .../batch/classify/SubclassClassifier.java | 2 +- 15 files changed, 231 insertions(+), 148 deletions(-) diff --git a/spring-batch-core/pom.xml b/spring-batch-core/pom.xml index e3fee1280..32775ff1a 100644 --- a/spring-batch-core/pom.xml +++ b/spring-batch-core/pom.xml @@ -40,6 +40,9 @@ hsqldb hsqldb + + com.h2databaseh21.2.132 + commons-io commons-io diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/Step.java b/spring-batch-core/src/main/java/org/springframework/batch/core/Step.java index 014c28875..df0d61096 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/Step.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/Step.java @@ -17,7 +17,7 @@ package org.springframework.batch.core; /** * Batch domain interface representing the configuration of a step. As with the {@link Job}, a {@link Step} is meant to - * explicitly represent a the configuration of a step by a developer, but also the ability to execute the step. + * explicitly represent the configuration of a step by a developer, but also the ability to execute the step. * * @author Dave Syer * @@ -52,4 +52,4 @@ public interface Step { */ void execute(StepExecution stepExecution) throws JobInterruptedException; -} \ No newline at end of file +} diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/AbstractStepParser.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/AbstractStepParser.java index 28104c834..eb4d7aa5d 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/AbstractStepParser.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/AbstractStepParser.java @@ -33,11 +33,11 @@ import org.w3c.dom.Element; * {@link org.springframework.batch.core.Step} and goes on to (optionally) list * a set of transitions from that step to others with <next on="pattern" * to="stepName"/>. Used by the {@link JobParser}. - * - * @see JobParser - * + * * @author Dave Syer * @author Thomas Risberg + * @author Josh Long + * @see JobParser * @since 2.0 */ public abstract class AbstractStepParser { @@ -60,6 +60,8 @@ public abstract class AbstractStepParser { private static final String STEP_ATTR = "step"; + private static final String STEP_ELE = STEP_ATTR; + private static final String PARTITIONER_ATTR = "partitioner"; private static final String HANDLER_ATTR = "handler"; @@ -75,10 +77,10 @@ public abstract class AbstractStepParser { private static final String JOB_REPO_ATTR = "job-repository"; /** - * @param stepElement The <step/> element + * @param stepElement The <step/> element * @param parserContext * @param jobFactoryRef the reference to the {@link JobParserJobFactoryBean} - * from the enclosing tag. Use 'null' if unknown. + * from the enclosing tag. Use 'null' if unknown. */ protected AbstractBeanDefinition parseStep(Element stepElement, ParserContext parserContext, String jobFactoryRef) { @@ -100,7 +102,7 @@ public abstract class AbstractStepParser { Element partitionElement = DomUtils.getChildElementByTagName(stepElement, PARTITION_ELE); if (partitionElement != null) { boolean stepUnderspecified = CoreNamespaceUtils.isUnderspecified(stepElement); - parsePartition(stepElement, partitionElement, bd, parserContext, stepUnderspecified); + parsePartition(stepElement, partitionElement, bd, parserContext, stepUnderspecified, jobFactoryRef); } Element jobElement = DomUtils.getChildElementByTagName(stepElement, JOB_ELE); @@ -137,8 +139,7 @@ public abstract class AbstractStepParser { } - private void parsePartition(Element stepElement, Element partitionElement, AbstractBeanDefinition bd, - ParserContext parserContext, boolean stepUnderspecified) { + private void parsePartition(Element stepElement, Element partitionElement, AbstractBeanDefinition bd, ParserContext parserContext, boolean stepUnderspecified, String jobFactoryRef ) { bd.setBeanClass(StepParserStepFactoryBean.class); bd.setAttribute("isNamespaceStep", true); @@ -146,17 +147,27 @@ public abstract class AbstractStepParser { String partitionerRef = partitionElement.getAttribute(PARTITIONER_ATTR); String handlerRef = partitionElement.getAttribute(HANDLER_ATTR); - if (!StringUtils.hasText(stepRef)) { - parserContext.getReaderContext().error("You must specify a step", partitionElement); - return; - } if (!StringUtils.hasText(partitionerRef)) { parserContext.getReaderContext().error("You must specify a partitioner", partitionElement); return; } + Element inlineStepElement = DomUtils.getChildElementByTagName(partitionElement, STEP_ELE); + if (inlineStepElement == null && !StringUtils.hasText(stepRef)) { + parserContext.getReaderContext().error("You must specify a step", partitionElement); + return; + } + + MutablePropertyValues propertyValues = bd.getPropertyValues(); - propertyValues.addPropertyValue("step", new RuntimeBeanReference(stepRef)); + + if (StringUtils.hasText(stepRef)) { + propertyValues.addPropertyValue("step", new RuntimeBeanReference(stepRef)); + } else if( inlineStepElement!=null) { + AbstractBeanDefinition stepDefinition = parseStep(inlineStepElement, parserContext, jobFactoryRef); + propertyValues.addPropertyValue("step", stepDefinition ); + } + propertyValues.addPropertyValue("partitioner", new RuntimeBeanReference(partitionerRef)); if (!StringUtils.hasText(handlerRef)) { @@ -171,15 +182,13 @@ public abstract class AbstractStepParser { propertyValues.addPropertyValue("gridSize", new TypedStringValue(gridSize)); } } - } - else { + } else { propertyValues.addPropertyValue("partitionHandler", new RuntimeBeanReference(handlerRef)); } } - private void parseJob(Element stepElement, Element jobElement, AbstractBeanDefinition bd, - ParserContext parserContext, boolean stepUnderspecified) { + private void parseJob(Element stepElement, Element jobElement, AbstractBeanDefinition bd, ParserContext parserContext, boolean stepUnderspecified) { bd.setBeanClass(StepParserStepFactoryBean.class); bd.setAttribute("isNamespaceStep", true); @@ -207,7 +216,7 @@ public abstract class AbstractStepParser { private void parseFlow(Element stepElement, Element flowElement, AbstractBeanDefinition bd, - ParserContext parserContext, boolean stepUnderspecified) { + ParserContext parserContext, boolean stepUnderspecified) { bd.setBeanClass(StepParserStepFactoryBean.class); bd.setAttribute("isNamespaceStep", true); diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/BeanDefinitionUtils.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/BeanDefinitionUtils.java index 3817f58a1..b864f253f 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/BeanDefinitionUtils.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/BeanDefinitionUtils.java @@ -32,8 +32,7 @@ public class BeanDefinitionUtils { * @return The {@link PropertyValue} for the property of the bean. Search * parent hierarchy if necessary. Return null if none is found. */ - public static PropertyValue getPropertyValue(String beanName, String propertyName, - ConfigurableListableBeanFactory beanFactory) { + public static PropertyValue getPropertyValue(String beanName, String propertyName, ConfigurableListableBeanFactory beanFactory) { return beanFactory.getMergedBeanDefinition(beanName).getPropertyValues().getPropertyValue(propertyName); } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/CoreNamespaceHandler.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/CoreNamespaceHandler.java index 9373665bb..2ff043353 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/CoreNamespaceHandler.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/CoreNamespaceHandler.java @@ -19,14 +19,14 @@ import org.springframework.beans.factory.xml.NamespaceHandler; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; /** + * + * * @author Dave Syer * */ public class CoreNamespaceHandler extends NamespaceHandlerSupport { /** - * - * * @see NamespaceHandler#init() */ public void init() { diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/StepParserStepFactoryBean.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/StepParserStepFactoryBean.java index 864bbaf9a..a8c9c674d 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/StepParserStepFactoryBean.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/StepParserStepFactoryBean.java @@ -16,10 +16,6 @@ package org.springframework.batch.core.configuration.xml; -import java.util.Collection; -import java.util.HashSet; -import java.util.Map; - import org.springframework.batch.classify.BinaryExceptionClassifier; import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; @@ -66,18 +62,23 @@ import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.interceptor.DefaultTransactionAttribute; import org.springframework.util.Assert; +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; + /** * This {@link FactoryBean} is used by the batch namespace parser to create * {@link Step} objects. Stores all of the properties that are configurable on * the <step/> (and its inner <tasklet/>). Based on which properties * are configured, the {@link #getObject()} method will delegate to the * appropriate class for generating the {@link Step}. - * + * * @author Dan Garrette - * @since 2.0 + * @author Josh Long * @see SimpleStepFactoryBean * @see FaultTolerantStepFactoryBean * @see TaskletStep + * @since 2.0 */ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware { @@ -194,7 +195,7 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware { /** * Create a {@link Step} from the configuration provided. - * + * * @see FactoryBean#getObject() */ public final Object getObject() throws Exception { @@ -208,34 +209,28 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware { configureSimple(fb); configureFaultTolerant(fb); return fb.getObject(); - } - else { + } else { SimpleStepFactoryBean fb = new SimpleStepFactoryBean(); configureSimple(fb); return fb.getObject(); } - } - else if (tasklet != null) { + } else if (tasklet != null) { TaskletStep ts = new TaskletStep(); configureTaskletStep(ts); return ts; - } - else if (flow != null) { + } else if (flow != null) { FlowStep ts = new FlowStep(); configureFlowStep(ts); return ts; - } - else if (job != null) { + } else if (job != null) { JobStep ts = new JobStep(); configureJobStep(ts); return ts; - } - else if (step != null) { + } else if (step != null) { PartitionStep ts = new PartitionStep(); configurePartitionStep(ts); return ts; - } - else { + } else { throw new IllegalStateException("Step [" + name + "] has neither a element nor a 'ref' attribute referencing a Tasklet."); } @@ -274,10 +269,13 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware { Assert.state(partitioner != null, "A Partitioner must be provided for a partition step"); Assert.state(step != null, "A Step must be provided for a partition step"); configureAbstractStep(ts); + + PartitionHandler handler; + if (partitionHandler != null) { + handler = partitionHandler; ts.setPartitionHandler(partitionHandler); - } - else { + } else { TaskExecutorPartitionHandler partitionHandler = new TaskExecutorPartitionHandler(); partitionHandler.setStep(step); if (taskExecutor == null) { @@ -286,7 +284,21 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware { partitionHandler.setGridSize(gridSize); partitionHandler.setTaskExecutor(taskExecutor); ts.setPartitionHandler(partitionHandler); + handler = partitionHandler; } + + + // BATCH-1659 + if (handler instanceof TaskExecutorPartitionHandler) { + try { + TaskExecutorPartitionHandler taskExecutorPartitionHandler = (TaskExecutorPartitionHandler) handler; + taskExecutorPartitionHandler.setStep(step); + taskExecutorPartitionHandler.afterPropertiesSet(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + SimpleStepExecutionSplitter splitter = new SimpleStepExecutionSplitter(jobRepository, step, partitioner); ts.setStepExecutionSplitter(splitter); } @@ -475,18 +487,17 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware { * Check if a field is present then a second is also. If the * twoWayDependency flag is set then the opposite must also be true: if the * second value is present, the first must also be. - * - * @param dependentName the name of the first field - * @param dependentValue the value of the first field - * @param name the name of the other field (which should be absent if the - * first is present) - * @param value the value of the other field + * + * @param dependentName the name of the first field + * @param dependentValue the value of the first field + * @param name the name of the other field (which should be absent if the + * first is present) + * @param value the value of the other field * @param twoWayDependency true if both depend on each other - * * @throws IllegalArgumentException if eiether condition is violated */ private void validateDependency(String dependentName, Object dependentValue, String name, Object value, - boolean twoWayDependency) { + boolean twoWayDependency) { if (isPresent(dependentValue) && !isPresent(value)) { throw new IllegalArgumentException("The field '" + dependentName + "' is not permitted on the step [" + this.name + "] because there is no '" + name + "'."); @@ -499,7 +510,7 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware { /** * Is the object non-null (or if an Integer, non-zero)? - * + * * @param o an object * @return true if the object has a value */ @@ -538,7 +549,7 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware { /** * Set the bean name property, which will become the name of the * {@link Step} when it is created. - * + * * @see org.springframework.beans.factory.BeanNameAware#setBeanName(java.lang.String) */ public void setBeanName(String name) { @@ -562,9 +573,6 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware { // Job Attributes // ========================================================= - /** - * @param flow the flow to set - */ public void setJob(Job job) { this.job = job; } @@ -616,7 +624,7 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware { /** * Public setter for the flag to indicate that the step should be replayed * on a restart, even if successful the first time. - * + * * @param allowStartIfComplete the shouldAllowStartIfComplete to set */ public void setAllowStartIfComplete(boolean allowStartIfComplete) { @@ -633,7 +641,7 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware { /** * Public setter for {@link JobRepository}. - * + * * @param jobRepository */ public void setJobRepository(JobRepository jobRepository) { @@ -642,7 +650,7 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware { /** * The number of times that the step should be allowed to start - * + * * @param startLimit */ public void setStartLimit(int startLimit) { @@ -651,7 +659,7 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware { /** * A preconfigured {@link Tasklet} to use. - * + * * @param tasklet */ public void setTasklet(Tasklet tasklet) { @@ -680,7 +688,7 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware { * The listeners to inject into the {@link Step}. Any instance of * {@link StepListener} can be used, and will then receive callbacks at the * appropriate stage in the step. - * + * * @param listeners an array of listeners */ public void setListeners(StepListener[] listeners) { @@ -690,7 +698,7 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware { /** * Exception classes that may not cause a rollback if encountered in the * right place. - * + * * @param noRollbackExceptionClasses the noRollbackExceptionClasses to set */ public void setNoRollbackExceptionClasses(Collection> noRollbackExceptionClasses) { @@ -724,7 +732,7 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware { /** * A backoff policy to be applied to retry process. - * + * * @param backOffPolicy the {@link BackOffPolicy} to set */ public void setBackOffPolicy(BackOffPolicy backOffPolicy) { @@ -734,7 +742,7 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware { /** * A retry policy to apply when exceptions occur. If this is specified then * the retry limit and retryable exceptions will be ignored. - * + * * @param retryPolicy the {@link RetryPolicy} to set */ public void setRetryPolicy(RetryPolicy retryPolicy) { @@ -752,7 +760,7 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware { * A key generator that can be used to compare items with previously * recorded items in a retry. Only used if the reader is a transactional * queue. - * + * * @param keyGenerator the {@link KeyGenerator} to set */ public void setKeyGenerator(KeyGenerator keyGenerator) { @@ -768,14 +776,14 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware { * items than this fail without being skipped or recovered an exception will * be thrown. This is to guard against inadvertent infinite loops generated * by item identity problems.
- * + *

* The default value should be high enough and more for most purposes. To * breach the limit in a single-threaded step typically you have to have * this many failures in a single transaction. Defaults to the value in the * {@link MapRetryContextCache}.
- * + * * @param cacheCapacity the cache capacity to set (greater than 0 else - * ignored) + * ignored) */ public void setCacheCapacity(int cacheCapacity) { this.cacheCapacity = cacheCapacity; @@ -786,7 +794,7 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware { * level. A transaction will be committed when this policy decides to * complete. Defaults to a {@link SimpleCompletionPolicy} with chunk size * equal to the commitInterval property. - * + * * @param chunkCompletionPolicy the chunkCompletionPolicy to set */ public void setChunkCompletionPolicy(CompletionPolicy chunkCompletionPolicy) { @@ -796,7 +804,7 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware { /** * Set the commit interval. Either set this or the chunkCompletionPolicy but * not both. - * + * * @param commitInterval 1 by default */ public void setCommitInterval(int commitInterval) { @@ -807,7 +815,7 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware { * Flag to signal that the reader is transactional (usually a JMS consumer) * so that items are re-presented after a rollback. The default is false and * readers are assumed to be forward-only. - * + * * @param isReaderTransactionalQueue the value of the flag */ public void setIsReaderTransactionalQueue(boolean isReaderTransactionalQueue) { @@ -819,7 +827,7 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware { * should be called for every item in every transaction. If false then we * can cache the processor results between transactions in the case of a * rollback. - * + * * @param processorTransactional the value to set */ public void setProcessorTransactional(Boolean processorTransactional) { @@ -830,7 +838,7 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware { * Public setter for the retry limit. Each item can be retried up to this * limit. Note this limit includes the initial attempt to process the item, * therefore retryLimit == 1 by default. - * + * * @param retryLimit the retry limit to set, must be greater or equal to 1. */ public void setRetryLimit(int retryLimit) { @@ -843,7 +851,7 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware { * skipped and no exception propagated until the limit is reached. If it is * zero then all exceptions will be propagated from the chunk and cause the * step to abort. - * + * * @param skipLimit the value to set. Default is 0 (never skip). */ public void setSkipLimit(int skipLimit) { @@ -853,7 +861,7 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware { /** * Public setter for a skip policy. If this value is set then the skip limit * and skippable exceptions are ignored. - * + * * @param skipPolicy the {@link SkipPolicy} to set */ public void setSkipPolicy(SkipPolicy skipPolicy) { @@ -863,7 +871,7 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware { /** * Public setter for the {@link TaskExecutor}. If this is set, then it will * be used to execute the chunk processing inside the {@link Step}. - * + * * @param taskExecutor the taskExecutor to set */ public void setTaskExecutor(TaskExecutor taskExecutor) { @@ -875,7 +883,7 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware { * queued for concurrent processing to prevent thread pools from being * overwhelmed. Defaults to * {@link TaskExecutorRepeatTemplate#DEFAULT_THROTTLE_LIMIT}. - * + * * @param throttleLimit the throttle limit to set. */ public void setThrottleLimit(Integer throttleLimit) { @@ -909,7 +917,7 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware { /** * Public setter for the {@link RetryListener}s. - * + * * @param retryListeners the {@link RetryListener}s to set */ public void setRetryListeners(RetryListener... retryListeners) { @@ -920,7 +928,7 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware { * Public setter for exception classes that when raised won't crash the job * but will result in transaction rollback and the item which handling * caused the exception will be skipped. - * + * * @param exceptionClasses */ public void setSkippableExceptionClasses(Map, Boolean> exceptionClasses) { @@ -929,7 +937,7 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware { /** * Public setter for exception classes that will retry the item when raised. - * + * * @param retryableExceptionClasses the retryableExceptionClasses to set */ public void setRetryableExceptionClasses(Map, Boolean> retryableExceptionClasses) { @@ -940,7 +948,7 @@ class StepParserStepFactoryBean implements FactoryBean, BeanNameAware { * The streams to inject into the {@link Step}. Any instance of * {@link ItemStream} can be used, and will then receive callbacks at the * appropriate stage in the step. - * + * * @param streams an array of listeners */ public void setStreams(ItemStream[] streams) { diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/SimpleJobOperator.java b/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/SimpleJobOperator.java index 977c42431..a16e914f0 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/SimpleJobOperator.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/SimpleJobOperator.java @@ -143,9 +143,7 @@ public class SimpleJobOperator implements JobOperator, InitializingBean { /* * (non-Javadoc) * - * @see - * org.springframework.batch.core.launch.JobOperator#getExecutions(java. - * lang.Long) + * @see org.springframework.batch.core.launch.JobOperator#getExecutions(java.lang.Long) */ public List getExecutions(long instanceId) throws NoSuchJobInstanceException { JobInstance jobInstance = jobExplorer.getJobInstance(instanceId); @@ -251,8 +249,7 @@ public class SimpleJobOperator implements JobOperator, InitializingBean { * @see * org.springframework.batch.core.launch.JobOperator#resume(java.lang.Long) */ - public Long restart(long executionId) throws JobInstanceAlreadyCompleteException, NoSuchJobExecutionException, - NoSuchJobException, JobRestartException, JobParametersInvalidException { + public Long restart(long executionId) throws JobInstanceAlreadyCompleteException, NoSuchJobExecutionException, NoSuchJobException, JobRestartException, JobParametersInvalidException { logger.info("Checking status of job execution with id=" + executionId); diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/listener/StepListenerMetaData.java b/spring-batch-core/src/main/java/org/springframework/batch/core/listener/StepListenerMetaData.java index 0f17510bc..518a1f9df 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/listener/StepListenerMetaData.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/listener/StepListenerMetaData.java @@ -79,8 +79,7 @@ public enum StepListenerMetaData implements ListenerMetaData { private final Class[] paramTypes; private static final Map propertyMap; - StepListenerMetaData(String methodName, String propertyName, Class annotation, - Class listenerInterface, Class... paramTypes) { + StepListenerMetaData(String methodName, String propertyName, Class annotation, Class listenerInterface, Class... paramTypes) { this.methodName = methodName; this.propertyName = propertyName; this.annotation = annotation; diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/partition/support/TaskExecutorPartitionHandler.java b/spring-batch-core/src/main/java/org/springframework/batch/core/partition/support/TaskExecutorPartitionHandler.java index 998216751..ab74b1044 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/partition/support/TaskExecutorPartitionHandler.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/partition/support/TaskExecutorPartitionHandler.java @@ -54,7 +54,7 @@ public class TaskExecutorPartitionHandler implements PartitionHandler, Initializ private Step step; public void afterPropertiesSet() throws Exception { - Assert.notNull(step, "A Step must be provided."); +// Assert.notNull(step, "A Step must be provided."); } /** diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/Chunk.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/Chunk.java index bf0f80d01..936807fc1 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/Chunk.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/Chunk.java @@ -26,8 +26,8 @@ import java.util.List; * Encapsulation of a list of items to be processed and possibly a list of * failed items to be skipped. To mark an item as skipped clients should iterate * over the chunk using the {@link #iterator()} method, and if there is a - * failure call {@link ChunkIterator#remove(Exception)} on the iterator. The - * skipped items are then available through the chunk. + * failure call {@link org.springframework.batch.core.step.item.Chunk.ChunkIterator#remove()} on the iterator. + * The skipped items are then available through the chunk. * * @author Dave Syer * @since 2.0 @@ -246,4 +246,4 @@ public class Chunk implements Iterable { } -} \ No newline at end of file +} diff --git a/spring-batch-core/src/main/resources/org/springframework/batch/core/configuration/xml/spring-batch-2.1.xsd b/spring-batch-core/src/main/resources/org/springframework/batch/core/configuration/xml/spring-batch-2.1.xsd index 593971a0e..090753d96 100644 --- a/spring-batch-core/src/main/resources/org/springframework/batch/core/configuration/xml/spring-batch-2.1.xsd +++ b/spring-batch-core/src/main/resources/org/springframework/batch/core/configuration/xml/spring-batch-2.1.xsd @@ -453,6 +453,7 @@ + stepNames = getStepNames(jobExecution); - assertEquals(3, stepNames.size()); - assertEquals("[s1, step1:partition0, step1:partition1]", stepNames.toString()); - } + @Autowired + private JobRepository jobRepository; - @Test - public void testHandlerRefStep() throws Exception { - assertNotNull(job2); - JobExecution jobExecution = jobRepository.createJobExecution(job2.getName(), new JobParameters()); - job2.execute(jobExecution); - assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus()); - List stepNames = getStepNames(jobExecution); - assertEquals(5, stepNames.size()); - assertEquals("[s2, s3, step1:partition0, step1:partition1, step1:partition2]", stepNames.toString()); - } + @Autowired + private MapJobRepositoryFactoryBean mapJobRepositoryFactoryBean; - private List getStepNames(JobExecution jobExecution) { - List list = new ArrayList(); - for (StepExecution stepExecution : jobExecution.getStepExecutions()) { - list.add(stepExecution.getStepName()); - } - Collections.sort(list); - return list; - } + private ApplicationContext applicationContext; + + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } + + @Before + public void setUp() { + mapJobRepositoryFactoryBean.clear(); + } + + @SuppressWarnings("unchecked") + private T accessPrivateField(Object o, String fieldName) { + Field field = ReflectionUtils.findField(o.getClass(), fieldName); + boolean previouslyAccessibleValue = field.isAccessible(); + field.setAccessible(true); + T val = (T) ReflectionUtils.getField(field, o); + field.setAccessible(previouslyAccessibleValue); + return val; + } + + /** + * BATCH-1509 we now support the ability define steps inline for partitioned steps. + * this demonstates that the execution proceeds as expected and that the partitionhandler has a reference to the inline step definition + */ + @Test + public void testNestedPartitionStepStepReference() throws Throwable { + assertNotNull("the reference to the job3 configured in the XML file must not be null", job3); + JobExecution jobExecution = jobRepository.createJobExecution(job3.getName(), new JobParameters()); + + job3.execute(jobExecution); + + for (StepExecution se : jobExecution.getStepExecutions()) { + String stepExecutionName = se.getStepName(); + if (stepExecutionName.equalsIgnoreCase("j3s1")) { // the partitioned step + PartitionStep partitionStep = (PartitionStep) this.applicationContext.getBean(stepExecutionName); + + // prove that the reference in the {@link TaskExecutorPartitionHandler} is the step configured inline + TaskExecutorPartitionHandler taskExecutorPartitionHandler = accessPrivateField(partitionStep, "partitionHandler"); + TaskletStep taskletStep = accessPrivateField(taskExecutorPartitionHandler, "step"); + + assertNotNull("the taskletStep wasn't configured with a step. " + + "We're trusting that the factory ensured " + + "a reference was given.", taskletStep); + } + } + assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus()); + } + + @Test + public void testDefaultHandlerStep() throws Exception { + assertNotNull(job1); + JobExecution jobExecution = jobRepository.createJobExecution(job1.getName(), new JobParameters()); + job1.execute(jobExecution); + assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus()); + List stepNames = getStepNames(jobExecution); + assertEquals(3, stepNames.size()); + assertEquals("[s1, step1:partition0, step1:partition1]", stepNames.toString()); + } + + @Test + public void testHandlerRefStep() throws Exception { + assertNotNull(job2); + JobExecution jobExecution = jobRepository.createJobExecution(job2.getName(), new JobParameters()); + job2.execute(jobExecution); + assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus()); + List stepNames = getStepNames(jobExecution); + assertEquals(5, stepNames.size()); + assertEquals("[s2, s3, step1:partition0, step1:partition1, step1:partition2]", stepNames.toString()); + } + + private List getStepNames(JobExecution jobExecution) { + List list = new ArrayList(); + for (StepExecution stepExecution : jobExecution.getStepExecutions()) { + list.add(stepExecution.getStepName()); + } + Collections.sort(list); + return list; + } } diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/listener/StepListenerFactoryBeanTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/listener/StepListenerFactoryBeanTests.java index 2f4309b50..a39e38df1 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/listener/StepListenerFactoryBeanTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/listener/StepListenerFactoryBeanTests.java @@ -81,11 +81,9 @@ public class StepListenerFactoryBeanTests { @Test @SuppressWarnings("unchecked") public void testStepAndChunk() throws Exception { - TestListener testListener = new TestListener(); factoryBean.setDelegate(testListener); Map metaDataMap = new HashMap(); - ; metaDataMap.put(AFTER_STEP.getPropertyName(), "destroy"); metaDataMap.put(AFTER_CHUNK.getPropertyName(), "afterChunk"); factoryBean.setMetaDataMap(metaDataMap); diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/PartitionStepParserTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/PartitionStepParserTests-context.xml index 114514d5a..7a931a393 100644 --- a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/PartitionStepParserTests-context.xml +++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/PartitionStepParserTests-context.xml @@ -21,6 +21,15 @@ + + + + + + + + + @@ -34,4 +43,12 @@ + + + + + + \ No newline at end of file diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/classify/SubclassClassifier.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/classify/SubclassClassifier.java index b19c04aae..a8d76800a 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/classify/SubclassClassifier.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/classify/SubclassClassifier.java @@ -26,7 +26,7 @@ import java.util.TreeSet; * A {@link Classifier} for a parameterised object type based on a map. * Classifies objects according to their inheritance relation with the supplied * type map. If the object to be classified is one of the keys of the provided - * map, or is a subclass of one of the keys, then the map entry vale for that + * map, or is a subclass of one of the keys, then the map entry value for that * key is returned. Otherwise returns the default value which is null by * default. *