When developping and integrating applications together it is often useful to launch an external program. By doing this, many integration and implementation details are simplified (this integration technic also avoids license issues for some open source software integration).
This article explains how to launch an external program in Java and Ada and be able to read the process output to get the result.
Java Process Creation
The process creation is managed by the ProcessBuilder
Java class. An instance of this class holds the necessary information to create and launch a new process. This includes the command line and its arguments, the standard input and outputs, the environment variables and the working directory.
The process builder instance is created by specifying the command and its arguments. For this the constructor accepts a variable list of parameters of type String
.
import java.lang.ProcessBuilder;
...
final String cmd = "ls";
final String arg1 = "-l";
final ProcessBuilder pb = new ProcessBuilder(cmd, arg1);
When the process builder is initialized, we can invoke the start
method to create a new process. Each process is then represented by an instance of the Process
class. It is possible to invoke start
serveral times and each call creates a new process. It is necessary to catch the IOException
which can be raised if the process cannot be created.
import java.lang.Process;
...
try {
final Process p = pb.start();
...
} catch (final IOException ex) {
System.err.println("IO error: " + ex.getLocalizedMessage());
}
The Process
class gives access to the process output through an input stream represented by the InputStream
class. With this input stream, we can read what the process writes on its output. We will use a BufferedReader
class to read that output line by line.
import java.io.*;
...
final InputStream is = p.getInputStream();
final BufferedReader reader = new BufferedReader(new InputStreamReader(is));
By using the readLine
method, we can read a new line after each call. Once the whole stream is read, we have to close it. Closing the BufferedReader
will close the InputStream
associated with it.
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
reader.close();
Last step is to wait for the process termination and get the exit status: we can use the waitFor
method. Since this method can be interrupted, we have to catch the InterruptedException
.
try {
...
final int exit = p.waitFor();
if (exit != 0) {
System.err.printf("Command exited with status %d\n", exit);
}
} catch (final InterruptedException ex) {
System.err.println("Launch was interrupted...");
}
You can get the complete source from the file: Launch.java
Ada Process Creation
For the Ada example, we will create an application that invokes the nslookup
utility to resolve a set of host names. The list of host names is provided to nslookup
by writing on its standard input and the result is collected by reading the output.
We will use the Pipe_Stream
to launch the process, write on its input and read its output at the same time. The process is launched by calling the Open
procedure and specifying the pipe redirection modes: READ
is for reading the process output, WRITE
is for writing to its input and READ_WRITE
is for both.
with Util.Processes;
with Util.Streams.Pipes;
...
Pipe : aliased Util.Streams.Pipes.Pipe_Stream;
Pipe.Open ("nslookup", Util.Processes.READ_WRITE);
We can read or write on the pipe directly but using a Print_Stream
to write the text and the Buffered_Stream
to read the result simplifies the implementation. Both of them are connected to the pipe: the Print_Stream
will use the pipe output stream and the Buffered_Stream
will use the pipe input stream.
with Util.Streams.Buffered;
with Util.Streams.Texts;
...
Buffer : Util.Streams.Buffered.Buffered_Stream;
Print : Util.Streams.Texts.Print_Stream;
begin
-- Write on the process input stream
Buffer.Initialize (null, Pipe'Unchecked_Access, 1024);
Print.Initialize (Pipe'Unchecked_Access);
Before reading the process output, we send the input data to be solved by the process. By closing the print stream, we also close the pipe output stream, thus closing the process standard input.
Print.Write ("www.google.com" & ASCII.LF);
Print.Write ("set type=NS" & ASCII.LF);
Print.Write ("www.google.com" & ASCII.LF);
Print.Write ("set type=MX" & ASCII.LF);
Print.Write ("www.google.com" & ASCII.LF);
Print.Close;
We can now read the program output by using the Read
procedure and get the result in the Content
string. The Close
procedure is invoked on the pipe to close the pipe (input and output) and wait for the application termination.
Content : Unbounded_String;
-- Read the 'nslookup' output.
Buffer.Read (Content);
Pipe.Close;
Once the process has terminated, we can get the exit status by using the Get_Exit_Status
function.
Ada.Text_IO.Put_Line ("Exit status: "
& Integer'Image (Pipe.Get_Exit_Status));
References
launch.adb
util-streams-pipes.ads
util-streams-buffered.ads
util-streams-texts.ads