The InputStream
of my Process
should attach and detach whenever the user wants to see it or not. The attaching works fine, but the detach fails. Default answer to interrupt the readLine()
method is always to close the stream, but I cant in this case or the Process
will finish or at least not available for future attachments. This is how the stream is read:
BufferedReader reader = new BufferedReader(new InputStreamReader(getProcess().getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
To detach I tried some stuff:
SequenceInputStream
, failed: when one InputStream
was waiting for input, the other was not even calledUse reflections to unlock the read()
method inside any of the streams, failed: not sure why, but did not work. Should we go on with this try? Here is the sourcecode:
try {
Field modifiers = Field.class.getDeclaredField("modifiers");
modifiers.setAccessible(true);
Field fdecoder = stream.getClass().getDeclaredField("sd");
fdecoder.setAccessible(true);
modifiers.setInt(fdecoder, 1);
StreamDecoder decoder = (StreamDecoder) fdecoder.get(stream);
Field flock = decoder.getClass().getSuperclass().getDeclaredField("lock");
flock.setAccessible(true);
modifiers.setInt(flock, 1);
Object lock = (Object) flock.get(decoder);
synchronized (lock) {
lock.notifyAll();
}
} catch (NoSuchFieldException | IllegalAccessException e) {
Wrapper.handleException(Thread.currentThread(), e);
}
Not sure how I can fix this. Could you help me interrupting the readLine()
method without closing the stream, simple and performant? Thanks.
Edit: What do I mean by "performant"? My application has not much users, but a lot of processes. The answer by @EJP is not wrong - but unperformant in the case of my application. I cannot have hundreds of threads for hundreds of processes, but I can have as many processes as I have users watching. That's why I try to interrupt the process gracefully. Fewer threads, less running/blocked threads. Here is the application described (https://imgur.com/VUcYUfi.png) The Thread that sends the information to the user is the same that reads the input.
I didn't expect it to work, but futures are actually cancelable (but why?). After @Tarun Lalwani mentioned the TimeLimiter of Googles Guava library, I inspected the code, tried it in my examples (worked!) and rewrote it a bit - make it not time-based, but method-call-based?!
Here is what I got from my research: A wrapper for the BufferedReader
:
public class CancelableReader extends BufferedReader {
private final ExecutorService executor;
private Future future;
public CancelableReader(Reader in) {
super(in);
executor = Executors.newSingleThreadExecutor();
}
@Override
public String readLine() {
future = executor.submit(super::readLine);
try {
return (String) future.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} catch (CancellationException e) {
return null;
}
return null;
}
public void cancelRead() {
future.cancel(true);
}
}
This class
allows you to use the BufferedReader#readLine()
when you need it and cancel it when you want to continue / interrupt the Thread
it is running in. Here is some example code of it in action:
public static void main(String[] args) {
System.out.println("START");
CancelableReader reader = new CancelableReader(new InputStreamReader(System.in));
String line;
new Thread(() -> {
try {
Thread.sleep(10000);
reader.cancelRead();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
System.out.println("END");
}
And the output of it:
START
> Hello World!
Hello World!
> What's up?
What's up?
END //Exactly after 5 seconds, when the cancel was called
> Hey, you still there?
//No output as expected
And the last thing I wanna say is why this and not closing InputStream or create one Thread per process?
In this case the InputStream
is the stream of a Process
, which means we cannot close it. One way would be to unblock readLine()
and return null to finish the while
-loop, but this is made with Reflection
, which is not as beautiful as our solution now and didn't work for any reason. The application uses many processes but has a limited amount of users - thats why we decide for the amount of threads per user and not per process.
I hope you guys will find this Thread in the future and it is helpful for you. Would be awesome if you leave an upvote, so I can get back my rep of the bounty. Dont forget to upvote the comments either! They helped me alot and brought me to the right solution: Interrupt BufferedReader#readLine() without closing InputStream
You're going at this back to front.
You can't stop collecting the process's output, or you will stall the child process.
You want to stop displaying the output when the user doesn't want to see it. Look on it as a user interface issue only.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With