Skip to content

Commit 9209fb4

Browse files
committed
Move next parameters calculation logic in job operator
Resolves #4911
1 parent 780daff commit 9209fb4

File tree

4 files changed

+50
-196
lines changed

4 files changed

+50
-196
lines changed

spring-batch-core/src/main/java/org/springframework/batch/core/job/parameters/JobParametersBuilder.java

Lines changed: 1 addition & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2006-2024 the original author or authors.
2+
* Copyright 2006-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -52,40 +52,18 @@ public class JobParametersBuilder {
5252

5353
private Map<String, JobParameter<?>> parameterMap;
5454

55-
private JobExplorer jobExplorer;
56-
5755
/**
5856
* Default constructor. Initializes the builder with empty parameters.
5957
*/
6058
public JobParametersBuilder() {
6159
this.parameterMap = new HashMap<>();
6260
}
6361

64-
/**
65-
* @param jobExplorer {@link JobExplorer} used for looking up previous job parameter
66-
* information.
67-
*/
68-
public JobParametersBuilder(JobExplorer jobExplorer) {
69-
this.jobExplorer = jobExplorer;
70-
this.parameterMap = new HashMap<>();
71-
}
72-
7362
/**
7463
* Copy constructor. Initializes the builder with the supplied parameters.
7564
* @param jobParameters {@link JobParameters} instance used to initialize the builder.
7665
*/
7766
public JobParametersBuilder(JobParameters jobParameters) {
78-
this(jobParameters, null);
79-
}
80-
81-
/**
82-
* Copy constructor. Initializes the builder with the supplied parameters.
83-
* @param jobParameters {@link JobParameters} instance used to initialize the builder.
84-
* @param jobExplorer {@link JobExplorer} used for looking up previous job parameter
85-
* information.
86-
*/
87-
public JobParametersBuilder(JobParameters jobParameters, JobExplorer jobExplorer) {
88-
this.jobExplorer = jobExplorer;
8967
this.parameterMap = new HashMap<>(jobParameters.getParameters());
9068
}
9169

@@ -319,51 +297,4 @@ public JobParametersBuilder addJobParameters(JobParameters jobParameters) {
319297
return this;
320298
}
321299

322-
/**
323-
* Initializes the {@link JobParameters} based on the state of the {@link Job}. This
324-
* should be called after all parameters have been entered into the builder. All
325-
* parameters already set on this builder instance are appended to those retrieved
326-
* from the job incrementer, overriding any with the same key (this is the same
327-
* behavior as
328-
* {@link org.springframework.batch.core.launch.support.CommandLineJobRunner} with the
329-
* {@code -next} option and
330-
* {@link org.springframework.batch.core.launch.JobOperator#startNextInstance(String)}).
331-
* @param job The job for which the {@link JobParameters} are being constructed.
332-
* @return a reference to this object.
333-
*
334-
* @since 4.0
335-
*/
336-
public JobParametersBuilder getNextJobParameters(Job job) {
337-
Assert.state(this.jobExplorer != null, "A JobExplorer is required to get next job parameters");
338-
Assert.notNull(job, "Job must not be null");
339-
Assert.notNull(job.getJobParametersIncrementer(),
340-
"No job parameters incrementer found for job=" + job.getName());
341-
342-
String name = job.getName();
343-
JobParameters nextParameters;
344-
JobInstance lastInstance = this.jobExplorer.getLastJobInstance(name);
345-
JobParametersIncrementer incrementer = job.getJobParametersIncrementer();
346-
if (lastInstance == null) {
347-
// Start from a completely clean sheet
348-
nextParameters = incrementer.getNext(new JobParameters());
349-
}
350-
else {
351-
JobExecution previousExecution = this.jobExplorer.getLastJobExecution(lastInstance);
352-
if (previousExecution == null) {
353-
// Normally this will not happen - an instance exists with no executions
354-
nextParameters = incrementer.getNext(new JobParameters());
355-
}
356-
else {
357-
nextParameters = incrementer.getNext(previousExecution.getJobParameters());
358-
}
359-
}
360-
361-
// start with parameters from the incrementer
362-
Map<String, JobParameter<?>> nextParametersMap = new HashMap<>(nextParameters.getParameters());
363-
// append new parameters (overriding those with the same key)
364-
nextParametersMap.putAll(this.parameterMap);
365-
this.parameterMap = nextParametersMap;
366-
return this;
367-
}
368-
369300
}

spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/CommandLineJobRunner.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -373,8 +373,23 @@ int start(String jobPath, String jobIdentifier, String[] parameters, Set<String>
373373
}
374374

375375
if (opts.contains("-next")) {
376-
jobParameters = new JobParametersBuilder(jobParameters, jobExplorer).getNextJobParameters(job)
377-
.toJobParameters();
376+
JobInstance lastInstance = jobRepository.getLastJobInstance(jobName);
377+
JobParametersIncrementer incrementer = job.getJobParametersIncrementer();
378+
if (lastInstance == null) {
379+
// Start from a completely clean sheet
380+
jobParameters = incrementer.getNext(new JobParameters());
381+
}
382+
else {
383+
JobExecution previousExecution = jobRepository.getLastJobExecution(lastInstance);
384+
if (previousExecution == null) {
385+
// Normally this will not happen - an instance exists with no
386+
// executions
387+
jobParameters = incrementer.getNext(new JobParameters());
388+
}
389+
else {
390+
jobParameters = incrementer.getNext(previousExecution.getJobParameters());
391+
}
392+
}
378393
}
379394

380395
JobExecution jobExecution = launcher.run(job, jobParameters);

spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/SimpleJobOperator.java

Lines changed: 30 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.springframework.batch.core.job.JobInstance;
3535
import org.springframework.batch.core.job.parameters.JobParameters;
3636
import org.springframework.batch.core.job.parameters.JobParametersBuilder;
37+
import org.springframework.batch.core.job.parameters.JobParametersIncrementer;
3738
import org.springframework.batch.core.job.parameters.JobParametersInvalidException;
3839
import org.springframework.batch.core.step.Step;
3940
import org.springframework.batch.core.step.StepExecution;
@@ -223,51 +224,52 @@ public Long startNextInstance(String jobName)
223224
}
224225

225226
Job job = jobRegistry.getJob(jobName);
226-
JobParameters parameters = new JobParametersBuilder(jobRepository).getNextJobParameters(job).toJobParameters();
227-
if (logger.isInfoEnabled()) {
228-
logger.info(String.format("Attempting to launch job with name=%s and parameters=%s", jobName, parameters));
229-
}
230-
try {
231-
return run(job, parameters).getId();
232-
}
233-
catch (JobExecutionAlreadyRunningException e) {
234-
throw new UnexpectedJobExecutionException(
235-
String.format(ILLEGAL_STATE_MSG, "job already running", jobName, parameters), e);
236-
}
237-
catch (JobRestartException e) {
238-
throw new UnexpectedJobExecutionException(
239-
String.format(ILLEGAL_STATE_MSG, "job not restartable", jobName, parameters), e);
240-
}
241-
catch (JobInstanceAlreadyCompleteException e) {
242-
throw new UnexpectedJobExecutionException(
243-
String.format(ILLEGAL_STATE_MSG, "job instance already complete", jobName, parameters), e);
244-
}
245-
227+
return startNextInstance(job).getId();
246228
}
247229

248230
@Override
249231
public JobExecution startNextInstance(Job job)
250232
throws NoSuchJobException, UnexpectedJobExecutionException, JobParametersInvalidException {
251-
252-
JobParameters parameters = new JobParametersBuilder(jobRepository).getNextJobParameters(job).toJobParameters();
233+
Assert.notNull(job, "Job must not be null");
234+
Assert.notNull(job.getJobParametersIncrementer(),
235+
"No job parameters incrementer found for job=" + job.getName());
236+
String name = job.getName();
237+
JobParameters nextParameters;
238+
JobInstance lastInstance = jobRepository.getLastJobInstance(name);
239+
JobParametersIncrementer incrementer = job.getJobParametersIncrementer();
240+
if (lastInstance == null) {
241+
// Start from a completely clean sheet
242+
nextParameters = incrementer.getNext(new JobParameters());
243+
}
244+
else {
245+
JobExecution previousExecution = jobRepository.getLastJobExecution(lastInstance);
246+
if (previousExecution == null) {
247+
// Normally this will not happen - an instance exists with no executions
248+
nextParameters = incrementer.getNext(new JobParameters());
249+
}
250+
else {
251+
nextParameters = incrementer.getNext(previousExecution.getJobParameters());
252+
}
253+
}
253254
if (logger.isInfoEnabled()) {
254-
logger.info(String.format("Attempting to launch job with name=%s and parameters=%s", job.getName(),
255-
parameters));
255+
logger.info(String.format("Attempting to launch next instance of job with name=%s and parameters=%s",
256+
job.getName(), nextParameters));
256257
}
257258
try {
258-
return run(job, parameters);
259+
return run(job, nextParameters);
259260
}
260261
catch (JobExecutionAlreadyRunningException e) {
261262
throw new UnexpectedJobExecutionException(
262-
String.format(ILLEGAL_STATE_MSG, "job already running", job.getName(), parameters), e);
263+
String.format(ILLEGAL_STATE_MSG, "job already running", job.getName(), nextParameters), e);
263264
}
264265
catch (JobRestartException e) {
265266
throw new UnexpectedJobExecutionException(
266-
String.format(ILLEGAL_STATE_MSG, "job not restartable", job.getName(), parameters), e);
267+
String.format(ILLEGAL_STATE_MSG, "job not restartable", job.getName(), nextParameters), e);
267268
}
268269
catch (JobInstanceAlreadyCompleteException e) {
269270
throw new UnexpectedJobExecutionException(
270-
String.format(ILLEGAL_STATE_MSG, "job instance already complete", job.getName(), parameters), e);
271+
String.format(ILLEGAL_STATE_MSG, "job instance already complete", job.getName(), nextParameters),
272+
e);
271273
}
272274

273275
}

spring-batch-core/src/test/java/org/springframework/batch/core/JobParametersBuilderTests.java

Lines changed: 2 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2008-2024 the original author or authors.
2+
* Copyright 2008-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -30,16 +30,11 @@
3030
import org.springframework.batch.core.job.parameters.JobParameter;
3131
import org.springframework.batch.core.job.parameters.JobParameters;
3232
import org.springframework.batch.core.job.parameters.JobParametersBuilder;
33-
import org.springframework.batch.core.repository.explore.JobExplorer;
34-
import org.springframework.batch.core.launch.support.RunIdIncrementer;
3533

3634
import static org.assertj.core.api.Assertions.assertThat;
3735
import static org.junit.jupiter.api.Assertions.assertEquals;
3836
import static org.junit.jupiter.api.Assertions.assertFalse;
3937
import static org.junit.jupiter.api.Assertions.assertThrows;
40-
import static org.mockito.ArgumentMatchers.any;
41-
import static org.mockito.Mockito.mock;
42-
import static org.mockito.Mockito.when;
4338

4439
/**
4540
* @author Lucas Ward
@@ -54,8 +49,6 @@ class JobParametersBuilderTests {
5449

5550
private SimpleJob job;
5651

57-
private JobExplorer jobExplorer;
58-
5952
private List<JobInstance> jobInstanceList;
6053

6154
private List<JobExecution> jobExecutionList;
@@ -65,10 +58,9 @@ class JobParametersBuilderTests {
6558
@BeforeEach
6659
void initialize() {
6760
this.job = new SimpleJob("simpleJob");
68-
this.jobExplorer = mock();
6961
this.jobInstanceList = new ArrayList<>(1);
7062
this.jobExecutionList = new ArrayList<>(1);
71-
this.parametersBuilder = new JobParametersBuilder(this.jobExplorer);
63+
this.parametersBuilder = new JobParametersBuilder();
7264
}
7365

7466
@Test
@@ -162,90 +154,4 @@ void testAddJobParameter() {
162154
assertEquals("bar", parameters.get("foo").getValue());
163155
}
164156

165-
@Test
166-
void testGetNextJobParametersFirstRun() {
167-
job.setJobParametersIncrementer(new RunIdIncrementer());
168-
initializeForNextJobParameters();
169-
this.parametersBuilder.getNextJobParameters(this.job);
170-
defaultNextJobParametersVerify(this.parametersBuilder.toJobParameters(), 4);
171-
}
172-
173-
@Test
174-
void testGetNextJobParametersNoIncrementer() {
175-
initializeForNextJobParameters();
176-
final Exception expectedException = assertThrows(IllegalArgumentException.class,
177-
() -> this.parametersBuilder.getNextJobParameters(this.job));
178-
assertEquals("No job parameters incrementer found for job=simpleJob", expectedException.getMessage());
179-
}
180-
181-
@Test
182-
void testGetNextJobParameters() {
183-
this.job.setJobParametersIncrementer(new RunIdIncrementer());
184-
this.jobInstanceList.add(new JobInstance(1L, "simpleJobInstance"));
185-
this.jobExecutionList.add(getJobExecution(this.jobInstanceList.get(0), null));
186-
when(this.jobExplorer.getJobInstances("simpleJob", 0, 1)).thenReturn(this.jobInstanceList);
187-
when(this.jobExplorer.getJobExecutions(any())).thenReturn(this.jobExecutionList);
188-
initializeForNextJobParameters();
189-
this.parametersBuilder.getNextJobParameters(this.job);
190-
defaultNextJobParametersVerify(this.parametersBuilder.toJobParameters(), 4);
191-
}
192-
193-
@Test
194-
void testGetNextJobParametersRestartable() {
195-
this.job.setRestartable(true);
196-
this.job.setJobParametersIncrementer(new RunIdIncrementer());
197-
this.jobInstanceList.add(new JobInstance(1L, "simpleJobInstance"));
198-
this.jobExecutionList.add(getJobExecution(this.jobInstanceList.get(0), BatchStatus.FAILED));
199-
when(this.jobExplorer.getJobInstances("simpleJob", 0, 1)).thenReturn(this.jobInstanceList);
200-
when(this.jobExplorer.getJobExecutions(any())).thenReturn(this.jobExecutionList);
201-
initializeForNextJobParameters();
202-
this.parametersBuilder.addLong("NON_IDENTIFYING_LONG", 1L, false);
203-
this.parametersBuilder.getNextJobParameters(this.job);
204-
baseJobParametersVerify(this.parametersBuilder.toJobParameters(), 5);
205-
}
206-
207-
@Test
208-
void testGetNextJobParametersNoPreviousExecution() {
209-
this.job.setJobParametersIncrementer(new RunIdIncrementer());
210-
this.jobInstanceList.add(new JobInstance(1L, "simpleJobInstance"));
211-
when(this.jobExplorer.getJobInstances("simpleJob", 0, 1)).thenReturn(this.jobInstanceList);
212-
when(this.jobExplorer.getJobExecutions(any())).thenReturn(this.jobExecutionList);
213-
initializeForNextJobParameters();
214-
this.parametersBuilder.getNextJobParameters(this.job);
215-
baseJobParametersVerify(this.parametersBuilder.toJobParameters(), 4);
216-
}
217-
218-
@Test
219-
void testMissingJobExplorer() {
220-
this.parametersBuilder = new JobParametersBuilder();
221-
assertThrows(IllegalStateException.class, () -> this.parametersBuilder.getNextJobParameters(this.job));
222-
}
223-
224-
private void initializeForNextJobParameters() {
225-
this.parametersBuilder.addDate("SCHEDULE_DATE", date);
226-
this.parametersBuilder.addLong("LONG", 1L);
227-
this.parametersBuilder.addString("STRING", "string value");
228-
}
229-
230-
private void defaultNextJobParametersVerify(JobParameters parameters, int paramCount) {
231-
baseJobParametersVerify(parameters, paramCount);
232-
assertEquals(1, parameters.getLong("run.id"));
233-
}
234-
235-
private void baseJobParametersVerify(JobParameters parameters, int paramCount) {
236-
assertEquals(date, parameters.getDate("SCHEDULE_DATE"));
237-
assertEquals(1L, parameters.getLong("LONG").longValue());
238-
assertEquals("string value", parameters.getString("STRING"));
239-
assertEquals(paramCount, parameters.getParameters().size());
240-
}
241-
242-
private JobExecution getJobExecution(JobInstance jobInstance, BatchStatus batchStatus) {
243-
JobExecution jobExecution = new JobExecution(jobInstance, 1L, null);
244-
if (batchStatus != null) {
245-
jobExecution.setStatus(batchStatus);
246-
}
247-
return jobExecution;
248-
249-
}
250-
251157
}

0 commit comments

Comments
 (0)