Ads 468x60px

Monday, July 25, 2011

The Runtime and Process Classes in JAVA


The Runtime and Process Classes

One of the simplest means of integrating Java with non-Java systems involves the creation of one or more command-line driven executables that use the non-Java components directly. The Runtime and Process classes in the java.lang package provide methods that allow a Java application to execute and monitor non-Java processes.
The Java application communicates with the executable by creating a command array and calling Runtime.exec(). The executable can communicate a simple integer back using its return code. If more complex communication is required, the Java application can read from the process standard output and write to the process standard input.
Typical use of the Runtime and Process objects involves setting up the command or command/argument array to execute, executing the command, retrieving the input and output streams of the spawned process, and monitoring the process status.
The disadvantage to using command-line driven executables is that the executables are not very tightly integrated with the Java application. Parameter passing is complex, and the interface certainly doesn't resemble the clean clarity of a set of defined Java classes. However, for simple cases, it is probably simpler to define separate executables than classes with C-based native methods.

Executing External Programs Using Runtime

The Runtime class exposes several versions of an exec() method that may be used to execute non-Java processes. The simplest version takes one argument-a string containing the system command to execute. Other versions include arrays of arguments to pass to the system command. The exec() method returns a Java Process object that can be used to monitor the spawned process.

Note:
Because the Runtime class can be used to execute non-Java applications, the Runtime.exec() method isn't available from Java applets. Applets can't be allowed to execute non-Java processes because the processes aren't forced to obey Java security restrictions.

The Runtime class can't be used to instantiate a Runtime object. Instead, the Runtime class includes a static method, getRuntime(), that returns a pre-existing Runtime object. You can call the exec() method from that object.

Monitoring Executed Applications Using Process

The Runtime.exec() method returns a Process object. Process objects can be used to control and monitor the execution of spawned processes. The getErrorStream(), getInputStream(), and getOutputStream() methods of the Process object can be used to get the stderr, stdout, and stdin streams of the spawned process. The user might wait for the natural termination of the process using the waitFor() method, or can force termination using the destroy() method. The exit code of the process after termination can be retrieved using the exitValue() method.
The following code demonstrates the use of the Runtime object to spawn a Process whose output and exit value are monitored by a Java application:
Process proc = Runtime.getRuntime().exec(ANonJava.exe@);
InputStream in = proc.getInputStream();
byte buff[] = new byte[1024];
int cbRead;

try {
    while ((cbRead = in.read(buff)) != -1) {
        // Use the output of the process...
    }
} catch (IOException e) {
    // Insert code to handle exceptions that occur
    // when reading the process output
}

// No more output was available from the process, so...

// Ensure that the process completes
try {
    proc.waitFor();
} catch (InterruptedException) {
    // Handle exception that could occur when waiting
    // for a spawned process to terminate
}

// Then examine the process exit code
if (proc.exitValue() == 1) {
    // Use the exit value...
}
Tip:
When you retrieve a handle to the output stream of a spawned process with the Process.getInputStream() method, it isn't possible to use the available() method on the returned InputStream object. A reasonable workaround is to use the read(byte[]) method of the InputStream, which returns -1 when the end of the stream is reached.

If you're developing executables that pass information back to Java classes using standard output, you should probably adopt an easily parsable output format. This enables your java class to split up and coerce the data from your executable as necessary.

A Practical Example: DAOCmd

The DAOCmd project, available on the source code CD-ROM, includes a java class that calls a non-Java executable that returns the results of a database query. The parameters are passed from the java class to the non-Java executable by means of the command line, and the results of the database query are written to standard output by the executable. The java class reads the results and, in the example, simply echoes the results to the standard output of the Java environment.
The TestDAO class does all of the work on the Java side; the main() method creates the command line for the executable, runs the executable, and reads and echoes the results. The implementation of the main() method follows:
public static void main(String args[]) {
    Runtime rt = Runtime.getRuntime();
    Process proc;

    // Create the command array to pass to the executable
    String cmd[] = new String[args.length+1];
    cmd[0] = "DAOCmd";

    // Prepare the command array for the executable
    for (int iArg = 0; iArg < args.length; iArg++) {
        // All arguments are quoted to ensure that
        // arguments are passed correctly to the
        // spawned process
        cmd[iArg+1] = "\"" + args[iArg] + "\"";
    }

    // Attempt to loop and retrieve all of the output
    // from the spawned process
    try {
        proc = rt.exec(cmd);
        DataInputStream in = new DataInputStream(proc.getInputStream());
        String strLine;
        boolean tContinue = true;
        byte buf[] = new byte[256];
        int cbRead;

        while ((cbRead = in.read(buf)) != -1) {
            // Simply echo the output from the spawned process
            // to the Java application's stdout
            System.out.print(new String(buf, 0, 0, cbRead));
        }

        // Wait for the spawned process to terminate
        proc.waitFor();
    } catch (Exception e) {
        System.out.println(e);
    }
}
The first step is to create the command array for the executable. The first element of the command array is the constant DAOCmd, which is the name of the executable. The remaining arguments are from the command line that was used to run the java class. Each argument is quoted to ensure that it is interpreted by the executable as a string (if an argument to the java class was one or more words enclosed in quotes-the Java interpreter strips off the quotes but maintains the string with embedded white space as a single string within the argument array passed to main()).
Next, the executable is executed using the Runtime.exec() method. The standard output stream of the executable is retrieved from the Process object returned by exec(). The Java method loops to retrieve the output of the Process using the read() method. When no more data is available to read, the loop terminates, at which point the java class waits for the process to terminate using the waitFor() method. After the process has terminated, the java class can retrieve the process exit code using the Process.exitValue() method, and act accordingly.

0 comments:

Post a Comment