Skip to content

Commit 746c919

Browse files
Philippe Truchefmbenhassine
Philippe Truche
authored andcommitted
Support command as array of strings
setCommand now supports passing in an array of strings. When a single string is supplied, it is tokenized by the Runtime#exec method. When an array of strings is supplied, the array is supplied as is to the Runtime#exec method in which case no tokenization takes place. Resolves #752
1 parent 4eefb1d commit 746c919

File tree

2 files changed

+39
-8
lines changed

2 files changed

+39
-8
lines changed

spring-batch-core/src/main/java/org/springframework/batch/core/step/tasklet/SystemCommandTasklet.java

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
* {@link #setTaskExecutor(TaskExecutor)} - timeout value is required to be set, so that
4646
* the batch job does not hang forever if the external process hangs.
4747
*
48-
* Tasklet periodically checks for termination status (i.e. {@link #setCommand(String)}
48+
* Tasklet periodically checks for termination status (i.e. {@link #setCommand(String...)}
4949
* finished its execution or {@link #setTimeout(long)} expired or job was interrupted).
5050
* The check interval is given by {@link #setTerminationCheckInterval(long)}.
5151
*
@@ -66,7 +66,7 @@ public class SystemCommandTasklet implements StepExecutionListener, StoppableTas
6666

6767
private CommandRunner commandRunner = new JvmCommandRunner();
6868

69-
private String command;
69+
private String[] cmdArray;
7070

7171
private String[] environmentParams = null;
7272

@@ -102,8 +102,14 @@ public RepeatStatus execute(StepContribution contribution, ChunkContext chunkCon
102102

103103
@Override
104104
public Integer call() throws Exception {
105-
Process process = commandRunner.exec(command, environmentParams, workingDirectory);
106-
return process.waitFor();
105+
if (cmdArray.length == 1) {
106+
String command = cmdArray[0];
107+
Process process = commandRunner.exec(command, environmentParams, workingDirectory);
108+
return process.waitFor();
109+
} else {
110+
Process process = Runtime.getRuntime().exec(cmdArray, environmentParams, workingDirectory);
111+
return process.waitFor();
112+
}
107113
}
108114

109115
});
@@ -134,6 +140,7 @@ else if (System.currentTimeMillis() - t0 > timeout) {
134140
}
135141
else if (execution.isTerminateOnly()) {
136142
systemCommandTask.cancel(interruptOnCancel);
143+
String command = String.join(" ", cmdArray);
137144
throw new JobInterruptedException("Job interrupted while executing system command '" + command + "'");
138145
}
139146
else if (stopped) {
@@ -155,10 +162,17 @@ public void setCommandRunner(CommandRunner commandRunner) {
155162
}
156163

157164
/**
158-
* @param command command to be executed in a separate system process
165+
* @param command command to be executed in a separate system process. Either a single command can be supplied
166+
* to be tokenized with a space delimiter, or the command and its arguments are supplied as multiple
167+
* strings that are not tokenized.
168+
* <p>
169+
* <p>Possible calls to setCommand:
170+
*
171+
* <pre> {@code setCommand("myCommand myArg1 myArg2");}</pre>
172+
* <pre> {@code setCommand("myCommand", "myArg1", "myArg2 'args for myArg2'");}</pre>
159173
*/
160-
public void setCommand(String command) {
161-
this.command = command;
174+
public void setCommand(String... command) {
175+
this.cmdArray = command ;
162176
}
163177

164178
/**
@@ -187,7 +201,10 @@ public void setWorkingDirectory(String dir) {
187201
@Override
188202
public void afterPropertiesSet() throws Exception {
189203
Assert.notNull(commandRunner, "CommandRunner must be set");
190-
Assert.hasLength(command, "'command' property value is required");
204+
Assert.notNull(cmdArray, "'cmdArray' property value is required with at least 1 element");
205+
Assert.notEmpty(cmdArray, "'cmdArray' property value is required with at least 1 element");
206+
Assert.noNullElements(cmdArray, "'cmdArray' property value is required with at least 1 element");
207+
Assert.hasLength(cmdArray[0], "'cmdArray' property value is required with at least 1 element");
191208
Assert.notNull(systemProcessExitCodeMapper, "SystemProcessExitCodeMapper must be set");
192209
Assert.isTrue(timeout > 0, "timeout value must be greater than zero");
193210
Assert.notNull(taskExecutor, "taskExecutor is required");

spring-batch-core/src/test/java/org/springframework/batch/core/step/tasklet/SystemCommandTaskletIntegrationTests.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,20 @@ private void initializeTasklet() {
8484
tasklet.setTaskExecutor(new SimpleAsyncTaskExecutor());
8585
}
8686

87+
/*
88+
* Power usage scenario - successful execution of system command.
89+
*/
90+
@Test
91+
public void testExecuteWithSeparateArgument() throws Exception {
92+
tasklet.setCommand(getJavaCommand(), "--version");
93+
tasklet.afterPropertiesSet();
94+
95+
log.info("Executing command: " + getJavaCommand() + " --version");
96+
RepeatStatus exitStatus = tasklet.execute(stepExecution.createStepContribution(), null);
97+
98+
assertEquals(RepeatStatus.FINISHED, exitStatus);
99+
}
100+
87101
/*
88102
* Regular usage scenario - successful execution of system command.
89103
*/

0 commit comments

Comments
 (0)