OPEN - BATCH-152: Add facility for calling system commands via a Tasklet
added SystemCommandTasklet to samples (example usage in taskletJob)
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
package org.springframework.batch.sample.tasklet;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
import org.springframework.batch.sample.tasklet.ConfigurableSystemProcessExitCodeMapper;
|
||||
|
||||
/**
|
||||
* Tests for {@link ConfigurableSystemProcessExitCodeMapper}
|
||||
*/
|
||||
public class ConfigurableSystemProcessExitCodeMapperTests extends TestCase {
|
||||
|
||||
private ConfigurableSystemProcessExitCodeMapper mapper = new ConfigurableSystemProcessExitCodeMapper();
|
||||
|
||||
/**
|
||||
* Regular usage scenario - mapping adheres to injected values
|
||||
*/
|
||||
public void testMapping() {
|
||||
Map mappings = new HashMap() {{
|
||||
put(new Integer(0), ExitStatus.FINISHED);
|
||||
put(new Integer(1), ExitStatus.FAILED);
|
||||
put(new Integer(2), ExitStatus.CONTINUABLE);
|
||||
put(new Integer(3), ExitStatus.NOOP);
|
||||
put(new Integer(4), ExitStatus.UNKNOWN);
|
||||
put(ConfigurableSystemProcessExitCodeMapper.ELSE_KEY, ExitStatus.UNKNOWN);
|
||||
}};
|
||||
|
||||
mapper.setMappings(mappings);
|
||||
|
||||
//check explicitly defined values
|
||||
for (Iterator iterator = mappings.entrySet().iterator(); iterator.hasNext();) {
|
||||
Map.Entry entry = (Map.Entry) iterator.next();
|
||||
if (entry.getKey().equals(ConfigurableSystemProcessExitCodeMapper.ELSE_KEY)) continue;
|
||||
|
||||
int exitCode = ((Integer)entry.getKey()).intValue();
|
||||
assertSame(entry.getValue(), mapper.getExitStatus(exitCode));
|
||||
}
|
||||
|
||||
//check the else clause
|
||||
assertSame(mappings.get(ConfigurableSystemProcessExitCodeMapper.ELSE_KEY),
|
||||
mapper.getExitStatus(5));
|
||||
}
|
||||
|
||||
/**
|
||||
* Else clause is required in the injected map - setter checks its presence.
|
||||
*/
|
||||
public void testSetMappingsMissingElseClause() {
|
||||
Map missingElse = Collections.EMPTY_MAP;
|
||||
try {
|
||||
mapper.setMappings(missingElse);
|
||||
fail();
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
Map containsElse = new HashMap() {{
|
||||
put(ConfigurableSystemProcessExitCodeMapper.ELSE_KEY, ExitStatus.FAILED);
|
||||
}};
|
||||
// no error expected now
|
||||
mapper.setMappings(containsElse);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.springframework.batch.sample.tasklet;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
import org.springframework.batch.sample.tasklet.SimpleSystemProcessExitCodeMapper;
|
||||
|
||||
/**
|
||||
* Tests for {@link SimpleSystemProcessExitCodeMapper}.
|
||||
*/
|
||||
public class SimpleSystemProcessExitCodeMapperTests extends TestCase {
|
||||
|
||||
private SimpleSystemProcessExitCodeMapper mapper = new SimpleSystemProcessExitCodeMapper();
|
||||
|
||||
/**
|
||||
* 0 -> ExitStatus.FINISHED
|
||||
* else -> ExitStatus.FAILED
|
||||
*/
|
||||
public void testMapping() {
|
||||
assertEquals(ExitStatus.FINISHED, mapper.getExitStatus(0));
|
||||
assertEquals(ExitStatus.FAILED, mapper.getExitStatus(1));
|
||||
assertEquals(ExitStatus.FAILED, mapper.getExitStatus(-1));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
package org.springframework.batch.sample.tasklet;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
import org.springframework.batch.sample.tasklet.SystemCommandException;
|
||||
import org.springframework.batch.sample.tasklet.SystemCommandTasklet;
|
||||
import org.springframework.batch.sample.tasklet.SystemProcessExitCodeMapper;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Tests for {@link SystemCommandTasklet}.
|
||||
*/
|
||||
public class SystemCommandTaskletIntegrationTests extends TestCase {
|
||||
|
||||
private static final Log log = LogFactory.getLog(SystemCommandTaskletIntegrationTests.class);
|
||||
|
||||
private SystemCommandTasklet tasklet = new SystemCommandTasklet();
|
||||
|
||||
protected 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
|
||||
}
|
||||
|
||||
/**
|
||||
* Regular usage scenario - successful execution of system command.
|
||||
*/
|
||||
public void testExecute() throws Exception {
|
||||
String command = "java -version";
|
||||
tasklet.setCommand(command);
|
||||
tasklet.afterPropertiesSet();
|
||||
|
||||
log.info("Executing command: " + command);
|
||||
ExitStatus exitStatus = tasklet.execute();
|
||||
|
||||
assertEquals(ExitStatus.FINISHED, exitStatus);
|
||||
}
|
||||
|
||||
/**
|
||||
* Failed execution scenario - error exit code returned by system command.
|
||||
*/
|
||||
public void testExecuteFailure() throws Exception {
|
||||
String command = "java -invalid-argument";
|
||||
tasklet.setCommand(command);
|
||||
tasklet.afterPropertiesSet();
|
||||
|
||||
log.info("Executing command: " + command);
|
||||
ExitStatus exitStatus = tasklet.execute();
|
||||
|
||||
assertEquals(ExitStatus.FAILED, exitStatus);
|
||||
}
|
||||
|
||||
/**
|
||||
* Failed execution scenario - execution time exceeds timeout.
|
||||
*/
|
||||
public void testExecuteTimeout() throws Exception {
|
||||
String command = "sleep 3";
|
||||
tasklet.setCommand(command);
|
||||
tasklet.setTimeout(10);
|
||||
tasklet.afterPropertiesSet();
|
||||
|
||||
log.info("Executing command: " + command);
|
||||
try {
|
||||
tasklet.execute();
|
||||
fail();
|
||||
}
|
||||
catch (SystemCommandException e) {
|
||||
assertTrue(e.getMessage().contains("did not finish successfully within the timeout"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Command property value is required to be set.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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().getSimpleName(), 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());
|
||||
}
|
||||
|
||||
/**
|
||||
* Exit code mapper containing mapping logic expected by the tests. 0 means
|
||||
* finished successfully, other value means failure.
|
||||
*/
|
||||
private static class TestExitCodeMapper implements SystemProcessExitCodeMapper {
|
||||
|
||||
public ExitStatus getExitStatus(int exitCode) {
|
||||
if (exitCode == 0) {
|
||||
return ExitStatus.FINISHED;
|
||||
} else {
|
||||
return ExitStatus.FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user