You are not logged in.
Hi,
For some long and twisted reason, I need to execute native programs from a Java program. Since this program is only every intended to be run on Linux, this doesn't bother me breaking the x-platform rules for Java! So, Runtime.exec() is the normal way to do this.
However, Runtime.exec() runs executes the command and then dishes basck the output once it's finished. The external program I wish to run takes a while and actually prints various pieces of feedback during its processing. I'd like to display this feedback to the user as it happens, rather than dumping it all at the end.
I can't think of anyway to do this. I've been looking for "Java shells" but they are reimplemented shells with only let you run classes on the classpath. Any ideas from the gurus around here?
Cheers
Offline
Uh... only thing I can suggest is taking a look at sources for JEdit's console plugin...
Dusty
Offline
can't you just use (sorry don't know the java terminology) a read-only textbox? just continually append info to it? set the background black and foreground white and it looks like a terminal 8)
Offline
maybe something like this?
NOTE: I did not test this code, just found it on the web. It may need updating b/c I'm not sure which JSDK version the author was using.
import java.io.*;
class execInput {
public static void main(String Argv[]) {
try {
String ls_str;
Process ls_proc = Runtime.getRuntime().exec("/bin/ls -aFl");
// get its output (your input) stream
DataInputStream ls_in = new DataInputStream(
ls_proc.getInputStream());
try {
while ((ls_str = ls_in.readLine()) != null) {
System.out.println(ls_str);
}
} catch (IOException e) {
System.exit(0);
}
} catch (IOException e1) {
System.err.println(e1);
System.exit(1);
}
System.exit(0);
}
}
Offline
Phrak,
I have no problem with the presentation part of things. As I tried to say in my original post, it's a matter of getting the text to append in the first place (in a regular fashion, that is!).
As an example, say I want to run ./configure via Java's Runtime.exec() method. As you all know, it tends to take a minute or two. But the problem here is that Runtime.exec doesn't complete until the command you executed completes. That means, you would see a blank screen for a minute, and then suddenly a full text dump. I was looking for a way to get this print-as-you-go, sort of thing.
Anyway, it looks like I'm making some progress. I have set up a couple of threads that keep probing the Process object that is returned by Runtime.exec() and that seems to be working (ish.)
Offline
Wow, you must have posted just a minute after me!
Offline
iBertus, your suggested approach is the typical approach but suffers with the problem of only dumping everything at the end.
Offline
oops.. didn't think about that, sorry.
Offline
np. Thanks for having a go anyway
Offline
What I would do in python, is first fork, then open a two way pipe between the processes (maybe a file pipe). Have one process perform the command, and pipe its output to the other process. The other process can then read, in a non blocking fashion, from the pipe.
I dont believe simple threading would work, but you could try it.
"Be conservative in what you send; be liberal in what you accept." -- Postel's Law
"tacos" -- Cactus' Law
"t̥͍͎̪̪͗a̴̻̩͈͚ͨc̠o̩̙͈ͫͅs͙͎̙͊ ͔͇̫̜t͎̳̀a̜̞̗ͩc̗͍͚o̲̯̿s̖̣̤̙͌ ̖̜̈ț̰̫͓ạ̪͖̳c̲͎͕̰̯̃̈o͉ͅs̪ͪ ̜̻̖̜͕" -- -̖͚̫̙̓-̺̠͇ͤ̃ ̜̪̜ͯZ͔̗̭̞ͪA̝͈̙͖̩L͉̠̺͓G̙̞̦͖O̳̗͍
Offline
Ah ok, I didn't understand that it was the actual output that was the issue.... like cactus said, there's usually a way to make a programatic pipe... in c++ you're use "tie" to tie two streams together... and fill a stringstream up with the output from the program...
you would probably want to make like a "ThreadedExec" or some class... that exposes the tied stream...
otherwise you won't get the output until the call returns
Offline
another possibility is to execute it with "command &> /tmp/outbuffer" and have that file be constantly monitored for new lines (is there a FileWatcher interface in java like in .NET? exposes a FileChanged method?)
Offline
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();
}
}
}
public class GoodLinuxExec
{
public static void main(String args[])
{
if (args.length < 1)
{
System.out.println("USAGE: java GoodLinuxExec <cmd>");
System.exit(1);
}
try
{
Runtime rt = Runtime.getRuntime();
System.out.println("Execing " + args[0]);
Process proc = rt.exec(args);
// 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();
}
}
}
stolen from this article and adapted to suit an os which does not start with the letter W.
Offline
Offline
If there's no user interaction, you can use the technique I used in my ABS GUI
I made a class called Run (you can get it from ssabsgui.jar) that sends application output as streams. I used those streams like so:
...
private void makepkg(String pkgWorkingDirectory) {
Run run = new Run();
String[] cmd = {"makepkg"};
outputArea.append("# makepkg");
//Scroll along with the output
outputArea.setCaretPosition(outputArea.getText().length()-1);
try {
BufferedReader[] outputs = run.it(cmd , null , new File(pkgWorkingDirectory),true);
String stdoutput = "";
while (stdoutput != null) {
String line = " "+stdoutput+"n";
outputArea.append(line);
outputArea.setCaretPosition(outputArea.getText().length()-line.length());
stdoutput = outputs[0].readLine();
}
String stderr = "";
while (stderr != null) {
String line = " "+stderr+"n";
outputArea.append(line);
outputArea.setCaretPosition(outputArea.getText().length()-line.length());
stderr = outputs[1].readLine();
}
} catch (Exception e) {
System.err.println("Error running makepkg");
}
}
...
I run this method with the SwingWorker class from Sun so that the gui doesn't block.
It's not TRUE output in the sense that the error stream is printed AFTER the standard output, but most apps use the error stream for errors,so they should come last, right?
v/r
Suds
Offline
stolen from this article and adapted to suit an os which does not start with the letter W.
Aye - that article was the inspiration for my current approach!
Offline
If there's no user interaction, you can use the technique I used in my ABS GUI
I made a class called Run (you can get it from ssabsgui.jar) that sends application output as streams.
[snip code]
It's not TRUE output in the sense that the error stream is printed AFTER the standard output, but most apps use the error stream for errors,so they should come last, right?
Hmm... I like the look of this. I shall give it a whirl! Cheers
Offline
Let me know how it turns out.
v/r
Suds
Offline
Let me know how it turns out.
Your Run class is being successully employed in my pacman front-end program
http://bbs.archlinux.org/viewtopic.php?t=13232
Cheers!
Offline
Glad to hear it! Cheers!
v/r
Suds
Offline