skip to Main Content

I have simple java program for compiling java classes.
I created a JAR of this program and when I run it on Ubuntu I pass to the jar the path of folder with java files.

public class Main {
    public static void main(String[] args) throws IOException, InterruptedException {
        compile(args[0]);
    }
    //pathToFiles - is a value from command line arguments
    private static void compile(String pathToFiles) throws IOException, InterruptedException {
        List<String> cmdList = new ArrayList<>();
        cmdList.add("javac");
        cmdList.add(pathToFiles); 
        System.out.println("cmd: "+cmdList);


        ProcessBuilder pb = new ProcessBuilder(cmdList);
        Process process = pb.start();
        int exitValue = process.waitFor();
        if (exitValue != 0) {
            generateCompileException(process);
        }


    }
    //method just generates error message if there was an error
    private static void generateCompileException(Process process){
        StringBuilder response = new StringBuilder();
        try (final BufferedReader b = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
            String line;
            if ((line = b.readLine()) != null)
                response.append(line);
        } catch (final IOException e) {
            e.printStackTrace();
        }
        throw new RuntimeException(response.toString());
    }
}

When I pass path containing single java file it works:

java -jar co-1.jar /home/admin/test2/Calculator.java

But I want to compile multiple files. When I pass path containing multiple files I get error: file not found.

java -jar co-1.jar '/home/admin/test2/*.java'

enter image description here

PS: If I run a javac command manually with multiple files, it will work:
enter image description here

###################################

UPDATE:

I’ve added bash command to ProcessBuilder:

private static void compile(String pathToFiles) throws IOException, InterruptedException {
    List<String> cmdList = new ArrayList<>();
    cmdList.add("bash");
    cmdList.add("-c");
    cmdList.add("javac");
    cmdList.add(pathToFiles);
    System.out.println("Processor builder command: "+cmdList);
    ProcessBuilder pb = new ProcessBuilder(cmdList);
    Process process = pb.start();
    int exitValue = process.waitFor();
    if (exitValue != 0) {
        System.out.println("Finished with error. Exit value: "+exitValue);
        generateCompileException(process);
    }
}

But process withished with error code 2 with empty response from ProcessBuilder.

PS: RuntimeException was thrown by this line: throw new RuntimeException(response.toString());
enter image description here

2

Answers


  1. Remove quotes and use the command as below.

    java -cp co-1.jar:/home/admin/test2/* Main.class <args>
    

    See also

    https://docs.oracle.com/javase/7/docs/technotes/tools/windows/classpath.html

    PS: Unix uses :(colon) as delimiter and windows uses ;(semi-colon) delimiter to separate multiple paths.

    Login or Signup to reply.
  2. ProcessBuilder will not evaluate wildcards, as that is a feature of your terminal (such as bash). If you want wildcard to be expanded you need to run bash inside ProcessBuilder command, such as:

    String commandContainingWildcard = "javac /blah/*.java";
    ProcessBuilder pb = new ProcessBuilder("bash", "-c", commandContainingWildcard);
    ... // start() etc
    

    For the above to work you need to have "bash" or whatever shell you use in your path, otherwise you will need to use full path to bash (such as "/bin/bash").

    The third argument for command to compile must exactly match what works inside your terminal and must be the entire value not "javac" followed by wildcard. Remove single quotes around *.java (so that ProcessBuilder is provided with three command line parameters, not four or more).

    However I suggest that ProcessBuilder with bash isn’t the best way to do this work. You could try Java compiler tool interface, and get rid of wildcard by easy use of Files.find(dir, 1, (p,a) -> p.getFileName().toString().endsWith(".java")) to scan for all java files and join the paths explicitly for compilation.

    UPDATE

    Having now resolved your problem you may now find that the javac process fails / freezes due the incorrect way you read the stderr stream – this needs to happen at same time as stdout and before process.waitFor(). An easy fix is to consume stdout+stderr together:

    ProcessBuilder pb = new ProcessBuilder(cmdList);
    pb.redirectErrorStream(true); 
    Process process = pb.start();
    ByteArrayOutputStream response = new ByteArrayOutputStream();
    process.getInputStream().transferTo(response);
    int exitValue = process.waitFor();
    if (exitValue != 0) {
        System.out.println("Finished with error. Exit value: "+exitValue);
        throw new RuntimeException(new String(response.toByteArray()));
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search