reading output of a child process Interruptibly - java

I want to launch a child proces and read its output until EOF or until an internal flag is cleared.
My first attempt was to call InputStream.close() in another thread, but although it works for sockets, it doesn't work with the result of Process.getInputStream(): the main thread is still waiting in read() and the killer thread either hangs in close0() (windows) or continues with no effect (linux).
Then I tried to check InputStream.available(), but it doesn't detect EOF: it returns 0.
public class BbbTest extends TestCase {
private Process proc;
private InputStream getStream() throws IOException {
//if ("".length() == 0) return new java.net.Socket("intra", 80).getInputStream();
String[] cmd;
if (System.getProperty("os.name").toLowerCase().startsWith("windows")) {
cmd = new String[] { "cmd", "/c", "more & more" };
} else {
cmd = new String[] { "sh", "-c", "cat; cat" };
}
Process proc = Runtime.getRuntime().exec(cmd);
return proc.getInputStream();
}
public void testB() throws Exception {
final InputStream in = getStream();
final Thread readingThread = Thread.currentThread();
Thread t = new Thread("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx") {
#Override
public void run() {
try {
sleep(1000);
if (proc != null) proc.destroy(); // this won't help
readingThread.interrupt(); // this won't help either
in.close(); // this will do nothing and may even hang
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException ( );
}
}
};
t.setDaemon(true);
t.start();
try {
in.read();
assertTrue(false);
} catch (IOException e) {
// nothing
}
}
}
My last hope is to steal the channel from the returned stream and use nio

In windows , this Command will not terminate.
cmd = new String[] { "cmd", "/c", "more & more" };
Instead of try this simple command
cmd = new String[] { "cmd", "/c", "dir" };
And if you want to read your stream
public static void slurp(final InputStream is, final int bufferSize)
{
try(final Reader in = new InputStreamReader(is, "UTF-8");
BufferedReader br = new BufferedReader(in);){
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
Call this method by passing stream like this.
final BbbTest bbTest = new BbbTest();
Thread t = new Thread("xsdfds") {
#Override
public void run() {
try {
slurp(bbTest.getStream());
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException ( );
}
}
};
t.start();
If you want to kill the process from child thread.
Create setter and getter method for Process in BbbTest like this
public class BbbTest {
private Process proc;
/**
* #return the proc
*/
public Process getProc() {
return proc;
}
/**
* #param proc the proc to set
*/
public void setProc(Process proc) {
this.proc = proc;
}
private InputStream getStream() throws IOException {
String[] cmd;
if (System.getProperty("os.name").toLowerCase().startsWith("windows")) {
// cmd = new String[] { "cmd", "/c", "more & more" };
cmd = new String[] { "cmd", "/c", "dir" };
} else {
cmd = new String[] { "sh", "-c", "cat; cat" };
}
proc = Runtime.getRuntime().exec(cmd);
return proc.getInputStream();
}
}
Now using process you can destroy
bbTest.getProc().destroy();
If you want to kill the process based on output of other stream, you can add logic in slurp method by checking line content and terminate using destroy
UPDATE
Simple Demo
public class BbbTest {
private Process startProcess(String[] commands) throws IOException {
Process proc = Runtime.getRuntime().exec(commands);
return proc;
}
public static void slurp(final InputStream is)
{
try(final Reader in = new InputStreamReader(is, "UTF-8");
BufferedReader br = new BufferedReader(in);){
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
if (line.equals("end")){
break;
}
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String args[]) throws Exception {
final BbbTest bbTest = new BbbTest();
final Process process = bbTest.startProcess(new String[] { "cmd", "/c", "more" });
Thread t = new Thread("xsdfds") {
#Override
public void run() {
try {
slurp(process.getInputStream());
process.destroy();
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException ( );
}
}
};
t.start();
try(OutputStream out = process.getOutputStream();
BufferedOutputStream outStream = new BufferedOutputStream(out);){
outStream.write("Test".getBytes());
outStream.write("\n".getBytes());
outStream.write("end".getBytes());
outStream.write("\n".getBytes());
outStream.flush();
}
}
}
Look at this sample : You should have some condition to terminate the child thread ( reading prcoess) like if the stream writes "end" / or End of EOF ( process closed inputstream )

Related

how to run c executable file from java from linux-bash by " jna" (not from windows-cmd)

for linux-text, I used to make such an effort:
ProcessBuilder p = new ProcessBuilder("/path/to/file/c/executable");
p.start();
but I had trouble input and output.
This was another suggestion:
Kernel32.java:
public interface Kernel32 extends StdCallLibrary {
----
};
public Kernel32 INSTANCE = (Kernel32) Native.loadLibrary("Kernel32", Kernel32.class, WIN32API_OPTIONS);
----
}
RunTest.java
public class RunTest {
---
static HANDLE inputFile = null;
static void createChildProcess(String cmd){
String szCmdline = cmd;
PROCESS_INFORMATION processInformation = new PROCESS_INFORMATION();
STARTUPINFO startupInfo = new STARTUPINFO();
--
// Create the child process.
if (!Kernel32.INSTANCE.CreateProcess(
--
System.err.println(Kernel32.INSTANCE.GetLastError());
}
else {
com.sun.jna.platform.win32.Kernel32.INSTANCE.WaitForSingleObject(processInformation.hProcess, 0xFFFFFFFF);
---
}
}
static void WriteToPipe()
// Stop when there is no more data.
{
IntByReference dwRead = new IntByReference();
--
for (;;)
{
--
}
That was too long to test as same as jni solution, is there a simpler instruction?
You don't need JNA or JNI (except as implemented by the JDK) to execute a command. It's a simple as using Runtime.exec() and capturing the output.
I've implemented this in a class here. Bits you need are simplified below.
public static List<String> runNative(String[] cmdToRunWithArgs, String[] envp) {
Process p = null;
try {
p = Runtime.getRuntime().exec(cmdToRunWithArgs, envp);
return getProcessOutput(p);
} catch (SecurityException | IOException e) {
// handle exception
} finally {
// Ensure all resources are released
if (p != null) {
p.destroy(); // also closes streams except...
// on Windows and Solaris must close streams separately
}
}
return Collections.emptyList(); // or throw exception
}
private static List<String> getProcessOutput(Process p) {
ArrayList<String> sa = new ArrayList<>();
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(p.getInputStream(), Charset.defaultCharset()))) {
String line;
while ((line = reader.readLine()) != null) {
sa.add(line);
}
p.waitFor();
} catch (IOException e) {
// handle exception
} catch (InterruptedException ie) {
// handle exception
Thread.currentThread().interrupt();
}
return sa;
}
Then just call
String[] envp = new String[] { "LC_ALL=C" };
String[] cmd = new String[] { "/path/to/cmd", "arg1", "arg2" };
List<String> output = runNative(cmd, envp);

How do I read from BufferedReader real-time from a process output in Java?

I am trying to read real-time the data from a process using a BufferedReader and redirect it to a TextArea. However, I have noticed that when the process is running the .bat, it tends to freeze and cause a lag to the JavaFX TextArea. The ".bat" fiel that runs prints out a ..... one one line to indicate progress, and I believe this is where it is failing at.
I had an idea to have the program wait a certain amount of time, then it executes, but because its all on one line it also fails. Please help
Code:
while(iterator.hasNext()) {
Map.Entry mentry = (Map.Entry)iterator.next();
String taskPath = " /k d: && cd DATA\\Virtualization\\Users && ESXRun.bat";
ProcessBuilder pb = new ProcessBuilder("cmd",taskPath);
Process process = pb.start();
BufferedReader stdInput = new BufferedReader(new InputStreamReader(process.getInputStream()));
String s = "";
// read the output from the command
while ((s = stdInput.readLine()) != null) {
//TextArea
cliLog.appendText(s);
cliLog.appendText("\n");
}
process.waitFor();
process.destroy();
}
This is just the concept to demonstrate the issue.
You have to customize it and handle exceptions.
public class TextAreaBash extends Application implements Runnable {
private final TextArea textArea = new TextArea();
public static void main(final String[] args) {
Application.launch(args);
}
#Override
public void start(final Stage primaryStage) throws Exception {
primaryStage.setScene(new Scene(new VBox(textArea), 300, 200));
primaryStage.show();
ping();
}
public void ping() {
new Thread(this).start();
}
#Override
public void run() {
try {
final ProcessBuilder processBuilder = new ProcessBuilder("cmd", "/C", "ping -a www.google.com -n 10");
final Process process = processBuilder.start();
final InputStreamReader inputStreamReader = new InputStreamReader(process.getInputStream());
while (appendText(inputStreamReader)) {
;
}
process.waitFor();
process.destroy();
} catch (final Exception ex) {
ex.printStackTrace();
}
}
private boolean appendText(final InputStreamReader inputStreamReader) {
try {
final char[] buf = new char[256];
final int read = inputStreamReader.read(buf);
if (read < 1) {
return false;
}
Platform.runLater(() -> {
textArea.appendText(new String(buf));
});
return true;
} catch (final IOException e) {
e.printStackTrace();
}
return false;
}
}

Run exe by java Process, get error code 109

Process p = Runtime.getRuntime().exec("myexe.exe");
BufferedReader br = null;
try{
br = new BufferedReader(new InputStreamReader(p.getInputStream(), "GB2312"));
String value = null;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
}finally{
IOUtils.close(br);
}
Then, the output likes below, not the string I want:
Child: Can't read length for data, error code 109
It seems that the problem appears, because of the output of the exe which is too long. Can ProcessBuilder solve it ?
As a general rule of thumb, you should always read the output of Process before you call waitFor (or use a background Thread to read it while you waitFor)
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
public class PBDemo {
public static void main(String[] args) throws Exception {
String s;
ProcessBuilder pb = new ProcessBuilder("myexe.exe");
pb.redirectErrorStream(true);
try {
Process pro = pb.start();
InputConsumer ic = new InputConsumer(pro.getInputStream());
System.out.println("...Waiting");
int exitCode = pro.waitFor();
ic.join();
System.out.println("Process exited with " + exitCode);
} catch (Exception e) {
System.out.println("sorry" + e);
e.printStackTrace();
}
}
public static class InputConsumer extends Thread {
private InputStream is;
public InputConsumer(InputStream is) {
this.is = is;
start();
}
#Override
public void run() {
try {
int in = -1;
while ((in = is.read()) != -1) {
System.out.print((char) in);
}
} catch (IOException exp) {
exp.printStackTrace();
}
}
}
}
In the past, I've either provided an Observer Pattern to the InputConsumer, through which some other party can be notified as new input comes in or otherwised cached the output so I can process it after the process has completed, based on your needs

cmd command from java application error

Recently I added "adb devices" in the nano ./bash_profile so that I can run it from any directory.
I used one java application to run
public static void main(String [] args) {
executeCmd("adb devices");
}
private static void executeCmd(String string) {
InputStream pipedOut = null;
try {
Process aProcess = Runtime.getRuntime().exec(string);
// These two thread shall stop by themself when the process end
Thread pipeThread = new Thread(new StreamGobber(aProcess.getInputStream()));
Thread errorThread = new Thread(new StreamGobber(aProcess.getErrorStream()));
pipeThread.start();
errorThread.start();
aProcess.waitFor();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
class StreamGobber implements Runnable {
private InputStream Pipe;
public StreamGobber(InputStream pipe) {
if(pipe == null) {
throw new NullPointerException("bad pipe");
}
Pipe = pipe;
}
public void run() {
try {
byte buffer[] = new byte[2048];
int read = Pipe.read(buffer);
while(read >= 0) {
System.out.write(buffer, 0, read);
read = Pipe.read(buffer);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(Pipe != null) {
try {
Pipe.close();
} catch (IOException e) {
}
}
}
when I run any other commands such as "ls" it's working fine!!
I'm using mac ..
thanks :)
Maybe global path problem on mac. You can try run with absolute adb program path as command.

What character does a BufferedReader interpret as the end of the stream?

When reading from a socket using a BufferedReader it states that the readLine() method returns
A String containing the contents of the line, not including any line-termination characters, or null if the end of the stream has been reached
How does it know that it's reached the end of the stream? What sequence of characters does it use to determine this.
I want to simulate sending the same sequence of characters to properly close another connection that uses PipedStreams.
Edit:
Here is the code in question. From the responses it looks like there is no such sequence and calling close() on the PipedOutput stream should unblock the readLine() on the output stream. It doesn't appear to be doing this at the moment which is why I was confused so I'm thinking it might be a bug somewhere else.
What's happening is the incomingEventIn.close() line appears to be blocking when inputLine = incomingEventIn.readLine() is blocking. If inputLine = incomingEventIn.readLine() isn't being executed on the other thread then incomingEventIn.close() executes fine. Why is this happening?
public class SocketManager {
private Socket socket = null;
private PrintWriter out = null;
private BufferedReader in = null;
private PipedOutputStream incomingEventOutStream = null;
private PrintWriter incomingEventOut = null;
private BufferedReader incomingEventIn = null;
private PipedOutputStream incomingResponsOutStream = null;
private PrintWriter incomingResponseOut = null;
private BufferedReader incomingResponseIn = null;
private ArrayList<AsteriskLiveComsEventListener> listeners = new ArrayList<AsteriskLiveComsEventListener>();
private final ExecutorService eventsDispatcherExecutor;
private String ip;
private int port;
private Object socketLock = new Object();
public SocketManager(String ip, int port) {
this.ip = ip;
this.port = port;
eventsDispatcherExecutor = Executors.newSingleThreadExecutor();
}
public void connect() throws UnableToConnectException, AlreadyConnectedException {
synchronized(socketLock) {
if (socket != null && !socket.isClosed()) {
throw (new AlreadyConnectedException());
}
try {
socket = new Socket(ip, port);
out = new PrintWriter(socket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
incomingEventOutStream = new PipedOutputStream();
incomingEventIn = new BufferedReader(new InputStreamReader(new PipedInputStream(incomingEventOutStream)));
incomingEventOut = new PrintWriter(incomingEventOutStream);
incomingResponsOutStream = new PipedOutputStream();
incomingResponseIn = new BufferedReader(new InputStreamReader(new PipedInputStream(incomingResponsOutStream)));
incomingResponseOut = new PrintWriter(incomingResponsOutStream);
} catch (IOException e) {
throw (new UnableToConnectException());
}
new Thread(new IncomingEventThread()).start();
new Thread(new SocketThread()).start();
}
}
public void disconnect() throws NotConnectedException {
disconnect(false);
}
private void disconnect(boolean notRequested) throws NotConnectedException {
synchronized(socketLock) {
if (!isConnected()) {
throw (new NotConnectedException());
}
try {
incomingEventIn.close();
} catch (IOException e2) {}
// IT NEVER GETS TO HERE!
incomingEventOut.close();
try {
incomingResponseIn.close();
} catch (IOException e1) {}
System.out.println("disconnecting");
incomingResponseOut.close();
try {
socket.shutdownInput();
} catch (IOException e) {}
try {
socket.shutdownOutput();
} catch (IOException e) {}
try {
socket.close();
} catch (IOException e) {}
if (notRequested) {
System.out.println("disconnecting event");
dispatchEvent(new ConnectionLostEvent());
}
}
}
public boolean isConnected() {
synchronized(socketLock) {
return (socket != null && !socket.isClosed());
}
}
public void addEventListener(AsteriskLiveComsEventListener a) {
synchronized(listeners) {
listeners.add(a);
}
}
public void removeEventListener(AsteriskLiveComsEventListener a) {
synchronized(listeners) {
listeners.remove(a);
}
}
private void dispatchEvent(final AsteriskLiveComsEvent e) {
synchronized (listeners) {
synchronized (eventsDispatcherExecutor) {
eventsDispatcherExecutor.execute(new Runnable()
{
public void run()
{
for(int i=0; i<listeners.size(); i++) {
listeners.get(i).onAsteriskLiveComsEvent(e);
}
}
});
}
}
}
public JSONObject sendRequest(JSONObject request) throws JSONException, NotConnectedException {
synchronized(socketLock) {
System.out.println("sending request "+request.toString());
out.println(request.toString());
try {
return new JSONObject(incomingResponseIn.readLine());
} catch (IOException e) {
// lets close the connection
try {
disconnect(true);
} catch (NotConnectedException e1) {}
throw(new NotConnectedException());
}
}
}
private class SocketThread implements Runnable {
#Override
public void run() {
String inputLine = null;
try {
while((inputLine = in.readLine()) != null) {
// determine if this is a response or event and send to necessary location
JSONObject lineJSON = new JSONObject(inputLine);
if (lineJSON.getString("type").equals("response")) {
incomingResponseOut.println(inputLine);
incomingResponseOut.flush();
}
else if (lineJSON.getString("type").equals("event")) {
incomingEventOut.println(inputLine);
incomingEventOut.flush();
}
}
if (isConnected()) {
try {
disconnect(true);
} catch (NotConnectedException e) {}
}
} catch (IOException e) {
// try and disconnect (if not already disconnected) and end thread
if (isConnected()) {
try {
disconnect(true);
} catch (NotConnectedException e1) {}
}
}
}
}
private class IncomingEventThread implements Runnable {
#Override
public void run() {
String inputLine = null;
try {
while((inputLine = incomingEventIn.readLine()) != null) {
JSONObject lineJSON = new JSONObject(inputLine);
String eventType = lineJSON.getString("eventType");
// determine what type of event it is and then fire one that represents it
if (eventType.equals("channelAdded")) {
JSONObject a = lineJSON.getJSONObject("payload");
Hashtable<String,Object> data = new Hashtable<String,Object>();
Object[] keys = a.keySet().toArray();
for(int i=0; i<keys.length; i++) {
data.put((String) keys[i], a.get((String) keys[i]));
}
dispatchEvent(new ChannelAddedEvent(data));
}
else if (eventType.equals("channelRemoved")) {
dispatchEvent(new ChannelRemovedEvent(lineJSON.getJSONObject("payload").getInt("channelId")));
}
else if (eventType.equals("channelsToRoom")) {
ArrayList<Integer> data = new ArrayList<Integer>();
JSONObject a = lineJSON.getJSONObject("payload");
JSONArray ids = a.getJSONArray("channelIds");
for(int i=0; i<ids.length(); i++) {
data.add(ids.getInt(i));
}
dispatchEvent(new ChannelsToRoomEvent(data));
}
else if (eventType.equals("channelToHolding")) {
dispatchEvent(new ChannelToHoldingEvent(lineJSON.getJSONObject("payload").getInt("channelId")));
}
else if (eventType.equals("channelVerified")) {
dispatchEvent(new ChannelVerifiedEvent(lineJSON.getJSONObject("payload").getInt("channelId")));
}
else if (eventType.equals("serverResetting")) {
dispatchEvent(new ServerResettingEvent());
}
}
} catch (IOException e) {}
System.out.println("here");
}
}
Edit 2:
I think it's a deadlock issue somewhere because if I put some breakpoints in before it in the debugger it runs fine and inputLine = incomingEventIn.readLine() returns null. If I try and run it normally it locks up.
Edit 3: Solved thanks to Gray's answer. The input stream is being closed before the output which was causing the lock up. It needs to be the other way around. Closing the output stream first then informs the input stream that the stream is closed and unblocks the readLine() method.
How does it know that it's reached the end of the stream? What sequence of characters does it use to determine this.
The answer to this is OS dependent but the OS' I'm familiar with, no EOF characters are read. The OS returns to the underlying caller the return values that indicate that the stream (file-descriptor) has reached EOF. The JVM sees the return value and returns the appropriate return (null, -1, ...) to the InputStream or Reader caller depending on the method.
I want to simulate sending the same sequence of characters to properly close another connection that uses PipedStreams.
If you are reading from a PipedReader then you close the associated PipedWriter. The Reader or InputStream will then return the appropriate EOF value to the caller.
Edit:
Since your IncomingEventThread is reading from incomingEventIn, the disconnect() method should close the incomingEventOut first. The thread should close the in side itself. Then you should close the response out.
I would not have the thread call disconnect(...). It should only close it's reader and writer, not all of the streams.
Check out this question:
what is character for end of file of filestream?
There isn't one. The OS knows when the stream reaches its end via the file size, the TCP FIN bit, or other out-of-band mechanisms depending on the source. The only exception I'm aware of is that the terminal driver recognizes Ctrl/d or Ctrl/z as EOF when types by a keyboard, but again that's the OS, not the Java stream or reader.
From your point of view, just call close on PipedOutputStream that you use to connect to your test.
The actual close of the socket is performed by the TCP stack on client and server.
This should do (note that you cannot read/write piped streams on the same thread, hence the 2 methods and a thread creation):
void runTest ( final PipedInputStream sink ) throws Exception
{
try( final PipedOutputStream stream = new PipedOutputStream( sink ) )
{
try ( final OutputStreamWriter swriter =
new OutputStreamWriter( stream, "UTF-8" )
)
{
try ( final PrintWriter writer = new PrintWriter( swriter ) )
{
writer.println( "Hello" );
writer.println( "World!" );
}
}
}
}
void test ( final PipedInputStream sink ) throws InterruptedException
{
final Thread outputThread =
new Thread(
new Runnable ( )
{
#Override
public void run ( )
{
try
{
runTest( sink );
}
catch ( final Exception ex )
{
throw new RuntimeException( ex );
}
}
}
);
outputThread.start( );
outputThread.join( );
}

Categories

Resources