Calling a Native Program from within Java
Join the DZone community and get the full member experience.
Join For FreeIt 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(); }
Opinions expressed by DZone contributors are their own.
Trending
-
Building the World's Most Resilient To-Do List Application With Node.js, K8s, and Distributed SQL
-
AI and Cybersecurity Protecting Against Emerging Threats
-
What Is JHipster?
-
Grow Your Skills With Low-Code Automation Tools
Comments