Java Ninja Chronicles By Norris Shelton

Things I learned in the pursuit of code

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 calling processBuilder.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 use ProcessBuilder to execute native processes from a Java application.

January 9th, 2015

Posted In: Java

Tags: , , , , ,

Leave a Comment

LinkedIn Auto Publish Powered By : XYZScripts.com