8

I want to be able to run an external program concurrently with my Java code, i.e. I want to start the program, then return control to the calling method while keeping the external program running at the same time. The Java code will then keep generating input and send it to the external program and receive output back.

I don't want to keep loading the external program as it has very high overhead. What is the best way to accomplish this? Thanks!

aioobe
  • 413,195
  • 112
  • 811
  • 826
j.lee
  • 601
  • 3
  • 10
  • 11
  • 1
    What you describe is the default behaviour of `Runtime.exec()` (and `ProcessBuilder). Read [this great article on common stumbling blocks (and their solutions)](http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html) – Joachim Sauer Apr 08 '11 at 07:08
  • larson: I do really, really, suggest not producing anything on the streams: http://stackoverflow.com/questions/4674179 Otherwise you're in for a world of hurt and pain. I'm creating more external process on more machines than probably *anyone* here (that is: on an app shipped on a *lot* of user systems and constantly creating external processes) so I know a tiny bit what I'm talking about. I use temporary files for process communications (network has issues, like triggering AV notifications, which may make your users freak out and trying to manipulate stdin/out from Java is delusional). – SyntaxT3rr0r Apr 08 '11 at 08:16

5 Answers5

14

Have a look at ProcessBuilder. Once you've set up the ProcessBuilder and executed start you'll have a handle to a Process to which you can feed input and read output.

Here's a snippet to get you started:

ProcessBuilder pb = new ProcessBuilder("/bin/bash");
Process proc = pb.start();

// Start reading from the program
final Scanner in = new Scanner(proc.getInputStream());
new Thread() {
    public void run() {
        while (in.hasNextLine())
            System.out.println(in.nextLine());
    }
}.start();

// Write a few commands to the program.
PrintWriter out = new PrintWriter(proc.getOutputStream());
out.println("touch hello1");
out.flush();

out.println("touch hello2");
out.flush();

out.println("ls -la hel*");
out.flush();

out.close();

Output:

-rw-r--r-- 1 aioobe aioobe 0 2011-04-08 08:29 hello1
-rw-r--r-- 1 aioobe aioobe 0 2011-04-08 08:29 hello2
aioobe
  • 413,195
  • 112
  • 811
  • 826
  • I tried that, but since my external program waits for stdin, it blocks so I don't get control back from my calling method. I need control back since the input to external program is generated later. – j.lee Apr 08 '11 at 06:27
  • It's probably the reading from the external program that blocks. You need to put that in a separate thread, unless you know precisely what to read when. – aioobe Apr 08 '11 at 06:30
2

YOu can launch the external app with Runtime.getRuntime().exec(...)

To send data to the external program, you can either send data on the Processes output stream (You get a Process object back from exec) or you can open sockets and communicate that way.

MeBigFatGuy
  • 28,272
  • 7
  • 61
  • 66
0

I had issues trying to achieve bidirectional communication with the external process through stdin/stdout, because of blocking. In the end I found a github gist which allowed me solve the issue simply and elegantly; that gist is actually based on a stackoverflow answer.

See that other answer for sample code, but the core of the idea is to set up an event loop for reading and writing (while loop with 10ms sleeping), and using low-level stream operations so that no caching and blocking is going on -- only try to read if you know the other process in fact wrote something (through InputStream.available()).

It leads to a bit strange programming style, but the code is much simpler than it would be if using threads, and does the job pretty well.

Emmanuel Touzery
  • 9,008
  • 3
  • 65
  • 81
0

I think you will find the Javadoc for class java.lang.Process helpful. Of note, you can get the input and output streams from a Process to communicate with it while it is running.

ChrisH
  • 4,788
  • 26
  • 35
0

I second the answer about using ProcessBuilder. If you want to know more details about this, and why you should prefer it to Runtime.exec(), see this entry in the Java glossary. It also shows how to use threads to communicate with the external process.

Christoph Seibert
  • 1,451
  • 10
  • 5