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 cb55a4dc8..0c350e54b 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
@@ -1,5 +1,5 @@
/*
- * Copyright 2006-2021 the original author or authors.
+ * Copyright 2006-2022 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.
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/tasklet/CommandRunner.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/tasklet/CommandRunner.java
index 5c2a78814..fbaa6195e 100644
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/tasklet/CommandRunner.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/tasklet/CommandRunner.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006-2021 the original author or authors.
+ * Copyright 2006-2022 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.
@@ -19,50 +19,35 @@ import java.io.File;
import java.io.IOException;
/**
- * Interface for executing commands. This abstraction is only
- * useful in order to allow classes that make {@link Runtime#exec} calls
- * to be testable, since the invoked command might not be
- * available during tests execution.
+ * Strategy interface for executing commands. This abstraction is useful to decouple the
+ * command execution from the enclosing tasklet so that implementations can be unit tested
+ * in isolation.
*
* @author Stefano Cordio
- * @since FIXME
+ * @author Mahmoud Ben Hassine
+ * @since 5.0
*/
public interface CommandRunner {
/**
- * Executes the specified string command in a separate process with the
- * specified environment and working directory.
+ * Executes the specified string command in a separate process with the specified
+ * environment and working directory.
+ * @param command a specified system command.
+ * @param envp array of strings, each element of which has environment variable
+ * settings in the format name=value, or {@code null} if the subprocess
+ * should inherit the environment of the current process.
+ * @param dir the working directory of the subprocess, or {@code null} if the
+ * subprocess should inherit the working directory of the current process.
+ * @return A new {@link Process} object for managing the subprocess
+ * @throws SecurityException If a security manager exists and its
+ * {@link SecurityManager#checkExec checkExec} method doesn't allow creation of the
+ * subprocess
+ * @throws IOException If an I/O error occurs
+ * @throws NullPointerException If {@code command} is {@code null}, or one of the
+ * elements of {@code envp} is {@code null}
+ * @throws IllegalArgumentException If {@code command} is empty
*
- * @param command a specified system command.
- *
- * @param envp array of strings, each element of which
- * has environment variable settings in the format
- * name=value, or
- * {@code null} if the subprocess should inherit
- * the environment of the current process.
- *
- * @param dir the working directory of the subprocess, or
- * {@code null} if the subprocess should inherit
- * the working directory of the current process.
- *
- * @return A new {@link Process} object for managing the subprocess
- *
- * @throws SecurityException
- * If a security manager exists and its
- * {@link SecurityManager#checkExec checkExec}
- * method doesn't allow creation of the subprocess
- *
- * @throws IOException
- * If an I/O error occurs
- *
- * @throws NullPointerException
- * If {@code command} is {@code null},
- * or one of the elements of {@code envp} is {@code null}
- *
- * @throws IllegalArgumentException
- * If {@code command} is empty
- *
- * @see Runtime#exec(String, String[], File)
+ * @see Runtime#exec(String, String[], File)
*/
Process exec(String command, String[] envp, File dir) throws IOException;
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/tasklet/JvmCommandRunner.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/tasklet/JvmCommandRunner.java
index a1afa83ab..710f4590b 100644
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/tasklet/JvmCommandRunner.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/tasklet/JvmCommandRunner.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006-2021 the original author or authors.
+ * Copyright 2006-2022 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.
@@ -20,12 +20,12 @@ import java.io.IOException;
/**
* Implementation of the {@link CommandRunner} interface that calls the standard
- * {@link Runtime#exec} method. It should be noted that there is no unit tests for
- * this class, since there is only one line of actual code, that would only be
- * testable by mocking {@link Runtime}.
+ * {@link Runtime#exec} method. It should be noted that there is no unit tests for this
+ * class, since there is only one line of actual code, that would only be testable by
+ * mocking {@link Runtime}.
*
* @author Stefano Cordio
- * @since FIXME
+ * @since 5.0
*/
public class JvmCommandRunner implements CommandRunner {
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/tasklet/SystemCommandTasklet.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/tasklet/SystemCommandTasklet.java
index 4ed3fff54..7e614e36b 100644
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/tasklet/SystemCommandTasklet.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/tasklet/SystemCommandTasklet.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006-2021 the original author or authors.
+ * Copyright 2006-2022 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.
@@ -64,7 +64,7 @@ public class SystemCommandTasklet implements StepExecutionListener, StoppableTas
protected static final Log logger = LogFactory.getLog(SystemCommandTasklet.class);
- private static CommandRunner commandRunner = new JvmCommandRunner();
+ private CommandRunner commandRunner = new JvmCommandRunner();
private String command;
@@ -144,24 +144,14 @@ public class SystemCommandTasklet implements StepExecutionListener, StoppableTas
}
}
- /**
- * Static setter for the {@link CommandRunner} so it can be adjusted before
- * dependency injection. Typically overridden by
- * {@link #setCommandRunner(CommandRunner)}.
- *
- * @param commandRunner {@link CommandRunner} instance to be used by SystemCommandTasklet instance.
- */
- public static void presetCommandRunner(CommandRunner commandRunner) {
- SystemCommandTasklet.commandRunner = commandRunner;
- }
-
/**
* Injection setter for the {@link CommandRunner}.
- *
- * @param commandRunner {@link CommandRunner} instance to be used by SystemCommandTasklet instance.
+ * @param commandRunner {@link CommandRunner} instance to be used by
+ * SystemCommandTasklet instance. Defaults to {@link JvmCommandRunner}.
+ * @since 5.0
*/
public void setCommandRunner(CommandRunner commandRunner) {
- SystemCommandTasklet.commandRunner = commandRunner;
+ this.commandRunner = commandRunner;
}
/**
diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/step/tasklet/SystemCommandTaskletIntegrationTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/step/tasklet/SystemCommandTaskletIntegrationTests.java
index 7295bb97d..4a44ce26d 100644
--- a/spring-batch-core/src/test/java/org/springframework/batch/core/step/tasklet/SystemCommandTaskletIntegrationTests.java
+++ b/spring-batch-core/src/test/java/org/springframework/batch/core/step/tasklet/SystemCommandTaskletIntegrationTests.java
@@ -172,27 +172,8 @@ class SystemCommandTaskletIntegrationTests {
*/
@Test
public void testCommandRunnerNotSet() throws Exception {
- SystemCommandTasklet.presetCommandRunner(null);
- try {
- tasklet.afterPropertiesSet();
- fail();
- }
- catch (IllegalArgumentException e) {
- // expected
- } finally {
- SystemCommandTasklet.presetCommandRunner(new JvmCommandRunner());
- }
-
tasklet.setCommandRunner(null);
- try {
- tasklet.afterPropertiesSet();
- fail();
- }
- catch (IllegalArgumentException e) {
- // expected
- } finally {
- SystemCommandTasklet.presetCommandRunner(new JvmCommandRunner());
- }
+ assertThrows(IllegalArgumentException.class, tasklet::afterPropertiesSet);
}
/*
@@ -296,50 +277,42 @@ class SystemCommandTaskletIntegrationTests {
@Test
public void testExecuteWithSuccessfulCommandRunnerMockExecution() throws Exception {
- try {
- StepContribution stepContribution = stepExecution.createStepContribution();
- CommandRunner commandRunner = mock(CommandRunner.class);
- Process process = mock(Process.class);
- String command = "invalid command";
+ StepContribution stepContribution = stepExecution.createStepContribution();
+ CommandRunner commandRunner = mock(CommandRunner.class);
+ Process process = mock(Process.class);
+ String command = "invalid command";
- when(commandRunner.exec(eq(command), any(), any())).thenReturn(process);
- when(process.waitFor()).thenReturn(0);
+ when(commandRunner.exec(eq(command), any(), any())).thenReturn(process);
+ when(process.waitFor()).thenReturn(0);
- SystemCommandTasklet.presetCommandRunner(commandRunner);
- tasklet.setCommand(command);
- tasklet.afterPropertiesSet();
+ tasklet.setCommandRunner(commandRunner);
+ tasklet.setCommand(command);
+ tasklet.afterPropertiesSet();
- RepeatStatus exitStatus = tasklet.execute(stepContribution, null);
+ RepeatStatus exitStatus = tasklet.execute(stepContribution, null);
- assertEquals(RepeatStatus.FINISHED, exitStatus);
- assertEquals(ExitStatus.COMPLETED, stepContribution.getExitStatus());
- } finally {
- SystemCommandTasklet.presetCommandRunner(new JvmCommandRunner());
- }
+ assertEquals(RepeatStatus.FINISHED, exitStatus);
+ assertEquals(ExitStatus.COMPLETED, stepContribution.getExitStatus());
}
@Test
public void testExecuteWithFailedCommandRunnerMockExecution() throws Exception {
- try {
- StepContribution stepContribution = stepExecution.createStepContribution();
- CommandRunner commandRunner = mock(CommandRunner.class);
- Process process = mock(Process.class);
- String command = "invalid command";
+ StepContribution stepContribution = stepExecution.createStepContribution();
+ CommandRunner commandRunner = mock(CommandRunner.class);
+ Process process = mock(Process.class);
+ String command = "invalid command";
- when(commandRunner.exec(eq(command), any(), any())).thenReturn(process);
- when(process.waitFor()).thenReturn(1);
+ when(commandRunner.exec(eq(command), any(), any())).thenReturn(process);
+ when(process.waitFor()).thenReturn(1);
- SystemCommandTasklet.presetCommandRunner(commandRunner);
- tasklet.setCommand(command);
- tasklet.afterPropertiesSet();
+ tasklet.setCommandRunner(commandRunner);
+ tasklet.setCommand(command);
+ tasklet.afterPropertiesSet();
- RepeatStatus exitStatus = tasklet.execute(stepContribution, null);
+ RepeatStatus exitStatus = tasklet.execute(stepContribution, null);
- assertEquals(RepeatStatus.FINISHED, exitStatus);
- assertEquals(ExitStatus.FAILED, stepContribution.getExitStatus());
- } finally {
- SystemCommandTasklet.presetCommandRunner(new JvmCommandRunner());
- }
+ assertEquals(RepeatStatus.FINISHED, exitStatus);
+ assertEquals(ExitStatus.FAILED, stepContribution.getExitStatus());
}
}