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 c944f17b1..00d974cd5 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 @@ -16,8 +16,10 @@ package org.springframework.batch.core.launch.support; import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Properties; import java.util.Set; @@ -28,6 +30,7 @@ import org.springframework.batch.core.ExitStatus; 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.JobParametersIncrementer; import org.springframework.batch.core.configuration.JobLocator; @@ -35,7 +38,6 @@ import org.springframework.batch.core.converter.DefaultJobParametersConverter; import org.springframework.batch.core.converter.JobParametersConverter; import org.springframework.batch.core.explore.JobExplorer; import org.springframework.batch.core.launch.JobLauncher; -import org.springframework.batch.core.launch.JobOperator; import org.springframework.batch.core.launch.JobParametersNotFoundException; import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; @@ -63,17 +65,18 @@ import org.springframework.util.StringUtils; * command line launcher can be used to load the job and its context from a * single location. All dependencies of the launcher will then be satisfied by * autowiring by type from the combined application context. Default values are - * provided for all fields except the {@link JobLauncher} and {@link JobLocator}. - * Therefore, if autowiring fails to set it (it should be noted that dependency - * checking is disabled because most of the fields have default values and thus - * don't require dependencies to be fulfilled via autowiring) then an exception - * will be thrown. It should also be noted that even if an exception is thrown - * by this class, it will be mapped to an integer and returned. + * provided for all fields except the {@link JobLauncher} and {@link JobLocator} + * . Therefore, if autowiring fails to set it (it should be noted that + * dependency checking is disabled because most of the fields have default + * values and thus don't require dependencies to be fulfilled via autowiring) + * then an exception will be thrown. It should also be noted that even if an + * exception is thrown by this class, it will be mapped to an integer and + * returned. *
* *- * Notice a property is available to set the {@link SystemExiter}. This class - * is used to exit from the main method, rather than calling System.exit() + * Notice a property is available to set the {@link SystemExiter}. This class is + * used to exit from the main method, rather than calling System.exit() * directly. This is because unit testing a class the calls System.exit() is * impossible without kicking off the test within a new Jvm, which it is * possible to do, however it is a complex solution, much more so than @@ -85,38 +88,52 @@ import org.springframework.util.StringUtils; *
* *
- * jobPath jobName (jobParameters)*
+ * jobPath jobName (jobParameters)*
*
*
* + * The command line options are as follows *
+ * If the -next option is used the parameters on the command line
+ * (if any) are appended to those retrieved from the incrementer, overriding any
+ * with the same key.
+ *
* The combined application context must contain only one instance of - * {@link JobLauncher}. The job parameters passed in to the command line will - * be converted to {@link Properties} by assuming that each individual element - * is one parameter that is separated by an equals sign. For example, + * {@link JobLauncher}. The job parameters passed in to the command line will be + * converted to {@link Properties} by assuming that each individual element is + * one parameter that is separated by an equals sign. For example, * "vendor.id=290232". Below is an example arguments list: " * *
*
* java org.springframework.batch.execution.bootstrap.support.CommandLineJobRunner testJob.xml
* testJob schedule.date=2008/01/24 vendor.id=3902483920
- *
+ *
*
- * Once arguments have been successfully parsed, autowiring will be used to set
- * various dependencies. The {@JobLauncher} for example, will be loaded this way. If
- * none is contained in the bean factory (it searches by type) then a
- * {@link BeanDefinitionStoreException} will be thrown. The same exception will also
- * be thrown if there is more than one present. Assuming the JobLauncher has been
- * set correctly, the jobName argument will be used to obtain an actual {@link Job}.
- * If a {@link JobLocator} has been set, then it will be used, if not the beanFactory
- * will be asked, using the jobName as the bean id.
+ *
+ * Once arguments have been successfully parsed, autowiring will be used to set
+ * various dependencies. The {@JobLauncher} for example, will be
+ * loaded this way. If none is contained in the bean factory (it searches by
+ * type) then a {@link BeanDefinitionStoreException} will be thrown. The same
+ * exception will also be thrown if there is more than one present. Assuming the
+ * JobLauncher has been set correctly, the jobName argument will be used to
+ * obtain an actual {@link Job}. If a {@link JobLocator} has been set, then it
+ * will be used, if not the beanFactory will be asked, using the jobName as the
+ * bean id.
+ *
*
* @author Dave Syer
* @author Lucas Ward
@@ -146,7 +163,7 @@ public class CommandLineJobRunner {
public void setLauncher(JobLauncher launcher) {
this.launcher = launcher;
}
-
+
/**
* Injection setter for {@link JobExplorer}.
*
@@ -155,7 +172,7 @@ public class CommandLineJobRunner {
public void setJobExplorer(JobExplorer jobExplorer) {
this.jobExplorer = jobExplorer;
}
-
+
/**
* Injection setter for the {@link ExitCodeMapper}.
*
@@ -192,6 +209,10 @@ public class CommandLineJobRunner {
systemExiter.exit(status);
}
+ /**
+ * {@link JobLocator} to find a job to run.
+ * @param jobLocator a {@link JobLocator}
+ */
public void setJobLocator(JobLocator jobLocator) {
this.jobLocator = jobLocator;
}
@@ -209,10 +230,12 @@ public class CommandLineJobRunner {
context = new ClassPathXmlApplicationContext(jobPath);
context.getAutowireCapableBeanFactory().autowireBeanProperties(this,
AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, false);
-
- Assert.notNull(launcher,"A JobLauncher must be provided. Please add one to the configuration.");
- if (opts.contains("-restart") || opts.contains("-next")) {
- Assert.notNull(jobExplorer,"A JobExplorer must be provided for a restart or start next operation. Please add one to the configuration.");
+
+ Assert.notNull(launcher, "A JobLauncher must be provided. Please add one to the configuration.");
+ if (opts.contains("-restart") || opts.contains("-next")) {
+ Assert
+ .notNull(jobExplorer,
+ "A JobExplorer must be provided for a restart or start next operation. Please add one to the configuration.");
}
Job job;
@@ -222,14 +245,18 @@ public class CommandLineJobRunner {
else {
job = (Job) context.getBean(jobName);
}
-
+
JobParameters jobParameters = jobParametersConverter.getJobParameters(StringUtils
.splitArrayElementsIntoProperties(parameters, "="));
if (opts.contains("-restart")) {
jobParameters = getLastFailedJobParameters(jobName);
- } else if (opts.contains("-next")) {
- jobParameters = getNextJobParameters(jobName, job);
+ }
+ else if (opts.contains("-next")) {
+ JobParameters nextParameters = getNextJobParameters(job);
+ Map map = new HashMap(nextParameters.getParameters());
+ map.putAll(jobParameters.getParameters());
+ jobParameters = new JobParameters(map);
}
JobExecution jobExecution = launcher.run(job, jobParameters);
@@ -250,31 +277,31 @@ public class CommandLineJobRunner {
/**
* @param jobName
* @return
- * @throws JobParametersNotFoundException
+ * @throws JobParametersNotFoundException
*/
private JobParameters getLastFailedJobParameters(String jobName) throws JobParametersNotFoundException {
int start = 0;
int count = 100;
List lastInstances = jobExplorer.getJobInstances(jobName, start, count);
-
+
JobParameters jobParameters = null;
while (!lastInstances.isEmpty()) {
for (JobInstance jobInstance : lastInstances) {
List jobExecutions = jobExplorer.getJobExecutions(jobInstance);
- if (jobExecutions==null || jobExecutions.isEmpty()) {
+ if (jobExecutions == null || jobExecutions.isEmpty()) {
continue;
}
- JobExecution jobExecution = jobExecutions.get(jobExecutions.size()-1);
+ JobExecution jobExecution = jobExecutions.get(jobExecutions.size() - 1);
if (jobExecution.getStatus().isGreaterThan(BatchStatus.STOPPING)) {
jobParameters = jobInstance.getJobParameters();
break;
}
}
-
- if (jobParameters!=null) {
+
+ if (jobParameters != null) {
break;
}
@@ -283,20 +310,20 @@ public class CommandLineJobRunner {
}
- if (jobParameters==null) {
- throw new JobParametersNotFoundException("No job parameters found for failed execution of job=" + jobName);
+ if (jobParameters == null) {
+ throw new JobParametersNotFoundException("No job parameters found for failed execution of job=" + jobName);
}
return jobParameters;
}
/**
- * @param jobName
- * @param job
- * @return
- * @throws JobParametersNotFoundException
+ * @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(String jobName, Job job) throws JobParametersNotFoundException {
+ private JobParameters getNextJobParameters(Job job) throws JobParametersNotFoundException {
+ String jobName = job.getName();
JobParameters jobParameters;
List lastInstances = jobExplorer.getJobInstances(jobName, 0, 1);
@@ -308,7 +335,8 @@ public class CommandLineJobRunner {
if (lastInstances.isEmpty()) {
jobParameters = incrementer.getNext(new JobParameters());
if (jobParameters == null) {
- throw new JobParametersNotFoundException("No bootstrap parameters found from incrementer for job=" + jobName);
+ throw new JobParametersNotFoundException("No bootstrap parameters found from incrementer for job="
+ + jobName);
}
}
else {
@@ -323,26 +351,32 @@ public class CommandLineJobRunner {
* such contexts. No exception are thrown from this method, rather
* exceptions are logged and an integer returned through the exit status in
* a {@link JvmSystemExiter} (which can be overridden by defining one in the
- * Spring context).
Parameters can be provided in the form key=value,
- * and will be converted using the injected {@link JobParametersConverter}.
+ * Spring context).
+ * Parameters can be provided in the form key=value, and will be converted
+ * using the injected {@link JobParametersConverter}.
*
- * @param args
- *
+ * @param args
*
+ * - -restart: (optional) if the job has failed or stopped and the most
+ * recent execution should be restarted
+ * - -next: (optional) if the job has a {@link JobParametersIncrementer}
+ * that can be used to launch the next in a sequence
* - jobPath: the xml application context containing a {@link Job}
*
- jobName: the bean id of the job.
*
- jobParameters: 0 to many parameters that will be used to launch a
* job.
*
+ * The options (-restart, -next) can occur anywhere in the
+ * command line.
*
*/
public static void main(String[] args) {
CommandLineJobRunner command = new CommandLineJobRunner();
-
+
Set opts = new HashSet();
List params = new ArrayList();
-
+
int count = 0;
String jobPath = null;
String jobName = null;
@@ -350,13 +384,14 @@ public class CommandLineJobRunner {
for (String arg : args) {
if (arg.startsWith("-")) {
opts.add(arg);
- } else {
+ }
+ else {
switch (count) {
case 0:
- jobPath = arg;
+ jobPath = arg;
break;
case 1:
- jobName = arg;
+ jobName = arg;
break;
default:
params.add(arg);
@@ -366,7 +401,7 @@ public class CommandLineJobRunner {
}
}
- if (jobPath==null || jobName==null) {
+ if (jobPath == null || jobName == null) {
logger.error("At least 2 arguments are required: JobPath and JobName.");
command.exit(1);
}
diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/launch/support/CommandLineJobRunnerTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/launch/support/CommandLineJobRunnerTests.java
index dfa9379c9..b87983c83 100644
--- a/spring-batch-core/src/test/java/org/springframework/batch/core/launch/support/CommandLineJobRunnerTests.java
+++ b/spring-batch-core/src/test/java/org/springframework/batch/core/launch/support/CommandLineJobRunnerTests.java
@@ -18,17 +18,28 @@ package org.springframework.batch.core.launch.support;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
import java.util.Properties;
+import java.util.Set;
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
+import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.ExitStatus;
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.JobParametersBuilder;
+import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.converter.DefaultJobParametersConverter;
import org.springframework.batch.core.converter.JobParametersConverter;
+import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.util.ClassUtils;
@@ -78,12 +89,20 @@ public class CommandLineJobRunnerTests {
assertEquals(1, StubSystemExiter.status);
}
- // can't test because it will cause the system to exit.
- // public void testInvalidArgs(){
- //
- // String[] args = new String[]{jobPath, jobName};
- // CommandLineJobRunner.main(args);
- // }
+ @Test
+ @Ignore
+ public void testInvalidArgs() {
+ String[] args = new String[] {};
+ CommandLineJobRunner.main(args);
+ assertEquals(1, StubSystemExiter.status);
+ }
+
+ @Test
+ public void testWrongJobName() {
+ String[] args = new String[] { jobPath, "no-such-job" };
+ CommandLineJobRunner.main(args);
+ assertEquals(1, StubSystemExiter.status);
+ }
@Test
public void testWithNoParameters() throws Throwable {
@@ -93,6 +112,27 @@ public class CommandLineJobRunnerTests {
assertEquals(new JobParameters(), StubJobLauncher.jobParameters);
}
+ @Test
+ public void testRestart() throws Throwable {
+ String[] args = new String[] { jobPath, "-restart", jobName };
+ JobParameters jobParameters = new JobParametersBuilder().addString("foo", "bar").toJobParameters();
+ StubJobExplorer.jobInstances = Arrays.asList(new JobInstance(0L, jobParameters, jobName));
+ CommandLineJobRunner.main(args);
+ assertEquals(0, StubSystemExiter.status);
+ assertEquals(jobParameters, StubJobLauncher.jobParameters);
+ }
+
+ @Test
+ public void testNext() throws Throwable {
+ String[] args = new String[] { jobPath, "-next", jobName };
+ JobParameters jobParameters = new JobParametersBuilder().addString("foo", "bar").toJobParameters();
+ StubJobExplorer.jobInstances = Arrays.asList(new JobInstance(1L, jobParameters, jobName));
+ CommandLineJobRunner.main(args);
+ assertEquals(0, StubSystemExiter.status);
+ jobParameters = new JobParametersBuilder().addString("foo", "spam").toJobParameters();
+ assertEquals(jobParameters, StubJobLauncher.jobParameters);
+ }
+
@Test
public void testDestroyCallback() throws Throwable {
String[] args = new String[] { jobPath, jobName };
@@ -118,7 +158,7 @@ public class CommandLineJobRunnerTests {
}
}
- private static class StubJobLauncher implements JobLauncher {
+ public static class StubJobLauncher implements JobLauncher {
public static JobExecution jobExecution;
@@ -151,7 +191,48 @@ public class CommandLineJobRunnerTests {
}
}
- private static class StubJobParametersConverter implements JobParametersConverter {
+ public static class StubJobExplorer implements JobExplorer {
+
+ static List jobInstances = new ArrayList();
+
+ public Set findRunningJobExecutions(String jobName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public JobExecution getJobExecution(Long executionId) {
+ throw new UnsupportedOperationException();
+ }
+
+ public List getJobExecutions(JobInstance jobInstance) {
+ if (jobInstance.getId() == 0) {
+ return Arrays.asList(createJobInstance(jobInstance, BatchStatus.FAILED));
+ }
+ return Arrays.asList(createJobInstance(jobInstance, BatchStatus.COMPLETED));
+ }
+
+ private JobExecution createJobInstance(JobInstance jobInstance, BatchStatus status) {
+ JobExecution jobExecution = new JobExecution(jobInstance, 1L);
+ jobExecution.setStatus(status);
+ jobExecution.setStartTime(new Date());
+ jobExecution.setEndTime(new Date());
+ return jobExecution;
+ }
+
+ public JobInstance getJobInstance(Long instanceId) {
+ throw new UnsupportedOperationException();
+ }
+
+ public List getJobInstances(String jobName, int start, int count) {
+ return jobInstances;
+ }
+
+ public StepExecution getStepExecution(Long jobExecutionId, Long stepExecutionId) {
+ throw new UnsupportedOperationException();
+ }
+
+ }
+
+ public static class StubJobParametersConverter implements JobParametersConverter {
JobParametersConverter delegate = new DefaultJobParametersConverter();
diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/launch/support/TestJobParametersIncrementer.java b/spring-batch-core/src/test/java/org/springframework/batch/core/launch/support/TestJobParametersIncrementer.java
new file mode 100644
index 000000000..aadef74b6
--- /dev/null
+++ b/spring-batch-core/src/test/java/org/springframework/batch/core/launch/support/TestJobParametersIncrementer.java
@@ -0,0 +1,13 @@
+package org.springframework.batch.core.launch.support;
+
+import org.springframework.batch.core.JobParameters;
+import org.springframework.batch.core.JobParametersBuilder;
+import org.springframework.batch.core.JobParametersIncrementer;
+
+public class TestJobParametersIncrementer implements JobParametersIncrementer {
+
+ public JobParameters getNext(JobParameters parameters) {
+ return new JobParametersBuilder().addString("foo", "spam").toJobParameters();
+ }
+
+}
diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/launch/support/job.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/launch/support/job.xml
index 05952ac09..31c094e46 100644
--- a/spring-batch-core/src/test/resources/org/springframework/batch/core/launch/support/job.xml
+++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/launch/support/job.xml
@@ -1,35 +1,33 @@
-
+
+
+
+
-
+
-
+
-
+
+
+