Unable to get output from Apache Commons Exec - java

Although the titles are very similar, this questions is NOT a duplicate of Process output from apache-commons exec.
I am trying to get the output of a command by using apache-commons exec. Here is what I am doing
import org.apache.commons.exec.*;
import java.io.ByteArrayOutputStream;
public class Sample {
private static void runCommand(String cmd) throws Exception {
ByteArrayOutputStream stdout = new ByteArrayOutputStream();
PumpStreamHandler psh = new PumpStreamHandler(stdout);
CommandLine cl = CommandLine.parse(cmd);
DefaultExecutor exec = new DefaultExecutor();
exec.setStreamHandler(psh);
exec.execute(cl);
System.out.println(stdout.toString());
}
public static void main(String... args) throws Exception {
String cmd1 = "python -c \"print(10)\"";
String cmd2 = "python -c \"import datetime; print(datetime.datetime.now())\"";
runCommand(cmd1); // prints 10
runCommand(cmd2); // should print the current datetime, but does not!
}
}
The problem is that runCommand(cmd2) does not print anything to the output. When I try running the command on terminal, it works fine.
I have tried this program with and without the IDE so I'm sure this has nothing to do with the IDE console.
Here's a screenshot
Here's a screenshot of the terminal
Python command running on the terminal

It works fine on mine PC from IDEA. Try to recreate the project. Add more information about your environment.
Try to put your python code into .py file and run it like "python test.py".

A colleague was able to come up with a solution to this problem. Changing
CommandLine cl = CommandLine.parse(cmd);
to
CommandLine cl = new CommandLine("/bin/sh");
cl.addArguments("-c");
cl.addArguments("'" + cmd + "'", false);
solved the issue.
The complete code looks as follows:
import org.apache.commons.exec.*;
import java.io.ByteArrayOutputStream;
public class Sample {
private static void runCommand(String cmd) throws Exception {
ByteArrayOutputStream stdout = new ByteArrayOutputStream();
PumpStreamHandler psh = new PumpStreamHandler(stdout);
// CommandLine cl = CommandLine.parse(cmd);
CommandLine cl = new CommandLine("/bin/sh");
cl.addArguments("-c");
cl.addArguments("'" + cmd + "'", false);
DefaultExecutor exec = new DefaultExecutor();
exec.setStreamHandler(psh);
exec.execute(cl);
System.out.println(stdout.toString());
}
public static void main(String[] args) throws Exception {
String cmd1 = "python -c \"print(10)\"";
String cmd2 = "python -c \"import datetime; print(datetime.datetime.now())\"";
runCommand(cmd1); // prints 10
runCommand(cmd2);
}
}

Related

Command Works Through Command Line, but Not When Using ProcessBuilder

I'm writing a program that includes a feature where the user can type in Java code into a text box and be able to compile and run it. The error I get is:
The two directories shown at the top are correct, and the command works when I do it manually through command prompt from the same working directory. I'm using Windows 10, and also here's the code:
public Process compile() throws IOException {
save(); //saves changes to source file
System.out.println(file.getCanonicalPath());
ProcessBuilder processBuilder = new ProcessBuilder("javac", file.getCanonicalPath());
processBuilder.directory(new File(settingsFile.getJdkPath()));
System.out.println(processBuilder.directory());
Process process = processBuilder.start(); //Throws exception
this.compiledFile = new File(file.getParentFile(), file.getName().replace(".java", ".class"));
return process;
}
File to compile:
Working directory:
Using this code, I was able to compile a Test.java file into a Test.class file on my Desktop.
import java.io.IOException;
public class App {
public static Process compile() throws IOException {
String myFilePath = "C:\\Users\\redacted\\Desktop\\Test.java";
String javacPath = "C:\\Program Files\\Java\\jdk1.8.0_171\\bin\\javac.exe";
ProcessBuilder processBuilder = new ProcessBuilder(javacPath, myFilePath);
return processBuilder.start();
}
public static void main(String[] args) throws IOException {
Process process = compile();
}
}
Using String javacPath = "javac.exe"; also worked, but that could be because my JDK bin is on my PATH variable.
There is something wrong with your paths or permissions in the ProcessBuilder constructor call.

How to run a jar file from the teamcity custom plugin code

I am working on developing a plugin for teamcity . The requirement is to run a jar file from the code which does some custom operation. I tried with the below code, but its not working for me.Any ideas on how to run the jar, links to documentation or sample code will help me a lot to progress further
public class CustomBuildProcess extends BuildProcessAdapter
{
private static final String jarDir = "\\plugins\\teamcity-custom-plugin-agent\\lib\\metrics-17.6.4.4.jar";
#Override
public void start()
{
buildStatus = startProcess();
}
private BuildFinishedStatus startProcess() throws IOException
{
final GeneralCommandLine cmd = new GeneralCommandLine();
cmd.setExePath("java -jar C:\\BuildAgent"+jarDir);
final ExecResult result = SimpleCommandLineProcessRunner.runCommand(cmd, new byte[0]);
}
The following code worked for me.
final Runtime rTime = Runtime.getRuntime();
final Process process = rTime.exec("java -jar
C:\\TeamCity\\BuildAgent\\plugins\\teamcity-cutom-plugin-agent\\lib\\metrics-17.6.4.4.jar");
logger.progressMessage(new String(IOUtils.toByteArray(process.getInputStream())));
PrintStream printStream = new PrintStream(process.getOutputStream());
logger.progressMessage(new String(IOUtils.toByteArray(process.getErrorStream())));

Java Cannot run program CreateProcess error=2

i'd like to run a command that executes a shell script with the java class "DefaultExecutor", but i get this error:
Cannot run program "get_encrypted_password.sh" (in directory "C:\Temp\scripts"): CreateProcess error=2 specified file not found".
the script works well with git bash.
can someone tell me where i'm doing wrong?
public Entity updateWithEncryptedPassword(Entity entity) throws IOException {
String password = entity.getPwd();
String security_key = "00000000000000000000000000000000";
String path = "C:/Temp/scripts";
CommandLine commandLine = CommandLine.parse("get_encrypted_password.sh");
commandLine.addArgument(password);
commandLine.addArgument(security_key);
String encrypted_password = Utils.runCommandAndGetOutput(commandLine, path);
entity.setNewPwd(encrypted_password);
return super.update(entity);
}
public static String runCommandAndGetOutput(CommandLine commandLine, String path) throws IOException {
DefaultExecutor defaultExecutor = new DefaultExecutor();
defaultExecutor.setExitValue(0);
defaultExecutor.setWorkingDirectory(new File(path));
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
PumpStreamHandler streamHandler = new PumpStreamHandler(outputStream);
defaultExecutor.setStreamHandler(streamHandler);
defaultExecutor.execute(commandLine);
return outputStream.toString();
}
Instead of executing "get_encrypted_password.sh", which cannot be ran under Windows, execute "bash", (probably git bash,) and pass "get_encrypted_password.sh" as a parameter to it, so that bash will execute your script.

I am getting error while executing sh file by using java code.. sudo: no tty present and no askpass program specified

I am getting error while executing sh file by using java code.From terminal, it is working fine
sudo: no tty present and no askpass program specified
My Code :-
package com.test;
import java.io.IOException;
import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.ExecuteException;
public class TestScript {
int iExitValue;
String sCommandString;
public void runScript(String command){
sCommandString = command;
CommandLine oCmdLine = CommandLine.parse(sCommandString);
DefaultExecutor oDefaultExecutor = new DefaultExecutor();
oDefaultExecutor.setExitValue(0);
try {
iExitValue = oDefaultExecutor.execute(oCmdLine);
} catch (ExecuteException e) {
System.err.println("Execution failed.");
e.printStackTrace();
} catch (IOException e) {
System.err.println("permission denied.");
e.printStackTrace();
}
}
public static void main(String args[]){
TestScript testScript = new TestScript();
testScript.runScript("sh /home/FTP-SCP-Project/shellscript.sh");
}
}
sh file :-
sudo scp -i /home/FTP-SCP-Project/src/lib/demo.pem -r /home/FTP-SCP-Project/test-output3 user#xx.xxx.xx.xxx:/var/www/html/projects/demo_reports/reporttest112
I have tried all other java code also but they are giving error while I have giving them all permision like chmod 777 or 755 or 600
String Pemfilepath="/home/shubham/Experiment_zone/FTP-SCP-Project/src/lib/cuelogic.pem";
String targetFolder = "/home/shubham/Experiment_zone/FTP-SCP-Project/test-output3";
String[] command = { "sudo scp -i "+Pemfilepath+" -r "+targetFolder+" "+"ubuntu#54.152.13.148:/var/www/html/projects/kumo_reports/reporttest1"};
// String command = "ls";
System.out.println(command[0]);
Process process = Runtime.getRuntime().exec(new String[] { "sudo ", "scp ", "-i " ,Pemfilepath," ", "-r"," ",targetFolder," ","user#xx.xxx.xx.xxx:/var/www/html/projects/demo_reports/reporttest1" });
BufferedReader reader = new BufferedReader(new InputStreamReader(
process.getInputStream()));
String s;
while ((s = reader.readLine()) != null) {
System.out.println("Script output: " + s);
}
But they are also giving error as below :-
Exception in thread "main" java.io.IOException: Cannot run program
"sudo ": error=2, No such file or directory at
java.lang.ProcessBuilder.start(ProcessBuilder.java:1047) at
java.lang.Runtime.exec(Runtime.java:617) at
java.lang.Runtime.exec(Runtime.java:485) at
com.test.shelllocallaunch.main(shelllocallaunch.java:14) Caused by:
java.io.IOException: error=2, No such file or directory at
java.lang.UNIXProcess.forkAndExec(Native Method) at
java.lang.UNIXProcess.(UNIXProcess.java:187) at
java.lang.ProcessImpl.start(ProcessImpl.java:130) at
java.lang.ProcessBuilder.start(ProcessBuilder.java:1028) ... 3 more
I have observe that if the command is small like ls everything is fine but my above command is not executing while it is working fine from terminal.
Please help if I missing anything
"echo "+pass+" | "+"sudo -S Worked for me
My full code seems like below :-
String pass = "\"Yourpassword\"";
out.println("echo "+pass+" | "+"sudo -S scp -i "+Pemfilepath+" -r "+targetFolder+" "+"user#xx.xxx.xx.xxx:/var/www/html/projects/demoproject");
Hope it will help you :)

Process output from apache-commons exec

I am at my wits end here. I'm sure this is something simple and I most likely have huge holes in my understanding of java and streams. I think there are so many classes that I'm a bit overwhelmed with trying to poke through the API to figure out when and how I want to use the multitude of input/output streams.
I just learned about the existence of the apache commons library (self teaching java fail), and am currently trying to convert some of my Runtime.getRuntime().exec to use the commons - exec. Already it's fixed some of the once every 6 months this problem crops up then goes away style problems with exec.
The code executes a perl script, and displays the stdout from the script in the GUI as it is running.
The calling code is inside of a swingworker.
I'm getting lost how to use the pumpStreamHandler... anyway here is the old code:
String pl_cmd = "perl script.pl"
Process p_pl = Runtime.getRuntime().exec( pl_cmd );
BufferedReader br_pl = new BufferedReader( new InputStreamReader( p_pl.getInputStream() ) );
stdout = br_pl.readLine();
while ( stdout != null )
{
output.displayln( stdout );
stdout = br_pl.readLine();
}
I guess this is what I get for copy pasting code I don't fully understand a long time ago. The above I assume is executing the process, then grabs the outputstream (via "getInputStream"?), places it into a buffered reader, then will just loop there until the buffer is empty.
What I don't get is why there is no need for a 'waitfor' style command here? Isn't it possible that there will be some time in which the buffer will be empty, exit the loop, and continue on while the process is still going? When I run it, this doesn't seem to be the case.
In any event, I'm trying to get the same behavior using commons exec, basically again going from google found code:
DefaultExecuteResultHandler rh = new DefaultExecuteResultHandler();
ExecuteWatchdog wd = new ExecuteWatchdog( ExecuteWatchdog.INFINITE_TIMEOUT );
Executor exec = new DefaultExecutor();
ByteArrayOutputStream out = new ByteArrayOutputStream();
PumpStreamHandler psh = new PumpStreamHandler( out );
exec.setStreamHandler( psh );
exec.setWatchdog( wd );
exec.execute(cmd, rh );
rh.waitFor();
I'm trying to figure out what pumpstreamhandler is doing. I assume that this will take the output from the exec object, and fill the OutputStream I provide it with the bytes from the perl script's stdout/err?
If so how would you get the above behavior to have it stream the output line by line? In examples people show you call the out.toString() at the end, and I assume this would just give me a dump of all the output from the script once it is done running? How would you do it such that it would show the output as it is running line by line?
------------Future Edit ---------------------
Found this via google and works nice as well:
public static void main(String a[]) throws Exception
{
ByteArrayOutputStream stdout = new ByteArrayOutputStream();
PumpStreamHandler psh = new PumpStreamHandler(stdout);
CommandLine cl = CommandLine.parse("ls -al");
DefaultExecutor exec = new DefaultExecutor();
exec.setStreamHandler(psh);
exec.execute(cl);
System.out.println(stdout.toString());
}
Don't pass a ByteArrayOutputStream to the PumpStreamHandler, use an implementation of the abstract class org.apache.commons.exec.LogOutputStream. From the javadoc:
The implementation parses the incoming data to construct a line and passes the complete line to an user-defined implementation.
Thus the LogOutputStram is preprocessing the output to give you the control of handling individual lines instead of the raw bytes. Something like this:
import java.util.LinkedList;
import java.util.List;
import org.apache.commons.exec.LogOutputStream;
public class CollectingLogOutputStream extends LogOutputStream {
private final List<String> lines = new LinkedList<String>();
#Override protected void processLine(String line, int level) {
lines.add(line);
}
public List<String> getLines() {
return lines;
}
}
Then after the blocking call to exec.execute your getLines() will have the standard out and standard error you are looking for. The ExecutionResultHandler is optional from the perspective of just executing the process, and collecting all the stdOut/stdErr into a list of lines.
What I don't get is why there is no need for a 'waitfor' style command here? Isn't it possible that there will be some time in which the buffer will be empty, exit the loop, and continue on while the process is still going? When I run it, this doesn't seem to be the case.
readLine blocks. That is, your code will wait until a line has been read.
PumpStreamHandler
from Documentation
Copies standard output and error of subprocesses to standard output
and error of the parent process. If output or error stream are set to
null, any feedback from that stream will be lost.
Based on James A Wilson's answer I created the helper class "Execute". It wraps his answer
into a solution that also supplies the exitValue for convenience.
A single line is necessary to execute a command this way:
ExecResult result=Execute.execCmd(cmd,expectedExitCode);
The following Junit Testcase tests and shows how to use it:
Junit4 test case:
package com.bitplan.newsletter;
import static org.junit.Assert.*;
import java.util.List;
import org.junit.Test;
import com.bitplan.cmd.Execute;
import com.bitplan.cmd.Execute.ExecResult;
/**
* test case for the execute class
* #author wf
*
*/
public class TestExecute {
#Test
public void testExecute() throws Exception {
String cmd="/bin/ls";
ExecResult result = Execute.execCmd(cmd,0);
assertEquals(0,result.getExitCode());
List<String> lines = result.getLines();
assertTrue(lines.size()>0);
for (String line:lines) {
System.out.println(line);
}
}
}
Execute Java helper Class:
package com.bitplan.cmd;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.LogOutputStream;
import org.apache.commons.exec.PumpStreamHandler;
/**
* Execute helper using apache commons exed
*
* add this dependency to your pom.xml:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-exec</artifactId>
<version>1.2</version>
</dependency>
* #author wf
*
*/
public class Execute {
protected static java.util.logging.Logger LOGGER = java.util.logging.Logger
.getLogger("com.bitplan.cmd");
protected final static boolean debug=true;
/**
* LogOutputStream
* http://stackoverflow.com/questions/7340452/process-output-from
* -apache-commons-exec
*
* #author wf
*
*/
public static class ExecResult extends LogOutputStream {
private int exitCode;
/**
* #return the exitCode
*/
public int getExitCode() {
return exitCode;
}
/**
* #param exitCode the exitCode to set
*/
public void setExitCode(int exitCode) {
this.exitCode = exitCode;
}
private final List<String> lines = new LinkedList<String>();
#Override
protected void processLine(String line, int level) {
lines.add(line);
}
public List<String> getLines() {
return lines;
}
}
/**
* execute the given command
* #param cmd - the command
* #param exitValue - the expected exit Value
* #return the output as lines and exit Code
* #throws Exception
*/
public static ExecResult execCmd(String cmd, int exitValue) throws Exception {
if (debug)
LOGGER.log(Level.INFO,"running "+cmd);
CommandLine commandLine = CommandLine.parse(cmd);
DefaultExecutor executor = new DefaultExecutor();
executor.setExitValue(exitValue);
ExecResult result =new ExecResult();
executor.setStreamHandler(new PumpStreamHandler(result));
result.setExitCode(executor.execute(commandLine));
return result;
}
}
Its a very old thread but I had to use Apache Commons Exec and had to solve the same problem. I trust with last version of Apache Commons Exec published in 2014, below solution works well both with and without watchdog;
class CollectingLogOutputStream implements ExecuteStreamHandler {
private final List<String> lines = new LinkedList<String>();
public void setProcessInputStream(OutputStream outputStream) throws IOException
{
}
//important - read all output line by line to track errors
public void setProcessErrorStream(InputStream inputStream) throws IOException {
InputStreamReader isr = new InputStreamReader(inputStream);
BufferedReader br = new BufferedReader(isr);
String line="";
while( (line = br.readLine()) != null){
//use lines whereever you want - for now just print on console
System.out.println("error:"+line);
}
}
//important - read all output line by line to track process output
public void setProcessOutputStream(InputStream inputStream) throws IOException
{
InputStreamReader isr = new InputStreamReader(inputStream);
BufferedReader br = new BufferedReader(isr);
String line="";
while( (line = br.readLine()) != null){
//use lines whereever you want - for now just print on console
System.out.println("output:"+line);
}
}
public void start() throws IOException {
}
public void stop() throws IOException {
}
}
Above class can be set as StreamHandler for the executor as below;
//set newly created class stream handler for the executor
executor.setStreamHandler(new CollectingLogOutputStream());
Complete code is available here;
https://github.com/raohammad/externalprocessfromjava

Categories