spring-batch
spring-batch copied to clipboard
String array command with SystemCommandTasklet
dgray16 opened BATCH-2863 and commented
Hi. Using this chance I want to thank you for Spring Batch, it is amazing :)
Now, directly to the topic.
I want to use
org.springframework.batch.core.step.tasklet.SystemCommandTasklet
but there an issue.
There is method:
org.springframework.batch.core.step.tasklet.SystemCommandTasklet#setCommand
which accepts
java.lang.String
My task is simple: run network packet analyzer Wireshark before processing is started.
Bash command looks like this:
/bin/bash -c tshark -i enp1s0 -w ~/Documents/log.pcap -f "host 8.8.8.8"
The problem is that
java.util.StringTokenizer#StringTokenizer(java.lang.String)
is about to split provided String with Bash command into array, after it command is not working.
In this case helps a lot another method
java.lang.Runtime#exec(java.lang.String[], java.lang.String[], java.io.File)
So if I split manually command into this:
new String[] { "/bin/bash", "-c", "tshark -i enp1s0 -w ~/Documents/log.pcap -f \"host 8.8.8.8\""}
Works perfectly.
If there is tricky way to hack
java.util.StringTokenizer#StringTokenizer(java.lang.String)
to make it work with my example, I suggest to add JavaDoc above
org.springframework.batch.core.step.tasklet.SystemCommandTasklet#setCommand
If not, I suggest to provide possibility to use both forms, String[] + String.
And one more thing, there may be opportunity to run sh script by single String method (did not test it), but I am not interested in it. I am interested only in hardcoded bash command.
As a workaround for it right now I have written this code:
Tasklet tasklet = (contribution, chunkContext) -> {
new ProcessBuilder("/bin/bash", "-c", "tshark -i enp1s0 -w ~/Documents/log.pcap -f \"host 8.8.8.8\"").start();
return RepeatStatus.FINISHED;
};
In any case I am ready to create PR based on discussion here if that will be needed.
Affects: 4.2.1
Mahmoud Ben Hassine commented
SystemCommandTasklet#setEnvironmentParams is what you are looking for:
SystemCommandTasklet systemCommandTasklet = new SystemCommandTasklet();
systemCommandTasklet.setCommand("/bin/bash");
systemCommandTasklet.setEnvironmentParams(new String[] {"-c", "tshark -i enp1s0 -w ~/Documents/log.pcap -f \"host 8.8.8.8\""});
The array of arguments will be passed as parameters to the command when calling java.lang.Runtime#exec behind the scene.
EDIT: The example above is incorrect. setEnvironmentParams is meant for environment variables, not for command arguments. Apologies for the confusion as I seem to have misread the Javadoc of java.lang.Runtime#exec. Since SystemCommandTasklet is using the convenience method java.lang.Runtime#exec which takes a single String for the command and its arguments, the expected way to pass arguments is as follows:
SystemCommandTasklet systemCommandTasklet = new SystemCommandTasklet();
systemCommandTasklet.setCommand("/bin/bash -c tshark -i enp1s0 -w ~/Documents/log.pcap -f \"host 8.8.8.8\"");
The Javadoc of setCommand should be more precise about the expected input and how it will be tokenized if any. Now if the default tokenization done behind the scene by java.lang.Runtime#exec fails or does not work as expected (as initially reported in this issue), Spring Batch can't really help for that. I believe setCommand should accept a vararg of Strings for the command and its arguments to allow the user to specify tokens as needed.
dgray16 commented
Mahmoud Ben Hassine thanks!
I suggest adding this to https://github.com/spring-projects/spring-batch/tree/master/spring-batch-samples
Passing command arguments as environment parameters seems counter-intuitive. Does this mean the intent of SystemCommandTasklet#setEnvironmentParams is both command arguments and environment parameters?
I wouldn't expect that passing command arguments to setEnvironmentParams actually works. The array of environment parameters is passed as is to ProcessBuilder::environment. The example arguments above should get silently (compatibility with Java 4 ...) ignored there since they don't contain a =.
I have ideas for how to support this while maintaining backward compatibility. I also want to do it in a way that works for the requirements in BATCH-2318
The example in my previous comment is incorrect, I edited that comment to clarify things. Apologies for the confusion.
I believe setCommand should accept a vararg of Strings for the command and its arguments (similar to java.lang.ProcessBuilder#command(java.lang.String...)). The PR #3967 goes in the right direction to solve this issue.