Support getNextJobParameters from JobParametersBuilder.

resolves BATCH-2640

Cleanup and added tests to JobParametersBuilder.
This commit is contained in:
Glenn Renfro
2017-10-19 16:55:45 -04:00
committed by Michael Minella
parent 96a317c52f
commit a35784dbd6
2 changed files with 252 additions and 46 deletions

View File

@@ -16,10 +16,13 @@
package org.springframework.batch.core;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.util.Assert;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
@@ -36,20 +39,21 @@ import java.util.Properties;
*
* @author Lucas Ward
* @author Michael Minella
* @author Glenn Renfro
* @since 1.0
* @see JobParameters
* @see JobParameter
*/
public class JobParametersBuilder {
private final Map<String, JobParameter> parameterMap;
private Map<String, JobParameter> parameterMap;
/**
* Default constructor. Initializes the builder with empty parameters.
*/
public JobParametersBuilder() {
this.parameterMap = new LinkedHashMap<String, JobParameter>();
this.parameterMap = new LinkedHashMap<>();
}
/**
@@ -57,7 +61,7 @@ public class JobParametersBuilder {
* @param jobParameters {@link JobParameters} instance used to initialize the builder.
*/
public JobParametersBuilder(JobParameters jobParameters) {
this.parameterMap = new LinkedHashMap<String, JobParameter>(jobParameters.getParameters());
this.parameterMap = new LinkedHashMap<>(jobParameters.getParameters());
}
/**
@@ -67,7 +71,7 @@ public class JobParametersBuilder {
* @param properties the job parameters to be used
*/
public JobParametersBuilder(Properties properties) {
this.parameterMap = new LinkedHashMap<String, JobParameter>();
this.parameterMap = new LinkedHashMap<>();
if(properties != null) {
for (Map.Entry<Object, Object> curProperty : properties.entrySet()) {
@@ -84,7 +88,7 @@ public class JobParametersBuilder {
* @return a reference to this object.
*/
public JobParametersBuilder addString(String key, String parameter) {
parameterMap.put(key, new JobParameter(parameter, true));
this.parameterMap.put(key, new JobParameter(parameter, true));
return this;
}
@@ -97,7 +101,7 @@ public class JobParametersBuilder {
* @return a reference to this object.
*/
public JobParametersBuilder addString(String key, String parameter, boolean identifying) {
parameterMap.put(key, new JobParameter(parameter, identifying));
this.parameterMap.put(key, new JobParameter(parameter, identifying));
return this;
}
@@ -109,7 +113,7 @@ public class JobParametersBuilder {
* @return a reference to this object.
*/
public JobParametersBuilder addDate(String key, Date parameter) {
parameterMap.put(key, new JobParameter(parameter, true));
this.parameterMap.put(key, new JobParameter(parameter, true));
return this;
}
@@ -122,7 +126,7 @@ public class JobParametersBuilder {
* @return a reference to this object.
*/
public JobParametersBuilder addDate(String key, Date parameter, boolean identifying) {
parameterMap.put(key, new JobParameter(parameter, identifying));
this.parameterMap.put(key, new JobParameter(parameter, identifying));
return this;
}
@@ -134,7 +138,7 @@ public class JobParametersBuilder {
* @return a reference to this object.
*/
public JobParametersBuilder addLong(String key, Long parameter) {
parameterMap.put(key, new JobParameter(parameter, true));
this.parameterMap.put(key, new JobParameter(parameter, true));
return this;
}
@@ -147,7 +151,7 @@ public class JobParametersBuilder {
* @return a reference to this object.
*/
public JobParametersBuilder addLong(String key, Long parameter, boolean identifying) {
parameterMap.put(key, new JobParameter(parameter, identifying));
this.parameterMap.put(key, new JobParameter(parameter, identifying));
return this;
}
@@ -159,7 +163,7 @@ public class JobParametersBuilder {
* @return a reference to this object.
*/
public JobParametersBuilder addDouble(String key, Double parameter) {
parameterMap.put(key, new JobParameter(parameter, true));
this.parameterMap.put(key, new JobParameter(parameter, true));
return this;
}
@@ -172,7 +176,7 @@ public class JobParametersBuilder {
* @return a reference to this object.
*/
public JobParametersBuilder addDouble(String key, Double parameter, boolean identifying) {
parameterMap.put(key, new JobParameter(parameter, identifying));
this.parameterMap.put(key, new JobParameter(parameter, identifying));
return this;
}
@@ -183,7 +187,7 @@ public class JobParametersBuilder {
* @return a valid {@link JobParameters} object.
*/
public JobParameters toJobParameters() {
return new JobParameters(parameterMap);
return new JobParameters(this.parameterMap);
}
/**
@@ -195,7 +199,75 @@ public class JobParametersBuilder {
*/
public JobParametersBuilder addParameter(String key, JobParameter jobParameter) {
Assert.notNull(jobParameter, "JobParameter must not be null");
parameterMap.put(key, jobParameter);
this.parameterMap.put(key, jobParameter);
return this;
}
/**
* Initializes the {@link JobParameters} based on the state of the {@link Job}. This
* should be called after all parameters have been entered into the builder.
*
* @param job the job for which the {@link JobParameters} are being constructed.
* @param jobExplorer instance to a {@link JobExplorer}
* @return a reference to this object.
*
* @since 4.0
*/
public JobParametersBuilder getNextJobParameters(Job job, JobExplorer jobExplorer) {
String name = job.getName();
JobParameters nextParameters = new JobParameters();
List<JobInstance> lastInstances = jobExplorer.getJobInstances(name, 0, 1);
JobParametersIncrementer incrementer = job.getJobParametersIncrementer();
if (lastInstances.isEmpty()) {
// Start from a completely clean sheet
if (incrementer != null) {
nextParameters = incrementer.getNext(new JobParameters());
}
}
else {
List<JobExecution> previousExecutions = jobExplorer
.getJobExecutions(lastInstances.get(0));
JobExecution previousExecution = previousExecutions.get(0);
if (previousExecution == null) {
// Normally this will not happen - an instance exists with no executions
if (incrementer != null) {
nextParameters = incrementer.getNext(new JobParameters());
}
}
else if (isStoppedOrFailed(previousExecution) && job.isRestartable()) {
// Retry a failed or stopped execution
nextParameters = previousExecution.getJobParameters();
// Non-identifying additional parameters can be removed to a retry
removeNonIdentifying(this.parameterMap);
}
else if (incrementer != null) {
// New instance so increment the parameters if we can
nextParameters = incrementer.getNext(previousExecution.getJobParameters());
}
}
this.parameterMap = merge(nextParameters, this.parameterMap);
return this;
}
private void removeNonIdentifying(Map<String, JobParameter> parameters) {
HashMap<String, JobParameter> copy = new HashMap<>(parameters);
for (Map.Entry<String, JobParameter> parameter : copy.entrySet()) {
if (!parameter.getValue().isIdentifying()) {
parameters.remove(parameter.getKey());
}
}
}
private LinkedHashMap<String, JobParameter> merge(JobParameters parameters,
Map<String, JobParameter> additionals) {
Map<String, JobParameter> merged = new HashMap<>();
merged.putAll(parameters.getParameters());
merged.putAll(additionals);
return new LinkedHashMap<>(merged);
}
private boolean isStoppedOrFailed(JobExecution execution) {
BatchStatus status = execution.getStatus();
return (status == BatchStatus.STOPPED || status == BatchStatus.FAILED);
}
}

View File

@@ -15,76 +15,114 @@
*/
package org.springframework.batch.core;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.junit.Before;
import org.junit.Test;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.job.SimpleJob;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* @author Lucas Ward
* @author Michael Minella
* @author Glenn Renfro
*
*/
public class JobParametersBuilderTests {
JobParametersBuilder parametersBuilder = new JobParametersBuilder();
private JobParametersBuilder parametersBuilder;
Date date = new Date(System.currentTimeMillis());
private SimpleJob job;
private JobExplorer jobExplorer;
private List<JobInstance> jobInstanceList;
private List<JobExecution> jobExecutionList;
private Date date = new Date(System.currentTimeMillis());
@Before
public void initialize() {
this.parametersBuilder = new JobParametersBuilder();
this.job = new SimpleJob("simpleJob");
this.jobExplorer = mock(JobExplorer.class);
this.jobInstanceList = new ArrayList<>(1);
this.jobExecutionList = new ArrayList<>(1);
}
@Test
public void testNonIdentifyingParameters() {
parametersBuilder.addDate("SCHEDULE_DATE", date, false);
parametersBuilder.addLong("LONG", new Long(1), false);
parametersBuilder.addString("STRING", "string value", false);
JobParameters parameters = parametersBuilder.toJobParameters();
this.parametersBuilder.addDate("SCHEDULE_DATE", date, false);
this.parametersBuilder.addLong("LONG", new Long(1), false);
this.parametersBuilder.addString("STRING", "string value", false);
this.parametersBuilder.addDouble("DOUBLE", new Double(1), false);
JobParameters parameters = this.parametersBuilder.toJobParameters();
assertEquals(date, parameters.getDate("SCHEDULE_DATE"));
assertEquals(1L, parameters.getLong("LONG").longValue());
assertEquals("string value", parameters.getString("STRING"));
assertEquals(1, parameters.getDouble("DOUBLE").doubleValue(), 1e-15);
assertFalse(parameters.getParameters().get("SCHEDULE_DATE").isIdentifying());
assertFalse(parameters.getParameters().get("LONG").isIdentifying());
assertFalse(parameters.getParameters().get("STRING").isIdentifying());
assertFalse(parameters.getParameters().get("DOUBLE").isIdentifying());
}
@Test
public void testToJobRuntimeParamters(){
parametersBuilder.addDate("SCHEDULE_DATE", date);
parametersBuilder.addLong("LONG", new Long(1));
parametersBuilder.addString("STRING", "string value");
JobParameters parameters = parametersBuilder.toJobParameters();
public void testToJobRuntimeParameters(){
this.parametersBuilder.addDate("SCHEDULE_DATE", date);
this.parametersBuilder.addLong("LONG", new Long(1));
this.parametersBuilder.addString("STRING", "string value");
this.parametersBuilder.addDouble("DOUBLE", new Double(1));
JobParameters parameters = this.parametersBuilder.toJobParameters();
assertEquals(date, parameters.getDate("SCHEDULE_DATE"));
assertEquals(1L, parameters.getLong("LONG").longValue());
assertEquals(1, parameters.getDouble("DOUBLE").doubleValue(), 1e-15);
assertEquals("string value", parameters.getString("STRING"));
}
@Test
public void testNullRuntimeParamters(){
parametersBuilder.addDate("SCHEDULE_DATE", null);
parametersBuilder.addLong("LONG", null);
parametersBuilder.addString("STRING", null);
JobParameters parameters = parametersBuilder.toJobParameters();
public void testNullRuntimeParameters(){
this.parametersBuilder.addDate("SCHEDULE_DATE", null);
this.parametersBuilder.addLong("LONG", null);
this.parametersBuilder.addString("STRING", null);
this.parametersBuilder.addDouble("DOUBLE", null);
JobParameters parameters = this.parametersBuilder.toJobParameters();
assertEquals(null, parameters.getDate("SCHEDULE_DATE"));
assertEquals(0L, parameters.getLong("LONG").longValue());
assertEquals(null, parameters.getString("STRING"));
assertEquals(0, parameters.getLong("DOUBLE").doubleValue(), 1e-15);
}
@Test
public void testCopy(){
parametersBuilder.addString("STRING", "string value");
parametersBuilder = new JobParametersBuilder(parametersBuilder.toJobParameters());
Iterator<String> parameters = parametersBuilder.toJobParameters().getParameters().keySet().iterator();
this.parametersBuilder.addString("STRING", "string value");
this.parametersBuilder = new JobParametersBuilder(this.parametersBuilder.toJobParameters());
Iterator<String> parameters = this.parametersBuilder.toJobParameters().getParameters().keySet().iterator();
assertEquals("STRING", parameters.next());
}
@Test
public void testOrderedTypes(){
parametersBuilder.addDate("SCHEDULE_DATE", date);
parametersBuilder.addLong("LONG", new Long(1));
parametersBuilder.addString("STRING", "string value");
Iterator<String> parameters = parametersBuilder.toJobParameters().getParameters().keySet().iterator();
this.parametersBuilder.addDate("SCHEDULE_DATE", date);
this.parametersBuilder.addLong("LONG", new Long(1));
this.parametersBuilder.addString("STRING", "string value");
Iterator<String> parameters = this.parametersBuilder.toJobParameters().getParameters().keySet().iterator();
assertEquals("SCHEDULE_DATE", parameters.next());
assertEquals("LONG", parameters.next());
assertEquals("STRING", parameters.next());
@@ -92,10 +130,10 @@ public class JobParametersBuilderTests {
@Test
public void testOrderedStrings(){
parametersBuilder.addString("foo", "value foo");
parametersBuilder.addString("bar", "value bar");
parametersBuilder.addString("spam", "value spam");
Iterator<String> parameters = parametersBuilder.toJobParameters().getParameters().keySet().iterator();
this.parametersBuilder.addString("foo", "value foo");
this.parametersBuilder.addString("bar", "value bar");
this.parametersBuilder.addString("spam", "value spam");
Iterator<String> parameters = this.parametersBuilder.toJobParameters().getParameters().keySet().iterator();
assertEquals("foo", parameters.next());
assertEquals("bar", parameters.next());
assertEquals("spam", parameters.next());
@@ -104,9 +142,105 @@ public class JobParametersBuilderTests {
@Test
public void testAddJobParameter(){
JobParameter jobParameter = new JobParameter("bar");
parametersBuilder.addParameter("foo", jobParameter);
Map<String,JobParameter> parameters = parametersBuilder.toJobParameters().getParameters();
this.parametersBuilder.addParameter("foo", jobParameter);
Map<String,JobParameter> parameters = this.parametersBuilder.toJobParameters().getParameters();
assertEquals(1, parameters.size());
assertEquals("bar", parameters.get("foo").getValue());
}
@Test
public void testPropreties() {
Properties props = new Properties();
props.setProperty("SCHEDULE_DATE", "A DATE");
props.setProperty("LONG", "1");
props.setProperty("STRING", "string value");
this.parametersBuilder = new JobParametersBuilder(props);
JobParameters parameters = this.parametersBuilder.toJobParameters();
assertEquals("A DATE", parameters.getString("SCHEDULE_DATE"));
assertEquals("1", parameters.getString("LONG"));
assertEquals("string value", parameters.getString("STRING"));
assertFalse(parameters.getParameters().get("SCHEDULE_DATE").isIdentifying());
assertFalse(parameters.getParameters().get("LONG").isIdentifying());
assertFalse(parameters.getParameters().get("STRING").isIdentifying());
}
@Test
public void testGetNextJobParametersFirstRun(){
job.setJobParametersIncrementer(new RunIdIncrementer());
initializeForNextJobParameters();
this.parametersBuilder.getNextJobParameters(this.job, this.jobExplorer);
defaultNextJobParametersVerify(this.parametersBuilder.toJobParameters(), 4);
}
@Test
public void testGetNextJobParametersNoIncrementer(){
initializeForNextJobParameters();
this.parametersBuilder.getNextJobParameters(this.job, this.jobExplorer);
baseJobParametersVerify(this.parametersBuilder.toJobParameters(), 3);
}
@Test
public void testGetNextJobParameters(){
this.job.setJobParametersIncrementer(new RunIdIncrementer());
this.jobInstanceList.add(new JobInstance(1L, "simpleJobInstance"));
this.jobExecutionList.add(getJobExecution(this.jobInstanceList.get(0), null));
when(this.jobExplorer.getJobInstances("simpleJob",0,1)).thenReturn(this.jobInstanceList);
when(this.jobExplorer.getJobExecutions(any())).thenReturn(this.jobExecutionList);
initializeForNextJobParameters();
this.parametersBuilder.getNextJobParameters(this.job, this.jobExplorer);
defaultNextJobParametersVerify(this.parametersBuilder.toJobParameters(), 4);
}
@Test
public void testGetNextJobParametersRestartable(){
this.job.setRestartable(true);
this.job.setJobParametersIncrementer(new RunIdIncrementer());
this.jobInstanceList.add(new JobInstance(1L, "simpleJobInstance"));
this.jobExecutionList.add(getJobExecution(this.jobInstanceList.get(0), BatchStatus.FAILED));
when(this.jobExplorer.getJobInstances("simpleJob",0,1)).thenReturn(this.jobInstanceList);
when(this.jobExplorer.getJobExecutions(any())).thenReturn(this.jobExecutionList);
initializeForNextJobParameters();
this.parametersBuilder.addLong("NON_IDENTIFYING_LONG", new Long(1), false);
this.parametersBuilder.getNextJobParameters(this.job, this.jobExplorer);
baseJobParametersVerify(this.parametersBuilder.toJobParameters(), 3);
}
@Test
public void testGetNextJobParametersNoPreviousExecution(){
this.job.setJobParametersIncrementer(new RunIdIncrementer());
this.jobInstanceList.add(new JobInstance(1L, "simpleJobInstance"));
this.jobExecutionList.add(null);
when(this.jobExplorer.getJobInstances("simpleJob",0,1)).thenReturn(this.jobInstanceList);
when(this.jobExplorer.getJobExecutions(any())).thenReturn(this.jobExecutionList);
initializeForNextJobParameters();
this.parametersBuilder.getNextJobParameters(this.job, this.jobExplorer);
baseJobParametersVerify(this.parametersBuilder.toJobParameters(), 4);
}
private void initializeForNextJobParameters() {
this.parametersBuilder.addDate("SCHEDULE_DATE", date);
this.parametersBuilder.addLong("LONG", new Long(1));
this.parametersBuilder.addString("STRING", "string value");
}
private void defaultNextJobParametersVerify(JobParameters parameters, int paramCount) {
baseJobParametersVerify(parameters, paramCount);
assertEquals("1", parameters.getString("run.id"));
}
private void baseJobParametersVerify(JobParameters parameters, int paramCount) {
assertEquals(date, parameters.getDate("SCHEDULE_DATE"));
assertEquals(1L, parameters.getLong("LONG").longValue());
assertEquals("string value", parameters.getString("STRING"));
assertEquals(paramCount, parameters.getParameters().size());
}
private JobExecution getJobExecution(JobInstance jobInstance, BatchStatus batchStatus) {
JobExecution jobExecution = new JobExecution(jobInstance, 1L, null, "TestConfig");
if(batchStatus != null) {
jobExecution.setStatus(batchStatus);
}
return jobExecution;
}
}