/*
* Copyright 2008-2013 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.core.step.tasklet;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.File;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Before;
import org.junit.Test;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobInstance;
import org.springframework.batch.core.JobInterruptedException;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.util.Assert;
/**
* Tests for {@link SystemCommandTasklet}.
*/
public class SystemCommandTaskletIntegrationTests {
private static final Log log = LogFactory.getLog(SystemCommandTaskletIntegrationTests.class);
private SystemCommandTasklet tasklet = new SystemCommandTasklet();
private StepExecution stepExecution = new StepExecution("systemCommandStep", new JobExecution(new JobInstance(1L,
"systemCommandJob"), new JobParameters()));
@Before
public void setUp() throws Exception {
tasklet.setEnvironmentParams(null); // inherit from parent process
tasklet.setWorkingDirectory(null); // inherit from parent process
tasklet.setSystemProcessExitCodeMapper(new TestExitCodeMapper());
tasklet.setTimeout(5000); // long enough timeout
tasklet.setTerminationCheckInterval(500);
tasklet.setCommand("invalid command, change value for successful execution");
tasklet.setInterruptOnCancel(true);
tasklet.setTaskExecutor(new SimpleAsyncTaskExecutor());
tasklet.afterPropertiesSet();
tasklet.beforeStep(stepExecution);
}
/*
* Regular usage scenario - successful execution of system command.
*/
@Test
public void testExecute() throws Exception {
String command = "java -version";
tasklet.setCommand(command);
tasklet.afterPropertiesSet();
log.info("Executing command: " + command);
RepeatStatus exitStatus = tasklet.execute(stepExecution.createStepContribution(), null);
assertEquals(RepeatStatus.FINISHED, exitStatus);
}
/*
* Failed execution scenario - error exit code returned by system command.
*/
@Test
public void testExecuteFailure() throws Exception {
String command = "java org.springframework.batch.sample.tasklet.UnknownClass";
tasklet.setCommand(command);
tasklet.setTimeout(200L);
tasklet.afterPropertiesSet();
log.info("Executing command: " + command);
try {
StepContribution contribution = stepExecution.createStepContribution();
RepeatStatus exitStatus = tasklet.execute(contribution, null);
assertEquals(RepeatStatus.FINISHED, exitStatus);
assertEquals(ExitStatus.FAILED, contribution.getExitStatus());
}
catch (RuntimeException e) {
// on some platforms the system call does not return
assertEquals("Execution of system command did not finish within the timeout", e.getMessage());
}
}
/*
* The attempt to execute the system command results in exception
*/
@Test(expected = java.util.concurrent.ExecutionException.class)
public void testExecuteException() throws Exception {
String command = "non-sense-that-should-cause-exception-when-attempted-to-execute";
tasklet.setCommand(command);
tasklet.afterPropertiesSet();
tasklet.execute(null, null);
}
/*
* Failed execution scenario - execution time exceeds timeout.
*/
@Test
public void testExecuteTimeout() throws Exception {
String command = System.getProperty("os.name").toLowerCase().indexOf("win") >= 0 ?
"ping 1.1.1.1 -n 1 -w 3000" :
"sleep 3";
tasklet.setCommand(command);
tasklet.setTimeout(10);
tasklet.afterPropertiesSet();
log.info("Executing command: " + command);
try {
tasklet.execute(null, null);
fail();
}
catch (SystemCommandException e) {
assertTrue(e.getMessage().contains("did not finish within the timeout"));
}
}
/*
* Job interrupted scenario.
*/
@Test
public void testInterruption() throws Exception {
String command = System.getProperty("os.name").toLowerCase().indexOf("win") >= 0 ?
"ping 1.1.1.1 -n 1 -w 5000" :
"sleep 5";
tasklet.setCommand(command);
tasklet.setTerminationCheckInterval(10);
tasklet.afterPropertiesSet();
stepExecution.setTerminateOnly();
try {
tasklet.execute(null, null);
fail();
}
catch (JobInterruptedException e) {
System.out.println(e.getMessage());
assertTrue(e.getMessage().contains("Job interrupted while executing system command"));
assertTrue(e.getMessage().contains(command));
}
}
/*
* Command property value is required to be set.
*/
@Test
public void testCommandNotSet() throws Exception {
tasklet.setCommand(null);
try {
tasklet.afterPropertiesSet();
fail();
}
catch (IllegalArgumentException e) {
// expected
}
tasklet.setCommand("");
try {
tasklet.afterPropertiesSet();
fail();
}
catch (IllegalArgumentException e) {
// expected
}
}
/*
* Timeout must be set to non-zero value.
*/
@Test
public void testTimeoutNotSet() throws Exception {
tasklet.setCommand("not-empty placeholder");
tasklet.setTimeout(0);
try {
tasklet.afterPropertiesSet();
fail();
}
catch (IllegalArgumentException e) {
// expected
}
}
/*
* Working directory property must point to an existing location and it must
* be a directory
*/
@Test
public void testWorkingDirectory() throws Exception {
File notExistingFile = new File("not-existing-path");
Assert.state(!notExistingFile.exists());
try {
tasklet.setWorkingDirectory(notExistingFile.getCanonicalPath());
fail();
}
catch (IllegalArgumentException e) {
// expected
}
File notDirectory = File.createTempFile(this.getClass().getName(), null);
Assert.state(notDirectory.exists());
Assert.state(!notDirectory.isDirectory());
try {
tasklet.setWorkingDirectory(notDirectory.getCanonicalPath());
fail();
}
catch (IllegalArgumentException e) {
// expected
}
File directory = notDirectory.getParentFile();
Assert.state(directory.exists());
Assert.state(directory.isDirectory());
// no error expected now
tasklet.setWorkingDirectory(directory.getCanonicalPath());
}
/*
* test stopping a tasklet
*/
@Test
public void testStopped() throws Exception {
String command = System.getProperty("os.name").toLowerCase().indexOf("win") >= 0 ?
"ping 1.1.1.1 -n 1 -w 5000" :
"sleep 5";
tasklet.setCommand(command);
tasklet.setTerminationCheckInterval(10);
tasklet.afterPropertiesSet();
StepContribution contribution = stepExecution.createStepContribution();
//send stop
tasklet.stop();
tasklet.execute(contribution, null);
assertEquals(contribution.getExitStatus().getExitCode(),ExitStatus.STOPPED.getExitCode());
}
/**
* Exit code mapper containing mapping logic expected by the tests. 0 means
* finished successfully, other value means failure.
*/
private static class TestExitCodeMapper implements SystemProcessExitCodeMapper {
@Override
public ExitStatus getExitStatus(int exitCode) {
if (exitCode == 0) {
return ExitStatus.COMPLETED;
}
else {
return ExitStatus.FAILED;
}
}
}
}