BATCH-1509: added parser and XSD changes
fixed a comment...
This commit is contained in:
@@ -40,6 +40,9 @@
|
||||
<groupId>hsqldb</groupId>
|
||||
<artifactId>hsqldb</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId><artifactId>h2</artifactId><version>1.2.132</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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<I, O> implements FactoryBean, BeanNameAware {
|
||||
|
||||
@@ -194,7 +195,7 @@ class StepParserStepFactoryBean<I, O> 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<I, O> implements FactoryBean, BeanNameAware {
|
||||
configureSimple(fb);
|
||||
configureFaultTolerant(fb);
|
||||
return fb.getObject();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
SimpleStepFactoryBean<I, O> fb = new SimpleStepFactoryBean<I, O>();
|
||||
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 <chunk/> element nor a 'ref' attribute referencing a Tasklet.");
|
||||
}
|
||||
@@ -274,10 +269,13 @@ class StepParserStepFactoryBean<I, O> 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<I, O> 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<I, O> 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<I, O> 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<I, O> 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<I, O> 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<I, O> 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<I, O> implements FactoryBean, BeanNameAware {
|
||||
|
||||
/**
|
||||
* Public setter for {@link JobRepository}.
|
||||
*
|
||||
*
|
||||
* @param jobRepository
|
||||
*/
|
||||
public void setJobRepository(JobRepository jobRepository) {
|
||||
@@ -642,7 +650,7 @@ class StepParserStepFactoryBean<I, O> 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<I, O> implements FactoryBean, BeanNameAware {
|
||||
|
||||
/**
|
||||
* A preconfigured {@link Tasklet} to use.
|
||||
*
|
||||
*
|
||||
* @param tasklet
|
||||
*/
|
||||
public void setTasklet(Tasklet tasklet) {
|
||||
@@ -680,7 +688,7 @@ class StepParserStepFactoryBean<I, O> 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<I, O> 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<Class<? extends Throwable>> noRollbackExceptionClasses) {
|
||||
@@ -724,7 +732,7 @@ class StepParserStepFactoryBean<I, O> 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<I, O> 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<I, O> 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<I, O> 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.<br/>
|
||||
*
|
||||
* <p/>
|
||||
* 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}.<br/>
|
||||
*
|
||||
*
|
||||
* @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<I, O> 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<I, O> 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<I, O> 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<I, O> 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<I, O> 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 <code>retryLimit == 1</code> 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<I, O> 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<I, O> 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<I, O> 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<I, O> 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<I, O> 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<I, O> 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<Class<? extends Throwable>, Boolean> exceptionClasses) {
|
||||
@@ -929,7 +937,7 @@ class StepParserStepFactoryBean<I, O> 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<Class<? extends Throwable>, Boolean> retryableExceptionClasses) {
|
||||
@@ -940,7 +948,7 @@ class StepParserStepFactoryBean<I, O> 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) {
|
||||
|
||||
@@ -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<Long> 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);
|
||||
|
||||
|
||||
@@ -79,8 +79,7 @@ public enum StepListenerMetaData implements ListenerMetaData {
|
||||
private final Class<?>[] paramTypes;
|
||||
private static final Map<String, StepListenerMetaData> propertyMap;
|
||||
|
||||
StepListenerMetaData(String methodName, String propertyName, Class<? extends Annotation> annotation,
|
||||
Class<? extends StepListener> listenerInterface, Class<?>... paramTypes) {
|
||||
StepListenerMetaData(String methodName, String propertyName, Class<? extends Annotation> annotation, Class<? extends StepListener> listenerInterface, Class<?>... paramTypes) {
|
||||
this.methodName = methodName;
|
||||
this.propertyName = propertyName;
|
||||
this.annotation = annotation;
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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<W> implements Iterable<W> {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -453,6 +453,7 @@
|
||||
|
||||
<xsd:complexType name="partitionType">
|
||||
<xsd:all>
|
||||
<xsd:element name="step" type="stepType" minOccurs="0" maxOccurs="1" />
|
||||
<xsd:element name="handler" minOccurs="0" maxOccurs="1">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.batch.core.configuration.xml;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -25,75 +26,126 @@ import java.util.List;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.batch.core.BatchStatus;
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobParameters;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.*;
|
||||
import org.springframework.batch.core.partition.support.PartitionStep;
|
||||
import org.springframework.batch.core.partition.support.TaskExecutorPartitionHandler;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean;
|
||||
import org.springframework.batch.core.step.tasklet.TaskletStep;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class PartitionStepParserTests {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("job1")
|
||||
private Job job1;
|
||||
public class PartitionStepParserTests implements ApplicationContextAware {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("job2")
|
||||
private Job job2;
|
||||
@Autowired
|
||||
@Qualifier("job1")
|
||||
private Job job1;
|
||||
|
||||
@Autowired
|
||||
private JobRepository jobRepository;
|
||||
@Autowired
|
||||
@Qualifier("job2")
|
||||
private Job job2;
|
||||
|
||||
@Autowired
|
||||
private MapJobRepositoryFactoryBean mapJobRepositoryFactoryBean;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mapJobRepositoryFactoryBean.clear();
|
||||
}
|
||||
@Autowired
|
||||
@Qualifier("job3")
|
||||
private Job job3;
|
||||
|
||||
@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<String> 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<String> stepNames = getStepNames(jobExecution);
|
||||
assertEquals(5, stepNames.size());
|
||||
assertEquals("[s2, s3, step1:partition0, step1:partition1, step1:partition2]", stepNames.toString());
|
||||
}
|
||||
@Autowired
|
||||
private MapJobRepositoryFactoryBean mapJobRepositoryFactoryBean;
|
||||
|
||||
private List<String> getStepNames(JobExecution jobExecution) {
|
||||
List<String> list = new ArrayList<String>();
|
||||
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> 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<String> 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<String> stepNames = getStepNames(jobExecution);
|
||||
assertEquals(5, stepNames.size());
|
||||
assertEquals("[s2, s3, step1:partition0, step1:partition1, step1:partition2]", stepNames.toString());
|
||||
}
|
||||
|
||||
private List<String> getStepNames(JobExecution jobExecution) {
|
||||
List<String> list = new ArrayList<String>();
|
||||
for (StepExecution stepExecution : jobExecution.getStepExecutions()) {
|
||||
list.add(stepExecution.getStepName());
|
||||
}
|
||||
Collections.sort(list);
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -81,11 +81,9 @@ public class StepListenerFactoryBeanTests {
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testStepAndChunk() throws Exception {
|
||||
|
||||
TestListener testListener = new TestListener();
|
||||
factoryBean.setDelegate(testListener);
|
||||
Map<String, String> metaDataMap = new HashMap<String, String>();
|
||||
;
|
||||
metaDataMap.put(AFTER_STEP.getPropertyName(), "destroy");
|
||||
metaDataMap.put(AFTER_CHUNK.getPropertyName(), "afterChunk");
|
||||
factoryBean.setMetaDataMap(metaDataMap);
|
||||
|
||||
@@ -21,6 +21,15 @@
|
||||
<step id="s3" parent="step2"/>
|
||||
</job>
|
||||
|
||||
<job id="job3">
|
||||
<step id="j3s1" >
|
||||
<partition handler="handler1" partitioner="partitioner">
|
||||
<step parent="step2"/>
|
||||
</partition>
|
||||
</step>
|
||||
</job>
|
||||
|
||||
|
||||
<bean id="handler"
|
||||
class="org.springframework.batch.core.partition.support.TaskExecutorPartitionHandler"
|
||||
xmlns="http://www.springframework.org/schema/beans">
|
||||
@@ -34,4 +43,12 @@
|
||||
<beans:bean id="partitioner"
|
||||
class="org.springframework.batch.core.partition.support.SimplePartitioner" />
|
||||
|
||||
<!--
|
||||
for the inline case, demonstrates that we correctly ensure a reference to a step for both the default handler setup or a custom PartitionHandler
|
||||
-->
|
||||
<beans:bean id="handler1" class="org.springframework.batch.core.partition.support.TaskExecutorPartitionHandler">
|
||||
<!-- nb: we're not configuring a step! -->
|
||||
<beans:property name="gridSize" value="10" />
|
||||
</beans:bean>
|
||||
|
||||
</beans:beans>
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user