I wrote the code below. To run a bat file from Java app, I use a process.exec(). But the bat may hang sometime, so I need to set a timeout for this process. I start a new thread and new a process in the thread, I set a timeout in the thread, and kill the thread when it is timeout. But I found that the process couldn't be destroyed when timeout happens. So I am confused about how to kill the porcess?
The code:
StreamGobbler:
import java.util.*;
import java.io.*;
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();
}
}
}
Main:
public class test
{
public static void main(String args[]) throws InterruptedException
{
Runnable r = new ShengThread();
Thread sheng = new Thread(r);
sheng.start();
sheng.join(1000);
if (sheng.isAlive()) {
sheng.interrupt();
}
if (sheng.isAlive()) {
System.out.println("It is dead.");
}
}
}
class ShengThread implements Runnable {
public void run() {
Process proc = null;
try
{
String osName = System.getProperty("os.name" );
String[] cmd = new String[3];
if( osName.equals( "Windows XP" ) )
{
cmd[0] = "cmd" ;
cmd[1] = "/C" ;
cmd[2] = "c:\\status.bat";
}
Runtime rt = Runtime.getRuntime();
System.out.println(osName+"Execing " + cmd[0] + " " + cmd[1]
+ " " + cmd[2]);
try {
proc = rt.exec(cmd);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 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 (InterruptedException t)
{
System.out.println("start\n");
proc.destroy();
t.printStackTrace();
}
}
}
The Process.destroy() method forcibly destroys an external process ... if this is possible. (In some situations you can't destroy processes, but that's only marginally relevant.)
Related
I have an android process that I start. It is a getevent command. This (when ran in a console) gives continuous event occurances. I wish to collect these within an Android app. My current way of doing this will effectively collect "one off" process outputs but I can't seem to find a way of storing the continuous results from the getevent method as they occur.
The current code I have for this is the following. It works for things like "ls" but not for continuous streams such as "getevent".
try {
Process chmod = Runtime.getRuntime().exec("getevent -lt /dev/input/event1");
BufferedReader reader = new BufferedReader(new InputStreamReader(chmod.getInputStream()));
int read;
char[] buffer = new char[4096];
StringBuffer output = new StringBuffer();
while ((read = reader.read(buffer)) > 0) {
output.append(buffer, 0, read);
}
reader.close();
chmod.waitFor();
String outputString = output.toString();
Log.d("output", outputString);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
I had the same issue. Here is how I solved it.
First I created a TimerTask to log/read continuous output in a BufferReader of either a logcat or getevent. TimerTask creates new Thread, which runs in background and doesn't interfere or block other UI handlers in service class. So I believe it is safer that way.
Then I started the TimerTask from Service class in onCreate() method.
One thing is mine device was rooted, so I start with 'su' session. I believe 'sh' session is also works for non root devices.
Service class:
public class ServiceClassPhone extends Service {
....
private GetEventRecorder getEventRecorder;
#Override
public void onCreate() {
....
getEventRecorder= new GetEventRecorder();
getEventRecorder.start();
// (if there is any) postDelay other UI handlers in service
}
And GetEventRecorder class:
public class GetEventRecorder {
....
private static Logger mLogger = new Logger();
private GetEventRecorder mRecorder = null;
Timer timer;
// active su session
private Process mProcess;
// Byte Writer
private OutputStream mStdIn;
private DataOutputStream outputStream;
private BufferedReader br;
{
try {
mProcess = Runtime.getRuntime().exec("su");
outputStream = new DataOutputStream(mProcess.getOutputStream());
String comm1 = "getevent -l";
String comm2 = "logcat -c";
String close = "^C";
String newLine= "\n";
outputStream.writeBytes(comm1);
outputStream.writeBytes(newLine);
} catch (IOException e) {
Log.d(TAG, "Could not spawn su process");
e.printStackTrace();
}
}
public void start() {
try {
timer = new Timer();
timer.scheduleAtFixedRate(new GetEventRecorder.RecorderTask(), 0, 1000);
} catch (Exception e) {
Log.d(TAG, "Exception: " + e);
}
}
public void stop() {
if (mRecorder != null) {
mRecorder.stop();
mRecorder = null;
timer.cancel();
}
}
public void logGetEventData(){
....
}
}
private class RecorderTask extends TimerTask {
public RecorderTask() {}
#Override
public void run() {
try {
String comm1 = "getevent -l";
String comm2 = "logcat -c";
String close = "^C";
byte[] newLine = "\n".getBytes();
outputStream.writeBytes(close);
outputStream.flush();
br = new BufferedReader(new InputStreamReader(mProcess.getInputStream()));
boolean cont=true;
String line;
Log.d(TAG,"geteventLogs BufferedReader for continuous getevent reading... " );
if (br!=null ) {
Log.d(TAG,"geteventLogs BufferedReader is not null checking for readiness... ");
if (br.ready()) {
Log.d(TAG, "BufferedReader for getevent is not null and ready");
String separator = System.getProperty("line.separator");
while ((line = br.readLine()) != null && line.contains("event0:")
&& !line.contains("BufferedReader") ) {
...
logGetEventData();
}catch (Exception e) {
if (DBG) Log.d(TAG, "getevent recorder error: " + e);
}
}
}
}
I am working at an application with 2 button Run and Commit.
When i press Run, my application run a script in sql plus. The idea is that i want to remain in sql plus and make commit when i press the Commit button. Also i want to see the output of every command. The things are working fine till i press commit. The output is not showing..
My Run button action:
package pachet1;
import java.util.Scanner;
import javafx.scene.control.TextArea;
import jcifs.smb.NtlmPasswordAuthentication;
import jcifs.smb.SmbFile;
import java.io.*;
import java.net.MalformedURLException;
import java.nio.file.Files;
public class MyExec {
public static ProcessBuilder builder;
public static Process p;
public static BufferedReader bri;
public static BufferedReader bre;
public static BufferedWriter p_stdin;
public static void executeScript(String scriptName, String alias, String path, TextArea txtArea) throws IOException
{
NtlmPasswordAuthentication userCred = new NtlmPasswordAuthentication("domain",
"username", "password");
SmbFile smbFile=new SmbFile("path" + scriptName, userCred);
File file = new File("D://" + scriptName);
try (InputStream in = smbFile.getInputStream()) {
Files.copy(smbFile.getInputStream(), file.toPath());
}
//init shell
builder = new ProcessBuilder( "cmd" );
builder.redirectErrorStream(true);
try {
p = builder.start();
}
catch (IOException e) {
//System.out.println(e);
}
//get stdin of shell
p_stdin =
new BufferedWriter(new OutputStreamWriter(p.getOutputStream()));
// execute the desired command (here: ls) n times
int n=1;
try {
//single execution
p_stdin.write("sqlplus sys/sys#" + alias +" as sysdba");
p_stdin.newLine();
p_stdin.flush();
p_stdin.write("#"+file.toPath());
p_stdin.newLine();
p_stdin.flush();
// p_stdin.write("commit;");
// p_stdin.newLine();
// p_stdin.flush();
}
catch (IOException e) {
//System.out.println(e);
}
// finally close the shell by execution exit command
try {
p_stdin.write("");
p_stdin.newLine();
p_stdin.flush();
}
catch (IOException e) {
System.out.println(e);
}
// write stdout of shell (=output of all commands)
String line;
bri = new BufferedReader
(new InputStreamReader(p.getInputStream()));
bre = new BufferedReader
(new InputStreamReader(p.getErrorStream()));
while ((line = bri.readLine()) != null) {
txtArea.appendText(line + "\n");
System.out.println(line + "\n");
}
//bri.close();
while ((line = bre.readLine()) != null) {
txtArea.appendText(line + "\n");
System.out.println(line + "\n");
}
//bre.close();
System.out.println("Done.");
}
}
My commit button action:
Thread t1 = new Thread(new Runnable() {
public void run() {
try {
MyExec.p_stdin.write("commit");
MyExec.p_stdin.newLine();
MyExec.p_stdin.flush();
MyExec.p_stdin.write("exit");
MyExec.p_stdin.newLine();
MyExec.p_stdin.flush();
//txtArea.appendText("Commit complete.");
String line;
MyExec.bri = new BufferedReader
(new InputStreamReader(MyExec.p.getInputStream()));
MyExec.bre = new BufferedReader
(new InputStreamReader(MyExec.p.getErrorStream()));
while ((line = MyExec.bri.readLine()) != null) {
txtArea.appendText(line + "\n");
System.out.println(line + "\n");
}
MyExec.bri.close();
while ((line = MyExec.bre.readLine()) != null) {
txtArea.appendText(line + "\n");
System.out.println(line + "\n");
}
MyExec.bri.close();
System.out.println("Done.");
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
});
public void commit(ActionEvent e)
{
t1.start();
}
So i need in that thread to get the process output from run button and continues to display it, but it's not working..
The following batch script that works fine if I run it in a batch file (.bat). It gets all the files on the SFTP server and downloads to the local folder.
However, if I run this from the Java code, it gets only 4 files. Why is that it stops after it gets the 4 files?
Batch script:
winscp.com /log=C:\winscp\logs\winscp_detailed.log /xmllog=C:\winscp\logs\winscp.log /command ^
"open sftp://root:password#xx.xxx.xx.xx/ -privatekey=""C:\winscp\key.ppk"" -hostkey=""ssh-rsa 2048 xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx"" -passphrase=""password""" ^
"get /home/SFTP/* C:\LocalFolder1\" ^
"exit"
Java Code that executes the above command:
public HashMap runExternalProgram_Windows(String Command) {
String line;
InputStream stderr = null;
InputStream stdout = null;
HashMap _m = new HashMap();
try {
Process process = Runtime.getRuntime ().exec (Command);
Worker worker = new Worker(process);
worker.start();
try {
worker.join(30000);
if (worker.exit == null) {
throw new Exception();
}
} catch(Exception ex) {
_m.put("LOG_ERROR_EXTERNAL", "30 second time out...check connection");
ex.printStackTrace();
return _m;
} finally {
process.destroy();
}
stderr = process.getErrorStream ();
stdout = process.getInputStream ();
String _log_output = null;
// clean up if any output in stdout
BufferedReader brCleanUp = new BufferedReader (new InputStreamReader (stdout));
while ((line = brCleanUp.readLine ()) != null) {
if (_log_output==null) {
_log_output = line + "\n";
} else {
_log_output = _log_output + line + "\n";
}
}
brCleanUp.close();
_m.put("LOG_OUTPUT", _log_output);
String _log_error = null;
// clean up if any output in stderr
brCleanUp=new BufferedReader (new InputStreamReader (stderr));
while ((line = brCleanUp.readLine ()) != null) {
if (_log_error==null) {
_log_error = line + "\n";
} else {
_log_error = _log_error + line + "\n";
}
}
brCleanUp.close();
_m.put("LOG_ERROR_EXTERNAL", _log_error);
} catch (Exception e) {
//e.printStackTrace();
}
return _m;
}
private static class Worker extends Thread {
private final Process process;
private Integer exit;
private Worker(Process process) {
this.process = process;
}
public void run() {
try {
exit = new Integer(process.waitFor());
} catch (InterruptedException ignore) {
return;
}
}
}
Here is my sample code, I want to handle the command from standard input while running a new sub process. However, the exec method never returns if I read the system.in. The command in the exec() is very simple and has nothing to do with the stdin.
I'm wondering about is there any way to solve this? How can I start a new sub process while start another thread reading stdin?
public static void main(String[] args){
new Thread(new Runnable(){
public void run(){
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String command = null;
try{
while((command = reader.readLine()) != null){
System.out.println("Command Received:" + command);
}
}catch(Exception ex){
ex.printStackTrace();
//failed to listening command
}
}
}).start();
Process process = null;
try {
process = Runtime.getRuntime().exec("java -cp C:/agenttest Test");
System.out.println("never returns");
process.waitFor();
} catch (IOException e) {
throw new RuntimeException( e );
} catch (InterruptedException e) {
throw new RuntimeException( e );
}
}
The Test class is very simple, here is the Test.java
public static void main(String[] args){
System.out.println("Standard out");
System.out.println("Standard out");
System.err.println("Standard err");
System.out.println("Standard out");
try{
Thread.sleep(10000);
}catch(InterruptedException ex){}
}
The problem could be that you're not handling the error stream and input stream and are overrunning the platform's buffers. Try handling that output as per the famous article, When Runtime.exec() won't.
For example:
import java.io.*;
public class TestMain {
private static final String JAVA_CMD = "java";
private static final String CP = "-cp";
// *** your CLASS_PATH and PROG Strings will of course be different ***
private static final String CLASS_PATH = "C:/Users/hovercraft/Documents/workspace/Yr 2012A/bin";
private static final String PROG = "yr12.m07.b.Test2";
private static final String[] CMD_ARRAY = { JAVA_CMD, CP, CLASS_PATH, PROG };
public static void main(String[] args) {
new Thread(new Runnable() {
public void run() {
BufferedReader reader = new BufferedReader(new InputStreamReader(
System.in));
String command = null;
try {
while ((command = reader.readLine()) != null) {
System.out.println("Command Received:" + command);
}
} catch (Exception ex) {
ex.printStackTrace();
// failed to listening command
}
}
}).start();
Process process = null;
try {
ProcessBuilder processBuilder = new ProcessBuilder(CMD_ARRAY);
process = processBuilder.start();
InputStream inputStream = process.getInputStream();
setUpStreamGobbler(inputStream, System.out);
InputStream errorStream = process.getErrorStream();
setUpStreamGobbler(errorStream, System.err);
System.out.println("never returns");
process.waitFor();
} catch (IOException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public static void setUpStreamGobbler(final InputStream is, final PrintStream ps) {
final InputStreamReader streamReader = new InputStreamReader(is);
new Thread(new Runnable() {
public void run() {
BufferedReader br = new BufferedReader(streamReader);
String line = null;
try {
while ((line = br.readLine()) != null) {
ps.println("process stream: " + line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
You should keep reading the input stream, otherwise it will get blocked. It has nothing to do with JVM but the underyling operating system.
I have the following code and I can't figure out why it won't work:
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
final String p1 = "HELLO WORLD";
process(p1, bos);
Assert.assertEquals("BOS value should be: "+p1, p1, bos.toString("UTF-8"));
It prints:
junit.framework.ComparisonFailure: BOS value should be: HELLO WORLD expected:<[HELLO WORLD]> but was:<[]>
at junit.framework.Assert.assertEquals(Assert.java:81) etc...
and process looks like this:
public static void process(final String p1, final OutputStream os) {
final Runtime rt = Runtime.getRuntime();
try {
final String command = "echo " + p1;
log.info("Executing Command: " + command);
final Process proc = rt.exec(command);
// gobble error and output
StreamGobbler.go(proc.getErrorStream(), null);
StreamGobbler.go(proc.getInputStream(), os);
// wait for the exit
try {
final int exitVal = proc.waitFor();
log.info("Command Exit Code: " + exitVal);
} catch (InterruptedException e) {
log.error("Interrupted while waiting for command to execute", e);
}
} catch (IOException e) {
log.error("IO Exception while executing command", e);
}
}
private static class StreamGobbler extends Thread {
private final InputStream is;
private final OutputStream os;
private static StreamGobbler go(InputStream is, OutputStream os) {
final StreamGobbler gob = new StreamGobbler(is, os);
gob.start();
return gob;
}
private StreamGobbler(InputStream is, OutputStream os) {
this.is = is;
this.os = os;
}
public void run() {
try {
final PrintWriter pw = ((os == null) ? null : new PrintWriter(os));
final InputStreamReader isr = new InputStreamReader(is);
final BufferedReader br = new BufferedReader(isr);
String line = null;
while ((line = br.readLine()) != null) {
if (pw != null) {
pw.println(line);
}
log.info(line); // Prints HELLO WORLD to log
}
if (pw != null) {
pw.flush();
}
} catch (IOException ioe) {
log.error("IO error while globbing", ioe);
}
}
When I run the jUnit test I get an empty string as the actual. I don't understand why this wont work.
EDIT: I am using RHEL5 and eclipse 3.6 if that makes a difference at all.
maybe you should wait on the thread filling the stream:
Thread thr = StreamGobbler.go(proc.getInputStream(), os);
// wait for the exit
try {
final int exitVal = proc.waitFor();
log.info("Command Exit Code: " + exitVal);
thr.join();//waits for the gobbler that processes the stdout of the process
} catch (InterruptedException e) {
log.error("Interrupted while waiting for command to execute", e);
}