Is there a better way to implement multi-threading REPL? - java

I'm trying to implement a REPL application.
There are two threads, which are REPL and a loop function. The function will print messages from time to time, and I need the REPL to get the command of input then dispatch it.
This is my implement method (I use JLine):
Thread replThread = new Thread(() -> {
try {
Terminal terminal = TerminalBuilder.builder().system(true).build();
LineReader reader = LineReaderBuilder.builder().terminal(terminal).build();
for (;;) {
String line;
line = reader.readLine("> ");
System.out.println("input command: " + line);
}
} catch (IOException e) {
e.printStackTrace();
}
});
Thread loopThread = new Thread(() -> {
for (;;) {
try {
System.out.println("running");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
replThread.start();
loopThread.start();
This is the running screenshot
It shows that the prompt is overlapped with the prompt, and two threads' output conflicts with each other. I need the prompt (such as > ) to be always in the last line on the console.
So is there a better way or library can implement this requirement?

Related

`BufferReader` not showing the console input (typed) until ENTER is pressed (only in Windows & Mac, works in Linux)

I wanted to capture user input in two ways, one without timeout (waiting till life) another within some time period (if time exceeds/timeout it should be considered null or no input and move ahead). So I had this code.
public class Main {
public static void main(String[] args) {
System.out.println("Type Something (timeout 5 Sec)");
/* Read input within 5 Sec or done, whichever happens first*/
String inputTimeOut = executeInTime(Main::readInput, 5, TimeUnit.SECONDS);
System.out.println("TimeOut Inupt Given: " + inputTimeOut);
System.out.println("Type Something");
/* Read Input without time restriction */
String input = readInput();
System.out.println("Inupt Given: " + input);
}
public static String executeInTime(Callable<String> call, long wait, TimeUnit unit) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future<String> task = executorService.submit(call);
String input = null;
try {
input = task.get(wait, unit);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
task.cancel(true);
} finally {
executorService.shutdown();
}
return input;
}
public static String readInput() {
BufferedReader buffer = new BufferedReader(new InputStreamReader(System.in));
try {
while (!buffer.ready()) {
Thread.sleep(5);
}
String input = buffer.readLine();
return input;
} catch (IOException | InterruptedException e) {
return null;
}
}
}
It worked fine in Linux. Also in Wondows, the end behaviour is same, but there is a small problem.
In windows/Mac, I can't see what I've typed so far until I press enter. In Linux (Ubuntu) it appears as soon as I type.
Any idea why it's behaving differently in windows? And any other solution is also welcomed, as far as it allows the reading operation to be interruptible/killable and common (like my readInput()).
Update:
If I comment this
while (!buffer.ready()) {
Thread.sleep(5);
}
it shows while typing but does not end the execution after timeout.
(I can't use System.exit on timeout, sorry.)

How to run java.lang.Process one by one?

I need help in running maven command one by one through Java.
In my code I am trying to run maven build command and print the log to Swing TextArea window.
But the problem is Process.waitFor() is not working properly. As a result multiple processes
are running simultaneously and logs are not getting printed properly.
In Internet I found that waitFor() sometimes doesnt work. Instead I can use
Process.isAlive(). But I am not sure in my code how would I use that method.
Please help
Timer timer = new Timer(300, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
String line;
for (int i = 0; i < process.length; i++) {
BufferedReader buffer=new BufferedReader(new InputStreamReader(process[i].getInputStream()));
try {
if ((line = buffer.readLine()) != null) {
logTextArea[i].append(line + "\n");
} else {
((Timer) e.getSource()).stop();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
});
try {
for ( int count = 0 ; count < destList.getModel().getSize(); count++){
String projectPath = findDir(new File(codebasePath), destList.getModel().getElementAt(count).toString());
process[count] = Runtime.getRuntime().exec(System.getenv("M2_HOME")+"//bin//mvn.bat clean install -DskipTests -f "+projectPath + "\\pom.xml");
logTextArea[count] = new JTextArea(25, 60);
logFrame[count] = new JFrame("Show Log");
logFrame[count].add(new JScrollPane(logTextArea[count]), BorderLayout.CENTER);
logFrame[count].setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
logFrame[count].pack();
logFrame[count].setLocationByPlatform(true);
logFrame[count].setVisible(true);
timer.start();
process[count].waitFor()
}
} catch (IOException e) {
e.printStackTrace();
}
I am a bit puzzled where exactly you struggle, so maybe I am overlooking something, but you'd generally use it like so:
process[count].waitFor();
while (process[count].isAlive()) {
Thread.sleep(100);
}
You might want to get rid of waitFor completely and add some additional checks to the loop, e.g. a timeout or a signal to cancel the process, in case the process hangs, so your application can terminate properly:
while (process[count].isAlive()) {
if (someTimeoutCondition || someCancelCondition) {
// throw exception or do whatever else you want to do in this situation to gracefully exit
}
Thread.sleep(100);
}

Java- Runtime.getRuntime().exec() will not execute python.exe

For a project I need to start python.exe via Runtime.getRuntime.exec(). However, when I try to run it it won't execute, but it doesn't throw up an IOException. Here's the code:
try
{
Process process=Runtime.getRuntime().exec("C:\\Program Files (x86)\\PythonTest\\python.exe");
}
catch (IOException e)
{
System.out.println("Cannot find python.exe");
e.printStackTrace();
}
You need to get the output from the process and (waitFor() it to finish). Something like,
final String cmd = "C:/Program Files (x86)/PythonTest/python.exe";
Process p = Runtime.getRuntime().exec(cmd);
final InputStream is = p.getInputStream();
Thread t = new Thread(new Runnable() {
public void run() {
InputStreamReader isr = new InputStreamReader(is);
int ch;
try {
while ((ch = isr.read()) != -1) {
System.out.print((char) ch);
}
} catch (IOException e) {
e.printStackTrace();
}
}
});
t.start();
p.waitFor();
t.join();
To actually do something with python you'll want to get the OutputStream.
I think that the problem is due to eval incorrectly splitting the command string. My understanding is that exec("C:\\Program Files (x86)\\PythonTest\\python.exe") will attempt to run an application called "C:\\Program", passing it 2 command line arguments.
Try this instead:
exec(new String[]{"C:\\Program Files (x86)\\PythonTest\\python.exe"});
The exec(String, ...) command line parsing is primitive, and often has the incorrect behaviour from the programmer's perspective. The best bet is often to split the command and arguments yourself.

Sending output to external process

For some reason, I'm having problems sending output to a process that I've created in Java. The external process is running in a command prompt, and the peculiar thing is that I can click that, type, hit enter, and I'll get output from the program. It addition my program can read all the output coming from the program, it just can't send anything to it.
Anyways, here is the relevant code I'm using that just isn't working...
try {
ProcessBuilder builder=new ProcessBuilder(args);
builder.redirectErrorStream(true);
final Process p=builder.start();
// Process has been created and is running
try {
String b="";
BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
final BufferedWriter output = new BufferedWriter(new OutputStreamWriter(p.getOutputStream()));
new Thread(){public void run(){
// This thread will periodically send "get_time" to the process to get an update on its progress
while(true)
{
try {
Thread.sleep(1000);
p.exitValue();
// p.exitValue() only works when process has ended, so normal code goes in the catch block
output.close();
break;
// Leave the infinite loop if the program has closed
} catch (IOException ex) {
Logger.getLogger(OvMusicUI.class.getName()).log(Level.SEVERE, null, ex);
break;
// Leave the infinite loop if we tried closing our output stream, but it was already closed
} catch (InterruptedException ex) {
Logger.getLogger(OvMusicUI.class.getName()).log(Level.SEVERE, null, ex);
} catch (IllegalThreadStateException e) {
try {
System.out.println("Outputted: get_time");
output.write("get_time" + System.lineSeparator());
output.flush();
// Give the process some input
} catch (IOException ex) {
Logger.getLogger(OvMusicUI.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}}.start();
while((b = input.readLine()) != null){
System.out.println(new Time(System.currentTimeMillis()).toString() + " " + b);
// Log all output the process gives
}
input.close();
} catch (IOException ex) {
Logger.getLogger(OvMusicUI.class.getName()).log(Level.SEVERE, null, ex);
}
// More code here
} catch (IOException ex) {
Logger.getLogger(OvMusicUI.class.getName()).log(Level.SEVERE, null, ex);
}
If necessary, I can give an example command and the name of the external program being run so you can try it yourself...
Thanks!
EDIT: Here is an example of what's passed into the ProcessBuilder: Arrays.asList("VLC\vlc.exe", "-Irc", "-vvv", "http://www.youtube.com/watch?v=xfeys7Jfnx8", "--sout", "file/ogg:Untitled 1.ogg", "--play-and-exit", "--rc-quiet"). The only difference is I use absolute paths instead of relative paths. The program is VLC Media Player 2.0.7.
Your code has a few problems. First, you generally should not use exceptions for your regular control flow: it's expensive, it's difficult to read, and it makes handling actual errors more difficult. It's generally better to spawn another Thread that calls p.waitFor() and signals your main thread to complete, such as with wait/notify.
Also, your construction with the infinite loop and using break instead of return will make your code more difficult to debug; instead, use a Timer.
It looks like the output to your external program probably is working correctly but that the problem is just with reading its output. The program may be buffering its own output or may be detecting that it's not being run interactively and behaving differently.

How do I handle multiple streams in Java?

I'm trying to run a process and do stuff with its input, output and error streams. The obvious way to do this is to use something like select(), but the only thing I can find in Java that does that is Selector.select(), which takes a Channel. It doesn't appear to be possible to get a Channel from an InputStream or OutputStream (FileStream has a getChannel() method but that doesn't help here)
So, instead I wrote some code to poll all the streams:
while( !out_eof || !err_eof )
{
while( out_str.available() )
{
if( (bytes = out_str.read(buf)) != -1 )
{
// Do something with output stream
}
else
out_eof = true;
}
while( err_str.available() )
{
if( (bytes = err_str.read(buf)) != -1 )
{
// Do something with error stream
}
else
err_eof = true;
}
sleep(100);
}
which works, except that it never terminates. When one of the streams reaches end of file, available() returns zero so read() isn't called and we never get the -1 return that would indicate EOF.
One solution would be a non-blocking way to detect EOF. I can't see one in the docs anywhere. Alternatively is there a better way of doing what I want to do?
I see this question here:
link text
and although it doesn't exactly do what I want, I can probably use that idea, of spawning separate threads for each stream, for the particular problem I have now. But surely that isn't the only way to do it? Surely there must be a way to read from multiple streams without using a thread for each?
As you said, the solution outlined in this Answer is the traditional way of reading both stdout and stderr from a Process. A thread-per-stream is the way to go, even though it is slightly annoying.
You will indeed have to go the route of spawning a Thread for each stream you want to monitor. If your use case allows for combining both stdout and stderr of the process in question you need only one thread, otherwise two are needed.
It took me quite some time to get it right in one of our projects where I have to launch an external process, take its output and do something with it while at the same time looking for errors and process termination and also being able to terminate it when the java app's user cancels the operation.
I created a rather simple class to encapsulate the watching part whose run() method looks something like this:
public void run() {
BufferedReader tStreamReader = null;
try {
while (externalCommand == null && !shouldHalt) {
logger.warning("ExtProcMonitor("
+ (watchStdErr ? "err" : "out")
+ ") Sleeping until external command is found");
Thread.sleep(500);
}
if (externalCommand == null) {
return;
}
tStreamReader =
new BufferedReader(new InputStreamReader(watchStdErr ? externalCommand.getErrorStream()
: externalCommand.getInputStream()));
String tLine;
while ((tLine = tStreamReader.readLine()) != null) {
logger.severe(tLine);
if (filter != null) {
if (filter.matches(tLine)) {
informFilterListeners(tLine);
return;
}
}
}
} catch (IOException e) {
logger.logExceptionMessage(e, "IOException stderr");
} catch (InterruptedException e) {
logger.logExceptionMessage(e, "InterruptedException waiting for external process");
} finally {
if (tStreamReader != null) {
try {
tStreamReader.close();
} catch (IOException e) {
// ignore
}
}
}
}
On the calling side it looks like this:
Thread tExtMonitorThread = new Thread(new Runnable() {
public void run() {
try {
while (externalCommand == null) {
getLogger().warning("Monitor: Sleeping until external command is found");
Thread.sleep(500);
if (isStopRequested()) {
getLogger()
.warning("Terminating external process on user request");
if (externalCommand != null) {
externalCommand.destroy();
}
return;
}
}
int tReturnCode = externalCommand.waitFor();
getLogger().warning("External command exited with code " + tReturnCode);
} catch (InterruptedException e) {
getLogger().logExceptionMessage(e, "Interrupted while waiting for external command to exit");
}
}
}, "ExtCommandWaiter");
ExternalProcessOutputHandlerThread tExtErrThread =
new ExternalProcessOutputHandlerThread("ExtCommandStdErr", getLogger(), true);
ExternalProcessOutputHandlerThread tExtOutThread =
new ExternalProcessOutputHandlerThread("ExtCommandStdOut", getLogger(), true);
tExtMonitorThread.start();
tExtOutThread.start();
tExtErrThread.start();
tExtErrThread.setFilter(new FilterFunctor() {
public boolean matches(Object o) {
String tLine = (String)o;
return tLine.indexOf("Error") > -1;
}
});
FilterListener tListener = new FilterListener() {
private boolean abortFlag = false;
public boolean shouldAbort() {
return abortFlag;
}
public void matched(String aLine) {
abortFlag = abortFlag || (aLine.indexOf("Error") > -1);
}
};
tExtErrThread.addFilterListener(tListener);
externalCommand = new ProcessBuilder(aCommand).start();
tExtErrThread.setProcess(externalCommand);
try {
tExtMonitorThread.join();
tExtErrThread.join();
tExtOutThread.join();
} catch (InterruptedException e) {
// when this happens try to bring the external process down
getLogger().severe("Aborted because auf InterruptedException.");
getLogger().severe("Killing external command...");
externalCommand.destroy();
getLogger().severe("External command killed.");
externalCommand = null;
return -42;
}
int tRetVal = tListener.shouldAbort() ? -44 : externalCommand.exitValue();
externalCommand = null;
try {
getLogger().warning("command exit code: " + tRetVal);
} catch (IllegalThreadStateException ex) {
getLogger().warning("command exit code: unknown");
}
return tRetVal;
Unfortunately I don't have to for a self-contained runnable example, but maybe this helps.
If I had to do it again I would have another look at using the Thread.interrupt() method instead of a self-made stop flag (mind to declare it volatile!), but I leave that for another time. :)

Categories