Java Process Builder - Cannot run a simple program

1.4k views Asked by At

I have a program called darknet. It's a C-program made from Darknet.

I want to run the darknet program in a folder Darknet that looks like this:

enter image description here

I'm going to run darknet with Java Process Builder, but I get no responce from it when I run this code:

    // Arguments
    String darknetNamePath = darknet.getValue().getFilePath().replace("Darknet/", "./");
    String configurationFlag = configuration.getValue().getFilePath().replace("Darknet/", "");
    String weightsFlag = weights.getValue().getFilePath().replace("Darknet/", "");
    String imageFlag = "data/cameraSnap.png";
    String thresholdFlag = "-thresh " + thresholds.getValue();
    
    // Process builder
    ProcessBuilder processBuilder = new ProcessBuilder();
    processBuilder.directory(new File("Darknet")); // We need to stand inside the folder "Darknet"
    String commandString = "detect " + configurationFlag + " " + weightsFlag + " " + imageFlag + " " + thresholdFlag;
    System.out.println("darknetNamePath  = " + darknetNamePath);
    System.out.println("commandString  = " + commandString);
    processBuilder.command(darknetNamePath, commandString);
    Process process = processBuilder.start();
    BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
    String line;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }
    int exitCode = process.waitFor();
    System.out.println("\nExited with error code : " + exitCode);

Here is my output. Why doesn't it work for me?

darknetNamePath  = ./darknet
commandString  = detect cfg/yolov2-tiny.cfg weights/yolov2-tiny.weights data/cameraSnap.png -thresh 0.8

Exited with error code : 0

But when I call darknet file via terminal, then it works.

./darknet detect cfg/yolov2-tiny.cfg weights/yolov2-tiny.weights data/cameraSnap.png -thresh 0.6

UPDATE 2:

Here is my update.

    // Arguments
    String darknetNamePath = darknet.getValue().getFile().getAbsolutePath();
    String configurationFlag = configuration.getValue().getFilePath().replace("Darknet/", "");
    String weightsFlag = weights.getValue().getFilePath().replace("Darknet/", "");
    String imageFlag = "data/cameraSnap.png";
    String thresholdFlag = "-thresh " + thresholds.getValue();

    // Process builder
    ProcessBuilder processBuilder = new ProcessBuilder();
    processBuilder.command(darknetNamePath, "detect", configurationFlag, weightsFlag, imageFlag, thresholdFlag);
    Process process = processBuilder.start();
    if (process.getInputStream().read() == -1) {
        System.out.println(darknetNamePath);
        System.out.println("detect");
        System.out.println(configurationFlag);
        System.out.println(weightsFlag);
        System.out.println(imageFlag);
        System.out.println(thresholdFlag);
        System.out.printf("ERROR!");
    }
    BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
    String line;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }
    int exitCode = process.waitFor();
    System.out.println("\nExited with error code : " + exitCode);

Output:

/home/dell/Dokument/GitHub/Vaadin-DL4J-YOLO-Camera-Mail-Reporter/Vaadin-DL4J-YOLO-Camera-Mail-Reporter/Darknet/darknet
detect
cfg/yolov2-tiny.cfg
weights/yolov2-tiny.weights
data/cameraSnap.png
-thresh 0.3
ERROR!
Exited with error code : 0

UPDATE 3:

This works:

            // Arguments
            String darkPath = darknet.getValue().getFilePath().replace("Darknet/", "./"); // We need to call ./darknet, not absolute path
            String configurationFlag = configuration.getValue().getFilePath().replace("Darknet/", "");
            String weightsFlag = weights.getValue().getFilePath().replace("Darknet/", "");
            String imageFlag = "data/camera.png";
            String thresValue = String.valueOf(thresholds.getValue());

            // Process builder
            ProcessBuilder processBuilder = new ProcessBuilder();
            processBuilder.directory(new File("Darknet")); // Important
            processBuilder.command(darkPath, "detect", configurationFlag, weightsFlag, imageFlag, "-thresh", thresValue);
            processBuilder.redirectErrorStream(true); // Important
            Process process = processBuilder.start();
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
            int exitCode = process.waitFor();
            System.out.println("\nExited with error code : " + exitCode);
2

There are 2 answers

2
DuncG On BEST ANSWER

Your command must break all arguments into separate pieces - including thresholdFlag. It is a good idea to check if the executable exists. If it does not you should check where it is located or fix your Path variable to ensure that it can be located:

File darkpath = new File(darknetNamePath);
String [] cmd = new String[] { darkpath.getAbsolutePath(), "detect", configurationFlag, weightsFlag, imageFlag, "-thresh", String.valueOf(thresholds.getValue()) };

System.out.println("Path: "+darkpath+ " exists="+darkpath.exists());
System.out.println("exec "+Arrays.toString(cmd));

processBuilder.command(cmd);

It is also worth handling STDERR, the easiest way is to redirect STDERR=>STDOUT before calling processBuilder.start()

processBuilder.redirectErrorStream(true);

If you want Java to launch the executable without prefixing the absolute path it needs to be in one of these directories:

System.out.println("PATH COMPONENTS FOR JAVA LAUNCH:");
Arrays.asList(System.getenv("PATH").split(File.pathSeparator)).forEach(System.out::println);
6
Robert On

You are using ProcessBuilder the wrong way. The command method takes a executable and arguments as separate strings, not a path and then another string with the actual command and all its arguments. There is no shell involved to do word splitting on the command, so you pass all your distinct arguments as one argument.

I don't have darknet, so here's a simple command using the Unix echo command:

import java.io.*;

public class ProcessBuilderTest {
    public static void main(String[] args) throws Exception {
        ProcessBuilder processBuilder = new ProcessBuilder();
        String[] command = {"/bin/echo", "hello", "world"};
        processBuilder.command(command);
        Process process = processBuilder.start();
        BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        String line;
        while ((line = reader.readLine()) != null) {
            System.out.println(line);
        }
        int exitCode = process.waitFor();
        System.out.println("Exited with error code : " + exitCode);
    }
}

When I run this, I get:

robert@saaz:~$ java ProcessBuilderTest.java
hello world
Exited with error code : 0

It's not clear to my why your command doesn't produce an error. If I give a bad command (e.g., a trailing space behind "echo"), I get an exception:

Exception in thread "main" java.io.IOException: Cannot run program "/bin/echo ": error=2, No such file or directory

This may be OS specific. Or maybe you have some other darknet executable that gets picked up.