Runtime.getRuntime().exec( "myprog.exe" );
will spawn an external process (usually a program written in some language other
than Java) that runs in parallel with the Java execution. In W95/W98/Me/NT/W2K/XP/W2K3,
you must use an explicit *.exe or *.com
extension on the parameter. In Vista
it will still work even if you leave it off. Be careful to include it, or your
code will mysteriously fail on the older operating systems.
It is also best to fully qualify executable names so that the system executable
search path is irrelevant, and so you don’t pick up some stray program off
the path with the same name.
Starting with JDK 1.5,
exec has been replaced by
ProcessBuilder. start.
You should likely not be using
exec for new
programs. However, studying
exec is useful to
understand old programs that use
exec and to
understand why
ProcessBuilder is designed the way it
is. They have many things in common, especially the way command interpreters and
internal commands must be handled, and they both use the same
Process
class. So read about
exec first, then enjoy the
improved
ProcessBuilder.
Overloaded exec
There are also many overloaded forms of exec()
including this most general one:
The second argument can be a String [], and can be
used to set environment variables. In the second case, "C:\\SomeDirectory"
specifies a directory for the process to start in. If, for instance, your
process saves files to disk, then this form allows you to specify which
directory they will be saved in.
Command Interpreter
To run a *.BAT, *.CMD,
*.html *.BTM or
URL you must invoke the command processor with these as a parameter. These
extensions are not first class executables in Windows. They are input data for
the command processor. You must also invoke the command processor when you want
to use the < > | piping options, Here’s how,
presuming you are not interested in looking at the output:
exec only understands xxx.exe
and parameters. It knows nothing about program names without the *.exe
explicitly mentioned, | pipes, < redirection >, % environment parameter
subsitution, or *.bat, *.cmd
or *.html files. Further commands such as attrib,
cls, copy del,
dir, format, move
and rename are not *.exe files.
They are internal commands to the command interpreter. For all those things you
must spawn a command interpreter/shell script with an explicit .exe
extension (at least in W98/Me/NT/W2K/XP/W2K3/Vista,
in other OSes, you need a fully qualified executable name) and pass it the name
of your executable or internal command as a parameter.
Simplifications
There is also Marty
Hall’s Exec class, which simplifies the
process of using exec and tends to make the command
more stable. Currently, only the exec(String
cmd) is supported, with a workaround for exec(
String cmd, null,
String dir)
Launching A Browser
Windows and NT will let you feed a URL string to the command processor and it
will find a browser, launch the browser, and render the page, e.g.
Runtime.getRuntime().exec( "cmd.exe http://mindprod.com/index.html" );
Another lower level approach that does not require extension associations to be
quite as well set up is:
Note that a URL is not the same thing as a file name. You can point your browser
at a local file with something like this:
file://localhost/E:/mindprod/index.html
or
file:///E|/mindprod/index.html.
Runtime.getRuntime().exec( new String[] {
"open",
"-a",
"Firefox.app",
url.toString()
});
Composing just the right platform-specific command to launch browser and feed it
a URL to display can be frustrating. You can use the BrowserLauncher2
package to do that for you.
Note that
rundll32.exe url.dll,FileProtocolHandler file:///E|/mindprod/jgloss.html
won’t work on the command line because | is
reserved as the piping operator, though it will work as an exec
parameter passed directly to the rundll32.exe
executable.
Andrew Thompson
has a written a browser
launching class that determines the default browser.
Unix
For Unix/Linux you must spawn the program that can process the script, e.g. bash.
However, you can run scripts directly with exec if you
do two things:
- Start the script with #!bash or whatever the
interpreter’s name is.
- Mark the script file itself with the executable attribute.
Alternatively start the script interpreter, e.g.
Runtime.getRuntime().exec( new String[]{ "/bin/sh", "-c", "echo $SHELL"} );
Communicating With The Spawned Process
exec returns a Process
object that you can use to control, monitor or wait for the background process.
This example shows you how you can wait for the child, rather than letting it
run asynchronously. This example does not show the many possible other separate Threads
you might use to:
- Spawn the child (and continue processing).
- Write the child’s input from System.in
with Process.getOutputStream().
Note the stream is named relative to the parent, not the child.
- Read the child’s output from System.out
with Process.getInputStream().
Note the stream is named relative to the parent, not the child. You must
do this if there are more that a few line of output, or the child will stall,
constipated with unread output.
- Read the child’s output from System. err
with Process.getErrorStream().
- One each to read the child’s output from files, sockets etc.
- Wait for the child with Process.waitFor()
once you have all the hooks set up.
- Timer to kill the child, and interrupt the waitFor()
thread with Thread.interrupt()
if the child takes too long.
You need to start all these possible Threads after
you create the Process object, but before you call waitfor.
Process.getOutputStream lets you write out data that
will be used as input to the process. Don’t forget to include any "\n"
and "\r" characters the process is
expecting. Process.getInputStream lets you read in the
data that your process squirted out to stdout.
The other cruder way to get the output from the execed program is to spawn a
command processor and use the > or 2>
redirection operator to direct stdout or stderr to a file. When the spawned
program completes, read the file.
If you wait for the Process, you might want to do it
on some thread other than the main AWT event processing one, i.e. create a new
Thread, otherwise your app will not be able to process AWT events while you wait.
ON the Java side you may need to read and write to the child with separate
threads to avoid deadlocks. The deadlock occurs if you try to read and/or write
more than a certain amount (empirically it is around 4k). The deadlock
(a Mexican standoff freeze) happens when the child wants you to listen when you
are still trying to feed it more, or when the child is hungry for input and you
won’t send it any more until it has spoken.
Applets
You can’t use exec from an unsigned Applet
otherwise you would be able to wreck havoc on the client machine by spawning FORMAT
C:. If you need to do that, you will need to have a signed
Applet with high privilege.
If you are in an Applet and all you are trying to is
get a browser to render a page, you don’t need exec.
Simply use getAppletContext().showDocument( new
URL("http://domain/file.html"));
Gotchas
- If your child program produces more than a few lines of output,
your parent program must capture and process it, otherwise the child will stall
with constipation.
- If you are used to C exec, watch out. Java does not use
the dummy parameter 0 which duplicates the name of the program.
- In a similar way, Java main methods don’t see
that dummy parameter either.
- Your execed program will stall if it produces more than a few lines out output.
You have to make provision via the Process object
for the parent program to read the child’s System.
out and System.err
output if the child produces more than a few lines.
- You must use the .exe suffix. exec
is not smart enough to figure out the extension. A command interpreter can
however figure out if you meant .bat, .com
or .exe, however. Spawn a command interpreter instead,
with the program name as a parameter. See the example
code above.
- For internal command interpreter commands like dir and del,
you must spawn a command interpreter with the command as a parameter. You can’t
spawn the command directly.
ProcessBuilder
Starting with Java 1.5 there is a better way of handling exec
called ProcessBuilder. It has a number of advantages
over exec.
- It lets you control the environment.
- There are separate methods for setting up the various aspects of the spawn,
rather than confusing positional parameters.
- It will merge the child stderr and stdout
streams for you.
Here is how you use it:
Threads to Tend the Child
Here is an example of using Threads to communicate with the child process to
feed it input and read its output. This example does not show a separate System.
err reader or a timeout timer, but this example should
be enough to get you started. You need code like this, or the child will freeze,
unable to continue until someone reads its output.
Spawning java.exe
I mentioned earlier that exec is used to start non-Java apps, though in
principle you could spawn a copy of java.exe. However,
there are cleaner and faster ways to get that same effect.
Spawning javac.exe
There are a three basic techniques to spawn a copy of the Java compiler: JavaCompiler,
sun.tools.javac.Main, or spawning javac.exe
with ProcessBuilder or exec.
Desktop Spawning
In JDK 1.6+ there is a simplified exec technique. You cannot communicate with
the child task.
- Desktop.open will
launch the appropriate application to handle the given file.
- Desktop.browse will
launch a browser and display a URL.
- Desktop.edit will
launch as appropriate editor of some sort on the given file.
- Desktop.mail will
launch the user’s favourite mail program.
- Desktop.print will
launch the appropriate application to print the given file.
Learning More
Sun’s Javadoc on the
exec method : available:
Sun’s Javadoc on the
Process class : available:
Sun’s Javadoc on the
ProcessBuilder class : available:
Sun’s Javadoc on the
Desktop class : available: