Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Calling a Native Program from within Java

DZone's Guide to

Calling a Native Program from within Java

· Java Zone
Free Resource

Learn how to troubleshoot and diagnose some of the most common performance issues in Java today. Brought to you in partnership with AppDynamics.

It had been a long time since I had a need to call a system process from inside Java. The last time I needed to do this, I used Runtime.exec(). Now there is a new sheriff in town… ProcessBuilder. ProcessBuilder was introduced in JDK 5 and makes it super simple to call native processes.

I had two separate cases that I needed to solve. One was a call to a C program with a parameter that returned one line of objects that I needed to parse. The other was a call to a C and it returned multiple lines to me. I only cared about some of the lines of output from the program.

Here is the first example. It calls a C program with one parameter and parses the single line of output. I can call the C program from the command line via:

/usr/local/bin/randint32  5

The output is similar to:

2048211571,-446295626,-1826753911,773673606,-163405466,

Here is the Java method that I used to call the C program and parse the data:

  • Line 11 – 12 – The ProcessBuilder is instantiated with the command as the first parameter and the parameter to the command as the second parameter.
  • Line 13 – This is where the process is returned from calling start()on the ProcessBuilder object.
  • Line 15 – The input stream is retrieved from the process object.

Beyond the Process/ProcessBuilder processing is normal Java code. The process returns me one line, which contains comma-separated integers. I split the string into a bunch of strings containing integer data. I convert them to integers and add them to the Queue. I also set a couple of flags that are related to the application, but are beyond the scope of this article.

/**
 * Fills the integer queue with integers.  Opens the process that calls the USB device, reads the data, splits the
 * CSV into integers and adds them to the queue.  Sets the RNG device to available as it writes integers to the
 * queue.
 * @throws InterruptedException throws if the put operation is interrupted by a timeout
 */
public void getRandomIntegerData() throws IOException, InterruptedException {
    int integersToGenerate = DESIRED_QUEUE_LENGTH - integerQueue.size();
    try {
        logger.info("Retrieving {} integers from Comscire by calling randint32...", integersToGenerate);
        ProcessBuilder processBuilder = new ProcessBuilder("/usr/local/bin/randint32",
                                                           Integer.toString(integersToGenerate));
        Process process = processBuilder.start();
 
        BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
        String data = br.readLine();
        for (String stringInt : data.split(",")) {
            integerQueue.put(Integer.valueOf(stringInt));
            // check it to ensure that it hasn't been emptied as fast as it could be filled
            if (integerQueue.size() > 0) {
                setAvailable(true);
                setErrors(false);
            }
        }
    } catch (IOException e) {
        setErrors(true);
        logger.error("Unable to get integers from Comscire process", e);
        throw e;
    }
}

The next C program that I had to call returned diagnostic information from a Comscire hardware random number generator. The command that I used to retrieve the data is:

/usr/local/bin/diagnostics

The data is returned as a series of lines containing the following data. I was only concerned with two of the lines from the output:

  • Status: QNG device reports success.
  • DeviceID QWR40026
Start Device...
 
Status: QNG device reports success.
 
DeviceID QWR40026
 
 
Get 128 Raw Bytes - Level 2, Stream 3 (hex)
 
76 6D 02 B6 BC 05 1D B1 7C 92 BC D7 69 2D 86 27 CC 5D EE 16 4F 17 97 EA B9 BA 32 AE E4 FC FA 57 4B 9C 74 48 0C 51 79 5B 47 82 3C 4B 66 EE 5A EA 83 C2 12 10 AF F2 8F 7B E2 D0 CD 84 40 96 94 A3 30 2D C3 AD 7F FD 06 9C 1F 2A 20 26 41 73 B9 EA 4B 82 DE AB 31 D8 FA 5D 20 1E EA 0F F2 D6 5A BB 4A 82 67 70 C4 C8 40 6E CA 7B 84 DA B4 3D 99 0A A6 BC 36 C3 94 D9 84 D9 F9 5B 00 95 01 4C 64 E2
 
EXIT...
  • Line 9 – The ProcessBuilder is instantiated with the command.
  • Line 10 – The process is assigned from callingprocessBuilder.start().
  • Line 12 – The input stream is retrieved from the process object.

I loop over the individual lines of data looking for the 2 lines of data that I need to retrieve. I add those to a StringBuilder. Just for efficiency’s sake, I look for a line that tells me that I am done. There is no data that I am concerned with after this line.

/**
 * Retrieves the diagnostic information from the Comscire device.
 * @return String containing the "Status" and "DeviceID" information
 * @throws IOException
 */
public String getDiagnostics() throws IOException {
    StringBuilder output = new StringBuilder();
    try {
        ProcessBuilder processBuilder = new ProcessBuilder("/usr/local/bin/diagnostics");
        Process process = processBuilder.start();
 
        BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
        String data = null;
        boolean done = false;
        while ((data = br.readLine()) != null && !done) {
            if (StringUtils.startsWith(data, "Status") ||
                StringUtils.startsWith(data, "DeviceID")) {
                output.append("\t").append(data).append("\n");
            } else if (StringUtils.startsWith(data, "Get 128 Raw Bytes")) {
                done = true;
            }
        }
    } catch (IOException e) {
        setErrors(true);
        logger.error("Unable to get diagnostics from Comscire process", e);
        throw e;
    }
 
    return output.toString();
}
There you have it. Two examples showing how easy it is to useProcessBuilder to execute native processes from a Java application.




















Understand the needs and benefits around implementing the right monitoring solution for a growing containerized market. Brought to you in partnership with AppDynamics.

Topics:

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}