Getting output from ssh with BufferedReader - java

I'm using the following script to execute commands and get output:
Runtime rt = Runtime.getRuntime();
static Runtime rt;
static Process proc;
public static void main(String[] Args) throws IOException, InterruptedException {
rt = Runtime.getRuntime();
String[] commands = {"ssh.exe","-o BatchMode=yes", "root#192.168.1.1", "cat /var/a.txt"};
// String[] commands = {"ssh.exe", "root#192.168.0.1"};
// String[] commands = { "ls", "-la" };
proc = rt.exec(commands);
new Thread("out") {
public void run() {
System.out.println("Thread: " + getName() + " running");
BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream()));
String s = null;
// read the output from the command
System.out.println("StdOut:\n");
try {
while ((s = stdInput.readLine()) != null) {
System.out.println("$ " + s);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}.start();
new Thread("err") {
public void run() {
System.out.println("Thread: " + getName() + " running");
BufferedReader stdError = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
String s = null;
// read any errors from the attempted command
System.out.println("StdErr:\n");
try {
while ((s = stdError.readLine()) != null) {
System.out.println("! " + s);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}.start();
Thread.sleep(10000);
System.out.println("end");
}
If I execute "ls -la" or "ssh" I get the expected output. However, attempting to get a.txt content from remote device (line 1) fails and the operation hangs.
Executing "ssh root#192.168.0.1 cat a.txt" from command line works and retrieves the content.
Why is this happening? How can it be fixed?
Thanks

Because you need to read the two streams in separate threads, or merge them. You can't assume that the process will exit and therefore close its stderr before writing anything to stdtout, or rather without writing so little to stdout that it won't block.

Related

Spring Boot - Should I implement multi-threading to resolve this problem?

I've been working on some web project and one of its requests execute command line using Java Process.
This is the method.
#ResponseBody
#RequestMapping(value = "/startTest", method = RequestMethod.POST)
public String startTest(int test_id) {
...
String cmd = "..."
ProcessUtil pu = new ProcessUtil();
try {
pu.execute(cmd);
File file = new File(System.getProperty("user.dir")
+ "\\datas\\cypress\\videos\\examples\\main.spec.js.mp4");
File fileToMove = new File(
".\\\\datas\\\\results\\" + uitest.getTest_filename() + ".mp4");
file.renameTo(fileToMove);
return "success";
} catch (Exception e) {
return "fail";
}
}
And Here is the ProcessUtil.java
public class ProcessUtil {
public static void execute(String cmd) {
Process process = null;
Runtime runtime = Runtime.getRuntime();
StringBuffer successOutput = new StringBuffer();
StringBuffer errorOutput = new StringBuffer();
BufferedReader successBufferReader = null;
BufferedReader errorBufferReader = null;
String msg = null;
List<String> cmdList = new ArrayList<String>();
if (System.getProperty("os.name").indexOf("Windows") > -1) {
cmdList.add("cmd");
cmdList.add("/c");
} else {
cmdList.add("/bin/sh");
cmdList.add("-c");
}
cmdList.add(cmd);
String[] array = cmdList.toArray(new String[cmdList.size()]);
try {
process = runtime.exec(array);
successBufferReader = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8"));
while ((msg = successBufferReader.readLine()) != null) {
successOutput.append(msg + System.getProperty("line.separator"));
}
errorBufferReader = new BufferedReader(new InputStreamReader(process.getErrorStream(), "UTF-8"));
while ((msg = errorBufferReader.readLine()) != null) {
errorOutput.append(msg + System.getProperty("line.separator"));
}
process.waitFor();
if (process.exitValue() == 0) {
System.out.println("success!");
System.out.println(successOutput.toString());
} else {
System.out.println("fail...");
System.out.println(successOutput.toString());
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
try {
process.destroy();
if (successBufferReader != null)
successBufferReader.close();
if (errorBufferReader != null)
errorBufferReader.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
The problem that I'm facing is when I open two Windows command windows and execute simultaneously it seemingly works fine. However, when I run that 'startTest' method by requesting to my server simultaneously, it pushes my CPU and RAM to nearly 100% and results seemed odd. I don't know much about multi-threading, but I guess I should execute the command via multiple windows(I think my commands were executed in the same environment and they crashed each other...). Please give me some advice to resolve this problem... Thank you in advance.

Executing linux mutt from Java Runtime.getRuntime not sending mail and not giving error

I try to send email using mutt in linux and java
if I execute the mutt command from linux command line the email send great
echo "test" | mutt -s "subject" -- "jojo#foo.com
now I have this simple java app that I try to execute the same command and I get nothing, not even error:
java -cp runtime-SNAPSHOT.jar MyApp "echo \"test\" | mutt -s \"subject\" \"jojo#foo.com\""
class StreamGobbler extends Thread
{
InputStream is;
String type;
StreamGobbler(InputStream is, String type)
{
this.is = is;
this.type = type;
}
public void run()
{
try
{
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line=null;
while ( (line = br.readLine()) != null)
System.out.println(type + ">" + line);
} catch (IOException ioe)
{
ioe.printStackTrace();
}
}
}
public class MyApp {
public static void main(String[] args) throws InterruptedException, IOException {
if (args.length < 1)
{
System.out.println("USAGE: java GoodWindowsExec <cmd>");
System.exit(1);
}
try
{
String[] cmd = new String[3];
Runtime rt = Runtime.getRuntime();
System.out.println("Execing " + args[0] );
Process proc = rt.exec(args[0]);
// any error message?
StreamGobbler errorGobbler = new
StreamGobbler(proc.getErrorStream(), "ERROR");
// any output?
StreamGobbler outputGobbler = new
StreamGobbler(proc.getInputStream(), "OUTPUT");
// kick them off
errorGobbler.start();
outputGobbler.start();
// any error???
int exitVal = proc.waitFor();
System.out.println("ExitValue: " + exitVal);
} catch (Throwable t)
{
t.printStackTrace();
}
}
what is wrong here?
You get no error as echo seems to be available on your system(usually as "/bin/echo") . The Stringtokenizer in the Runtime exec method passes the rest of your line as parameters to /bin/echo like this:
/bin/echo "\"test\"" "|" "mutt" "-s" "\"subject\"" "--" "\"jojo#foo.com\""
Well this is a valid comand as it calls /bin/echo and /bin/echo outputs all the parameters but never calls mutt. (btw. /bin/echo is a different echo than the one used in a Bash shell which is a builtin and behaves a little different...)
That they(Java) tokenize the command in the exec method may be convenient sometimes but leads to quite irritating effects like this because it makes one assume that something should work, that actually doesn't as in this case...
What you probably want is a shell executing your command line. So you have to actually execute a shell(I marked the change in the file):
public class MyApp {
static class StreamGobbler extends Thread {
InputStream is;
String type;
StreamGobbler(InputStream is, String type) {
this.is = is;
this.type = type;
}
public void run() {
try {
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line = null;
while ((line = br.readLine()) != null) {
System.out.println(type + ">" + line);
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException, IOException {
/*if (args.length < 1) {
System.out.println("USAGE: java GoodWindowsExec <cmd>");
System.exit(1);
}*/
args = new String[]{"echo \"test\" | grep -i \"s\" " };
try {
String[] cmd = new String[3];
Runtime rt = Runtime.getRuntime();
System.out.println("Execing " + args[0]);
//Change here: execute a shell with the command line instead of echo:
Process proc = rt.exec(new String[]{"/bin/sh","-c", args[0]});
// any error message?
StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream(), "ERROR");
// any output?
StreamGobbler outputGobbler = new StreamGobbler(proc.getInputStream(), "OUTPUT");
// kick them off
errorGobbler.start();
outputGobbler.start();
// any error???
int exitVal = proc.waitFor();
System.out.println("ExitValue: " + exitVal);
} catch (Throwable t) {
t.printStackTrace();
}
}
}
Sidenote. For a better minimal testcase:
I replaced your mutt command with some grep as I don't wan't to send mails ;)
I faked the java command line by creating the array("args") programatically.
made your StreamGobbler static in order to have it one file.
All that shouldn't change your testcase. What does make a difference is the rt.exec call that executes a shell instead of /bin/echo
example run:
Execing echo "test" | grep -i "s"
ExitValue: 0
OUTPUT>test

Executing 'adb logcat' command using Runtime class

I was trying to get the logcat content into a JTextPane. I used following code hoping it will return the content as String but it freeze and also, doesn't produce an error.
Process exec = null;
try {
exec = Runtime.getRuntime().exec("adb logcat -d");
InputStream errorStream = exec.getErrorStream();
BufferedReader ebr = new BufferedReader(new InputStreamReader(errorStream));
String errorLine;
while ((errorLine = ebr.readLine()) != null) {
System.out.println("[ERROR] :- " + errorLine);
}
if (exec.waitFor() == 0) {
InputStream infoStream = exec.getInputStream();
InputStreamReader isr = new InputStreamReader(infoStream);
BufferedReader ibr = new BufferedReader(isr);
String infoLine;
while ((infoLine = ibr.readLine()) != null) {
System.out.println("[INFO] :- " + infoLine);
}
}
} catch (IOException | InterruptedException ex) {
ex.printStackTrace();
} finally {
if (exec != null) {
exec.destroy();
}
}
I referred to some tutorials but, they were not filling my problem. Is this wrong? Are there any other methods to get the logcat content as a String programmatically? Sorry if this is a dumb question.
The issue you're seeing is that you're trying to process command streams and wait for the executing process, all in the same thread. It's blocking because the process reading the streams is waiting on the process and you're losing the stream input.
What you'll want to do is implement the function that reads/processes the command output (input stream) in another thread and kick off that thread when you start the process.
Second, you'll probably want to use ProcessBuilder rather than Runtime.exec.
Something like this can be adapted to do what you want:
public class Test {
public static void main(String[] args) throws Exception {
String startDir = System.getProperty("user.dir"); // start in current dir (change if needed)
ProcessBuilder pb = new ProcessBuilder("adb","logcat","-d");
pb.directory(new File(startDir)); // start directory
pb.redirectErrorStream(true); // redirect the error stream to stdout
Process p = pb.start(); // start the process
// start a new thread to handle the stream input
new Thread(new ProcessTestRunnable(p)).start();
p.waitFor(); // wait if needed
}
// mimics stream gobbler, but allows user to process the result
static class ProcessTestRunnable implements Runnable {
Process p;
BufferedReader br;
ProcessTestRunnable(Process p) {
this.p = p;
}
public void run() {
try {
InputStreamReader isr = new InputStreamReader(p.getInputStream());
br = new BufferedReader(isr);
String line = null;
while ((line = br.readLine()) != null)
{
// do something with the output here...
}
}
catch (IOException ex) {
ex.printStackTrace();
}
}
}
}

Java - How to send a value to child process using outputstream?

I relaunch the child process every time than i need to obtain a random number, but i think that it can be done using outPutStream but i don't know how. I saw the others post but i don't found nothing relevant for my problem. I don't want to use sockets.
Main Process.
public class ejercicio7 {
public static void main(String [] args){
boolean cerrado = false;
try {
while(!cerrado){
String entrada = JOptionPane.showInputDialog(null, "Introduce una entrada");
Process proceso = Runtime.getRuntime().exec("java -jar C:\\Users\\Cristian\\Desktop\\java\\numeros.jar \"" + entrada+"\"");
BufferedReader br = new BufferedReader(new InputStreamReader(proceso.getInputStream()));
String texto;
while((texto = br.readLine()) !=null){
if(Boolean.parseBoolean(texto)){
cerrado = true;
}else{
System.out.println(texto);
}
}
br.close();
proceso.destroy();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Child Process.
public static void main(String[] args) {
if(args.length <=0 && args[0].length() == 0){
System.out.println("No se ha introducido una entrada");
}else{
if(!args[0].equalsIgnoreCase("fin")){
System.out.println(Math.round(Math.random() * 10));
}else{
System.out.println("true");
}
}
}
}
Make your child process handling input stream:
public static void main(String[] args) throws Exception {
String line;
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in, "UTF-8"));
while (!"fin".equals(line = reader.readLine())) {
System.out.println(line);
}
System.out.println("buy");
}
Then in main process you can send messages to child process:
Process proceso = Runtime.getRuntime().exec("java -jar C:\\Users\\Cristian\\Desktop\\java\\numeros.jar");
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(proceso.getOutputStream()));
BufferedReader in = new BufferedReader(new InputStreamReader(proceso.getInputStream()));
String texto;
out.write(entrada + "\n");
out.flush();
while((texto = in.readLine()) !=null){
...

ProcessBuilder process not running

I'm fairly new to ProcessBuilder and working with threads. In it's current state I have a J-Button which starts a scheduled executor service. The scheduled executor service is used to delegate a process to one of two process builders. The application is meant to record a user conversation. During the conversation, after x minutes it creates a wav and delegates it to an available process for transcription. The problem begins when the transcription class is called. The process is started and the application runs as expected. However, the transcription process doesn't actually do anything until I exit the parent application. Only then it will begin. Checking the task manager it shows as a process but uses 0.0% of the CPU and around 238MB of memory until I exit then the two processes jump to 30%-40% and 500-1000 MB of memory. Also, I am using the .waitFor() but am using a thread to run the .waitFor() process as from what I gather it causes the application to hang. How would I go about fixing this. Sorry I am unable to provide more details but I'm new to this. Thanks in advance!
public class TranDelegator {
Future<?> futureTranOne = null;
Future<?> futureTranTwo = null;
ExecutorService transcriberOne = Executors.newFixedThreadPool(1);
ExecutorService transcriberTwo = Executors.newFixedThreadPool(1);
final Runnable transcribeChecker = new Runnable() {
public void run() {
String currentWav = null;
File inputFile = new File("C:\\convoLists/unTranscribed.txt");
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(inputFile));
} catch (FileNotFoundException e1) {
System.out.println("reader didn't initialize");
e1.printStackTrace();
}
try {
currentWav = reader.readLine();
} catch (IOException e) {
System.out.println("currentWav string issue");
e.printStackTrace();
}
try {
reader.close();
} catch (IOException e) {
System.out.println("reader couldn't close");
e.printStackTrace();
}
if(currentWav != null){
if (futureTranOne == null || futureTranOne.isDone()) {
futureTranOne = transcriberOne.submit((transcriptorOne));
}
else if (futureTranTwo == null || futureTranTwo.isDone()) {
futureTranTwo = transcriberTwo.submit((transcriptorTwo));
}
}
}
};
final Runnable transcriptorOne = new Runnable() {
public void run() {
System.out.println("ONE");
try {
String classpath = System.getProperty("java.class.path");
String path = "C:/Program Files/Java/jre7/bin/java.exe";
ProcessBuilder processBuilder = new ProcessBuilder(path, "-cp",
classpath, Transcriber.class.getName());
Process process = processBuilder.start();
try {
process.waitFor();
} catch (InterruptedException e) {
System.out.println("process.waitFor call failed");
e.printStackTrace();
}
} catch (IOException e) {
System.out.println("Unable to call transcribeConvo");
e.printStackTrace();
}
}
};
final Runnable transcriptorTwo = new Runnable() {
public void run() {
System.out.println("TWO");
try {
String classpath = System.getProperty("java.class.path");
String path = "C:/Program Files/Java/jre7/bin/java.exe";
ProcessBuilder processBuilder = new ProcessBuilder(path, "-cp",
classpath, Transcriber.class.getName());
Process process = processBuilder.start();
try {
process.waitFor();
} catch (InterruptedException e) {
System.out.println("process.waitFor call failed");
e.printStackTrace();
}
} catch (IOException e) {
System.out.println("Unable to call transcribeConvo");
e.printStackTrace();
}
}
};
}
public class Transcriber {
public static void main(String[] args) throws IOException,
UnsupportedAudioFileException {
retreiveEmpInfo();
TextoArray saveConvo = new TextoArray();
ArrayList<String> entireConvo = new ArrayList();
URL audioURL;
String currentWav = wavFinder();
ConfigReader configuration = new ConfigReader();
ArrayList<String> serverInfo = configuration
.readFromDoc("serverconfig");
while (currentWav != null) {
audioURL = new URL("file:///" + currentWav);
URL configURL = Transcriber.class.getResource("config.xml");
ConfigurationManager cm = new ConfigurationManager(configURL);
Recognizer recognizer = (Recognizer) cm.lookup("recognizer");
recognizer.allocate(); // allocate the resource necessary for the
// recognizer
System.out.println(configURL);
// configure the audio input for the recognizer
AudioFileDataSource dataSource = (AudioFileDataSource) cm
.lookup("audioFileDataSource");
dataSource.setAudioFile(audioURL, null);
// Loop until last utterance in the audio file has been decoded, in
// which case the recognizer will return null.
Result result;
while ((result = recognizer.recognize()) != null) {
String resultText = result.getBestResultNoFiller();
// System.out.println(result.toString());
Collections.addAll(entireConvo, resultText.split(" "));
}
new File(currentWav).delete();
saveConvo.Indexbuilder(serverInfo, entireConvo);
entireConvo.clear();
currentWav = wavFinder();
}
System.exit(0);
}
private static String wavFinder() throws IOException {
String currentWav = null;
int x = 1;
File inputFile = new File("C:\\convoLists/unTranscribed.txt");
File tempFile = new File("C:\\convoLists/unTranscribedtemp.txt");
BufferedReader reader = new BufferedReader(new FileReader(inputFile));
BufferedWriter writer = new BufferedWriter(new FileWriter(tempFile));
String currentLine = null;
String newLine = System.getProperty("line.separator");
while ((currentLine = reader.readLine()) != null) {
if (x == 1) {
currentWav = currentLine;
} else {
writer.write(currentLine);
writer.write(newLine);
}
x = 2;
}
reader.close();
writer.flush();
writer.close();
inputFile.delete();
// boolean successful =
tempFile.renameTo(inputFile);
// System.out.println("Success: " + successful);
// System.out.println("currentWav = " + currentWav);
return currentWav;
}
private static void retreiveEmpInfo() throws IOException {
File tempFile = new File("C:\\convoLists/tmp.txt");
BufferedReader reader = new BufferedReader(new FileReader(tempFile));
CurrentEmployeeInfo.setName(reader.readLine());
CurrentEmployeeInfo.setUserEmail(reader.readLine());
CurrentEmployeeInfo.setManagerEmail(reader.readLine());
reader.close();
}
}
This problem may be related to sub-process's input stream buffers.
You should clear the sub-process's input stream buffers.
These stream buffers got increased within the parent process's memory with time and at some moment your sub-process will stop responding.
There are few options to make sub-process work normally
Read continuously from sub-process's input streams
Redirect sub-process's input streams
Close sub-process's input streams
Closing sub-process's input streams
ProcessBuilder processBuilder = new ProcessBuilder(command);
Process process = processBuilder.start();
InputStream inStream = process.getInputStream();
InputStream errStream = process.getErrorStream();
try {
inStream.close();
errStream.close();
} catch (IOException e1) {
}
process.waitFor();
Reading sub-process's input streams
ProcessBuilder processBuilder = new ProcessBuilder(command);
Process process = processBuilder.start();
InputStreamReader tempReader = new InputStreamReader(new BufferedInputStream(p.getInputStream()));
final BufferedReader reader = new BufferedReader(tempReader);
InputStreamReader tempErrReader = new InputStreamReader(new BufferedInputStream(p.getErrorStream()));
final BufferedReader errReader = new BufferedReader(tempErrReader);
try {
while ((line = reader.readLine()) != null) {
}
} catch (IOException e) {
}
try {
while ((line = errReader.readLine()) != null) {
}
} catch (IOException e) {
}
process.waitFor();
Redirecting sub-process's input streams
ProcessBuilder processBuilder = new ProcessBuilder(command);
processBuilder.redirectInput();
processBuilder.redirectError();
Process process = processBuilder.start();
process.waitFor();
(from comments)
Looks like process hang is due to out/error streams becoming full. You need to consume these streams; possibly via a thread.
Java7 provides another way to redirect output.
Related : http://alvinalexander.com/java/java-exec-processbuilder-process-3

Categories

Resources