diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/JobParametersBuilder.java b/spring-batch-core/src/main/java/org/springframework/batch/core/JobParametersBuilder.java index a0728c5fbe..02fdef9458 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/JobParametersBuilder.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/JobParametersBuilder.java @@ -29,7 +29,7 @@ /** * Helper class for creating {@link JobParameters}. Useful because all * {@link JobParameter} objects are immutable, and must be instantiated separately - * to ensure typesafety. Once created, it can be used in the + * to ensure type safety. Once created, it can be used in the * same was a java.lang.StringBuilder (except, order is irrelevant), by adding * various parameter types and creating a valid {@link JobParameters} once * finished.
@@ -239,6 +239,10 @@ public JobParametersBuilder addJobParameters(JobParameters jobParameters) { /** * 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. + * All parameters already set on this builder instance will be appended to + * those retrieved from the job incrementer, overriding any with the same key (Same + * behaviour as {@link org.springframework.batch.core.launch.support.CommandLineJobRunner} + * with "-next" option and {@link org.springframework.batch.core.launch.JobOperator#startNextInstance(String)}) * * @param job the job for which the {@link JobParameters} are being constructed. * @return a reference to this object. @@ -246,59 +250,35 @@ public JobParametersBuilder addJobParameters(JobParameters jobParameters) { * @since 4.0 */ public JobParametersBuilder getNextJobParameters(Job job) { - if(this.jobExplorer == null) { - throw new IllegalStateException("A JobExplore is required to get next job parameters"); - } + Assert.state(this.jobExplorer != null, "A JobExplorer is required to get next job parameters"); + Assert.notNull(job, "Job must not be null"); + Assert.notNull(job.getJobParametersIncrementer(), "No job parameters incrementer found for job=" + job.getName()); String name = job.getName(); - JobParameters nextParameters = new JobParameters(); + JobParameters nextParameters; List lastInstances = this.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()); - } + nextParameters = incrementer.getNext(new JobParameters()); } else { - List previousExecutions = this.jobExplorer - .getJobExecutions(lastInstances.get(0)); - JobExecution previousExecution = previousExecutions.get(0); - if (previousExecution == null) { + List previousExecutions = this.jobExplorer.getJobExecutions(lastInstances.get(0)); + if (previousExecutions.isEmpty()) { // 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); + nextParameters = incrementer.getNext(new JobParameters()); } - else if (incrementer != null) { - // New instance so increment the parameters if we can + else { + JobExecution previousExecution = previousExecutions.get(0); nextParameters = incrementer.getNext(previousExecution.getJobParameters()); } } - this.parameterMap = addJobParameters(nextParameters) - .toJobParameters() - .getParameters(); + // start with parameters from the incrementer + Map nextParametersMap = new HashMap<>(nextParameters.getParameters()); + // append new parameters (overriding those with the same key) + nextParametersMap.putAll(this.parameterMap); + this.parameterMap = nextParametersMap; return this; } - - private void removeNonIdentifying(Map parameters) { - HashMap copy = new HashMap<>(parameters); - for (Map.Entry parameter : copy.entrySet()) { - if (!parameter.getValue().isIdentifying()) { - parameters.remove(parameter.getKey()); - } - } - } - - private boolean isStoppedOrFailed(JobExecution execution) { - BatchStatus status = execution.getStatus(); - return (status == BatchStatus.STOPPED || status == BatchStatus.FAILED); - } } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/CommandLineJobRunner.java b/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/CommandLineJobRunner.java index 3bcf735e92..13a09b9c40 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/CommandLineJobRunner.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/CommandLineJobRunner.java @@ -21,10 +21,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; -import java.util.Map; import java.util.Properties; import java.util.Set; @@ -36,8 +34,8 @@ import org.springframework.batch.core.Job; import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.JobInstance; -import org.springframework.batch.core.JobParameter; import org.springframework.batch.core.JobParameters; +import org.springframework.batch.core.JobParametersBuilder; import org.springframework.batch.core.JobParametersIncrementer; import org.springframework.batch.core.configuration.JobLocator; import org.springframework.batch.core.converter.DefaultJobParametersConverter; @@ -47,7 +45,6 @@ import org.springframework.batch.core.launch.JobExecutionNotRunningException; import org.springframework.batch.core.launch.JobExecutionNotStoppedException; import org.springframework.batch.core.launch.JobLauncher; -import org.springframework.batch.core.launch.JobParametersNotFoundException; import org.springframework.batch.core.launch.NoSuchJobException; import org.springframework.batch.core.repository.JobRepository; import org.springframework.beans.factory.BeanDefinitionStoreException; @@ -356,10 +353,9 @@ int start(String jobPath, String jobIdentifier, String[] parameters, Set } if (opts.contains("-next")) { - JobParameters nextParameters = getNextJobParameters(job); - Map map = new HashMap(nextParameters.getParameters()); - map.putAll(jobParameters.getParameters()); - jobParameters = new JobParameters(map); + jobParameters = new JobParametersBuilder(jobParameters, jobExplorer) + .getNextJobParameters(job) + .toJobParameters(); } JobExecution jobExecution = launcher.run(job, jobParameters); @@ -469,35 +465,6 @@ private Long getLongIdentifier(String jobIdentifier) { } } - /** - * @param job the job that we need to find the next parameters for - * @return the next job parameters if they can be located - * @throws JobParametersNotFoundException if there is a problem - */ - private JobParameters getNextJobParameters(Job job) throws JobParametersNotFoundException { - String jobIdentifier = job.getName(); - JobParameters jobParameters; - List lastInstances = jobExplorer.getJobInstances(jobIdentifier, 0, 1); - - JobParametersIncrementer incrementer = job.getJobParametersIncrementer(); - if (incrementer == null) { - throw new JobParametersNotFoundException("No job parameters incrementer found for job=" + jobIdentifier); - } - - if (lastInstances.isEmpty()) { - jobParameters = incrementer.getNext(new JobParameters()); - if (jobParameters == null) { - throw new JobParametersNotFoundException("No bootstrap parameters found from incrementer for job=" - + jobIdentifier); - } - } - else { - List lastExecutions = jobExplorer.getJobExecutions(lastInstances.get(0)); - jobParameters = incrementer.getNext(lastExecutions.get(0).getJobParameters()); - } - return jobParameters; - } - /** * Launch a batch job using a {@link CommandLineJobRunner}. Creates a new * Spring context for the job execution, and uses a common parent for all @@ -515,7 +482,7 @@ private JobParameters getNextJobParameters(Job job) throws JobParametersNotFound * interpreted either as the name of the job or the id of the job execution * that failed. *
  • -next: (optional) if the job has a {@link JobParametersIncrementer} - * that can be used to launch the next in a sequence
  • + * that can be used to launch the next instance in a sequence *
  • jobPath: the xml application context containing a {@link Job} *
  • jobIdentifier: the bean id of the job or id of the failed execution * in the case of a restart. diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/SimpleJobOperator.java b/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/SimpleJobOperator.java index 08d94dc0cc..6bbadefd4c 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/SimpleJobOperator.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/SimpleJobOperator.java @@ -1,5 +1,5 @@ /* - * Copyright 2006-2013 the original author or authors. + * Copyright 2006-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,7 +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.JobParametersBuilder; import org.springframework.batch.core.JobParametersInvalidException; import org.springframework.batch.core.Step; import org.springframework.batch.core.StepExecution; @@ -45,7 +45,6 @@ import org.springframework.batch.core.launch.JobInstanceAlreadyExistsException; import org.springframework.batch.core.launch.JobLauncher; import org.springframework.batch.core.launch.JobOperator; -import org.springframework.batch.core.launch.JobParametersNotFoundException; import org.springframework.batch.core.launch.NoSuchJobException; import org.springframework.batch.core.launch.NoSuchJobExecutionException; import org.springframework.batch.core.launch.NoSuchJobInstanceException; @@ -79,6 +78,7 @@ * @author Dave Syer * @author Lucas Ward * @author Will Schipp + * @author Mahmoud Ben Hassine * @since 2.0 */ public class SimpleJobOperator implements JobOperator, InitializingBean { @@ -334,30 +334,15 @@ public Long start(String jobName, String parameters) throws NoSuchJobException, * @see JobOperator#startNextInstance(String ) */ @Override - public Long startNextInstance(String jobName) throws NoSuchJobException, JobParametersNotFoundException, + public Long startNextInstance(String jobName) throws NoSuchJobException, UnexpectedJobExecutionException, JobParametersInvalidException { logger.info("Locating parameters for next instance of job with name=" + jobName); Job job = jobRegistry.getJob(jobName); - List lastInstances = jobExplorer.getJobInstances(jobName, 0, 1); - - JobParametersIncrementer incrementer = job.getJobParametersIncrementer(); - if (incrementer == null) { - throw new JobParametersNotFoundException("No job parameters incrementer found for job=" + jobName); - } - - JobParameters parameters; - if (lastInstances.isEmpty()) { - parameters = incrementer.getNext(new JobParameters()); - if (parameters == null) { - throw new JobParametersNotFoundException("No bootstrap parameters found for job=" + jobName); - } - } - else { - List lastExecutions = jobExplorer.getJobExecutions(lastInstances.get(0)); - parameters = incrementer.getNext(lastExecutions.get(0).getJobParameters()); - } + JobParameters parameters = new JobParametersBuilder(jobExplorer) + .getNextJobParameters(job) + .toJobParameters(); logger.info(String.format("Attempting to launch job with name=%s and parameters=%s", jobName, parameters)); try { diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/JobParametersBuilderTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/JobParametersBuilderTests.java index 9197d0a48b..2da832c0bc 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/JobParametersBuilderTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/JobParametersBuilderTests.java @@ -23,7 +23,9 @@ import java.util.Properties; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.springframework.batch.core.explore.JobExplorer; import org.springframework.batch.core.job.SimpleJob; @@ -56,6 +58,9 @@ public class JobParametersBuilderTests { private Date date = new Date(System.currentTimeMillis()); + @Rule + public ExpectedException expectedException = ExpectedException.none(); + @Before public void initialize() { this.job = new SimpleJob("simpleJob"); @@ -198,9 +203,10 @@ public void testGetNextJobParametersFirstRun(){ @Test public void testGetNextJobParametersNoIncrementer(){ + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("No job parameters incrementer found for job=simpleJob"); initializeForNextJobParameters(); this.parametersBuilder.getNextJobParameters(this.job); - baseJobParametersVerify(this.parametersBuilder.toJobParameters(), 3); } @Test @@ -226,14 +232,13 @@ public void testGetNextJobParametersRestartable(){ initializeForNextJobParameters(); this.parametersBuilder.addLong("NON_IDENTIFYING_LONG", new Long(1), false); this.parametersBuilder.getNextJobParameters(this.job); - baseJobParametersVerify(this.parametersBuilder.toJobParameters(), 3); + baseJobParametersVerify(this.parametersBuilder.toJobParameters(), 5); } @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(); diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/launch/support/SimpleJobOperatorTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/launch/support/SimpleJobOperatorTests.java index c53c708f21..f1613f25ec 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/launch/support/SimpleJobOperatorTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/launch/support/SimpleJobOperatorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2006-2013 the original author or authors. + * Copyright 2006-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -67,6 +67,7 @@ /** * @author Dave Syer * @author Will Schipp + * @author Mahmoud Ben Hassine * */ public class SimpleJobOperatorTests { @@ -153,6 +154,7 @@ public void testMandatoryProperties() throws Exception { */ @Test public void testStartNextInstanceSunnyDay() throws Exception { + jobParameters = new JobParameters(); JobInstance jobInstance = new JobInstance(321L, "foo"); when(jobExplorer.getJobInstances("foo", 0, 1)).thenReturn(Collections.singletonList(jobInstance)); when(jobExplorer.getJobExecutions(jobInstance)).thenReturn(Collections.singletonList(new JobExecution(jobInstance, new JobParameters())));