Refine contribution #3972

Related to #3955
This commit is contained in:
Mahmoud Ben Hassine
2022-10-12 15:32:56 +02:00
parent eccea800fb
commit 4eefb1d287
5 changed files with 60 additions and 112 deletions

View File

@@ -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.

View File

@@ -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 <i>name</i>=<i>value</i>, 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
* <i>name</i>=<i>value</i>, 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;

View File

@@ -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 {

View File

@@ -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;
}
/**

View File

@@ -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());
}
}