Configuring a Job

There are multiple implementations of the Job interface. However, these implementations are abstracted behind either the provided builders (for Java configuration) or the XML namespace (for XML-based configuration). The following example shows both Java and XML configuration:

  • Java

  • XML

@Bean
public Job footballJob(JobRepository jobRepository) {
    return new JobBuilder("footballJob", jobRepository)
                     .start(playerLoad())
                     .next(gameLoad())
                     .next(playerSummarization())
                     .build();
}

A Job (and, typically, any Step within it) requires a JobRepository. The configuration of the JobRepository is handled through the Java Configuration.

The preceding example illustrates a Job that consists of three Step instances. The job related builders can also contain other elements that help with parallelization (Split), declarative flow control (Decision), and externalization of flow definitions (Flow).

There are multiple implementations of the Job interface. However, the namespace abstracts away the differences in configuration. It has only three required dependencies: a name, JobRepository , and a list of Step instances. The following example creates a footballJob:

<job id="footballJob">
    <step id="playerload"          parent="s1" next="gameLoad"/>
    <step id="gameLoad"            parent="s2" next="playerSummarization"/>
    <step id="playerSummarization" parent="s3"/>
</job>

The preceding examples uses a parent bean definition to create the steps. See the section on step configuration for more options when declaring specific step details inline. The XML namespace defaults to referencing a repository with an id of jobRepository, which is a sensible default. However, you can explicitly override this default:

<job id="footballJob" job-repository="specialRepository">
    <step id="playerload"          parent="s1" next="gameLoad"/>
    <step id="gameLoad"            parent="s3" next="playerSummarization"/>
    <step id="playerSummarization" parent="s3"/>
</job>

In addition to steps, a job configuration can contain other elements that help with parallelization (<split>), declarative flow control (<decision>), and externalization of flow definitions (<flow/>).

Restartability

One key issue when executing a batch job concerns the behavior of a Job when it is restarted. The launching of a Job is considered to be a “restart” if a JobExecution already exists for the particular JobInstance. Ideally, all jobs should be able to start up where they left off, but there are scenarios where this is not possible. In this scenario, it is entirely up to the developer to ensure that a new JobInstance is created. However, Spring Batch does provide some help. If a Job should never be restarted but should always be run as part of a new JobInstance, you can set the restartable property to false.

  • Java

  • XML

The following example shows how to set the restartable field to false in Java:

Java Configuration
@Bean
public Job footballJob(JobRepository jobRepository) {
    return new JobBuilder("footballJob", jobRepository)
                     .preventRestart()
                     ...
                     .build();
}

The following example shows how to set the restartable field to false in XML:

XML Configuration
<job id="footballJob" restartable="false">
    ...
</job>

To phrase it another way, setting restartable to false means “this Job does not support being started again”. Restarting a Job that is not restartable causes a JobRestartException to be thrown. The following Junit code causes the exception to be thrown:

Job job = new SimpleJob();
job.setRestartable(false);

JobParameters jobParameters = new JobParameters();

JobExecution firstExecution = jobRepository.createJobExecution(job, jobParameters);
jobRepository.saveOrUpdate(firstExecution);

try {
    jobRepository.createJobExecution(job, jobParameters);
    fail();
}
catch (JobRestartException e) {
    // expected
}

The first attempt to create a JobExecution for a non-restartable job causes no issues. However, the second attempt throws a JobRestartException.

Intercepting Job Execution

During the course of the execution of a Job, it may be useful to be notified of various events in its lifecycle so that custom code can be run. SimpleJob allows for this by calling a JobListener at the appropriate time:

public interface JobExecutionListener {

    void beforeJob(JobExecution jobExecution);

    void afterJob(JobExecution jobExecution);
}

You can add JobListeners to a SimpleJob by setting listeners on the job.

  • Java

  • XML

The following example shows how to add a listener method to a Java job definition:

Java Configuration
@Bean
public Job footballJob(JobRepository jobRepository) {
    return new JobBuilder("footballJob", jobRepository)
                     .listener(sampleListener())
                     ...
                     .build();
}

The following example shows how to add a listener element to an XML job definition:

XML Configuration
<job id="footballJob">
    <step id="playerload"          parent="s1" next="gameLoad"/>
    <step id="gameLoad"            parent="s2" next="playerSummarization"/>
    <step id="playerSummarization" parent="s3"/>
    <listeners>
        <listener ref="sampleListener"/>
    </listeners>
</job>

Note that the afterJob method is called regardless of the success or failure of the Job. If you need to determine success or failure, you can get that information from the JobExecution:

public void afterJob(JobExecution jobExecution){
    if (jobExecution.getStatus() == BatchStatus.COMPLETED ) {
        //job success
    }
    else if (jobExecution.getStatus() == BatchStatus.FAILED) {
        //job failure
    }
}

The annotations corresponding to this interface are:

  • @BeforeJob

  • @AfterJob

Inheriting from a Parent Job

If a group of Jobs share similar but not identical configurations, it may help to define a “parent” Job from which the concrete Job instances can inherit properties. Similar to class inheritance in Java, a “child” Job combines its elements and attributes with the parent’s.

In the following example, baseJob is an abstract Job definition that defines only a list of listeners. The Job (job1) is a concrete definition that inherits the list of listeners from baseJob and merges it with its own list of listeners to produce a Job with two listeners and one Step (step1).

<job id="baseJob" abstract="true">
    <listeners>
        <listener ref="listenerOne"/>
    </listeners>
</job>

<job id="job1" parent="baseJob">
    <step id="step1" parent="standaloneStep"/>

    <listeners merge="true">
        <listener ref="listenerTwo"/>
    </listeners>
</job>

See the section on Inheriting from a Parent Step for more detailed information.

JobParametersValidator

A job declared in the XML namespace or using any subclass of AbstractJob can optionally declare a validator for the job parameters at runtime. This is useful when, for instance, you need to assert that a job is started with all its mandatory parameters. There is a DefaultJobParametersValidator that you can use to constrain combinations of simple mandatory and optional parameters. For more complex constraints, you can implement the interface yourself.

  • Java

  • XML

The configuration of a validator is supported through the Java builders:

@Bean
public Job job1(JobRepository jobRepository) {
    return new JobBuilder("job1", jobRepository)
                     .validator(parametersValidator())
                     ...
                     .build();
}

The configuration of a validator is supported through the XML namespace through a child element of the job, as the following example shows:

<job id="job1" parent="baseJob3">
    <step id="step1" parent="standaloneStep"/>
    <validator ref="parametersValidator"/>
</job>

You can specify the validator as a reference (as shown earlier) or as a nested bean definition in the beans namespace.