Executing shell-script with parameters using apache.commons.exec in Windows - java

I am trying to run a script using apache-commons-exec which was implemented using the java approximation to run. This script is executed in the production server (Linux) but I need to test it in my localhost to see that everything works OK.
Here is my code to launch cygwin and this code is working in the cmd.exe but it does not work when I try to launch it using commons.exec:
OutputStream outputStream = new ByteArrayOutputStream();
DefaultExecutor exec = new DefaultExecutor();
exec.setWatchdog(new ExecuteWatchdog(1000));
PumpStreamHandler streamHandler = new PumpStreamHandler(outputStream);
exec.setStreamHandler(streamHandler);
CommandLine cmdLine = CommandLine.parse("C:\\cygwin64\\bin\\bash");
cmdLine.addArgument("-c");
cmdLine.addArgument("/cygdrive/c/dev/launch.sh");
int exit = exec.execute(cmdLine);
logger.warn("Job exit: " + exit);
It returns 1 and no output or log error:
org.apache.commons.exec.ExecuteException: Process exited with an error: 1 (Exit value: 1)
Is there anything missing? How can I catch the output properly?

A bit of a guess this but might help.
Sometimes an exit code = 1 represents "success". However, Apache Commons Exec by default interprets an exit code = 1 as a failure and will throw an ExecuteException if the script in question exits with and exit code = 1.
You can tell your DefaultExecutor that "exit code = 1 = success" using the following code:
exec.setExitValue(1);
Might not be the reason but worth a go.

Related

Executing command in ProcessBuilder does not seem to work for "java" commands in Windows (Kotlin)

Currently trying to execute a .jar file programmatically. But to test out java, I tried running the the following:
val p = ProcessBuilder("cmd.exe", "/c", "java", "-version").start()
val results: List<String> = p.inputStream.bufferedReader().readLines()
assertThat("Results should contain java version: ", results, hasItem(containsString("java version")))
However, nothing seems to output.
I am successfully able to run:
val pb = ProcessBuilder("cmd.exe", "/c", "echo", "hello world")
I have tried adding a working directory where the java executable is located, but nothing happens.
I am running out of ideas on how to make this work. If I run cmd and type out java -version I get the version information.
What else could I do to get this to work?
ProcessBuilder writes the result of command java -version to error output Process.errorStream, not Process.inputStream.
Try this code:
val results: List<String> = p.errorStream.bufferedReader().readLines()
Also you may try Koproc lib
It's a small Kotlin lib to run process and execute commands based on Java ProcessBuilder
You may run java process with timeout = 120 sec :
val koproc = "java -jar some.jar".startProcess { timeoutSec = 120 }
koproc.use {
println("Out: ${it.readAvailableOut}")
println("Err: ${it.readAvailableErrOut}")
}
println("Full result after closing: ${koproc.result}")
Run cmd command:
// 'cmd.exe' process will be closed after timeout
val commandResult = "cmd.exe dirs".startCommand { timeoutSec = 1 }
// But you will get the output
println("Out: ${commandResult.out}")
See examples in unit tests: https://github.com/kochetkov-ma/koproc/blob/main/src/test/kotlin/ru/iopump/koproc/ExtensionKtIT.kt

How to wait for multi-threaded shell script execution to finish called inside my web service?

I have a java restful service method which executes a myscript.sh using processBuilder. My script takes one input (example - myscript.sh /path/to-a/folder).
Inside the script something like this
-> execute a command which is multithreaded i.e parallel processing
-> echo "my message"
Now when call my script from a linux command line it executes fine. First all the threads running finishes and then some text output from threaded command execution shown on terminal and then echo my message is shown.
But when I call the same script from java using processBuilder, the last echo message comes immidiately and execution ends.
Following the way I call my script from java
ProcessBuilder processBuilder = new ProcessBuilder("/bin/bash","/path/to/myscript.sh","/path/to/folder/data");
Process proc = processBuilder.start();
StringBuffer output = new StringBuffer();
BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()));
String line = "";
while((line = reader.readLine()) != null){
output.append(line + "\n");
}
System.out.println("### " + output);
I don't know whats happening, how to debug also.
Can someone enlighten me on how to get the same behaviour from shell script when run from terminal or from java processBuilder?
Use ProcessBuilder.redirectErrorStream(boolean redirectErrorStream) with argument true to merge the errors into output. Alternatively, you could also use the shell command syntax cmd 2>&1 to merge the error with output.
These are some of the cases why you may be immediately getting the output of the last echo statement (instead of the script taking time to run and return proper results):
Missing environment variables
The launched bash needs to source .bashrc or some such recource file
The launched bash may not be running in right directory (you can set this in ProcessBuilder)
The launched bash may not be finding some script/executable in its PATH
The launched bash may not be finding proper libraries in the path for any of the executables
Once you merge error, you would be able to debug and see the errors for yourself.
In your context, separate processes may be spawned in two ways:
1) Bash
/path/to/executables/executable &
This will spawn a new executable executable and you need to wait for it to finish. Here's an answer that will help you.
2) Java
Process exec = Runtime.getRuntime().exec(command);
status = exec.waitFor();
Essentially, you need to wait for the process to end before you start reading its std/err streams.
If I understand the problem correctly, adding just this line to your code should suffice: status = exec.waitFor() (Before you obtain the streams)
Here's the JavaDoc for Process.waitFor() :
Causes the current thread to wait, if necessary, until the process represented by this Process object has terminated. This method returns immediately if the subprocess has already terminated. If the subprocess has not yet terminated, the calling thread will be blocked until the subprocess exits.
Returns:
the exit value of the subprocess represented by this Process object. By convention, the value 0 indicates normal termination.
Throws:
InterruptedException - if the current thread is interrupted by another thread while it is waiting, then the wait is ended and an InterruptedException is thrown

Get exit value of CMD child process called from java program

In my java program I am trying to run a different program through CMD with its output appearing in a command window in the foreground and then analyze the exit code of the child program (foo) in the main java program. Unfortunately, all I seem to be able to access is the exit code of the CMD window, which is always 0.
The following is a snippet of what I'm doing:
ProcessBuilder pb = new ProcessBuilder();
pb.directory(new File(dir));
pb.command("cmd","/c","start","/wait","foo.exe",arg);
process = pb.start();
exitVal = process.waitFor();
but exitVal is always 0 regardless of how foo exits. How can I get just the exit code of foo?
I'm also pretty new to java so if there's a more elegant way of doing this, I'm open to suggestions.
I found a solution by modifying one of the things I had tried before to account for windows batch being finicky. The solution was to send another command to cmd to tell it to exit with the most recent error code. Earlier I had tried this by appending & exit %errorlevel% to the command but cmd variables are only updated at the end of each command line, not at the end of each command.
To force update, I used %^errorlevel% instead.

get errorlevel in Java

I write the code in Java to launch specific application via command prompt. So the code will open cmd and run the application from there. I want to get the errorlevel of the execution. I tried with
String errorlevel = System.getenv("errorlevel");
System.out.println(errorlevel);
It always returns zero either the execution is failed or not. I also tried with getProperty() and came up with the same result.
Then the other way I tried
Process p = Runtime.getRuntime().exec(command);
int i = p.waitFor();
System.out.println("errorLevel :" + ""+ i);
With this, i get the exitValue but not exactly what I want since I have to close the application to retrieve the value
What I want to know is whether it is possible to get the errorlevel of commandprompt without exit/close the process manually
Thanks before

Runtime.getRuntime.exec() works on Windows but not Linux

I am building a Java application that calls a system command and executes that command before returning control to the Java thread. The debugging step that I have is that I have made a perl script called test.pl and I call that script from the Java method and in Windows I get the expected output and a return value of 0. In Linux I get no output or error output and I get a return value of 136. I have spent extensive time online trying to figure out where I have gone wrong but as I have said, on Windows it runs. I think it must be a simple error that I am just not catching.
Here is the code derived from the excellent Runtime.exec() tutorial at http://www.javaworld.com/jw-12-2000/jw-1229-traps.html
try {
FileOutputStream fos = new FileOutputStream("output/" + this.startPosition + ".txt");
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec(new String[]{"perl", "/data/stat-positive-selection/thaddeus/treesim/chr2YRI/test.pl"});
//System.out.println(commandLine);
// any error message?
StreamGobbler errorGobbler = new
StreamGobbler(proc.getErrorStream(), "ERROR");
// any output?
StreamGobbler outputGobbler = new
StreamGobbler(proc.getInputStream(), "OUTPUT", fos);
// kick them off
errorGobbler.start();
outputGobbler.start();
// any error???
int exitVal = proc.waitFor();
System.out.println("ExitValue: " + exitVal);
fos.flush();
fos.close();
} catch (Throwable e){
e.printStackTrace();
}
}
I have figured it out and fixed the code
new exec call, the use of the shell and the path to perl are required
Process proc = rt.exec(new String[]{"/bin/bash", "-c", "/usr/bin/perl /data/stat-positive-selection/thaddeus/treesim/chr2YRI/test.pl"});
The differences between the old and new versions are:
you are running the command from a shell, and
you are specifying the full pathname of the "perl" command.
In fact, in this context there doesn't seem to be a great deal of point in running the command from a shell. (Hypothetically, your Perl application might depend on environment variables that are set during shell initialization. However, the same environment variables would normally be inherited by and then from the Java command's environment. So unless the Java command is being launched in a strange way, this scenario is ... unlikely.)
So I think the substantative difference1 is that you are using the full pathname for "perl" ... and you don't a subshell to do that.
So, in the interests of science ( :-) ) I suggest you try this:
Process proc = rt.exec(new String[]{
"/usr/bin/perl",
"/data/stat-positive-selection/thaddeus/treesim/chr2YRI/test.pl"});
1 - ... the difference that makes a difference
I replaced the whole Runtime.exec() structure with the Apache Commons Exec library. It fixed the problem I was having.
http://commons.apache.org/proper/commons-exec/tutorial.html

Categories