Search code examples
javaspringspring-restpmdstatic-code-analysis

Unable to run pmd inside java process inside spring


I want to run pmd inside a java process(created using ProcessBuilder) from within spring as a service.

public class PMDService {

  private ProcessBuilder processBuilder;

  private void createProcess() {
    processBuilder = new ProcessBuilder();
    final Map<String, String> envMap = processBuilder.environment();
    String path = envMap.get("Path");
    path += "../../../../../../../static-code-analyzers/pmd/bin;";
    envMap.put("Path", path);
  }


  public String getCommand(PMDParameters params) {
    final StringJoiner command = new StringJoiner(" ");
    command.add("cmd")
    .add("/c")
    .add("pmd")
    .add("-d")
    .add(params.getSourceCodePath())
    .add("-f")
    .add(params.getOutputFormat())
    .add("-R")
    .add(params.getResultSet())
    .add(">")
    .add(params.getResultsPath());

    return command.toString();
  }


  public void runAnalyzer(PMDParameters params) throws IOException, InterruptedException {
    createProcess();
    processBuilder.command(getCommand(params));
    final Process process = processBuilder.start();
    process.waitFor();
  }


When I test the rest endpoint using postman i get following error :

""message": "Cannot run program \"cmd /c pmd -d C:/bootcamp/Spring/springbootcode/springbootdemo -f xml -R rulesets/java/quickstart.xml > .\": CreateProcess error=2, The system cannot find the file specified","

Input json in postman

{
   "sourceCodePath": "C:/bootcamp/Spring/springbootcode/springbootdemo",
    "resultsPath": ".",
    "outputFormat": "xml",
    "resultSet": "rulesets/java/quickstart.xml"
 }

Solution

  • Analysis

    In the provided piece of code the program and its arguments are being passed as the single string value as the ProcessBuilder ProcessBuilder.command(String... command) method parameter.

    But that should not be the case: instead, the program and its arguments should be passed separately.

    Solution

    Let's use the ProcessBuilder ProcessBuilder.command(List<String> command) method.

    We will prepare the string list appropriately: it will contain the program and its arguments.

    The draft solution:

    public List<String> getProgramAndArguments(PMDParameters params) {
        final List<String> programAndArguments = new ArrayList<String>();
        programAndArguments.add("cmd");
        programAndArguments.add("/c");
        programAndArguments.add("pmd");
        programAndArguments.add("-d");
        programAndArguments.add(params.getSourceCodePath());
        programAndArguments.add("-f");
        programAndArguments.add(params.getOutputFormat());
        programAndArguments.add("-R");
        programAndArguments.add(params.getResultSet());
        programAndArguments.add(">");
        programAndArguments.add(params.getResultsPath());
        return programAndArguments;
    }
    
    public void runAnalyzer(PMDParameters params) throws IOException, InterruptedException {
        createProcess();
        processBuilder.command(getProgramAndArguments(params));
        final Process process = processBuilder.start();
        process.waitFor();
    }
    

    Additionally, please, make sure that params.getResultsPath() has the correct value by providing the correct input: now it is ..