RESOLVED - issue BATCH-1411: Allow a Job to specify its required JobParameters

This commit is contained in:
dsyer
2009-11-11 20:49:31 +00:00
parent 74a992965b
commit 0dba72d2a6
19 changed files with 591 additions and 191 deletions

View File

@@ -34,7 +34,7 @@ public interface Job {
* @return true if this job can be restarted after a failure
*/
boolean isRestartable();
/**
* Run the {@link JobExecution} and update the meta information like status
* and statistics as necessary. This method should not throw any exceptions
@@ -54,4 +54,6 @@ public interface Job {
*/
JobParametersIncrementer getJobParametersIncrementer();
void validate(JobParameters parameters) throws JobParametersInvalidException;
}

View File

@@ -0,0 +1,16 @@
package org.springframework.batch.core;
/**
* Exception for {@link Job} to signal that some {@link JobParameters} are
* invalid.
*
* @author Dave Syer
*
*/
public class JobParametersInvalidException extends JobExecutionException {
public JobParametersInvalidException(String msg) {
super(msg);
}
}

View File

@@ -17,7 +17,9 @@ package org.springframework.batch.core.configuration.support;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersIncrementer;
import org.springframework.batch.core.JobParametersInvalidException;
/**
* A {@link Job} that can optionally prepend a group name to another job's name,
@@ -68,6 +70,10 @@ public class GroupAwareJob implements Job {
public void execute(JobExecution execution) {
delegate.execute(execution);
}
public void validate(JobParameters parameters) throws JobParametersInvalidException {
delegate.validate(parameters);
}
/**
* Concatenates the group name and the delegate job name (joining with a

View File

@@ -76,6 +76,11 @@ public class JobParser extends AbstractSingleBeanDefinitionParser {
builder.addPropertyReference("jobRepository", repositoryAttribute);
}
String parametersValidator = element.getAttribute("parameters-validator");
if (StringUtils.hasText(parametersValidator)) {
builder.addPropertyReference("jobParametersValidator", parametersValidator);
}
String restartableAttribute = element.getAttribute("restartable");
if (StringUtils.hasText(restartableAttribute)) {
builder.addPropertyValue("restartable", restartableAttribute);

View File

@@ -17,6 +17,7 @@ package org.springframework.batch.core.configuration.xml;
import org.springframework.batch.core.JobExecutionListener;
import org.springframework.batch.core.JobParametersIncrementer;
import org.springframework.batch.core.job.JobParametersValidator;
import org.springframework.batch.core.job.flow.Flow;
import org.springframework.batch.core.job.flow.FlowJob;
import org.springframework.batch.core.repository.JobRepository;
@@ -42,6 +43,8 @@ class JobParserJobFactoryBean implements SmartFactoryBean {
private JobRepository jobRepository;
private JobParametersValidator jobParametersValidator;
private JobExecutionListener[] jobExecutionListeners;
private JobParametersIncrementer jobParametersIncrementer;
@@ -64,6 +67,10 @@ class JobParserJobFactoryBean implements SmartFactoryBean {
flowJob.setJobRepository(jobRepository);
}
if (jobParametersValidator != null) {
flowJob.setJobParametersValidator(jobParametersValidator);
}
if (jobExecutionListeners != null) {
flowJob.setJobExecutionListeners(jobExecutionListeners);
}
@@ -87,6 +94,10 @@ class JobParserJobFactoryBean implements SmartFactoryBean {
public void setJobRepository(JobRepository jobRepository) {
this.jobRepository = jobRepository;
}
public void setJobParametersValidator(JobParametersValidator jobParametersValidator) {
this.jobParametersValidator = jobParametersValidator;
}
public JobRepository getJobRepository() {
return this.jobRepository;

View File

@@ -29,7 +29,9 @@ import org.springframework.batch.core.JobExecutionException;
import org.springframework.batch.core.JobExecutionListener;
import org.springframework.batch.core.JobInstance;
import org.springframework.batch.core.JobInterruptedException;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersIncrementer;
import org.springframework.batch.core.JobParametersInvalidException;
import org.springframework.batch.core.StartLimitExceededException;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepExecution;
@@ -45,10 +47,10 @@ import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* Abstract implementation of the {@link Job} interface. Common dependencies such as a
* {@link JobRepository}, {@link JobExecutionListener}s, and various configuration
* parameters are set here. Therefore, common error handling and listener calling
* activities are abstracted away from implementations.
* Abstract implementation of the {@link Job} interface. Common dependencies
* such as a {@link JobRepository}, {@link JobExecutionListener}s, and various
* configuration parameters are set here. Therefore, common error handling and
* listener calling activities are abstracted away from implementations.
*
* @author Lucas Ward
* @author Dave Syer
@@ -67,6 +69,8 @@ public abstract class AbstractJob implements Job, StepLocator, BeanNameAware, In
private JobParametersIncrementer jobParametersIncrementer;
private JobParametersValidator jobParametersValidator = new DefaultJobParametersValidator();
/**
* Default constructor.
*/
@@ -85,6 +89,26 @@ public abstract class AbstractJob implements Job, StepLocator, BeanNameAware, In
this.name = name;
}
/**
* A validator for job parameters. Defaults to a vanilla
* {@link DefaultJobParametersValidator}.
*
* @param jobParametersValidator a validator instance
*/
public void setJobParametersValidator(JobParametersValidator jobParametersValidator) {
this.jobParametersValidator = jobParametersValidator;
}
/**
* Delegates to the {@link #setJobParametersValidator validator} supplied
* (defaults to just checking for null parameters).
*
* @see Job#validate(JobParameters)
*/
public void validate(JobParameters parameters) throws JobParametersInvalidException {
jobParametersValidator.validate(parameters);
}
/**
* Assert mandatory properties: {@link JobRepository}.
*
@@ -129,21 +153,21 @@ public abstract class AbstractJob implements Job, StepLocator, BeanNameAware, In
}
/**
* Retrieve the step with the given name. If there is no Step with the
* given name, then return null.
* Retrieve the step with the given name. If there is no Step with the given
* name, then return null.
*
* @param stepName
* @return the Step
*/
public abstract Step getStep(String stepName);
/**
* Retrieve the step names.
*
* @return the step names
*/
public abstract Collection<String> getStepNames();
/**
* Boolean flag to prevent categorically a job from restarting, even if it
* has failed previously.
@@ -235,7 +259,7 @@ public abstract class AbstractJob implements Job, StepLocator, BeanNameAware, In
*/
public final void execute(JobExecution execution) {
logger.debug("Job execution starting: "+execution);
logger.debug("Job execution starting: " + execution);
try {
@@ -248,8 +272,9 @@ public abstract class AbstractJob implements Job, StepLocator, BeanNameAware, In
try {
doExecute(execution);
logger.debug("Job execution complete: "+execution);
} catch (RepeatException e) {
logger.debug("Job execution complete: " + execution);
}
catch (RepeatException e) {
throw e.getCause();
}
}
@@ -259,7 +284,7 @@ public abstract class AbstractJob implements Job, StepLocator, BeanNameAware, In
// with it in the same way as any other interruption.
execution.setStatus(BatchStatus.STOPPED);
execution.setExitStatus(ExitStatus.COMPLETED);
logger.debug("Job execution was stopped: "+execution);
logger.debug("Job execution was stopped: " + execution);
}
@@ -291,9 +316,9 @@ public abstract class AbstractJob implements Job, StepLocator, BeanNameAware, In
catch (Exception e) {
logger.error("Exception encountered in afterStep callback", e);
}
jobRepository.update(execution);
}
}
@@ -347,9 +372,10 @@ public abstract class AbstractJob implements Job, StepLocator, BeanNameAware, In
logger.info("Executing step: [" + step + "]");
try {
step.execute(currentStepExecution);
} catch (JobInterruptedException e) {
}
catch (JobInterruptedException e) {
// Ensure that the job gets the message that it is stopping
// and can pass it on to other steps that are executing
// and can pass it on to other steps that are executing
// concurrently.
execution.setStatus(BatchStatus.STOPPING);
throw e;
@@ -357,13 +383,15 @@ public abstract class AbstractJob implements Job, StepLocator, BeanNameAware, In
jobRepository.updateExecutionContext(execution);
if (currentStepExecution.getStatus() == BatchStatus.STOPPING || currentStepExecution.getStatus() == BatchStatus.STOPPED) {
if (currentStepExecution.getStatus() == BatchStatus.STOPPING
|| currentStepExecution.getStatus() == BatchStatus.STOPPED) {
// Ensure that the job gets the message that it is stopping
execution.setStatus(BatchStatus.STOPPING);
throw new JobInterruptedException("Job interrupted by step execution");
}
} else {
}
else {
// currentStepExecution.setExitStatus(ExitStatus.NOOP);
}
@@ -411,11 +439,11 @@ public abstract class AbstractJob implements Job, StepLocator, BeanNameAware, In
+ "so it may be dangerous to proceed. " + "Manual intervention is probably necessary.");
}
if ((stepStatus == BatchStatus.COMPLETED && step.isAllowStartIfComplete() == false)
if ((stepStatus == BatchStatus.COMPLETED && step.isAllowStartIfComplete() == false)
|| stepStatus == BatchStatus.ABANDONED) {
// step is complete, false should be returned, indicating that the
// step should not be started
logger.info("Step already complete or not restartable, so no action to execute: "+lastStepExecution);
logger.info("Step already complete or not restartable, so no action to execute: " + lastStepExecution);
return false;
}

View File

@@ -0,0 +1,75 @@
package org.springframework.batch.core.job;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersInvalidException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
/**
* Default implementation of {@link JobParametersValidator}.
*
* @author Dave Syer
*
*/
public class DefaultJobParametersValidator implements JobParametersValidator, InitializingBean {
private Collection<String> requiredKeys = new HashSet<String>();
private Collection<String> optionalKeys = new HashSet<String>();
/**
* Check that there are no overlaps between required and optional keys.
* @throws IllegalStateException if there is an overlap
*/
public void afterPropertiesSet() throws IllegalStateException {
for (String key : requiredKeys) {
Assert.state(!optionalKeys.contains(key), "Optional keys canot be required: "+key);
}
}
/**
* Check the parameters meet the specification provided.
*
* @see JobParametersValidator#validate(JobParameters)
*/
public void validate(JobParameters parameters) throws JobParametersInvalidException {
if (parameters == null) {
throw new JobParametersInvalidException("The JobParameters can not be null");
}
Collection<String> missingKeys = new HashSet<String>();
for (String key : requiredKeys) {
if (!parameters.getParameters().containsKey(key)) {
missingKeys.add(key);
}
}
if (!missingKeys.isEmpty()) {
throw new JobParametersInvalidException("The JobParameters do not contain required keys: " + missingKeys);
}
}
/**
* The keys that are required in the parameters.
*
* @param requiredKeys the required key values
*/
public void setRequiredKeys(String[] requiredKeys) {
this.requiredKeys = new HashSet<String>(Arrays.asList(requiredKeys));
}
/**
* The keys that are optional in the parameters.
*
* @param optionalKeys the optional key values
*/
public void setOptionalKeys(String[] optionalKeys) {
this.optionalKeys = new HashSet<String>(Arrays.asList(optionalKeys));
}
}

View File

@@ -0,0 +1,24 @@
package org.springframework.batch.core.job;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersInvalidException;
/**
* Strategy interface for a {@link Job} to use in validating parameters.
*
* @author Dave Syer
*
*/
public interface JobParametersValidator {
/**
* Check the parameters meet whatever requirements are appropriate, and
* throw an exception if not.
*
* @param parameters some {@link JobParameters}
* @throws JobParametersInvalidException if the parameters are invalid
*/
void validate(JobParameters parameters) throws JobParametersInvalidException;
}

View File

@@ -18,6 +18,7 @@ package org.springframework.batch.core.launch;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersInvalidException;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.batch.core.repository.JobRestartException;
@@ -42,8 +43,8 @@ public interface JobLauncher {
* always be returned by this method, regardless of whether or not the
* execution was successful. If there is a past {@link JobExecution} which
* has paused, the same {@link JobExecution} is returned instead of a new
* one created. A exception will only be thrown if there is a failure to
* start the job. If the job encounters some error while processing, the
* one created. A exception will only be thrown if there is a failure to
* start the job. If the job encounters some error while processing, the
* JobExecution will be returned, and the status will need to be inspected.
*
* @return the {@link JobExecution} if it returns synchronously. If the
@@ -57,8 +58,10 @@ public interface JobLauncher {
* circumstances that preclude a re-start.
* @throws JobInstanceAlreadyCompleteException if the job has been run
* before with the same parameters and completed successfully
* @throws JobParametersInvalidException if the parameters are not valid for
* this job
*/
public JobExecution run(Job job, JobParameters jobParameters) throws JobExecutionAlreadyRunningException,
JobRestartException, JobInstanceAlreadyCompleteException;
JobRestartException, JobInstanceAlreadyCompleteException, JobParametersInvalidException;
}

View File

@@ -24,6 +24,7 @@ import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobInstance;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersIncrementer;
import org.springframework.batch.core.JobParametersInvalidException;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.UnexpectedJobExecutionException;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
@@ -31,10 +32,10 @@ import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteExcep
import org.springframework.batch.core.repository.JobRestartException;
/**
* Low level interface for inspecting and controlling jobs with access
* only to primitive and collection types. Suitable for a command-line client
* (e.g. that launches a new process for each operation), or a remote launcher
* like a JMX console.
* Low level interface for inspecting and controlling jobs with access only to
* primitive and collection types. Suitable for a command-line client (e.g. that
* launches a new process for each operation), or a remote launcher like a JMX
* console.
*
* @author Dave Syer
* @since 2.0
@@ -98,8 +99,9 @@ public interface JobOperator {
* name
* @throws JobInstanceAlreadyExistsException if a job instance with this
* name and parameters already exists
* @throws JobParametersInvalidException
*/
Long start(String jobName, String parameters) throws NoSuchJobException, JobInstanceAlreadyExistsException;
Long start(String jobName, String parameters) throws NoSuchJobException, JobInstanceAlreadyExistsException, JobParametersInvalidException;
/**
* Restart a failed or stopped {@link JobExecution}. Fails with an exception
@@ -117,9 +119,11 @@ public interface JobOperator {
* corresponding {@link Job} is no longer available for launching
* @throws JobRestartException if there is a non-specific error with the
* restart (e.g. corrupt or inconsistent restart data)
* @throws JobParametersInvalidException if the parameters are not valid for
* this job
*/
Long restart(long executionId) throws JobInstanceAlreadyCompleteException, NoSuchJobExecutionException,
NoSuchJobException, JobRestartException;
NoSuchJobException, JobRestartException, JobParametersInvalidException;
/**
* Launch the next in a sequence of {@link JobInstance} determined by the
@@ -138,10 +142,12 @@ public interface JobOperator {
* is launched
* @throws NoSuchJobException if there is no such job definition available
* @throws JobParametersNotFoundException if the parameters cannot be found
* @throws JobParametersInvalidException
* @throws UnexpectedJobExecutionException
* @throws UnexpectedJobExecutionException if an unexpected condition arises
*/
Long startNextInstance(String jobName) throws NoSuchJobException, JobParametersNotFoundException,
JobRestartException, JobExecutionAlreadyRunningException, JobInstanceAlreadyCompleteException;
JobRestartException, JobExecutionAlreadyRunningException, JobInstanceAlreadyCompleteException, UnexpectedJobExecutionException, JobParametersInvalidException;
/**
* Send a stop signal to the {@link JobExecution} with the supplied id. The

View File

@@ -21,6 +21,7 @@ import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobInstance;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersInvalidException;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
@@ -74,13 +75,18 @@ public class SimpleJobLauncher implements JobLauncher, InitializingBean {
* re-start is either not allowed or not needed.
* @throws JobInstanceAlreadyCompleteException if this instance has already
* completed successfully
* @throws JobParametersInvalidException
*/
public JobExecution run(final Job job, final JobParameters jobParameters)
throws JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException {
throws JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException,
JobParametersInvalidException {
Assert.notNull(job, "The Job must not be null.");
Assert.notNull(jobParameters, "The JobParameters must not be null.");
// Allow the job to veto the execution
job.validate(jobParameters);
final JobExecution jobExecution;
JobExecution lastExecution = jobRepository.getLastJobExecution(job.getName(), jobParameters);
if (lastExecution != null) {
@@ -112,7 +118,8 @@ public class SimpleJobLauncher implements JobLauncher, InitializingBean {
+ "] and the following status: [" + jobExecution.getStatus() + "]");
}
catch (Throwable t) {
logger.info("Job: [" + job + "] failed unexpectedly and fatally with the following parameters: [" + jobParameters + "]", t);
logger.info("Job: [" + job + "] failed unexpectedly and fatally with the following parameters: ["
+ jobParameters + "]", t);
rethrow(t);
}
}

View File

@@ -31,6 +31,7 @@ import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobInstance;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersIncrementer;
import org.springframework.batch.core.JobParametersInvalidException;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.UnexpectedJobExecutionException;
import org.springframework.batch.core.configuration.JobRegistry;
@@ -251,7 +252,7 @@ public class SimpleJobOperator implements JobOperator, InitializingBean {
* org.springframework.batch.core.launch.JobOperator#resume(java.lang.Long)
*/
public Long restart(long executionId) throws JobInstanceAlreadyCompleteException, NoSuchJobExecutionException,
NoSuchJobException, JobRestartException {
NoSuchJobException, JobRestartException, JobParametersInvalidException {
logger.info("Checking status of job execution with id=" + executionId);
@@ -279,7 +280,7 @@ public class SimpleJobOperator implements JobOperator, InitializingBean {
* org.springframework.batch.core.launch.JobOperator#start(java.lang.String,
* java.lang.String)
*/
public Long start(String jobName, String parameters) throws NoSuchJobException, JobInstanceAlreadyExistsException {
public Long start(String jobName, String parameters) throws NoSuchJobException, JobInstanceAlreadyExistsException, JobParametersInvalidException {
logger.info("Checking status of job with name=" + jobName);
@@ -319,7 +320,7 @@ public class SimpleJobOperator implements JobOperator, InitializingBean {
* @see JobOperator#startNextInstance(String )
*/
public Long startNextInstance(String jobName) throws NoSuchJobException, JobParametersNotFoundException,
UnexpectedJobExecutionException {
UnexpectedJobExecutionException, JobParametersInvalidException {
logger.info("Locating parameters for next instance of job with name=" + jobName);

View File

@@ -1,8 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.springframework.org/schema/batch" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<xsd:schema xmlns="http://www.springframework.org/schema/batch"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:tool="http://www.springframework.org/schema/tool"
targetNamespace="http://www.springframework.org/schema/batch" elementFormDefault="qualified" attributeFormDefault="unqualified"
targetNamespace="http://www.springframework.org/schema/batch"
elementFormDefault="qualified" attributeFormDefault="unqualified"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tool http://www.springframework.org/schema/tool/spring-tool-2.5.xsd"
version="2.1">
@@ -21,13 +22,15 @@
<xsd:documentation>
Defines a job composed of a set of steps and
transitions between steps. The job will be exposed in
the enclosing bean factory as a component of type Job
that can be launched using a JobLauncher.
the enclosing
bean factory as a component of type Job
that can be launched using a
JobLauncher.
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="description" minOccurs="0"/>
<xsd:element ref="description" minOccurs="0" />
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:group ref="flowGroup" />
<xsd:element name="listeners">
@@ -39,7 +42,8 @@
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element name="listener" type="jobExecutionListenerType" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="listener" type="jobExecutionListenerType"
minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
<xsd:attributeGroup ref="mergeAttribute" />
</xsd:complexType>
@@ -48,7 +52,22 @@
</xsd:sequence>
<xsd:attribute name="id" type="xsd:ID" use="required" />
<xsd:attributeGroup ref="jobRepositoryAttribute" />
<xsd:attribute name="incrementer" type="xsd:string" use="optional">
<xsd:attribute name="parameters-validator" type="xsd:string">
<xsd:annotation>
<xsd:documentation
source="java:org.springframework.batch.core.job.JobParametersValidator"><![CDATA[
The bean name of the JobParametersValidator to use.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type
type="org.springframework.batch.core.job.JobParametersValidator" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="incrementer" type="xsd:string"
use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to a JobParametersIncrementer bean definition. This will be
@@ -58,11 +77,13 @@
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref" />
<tool:expected-type type="org.springframework.batch.core.JobParametersIncrementer" />
<tool:expected-type
type="org.springframework.batch.core.JobParametersIncrementer" />
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="restartable" type="xsd:string" use="optional">
<xsd:attribute name="restartable" type="xsd:string"
use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
Whether the job should be retartable or not in case of failure. Set this to false
@@ -80,19 +101,20 @@
<xsd:documentation>
Defines a stage in job processing backed by a
Step. The id attribute must be specified since this
step definition will be referred to from other elements
step definition
will be referred to from other elements
to form a Job flow.
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="description" minOccurs="0"/>
<xsd:element ref="description" minOccurs="0" />
<xsd:choice minOccurs="0" maxOccurs="1">
<xsd:element name="tasklet">
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="taskletType">
<xsd:attributeGroup ref="jobRepositoryAttribute"/>
<xsd:attributeGroup ref="jobRepositoryAttribute" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -100,7 +122,7 @@
</xsd:choice>
</xsd:sequence>
<xsd:attribute name="id" type="xsd:ID" use="required" />
<xsd:attributeGroup ref="parentAttribute"/>
<xsd:attributeGroup ref="parentAttribute" />
<xsd:attributeGroup ref="abstractAttribute" />
</xsd:complexType>
</xsd:element>
@@ -110,7 +132,8 @@
<xsd:documentation>
A reference to a JobExecutionListener (or a POJO
if using before-job-method / after-job-method or
source level annotations).
source level
annotations).
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
@@ -125,7 +148,8 @@
<xsd:element name="step-listener">
<xsd:annotation>
<xsd:documentation>
A bean definition for a step listener (or POJO if using *-method attributes or source level
A bean definition for a step listener (or POJO if
using *-method attributes or source level
annotations)
</xsd:documentation>
</xsd:annotation>
@@ -148,7 +172,8 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="beans:identifiedType">
<xsd:attribute name="data-source" type="xsd:string" default="dataSource">
<xsd:attribute name="data-source" type="xsd:string"
default="dataSource">
<xsd:annotation>
<xsd:documentation source="java:javax.sql.DataSource"><![CDATA[
The bean name of the DataSource that is to be used. This attribute
@@ -162,7 +187,8 @@
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="transaction-manager" type="xsd:string" default="transactionManager">
<xsd:attribute name="transaction-manager" type="xsd:string"
default="transactionManager">
<xsd:annotation>
<xsd:documentation
source="java:org.springframework.transaction.PlatformTransactionManager"><![CDATA[
@@ -178,7 +204,8 @@
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="isolation-level-for-create" default="SERIALIZABLE" type="isolationType">
<xsd:attribute name="isolation-level-for-create"
default="SERIALIZABLE" type="isolationType">
<xsd:annotation>
<xsd:documentation><![CDATA[
The isolation level to use for creation of job execution entities.
@@ -231,28 +258,30 @@
<xsd:documentation>
Defines a stage in job processing backed by a
Step. The id attribute must be specified. The
step requires either a chunk definition,
a tasklet reference, or a reference to a
step requires either
a chunk definition,
a tasklet reference, or a reference to a
(possibly abstract) parent step.
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="description" minOccurs="0"/>
<xsd:element ref="description" minOccurs="0" />
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="tasklet" type="taskletType"/>
<xsd:element name="tasklet" type="taskletType" />
<xsd:group ref="transitions" />
</xsd:choice>
</xsd:sequence>
<xsd:attribute name="id" type="xsd:ID" use="required" />
<xsd:attributeGroup ref="parentAttribute"/>
<xsd:attributeGroup ref="parentAttribute" />
<xsd:attributeGroup ref="nextAttribute" />
</xsd:complexType>
</xsd:element>
<xsd:element name="split">
<xsd:annotation>
<xsd:documentation>
Declares job should split here into two or more subflows.
Declares job should split here into two or more
subflows.
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
@@ -260,7 +289,8 @@
<xsd:element name="flow">
<xsd:annotation>
<xsd:documentation>
A subflow within a job, having the same format as a job, but without a separate identity.
A subflow within a job, having the same
format as a job, but without a separate identity.
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
@@ -270,14 +300,17 @@
<xsd:group ref="transitions" />
</xsd:choice>
<xsd:attribute name="id" type="xsd:ID" use="required" />
<xsd:attribute name="task-executor" type="xsd:string" use="optional">
<xsd:attribute name="task-executor" type="xsd:string"
use="optional">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.core.task.TaskExecutor"><![CDATA[
<xsd:documentation
source="java:org.springframework.core.task.TaskExecutor"><![CDATA[
The task executor responsible for executing the task.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.core.task.TaskExecutor" />
<tool:expected-type
type="org.springframework.core.task.TaskExecutor" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
@@ -288,7 +321,8 @@
<xsd:element name="decision">
<xsd:annotation>
<xsd:documentation>
Declares job should query a decider to determine where execution should go next.
Declares job should query a decider to determine
where execution should go next.
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
@@ -297,13 +331,15 @@
<xsd:attribute name="decider" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation>
The decider is a reference to a JobExecutionDecider that can produce a status to base
The decider is a reference to a
JobExecutionDecider that can produce a status to base
the next
transition on.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.batch.core.job.flow.JobExecutionDecider" />
<tool:expected-type
type="org.springframework.batch.core.job.flow.JobExecutionDecider" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
@@ -315,9 +351,12 @@
<xsd:complexType name="taskletType">
<xsd:all>
<xsd:element name="chunk" type="chunkTaskletType" minOccurs="0" maxOccurs="1"/>
<xsd:element name="transaction-attributes" type="transaction-attributesType" minOccurs="0" maxOccurs="1"/>
<xsd:element name="no-rollback-exception-classes" minOccurs="0" maxOccurs="1">
<xsd:element name="chunk" type="chunkTaskletType"
minOccurs="0" maxOccurs="1" />
<xsd:element name="transaction-attributes" type="transaction-attributesType"
minOccurs="0" maxOccurs="1" />
<xsd:element name="no-rollback-exception-classes"
minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation><![CDATA[
List of exception classes that should not cause rollback if possible. This list
@@ -326,20 +365,24 @@
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:group ref="includeElementGroup" minOccurs="0" maxOccurs="unbounded"/>
<xsd:group ref="includeElementGroup" minOccurs="0"
maxOccurs="unbounded" />
<xsd:attributeGroup ref="mergeAttribute" />
</xsd:complexType>
</xsd:element>
<xsd:element name="listeners" type="stepListenersType" minOccurs="0" maxOccurs="1"/>
<xsd:element name="listeners" type="stepListenersType"
minOccurs="0" maxOccurs="1" />
</xsd:all>
<xsd:attribute name="ref" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation>
The tasklet is a reference to another bean definition that implements the Tasklet interface.
The tasklet is a reference to another bean definition that implements
the Tasklet interface.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.batch.core.step.tasklet.Tasklet"/>
<tool:expected-type
type="org.springframework.batch.core.step.tasklet.Tasklet" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
@@ -351,7 +394,8 @@
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="allow-start-if-complete" type="xsd:string" use="optional">
<xsd:attribute name="allow-start-if-complete" type="xsd:string"
use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
Set to true to allow a step to be started even if it is already complete.
@@ -374,7 +418,8 @@
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="task-executor" type="xsd:string" use="optional">
<xsd:attribute name="task-executor" type="xsd:string"
use="optional">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.core.task.TaskExecutor"><![CDATA[
The task executor responsible for executing the task.
@@ -386,7 +431,8 @@
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="throttle-limit" type="xsd:string" use="optional">
<xsd:attribute name="throttle-limit" type="xsd:string"
use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
This limits the number of tasks queued for concurrent
@@ -400,7 +446,8 @@
<xsd:complexType name="transaction-attributesType">
<xsd:attribute name="propagation">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.transaction.annotation.Propagation"><![CDATA[
<xsd:documentation
source="java:org.springframework.transaction.annotation.Propagation"><![CDATA[
The transaction propagation behavior.
]]></xsd:documentation>
</xsd:annotation>
@@ -418,7 +465,8 @@
</xsd:attribute>
<xsd:attribute name="isolation" type="isolationType">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.transaction.annotation.Isolation"><![CDATA[
<xsd:documentation
source="java:org.springframework.transaction.annotation.Isolation"><![CDATA[
The transaction isolation level.
]]></xsd:documentation>
</xsd:annotation>
@@ -444,8 +492,8 @@
<xsd:group name="beanElementGroup">
<xsd:choice>
<xsd:element ref="beans:bean"/>
<xsd:element ref="beans:ref"/>
<xsd:element ref="beans:bean" />
<xsd:element ref="beans:ref" />
</xsd:choice>
</xsd:group>
@@ -459,8 +507,9 @@
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:group ref="beanElementGroup" minOccurs="0" maxOccurs="1"/>
<xsd:attributeGroup ref="adapterMethodAttribute"/>
<xsd:group ref="beanElementGroup" minOccurs="0"
maxOccurs="1" />
<xsd:attributeGroup ref="adapterMethodAttribute" />
</xsd:complexType>
</xsd:element>
<xsd:element name="processor" minOccurs="0" maxOccurs="1">
@@ -471,8 +520,9 @@
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:group ref="beanElementGroup" minOccurs="0" maxOccurs="1"/>
<xsd:attributeGroup ref="adapterMethodAttribute"/>
<xsd:group ref="beanElementGroup" minOccurs="0"
maxOccurs="1" />
<xsd:attributeGroup ref="adapterMethodAttribute" />
</xsd:complexType>
</xsd:element>
<xsd:element name="writer" minOccurs="0" maxOccurs="1">
@@ -483,11 +533,13 @@
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:group ref="beanElementGroup" minOccurs="0" maxOccurs="1"/>
<xsd:attributeGroup ref="adapterMethodAttribute"/>
<xsd:group ref="beanElementGroup" minOccurs="0"
maxOccurs="1" />
<xsd:attributeGroup ref="adapterMethodAttribute" />
</xsd:complexType>
</xsd:element>
<xsd:element name="retry-listeners" minOccurs="0" maxOccurs="1">
<xsd:element name="retry-listeners" minOccurs="0"
maxOccurs="1">
<xsd:annotation>
<xsd:documentation><![CDATA[
List of all listeners for the step definition
@@ -496,7 +548,8 @@
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element name="listener" type="listenerType" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="listener" type="listenerType"
minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
<xsd:attributeGroup ref="mergeAttribute" />
</xsd:complexType>
@@ -520,7 +573,8 @@
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref" />
<tool:expected-type type="org.springframework.batch.item.ItemStream" />
<tool:expected-type
type="org.springframework.batch.item.ItemStream" />
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
@@ -530,7 +584,8 @@
<xsd:attributeGroup ref="mergeAttribute" />
</xsd:complexType>
</xsd:element>
<xsd:element name="skippable-exception-classes" minOccurs="0" maxOccurs="1">
<xsd:element name="skippable-exception-classes"
minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation><![CDATA[
List of exception classes that are skippable.
@@ -540,11 +595,13 @@
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:group ref="includeExcludeElementGroup" minOccurs="0" maxOccurs="unbounded"/>
<xsd:group ref="includeExcludeElementGroup" minOccurs="0"
maxOccurs="unbounded" />
<xsd:attributeGroup ref="mergeAttribute" />
</xsd:complexType>
</xsd:element>
<xsd:element name="retryable-exception-classes" minOccurs="0" maxOccurs="1">
<xsd:element name="retryable-exception-classes"
minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation><![CDATA[
List of exception classes that are retryable.
@@ -552,12 +609,14 @@
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:group ref="includeElementGroup" minOccurs="0" maxOccurs="unbounded"/>
<xsd:group ref="includeElementGroup" minOccurs="0"
maxOccurs="unbounded" />
<xsd:attributeGroup ref="mergeAttribute" />
</xsd:complexType>
</xsd:element>
</xsd:all>
<xsd:attribute name="commit-interval" type="xsd:string" use="optional">
<xsd:attribute name="commit-interval" type="xsd:string"
use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The number of items that will be processed before commit is called for the transaction.
@@ -612,14 +671,16 @@
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="cache-capacity" type="xsd:string" use="optional">
<xsd:attribute name="cache-capacity" type="xsd:string"
use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The capacity of the cache in the retry policy.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="reader-transactional-queue" type="xsd:string" use="optional">
<xsd:attribute name="reader-transactional-queue" type="xsd:string"
use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
Whether the reader is a transactional queue. If it is then items read should not be cached
@@ -627,16 +688,19 @@
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="chunk-completion-policy" type="xsd:string" use="optional">
<xsd:attribute name="chunk-completion-policy" type="xsd:string"
use="optional">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.batch.repeat.CompletionPolicy"><![CDATA[
<xsd:documentation
source="java:org.springframework.batch.repeat.CompletionPolicy"><![CDATA[
A transaction will be committed when this policy decides to
complete. Defaults to a SimpleCompletionPolicy with chunk size
equal to the commit-interval attribute.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="java:org.springframework.batch.repeat.CompletionPolicy" />
<tool:expected-type
type="java:org.springframework.batch.repeat.CompletionPolicy" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
@@ -679,7 +743,7 @@
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:attributeGroup ref="classAttribute"/>
<xsd:attributeGroup ref="classAttribute" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
@@ -687,28 +751,30 @@
<xsd:group name="includeExcludeElementGroup">
<xsd:choice>
<xsd:group ref="includeElementGroup"/>
<xsd:group ref="includeElementGroup" />
<xsd:element name="exclude">
<xsd:annotation>
<xsd:documentation>
Classify an exception as "excluded" from the set.
Classify an exception as "excluded" from the
set.
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:attributeGroup ref="classAttribute"/>
<xsd:attributeGroup ref="classAttribute" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:group>
<xsd:complexType name="listenerType">
<xsd:group ref="beanElementGroup" minOccurs="0" maxOccurs="1"/>
<xsd:group ref="beanElementGroup" minOccurs="0" maxOccurs="1" />
<xsd:attribute name="ref" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
A reference to a listener, a POJO with a
listener-annotated method, or a POJO with
a method referenced by a *-method attribute.
a method referenced by a
*-method attribute.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref" />
@@ -757,7 +823,8 @@
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="listener" type="stepListenerType" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="listener" type="stepListenerType"
minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
<xsd:attributeGroup ref="mergeAttribute" />
</xsd:complexType>
@@ -768,7 +835,8 @@
<xsd:annotation>
<xsd:documentation>
Defines a transition from this step to the
next one depending on the value of the exit
next
one depending on the value of the exit
status.
</xsd:documentation>
</xsd:annotation>
@@ -777,85 +845,107 @@
<xsd:annotation>
<xsd:documentation>A pattern to match against the exit status
code. Use * and ? as wildcard characters. When a
step finishes the most
specific match will be chosen to select the next step. Hint:
step finishes
the most
specific match will be chosen to select the next step.
Hint:
always include a default
transition with on=&quot;*&quot;.</xsd:documentation>
</xsd:annotation></xsd:attribute>
<xsd:attribute name="to" type="xsd:string" use="required" >
transition with on=&quot;*&quot;.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="to" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation>
The name of the step to go to next. Must resolve to one of the other steps in this job.
The name of the step to go to next. Must
resolve to one of the other steps in this job.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
</xsd:element>
<xsd:element name="stop">
<xsd:annotation>
<xsd:documentation>
Declares job should be stop at this point and provides pointer where execution should continue when
the job is restarted.
Declares job should be stop at this point and
provides pointer where execution should continue when
the job is
restarted.
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:attribute name="on" type="xsd:string" use="required" >
<xsd:attribute name="on" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation>A pattern to match against the exit status code. Use * and ? as wildcard characters.
When a step finishes the most specific match will be chosen to select the next step.</xsd:documentation>
</xsd:annotation></xsd:attribute>
<xsd:attribute name="restart" type="xsd:string" use="required" >
<xsd:annotation>
<xsd:documentation>The name of the step to start on when the stopped job is restarted.
Must resolve to one of the other steps in this job.</xsd:documentation>
<xsd:documentation>A pattern to match against the exit status
code. Use * and ? as wildcard characters.
When a step finishes the most specific match will be chosen to
select the next step.</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:attribute name="restart" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation>The name of the step to start on when the
stopped job is restarted.
Must resolve to one of the other steps in this job.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:element name="end">
<xsd:annotation>
<xsd:documentation>
Declares job should end at this point, without the possibility of restart.
BatchStatus will be COMPLETED. ExitStatus is configurable.
Declares job should end at this point, without
the possibility of restart.
BatchStatus will be COMPLETED. ExitStatus is configurable.
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:attribute name="on" type="xsd:string" use="required" >
<xsd:attribute name="on" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation>A pattern to match against the exit status code. Use * and ? as wildcard characters.
When a step finishes the most specific match will be chosen to select the next step.</xsd:documentation>
<xsd:documentation>A pattern to match against the exit status
code. Use * and ? as wildcard characters.
When a step finishes the most specific match will be chosen to
select the next step.</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="exit-code" use="optional" type="xsd:string" default="COMPLETED">
<xsd:attribute name="exit-code" use="optional" type="xsd:string"
default="COMPLETED">
<xsd:annotation>
<xsd:documentation>The exit code value to end on, defaults to COMPLETED.</xsd:documentation>
<xsd:documentation>The exit code value to end on, defaults to
COMPLETED.</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
</xsd:complexType>
</xsd:element>
<xsd:element name="fail">
<xsd:annotation>
<xsd:documentation>
Declares job should fail at this point. BatchStatus will be FAILED. ExitStatus is configurable.
Declares job should fail at this point.
BatchStatus will be FAILED. ExitStatus is configurable.
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:attribute name="on" type="xsd:string" use="required" >
<xsd:attribute name="on" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation>A pattern to match against the exit status code. Use * and ? as wildcard characters.
When a step finishes the most specific match will be chosen to select the next step.</xsd:documentation>
<xsd:documentation>A pattern to match against the exit status
code. Use * and ? as wildcard characters.
When a step finishes the most specific match will be chosen to
select the next step.</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="exit-code" use="optional" type="xsd:string" default="FAILED">
<xsd:attribute name="exit-code" use="optional" type="xsd:string"
default="FAILED">
<xsd:annotation>
<xsd:documentation>The exit code value to end on, defaults to FAILED.</xsd:documentation>
<xsd:documentation>The exit code value to end on, defaults to
FAILED.</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:group>
<xsd:attributeGroup name="jobRepositoryAttribute">
<xsd:attribute name="job-repository" type="xsd:string">
<xsd:annotation>
@@ -879,10 +969,11 @@
<xsd:attribute name="parent" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation>
The name of the parent bean from which the configuration should inherit.
The name of the parent bean from which the
configuration should inherit.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref"/>
<tool:annotation kind="ref" />
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
@@ -892,11 +983,15 @@
<xsd:attribute name="abstract" type="xsd:boolean" use="optional">
<xsd:annotation>
<xsd:documentation>
Is this bean "abstract", that is, not meant to be instantiated itself
but rather just serving as parent for concrete child bean definitions?
The default is "false". Specify "true" to tell the bean factory to not
try to instantiate that particular bean in any case.
Is this bean "abstract", that is, not meant to be
instantiated itself
but rather just serving as parent for concrete
child bean definitions?
The default is "false". Specify "true" to
tell the bean factory to not
try to instantiate that particular bean
in any case.
Note: This attribute will not be inherited by child bean definitions.
Hence, it needs to be specified per abstract bean definition.
</xsd:documentation>
@@ -908,24 +1003,27 @@
<xsd:attribute name="merge" type="xsd:boolean" use="optional">
<xsd:annotation>
<xsd:documentation>
Should this list be merged with the corresponding list provided
by the parent? If not, it will overwrite the parent list.
Should this list be merged with the corresponding
list provided
by the parent? If not, it will overwrite the parent list.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:attributeGroup>
<xsd:attributeGroup name="adapterMethodAttribute">
<xsd:attribute name="adapter-method" type="xsd:string" use="optional">
<xsd:attribute name="adapter-method" type="xsd:string"
use="optional">
<xsd:annotation>
<xsd:documentation>
This attribute indicates the method from the class that should
This attribute indicates the method from the
class that should
be used to dynamically create a proxy.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:attributeGroup>
<xsd:element name="description">
<xsd:annotation>
<xsd:documentation><![CDATA[
@@ -934,7 +1032,7 @@
]]></xsd:documentation>
</xsd:annotation>
<xsd:complexType mixed="true">
<xsd:choice minOccurs="0" maxOccurs="unbounded"/>
<xsd:choice minOccurs="0" maxOccurs="unbounded" />
</xsd:complexType>
</xsd:element>

View File

@@ -20,6 +20,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.junit.BeforeClass;
@@ -28,6 +29,7 @@ import org.springframework.aop.framework.Advised;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecutionListener;
import org.springframework.batch.core.job.AbstractJob;
import org.springframework.batch.core.job.DefaultJobParametersValidator;
import org.springframework.batch.core.listener.JobExecutionListenerSupport;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.repository.support.SimpleJobRepository;
@@ -182,6 +184,18 @@ public class JobParserTests {
}
}
@Test
public void testParametersValidator() {
ApplicationContext ctx = jobParserParentAttributeTestsCtx;
Job job = (Job) ctx.getBean("jobWithParametersValidator");
assertTrue(job instanceof AbstractJob);
Object validator = ReflectionTestUtils.getField(job, "jobParametersValidator");
assertTrue(validator instanceof DefaultJobParametersValidator);
@SuppressWarnings("unchecked")
Collection<String> keys = (Collection<String>) ReflectionTestUtils.getField(validator, "requiredKeys");
assertEquals(2, keys.size());
}
@Test
public void testListenerClearingJob() throws Exception {
assertEquals(0, getListeners("listenerClearingJob", jobParserParentAttributeTestsCtx).size());

View File

@@ -0,0 +1,43 @@
package org.springframework.batch.core.job;
import org.junit.Test;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.JobParametersInvalidException;
public class DefaultJobParametersValidatorTests {
private DefaultJobParametersValidator validator = new DefaultJobParametersValidator();
@Test(expected = JobParametersInvalidException.class)
public void testValidateNull() throws Exception {
validator.validate(null);
}
@Test
public void testValidateRequiredValues() throws Exception {
validator.setRequiredKeys(new String[] { "name", "value" });
validator
.validate(new JobParametersBuilder().addString("name", "foo").addLong("value", 111L).toJobParameters());
}
@Test(expected = JobParametersInvalidException.class)
public void testValidateRequiredValuesMissing() throws Exception {
validator.setRequiredKeys(new String[] { "name", "value" });
validator.validate(new JobParameters());
}
@Test
public void testValidateOptionalValues() throws Exception {
validator.setOptionalKeys(new String[] { "name", "value" });
validator.validate(new JobParameters());
}
@Test(expected = IllegalStateException.class)
public void testOptionalValuesAlsoRequired() throws Exception {
validator.setOptionalKeys(new String[] { "name", "value" });
validator.setRequiredKeys(new String[] { "foo", "value" });
validator.afterPropertiesSet();
}
}

View File

@@ -31,6 +31,7 @@ import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionException;
import org.springframework.batch.core.JobInterruptedException;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersInvalidException;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.repository.JobRepository;
@@ -42,7 +43,7 @@ import org.springframework.batch.support.transaction.ResourcelessTransactionMana
* @author Dave Syer
*
*/
public class AbstractJobTests {
public class ExtendedAbstractJobTests {
AbstractJob job = new StubJob("job");
@@ -109,6 +110,28 @@ public class AbstractJobTests {
assertTrue(e.getMessage().contains("JobRepository"));
}
}
@Test(expected=JobParametersInvalidException.class)
public void testValidatorWithNullParameters() throws Exception {
job.validate(null);
}
@Test
public void testValidatorWithNotNullParameters() throws Exception {
job.validate(new JobParameters());
// Should be free of side effects
}
@Test(expected=JobParametersInvalidException.class)
public void testSetValidator() throws Exception {
job.setJobParametersValidator(new DefaultJobParametersValidator() {
@Override
public void validate(JobParameters parameters) throws JobParametersInvalidException {
throw new JobParametersInvalidException("Expected");
}
});
job.validate(new JobParameters());
}
/**
* Runs the step and persists job execution context.

View File

@@ -21,7 +21,9 @@ import java.util.List;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersIncrementer;
import org.springframework.batch.core.JobParametersInvalidException;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.UnexpectedJobExecutionException;
import org.springframework.beans.factory.BeanNameAware;
@@ -132,6 +134,13 @@ public class JobSupport implements BeanNameAware, Job {
public boolean isRestartable() {
return restartable;
}
/**
* @see Job#validate(JobParameters)
*/
public void validate(JobParameters parameters) throws JobParametersInvalidException {
}
/*
* (non-Javadoc)

View File

@@ -17,10 +17,7 @@ import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.job.SimpleJob;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.repository.JobRestartException;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
@@ -308,8 +305,7 @@ public class FaultTolerantExceptionClassesTests implements ApplicationContextAwa
assertEquals("[1, 1, 1, 1]", tasklet.getCommitted().toString());
}
private StepExecution launchStep(String stepName) throws JobExecutionAlreadyRunningException, JobRestartException,
JobInstanceAlreadyCompleteException {
private StepExecution launchStep(String stepName) throws Exception {
SimpleJob job = new SimpleJob();
job.setName("job");
job.setJobRepository(jobRepository);

View File

@@ -1,84 +1,117 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/batch" xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<beans:beans xmlns="http://www.springframework.org/schema/batch"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-2.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<beans:import resource="common-context.xml" />
<job id="job1" parent="baseJob">
<step id="s1"><tasklet ref="dummyTasklet"/></step>
<step id="s1">
<tasklet ref="dummyTasklet" />
</step>
<listeners merge="true">
<listener>
<beans:bean class="org.springframework.batch.core.listener.JobExecutionListenerSupport"/>
<beans:bean
class="org.springframework.batch.core.listener.JobExecutionListenerSupport" />
</listener>
</listeners>
</job>
<job id="job2" parent="baseJob">
<step id="s2"><tasklet ref="dummyTasklet"/></step>
<step id="s2">
<tasklet ref="dummyTasklet" />
</step>
<listeners>
<listener>
<beans:bean class="org.springframework.batch.core.listener.JobExecutionListenerSupport"/>
<beans:bean
class="org.springframework.batch.core.listener.JobExecutionListenerSupport" />
</listener>
</listeners>
</job>
<job id="job3" parent="baseJob3">
<step id="s3"><tasklet ref="dummyTasklet"/></step>
<step id="s3">
<tasklet ref="dummyTasklet" />
</step>
<listeners merge="true">
<listener ref="listener1"/>
<listener ref="listener1" />
</listeners>
</job>
<job id="defaultRepoJob">
<step id="s4"><tasklet ref="dummyTasklet"/></step>
<step id="s4">
<tasklet ref="dummyTasklet" />
</step>
</job>
<job id="specifiedRepoJob" job-repository="dummyJobRepository">
<step id="s5"><tasklet ref="dummyTasklet"/></step>
<step id="s5">
<tasklet ref="dummyTasklet" />
</step>
</job>
<job id="inheritSpecifiedRepoJob" parent="baseSpecifiedRepoJob">
<step id="s6"><tasklet ref="dummyTasklet"/></step>
<step id="s6">
<tasklet ref="dummyTasklet" />
</step>
</job>
<job id="overrideInheritedRepoJob" parent="baseSpecifiedRepoJob" job-repository="jobRepository">
<step id="s7"><tasklet ref="dummyTasklet"/></step>
<job id="overrideInheritedRepoJob" parent="baseSpecifiedRepoJob"
job-repository="jobRepository">
<step id="s7">
<tasklet ref="dummyTasklet" />
</step>
</job>
<job id="baseSpecifiedRepoJob" abstract="true" job-repository="dummyJobRepository"/>
<job id="jobWithParametersValidator" parameters-validator="parametersValidator">
<step id="s8">
<tasklet ref="dummyTasklet" />
</step>
</job>
<beans:bean id="parametersValidator"
class="org.springframework.batch.core.job.DefaultJobParametersValidator">
<beans:property name="requiredKeys" value="name,value"/>
</beans:bean>
<job id="baseSpecifiedRepoJob" abstract="true" job-repository="dummyJobRepository" />
<job id="baseJob" abstract="true">
<listeners>
<listener>
<beans:bean class="org.springframework.batch.core.configuration.xml.DummyAnnotationJobExecutionListener"/>
<beans:bean
class="org.springframework.batch.core.configuration.xml.DummyAnnotationJobExecutionListener" />
</listener>
</listeners>
</job>
<job-listener id="listener1">
<beans:bean class="org.springframework.batch.core.configuration.xml.DummyAnnotationJobExecutionListener"/>
<beans:bean
class="org.springframework.batch.core.configuration.xml.DummyAnnotationJobExecutionListener" />
</job-listener>
<beans:bean id="baseJob3" abstract="true">
<beans:property name="jobExecutionListeners">
<beans:list>
<job-listener>
<beans:bean class="org.springframework.batch.core.listener.JobExecutionListenerSupport"/>
<beans:bean
class="org.springframework.batch.core.listener.JobExecutionListenerSupport" />
</job-listener>
</beans:list>
</beans:property>
</beans:bean>
<beans:bean id="dummyJobRepository" class="org.springframework.batch.core.configuration.xml.DummyJobRepository"/>
</beans:bean>
<beans:bean id="dummyJobRepository"
class="org.springframework.batch.core.configuration.xml.DummyJobRepository" />
<job id="listenerClearingJob" parent="baseJob">
<step id="listenerClearingJobStep"><tasklet ref="dummyTasklet"/></step>
<listeners/>
<step id="listenerClearingJobStep">
<tasklet ref="dummyTasklet" />
</step>
<listeners />
</job>
</beans:beans>