1 /*
   2  * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package jdk.jpackage.test;
  24 
  25 import java.io.IOException;
  26 import java.io.PrintWriter;
  27 import java.io.StringWriter;
  28 import java.nio.file.Files;
  29 import java.nio.file.Path;
  30 import java.util.ArrayList;
  31 import java.util.Arrays;
  32 import java.util.Collections;
  33 import java.util.List;
  34 import java.util.spi.ToolProvider;
  35 import java.util.stream.Stream;
  36 
  37 public final class Executor extends CommandArguments<Executor> {
  38 
  39     public Executor() {
  40         saveOutputType = SaveOutputType.NONE;
  41     }
  42 
  43     public Executor setExecutable(String v) {
  44         executable = v;
  45         if (executable != null) {
  46             toolProvider = null;
  47         }
  48         return this;
  49     }
  50 
  51     public Executor setToolProvider(ToolProvider v) {
  52         toolProvider = v;
  53         if (toolProvider != null) {
  54             executable = null;
  55         }
  56         return this;
  57     }
  58 
  59     public Executor setDirectory(Path v) {
  60         directory = v;
  61         return this;
  62     }
  63 
  64     public Executor setExecutable(JavaTool v) {
  65         return setExecutable(v.getPath().getAbsolutePath());
  66     }
  67 
  68     public Executor saveOutput() {
  69         saveOutputType = SaveOutputType.FULL;
  70         return this;
  71     }
  72 
  73     public Executor saveFirstLineOfOutput() {
  74         saveOutputType = SaveOutputType.FIRST_LINE;
  75         return this;
  76     }
  77 
  78     public class Result {
  79 
  80         Result(int exitCode) {
  81             this.exitCode = exitCode;
  82         }
  83 
  84         public String getFirstLineOfOutput() {
  85             return output.get(0).trim();
  86         }
  87 
  88         public List<String> getOutput() {
  89             return output;
  90         }
  91 
  92         public String getPrintableCommandLine() {
  93             return Executor.this.getPrintableCommandLine();
  94         }
  95 
  96         public Result assertExitCodeIs(int expectedExitCode) {
  97             Test.assertEquals(expectedExitCode, exitCode, String.format(
  98                     "Check command %s exited with %d code",
  99                     getPrintableCommandLine(), expectedExitCode));
 100             return this;
 101         }
 102 
 103         public Result assertExitCodeIsZero() {
 104             return assertExitCodeIs(0);
 105         }
 106 
 107         final int exitCode;
 108         private List<String> output;
 109     }
 110 
 111     public Result execute() {
 112         if (toolProvider != null) {
 113             return runToolProvider();
 114         }
 115 
 116         try {
 117             if (executable != null) {
 118                 return runExecutable();
 119             }
 120         } catch (RuntimeException e) {
 121             throw e;
 122         } catch (IOException | InterruptedException e) {
 123             throw new RuntimeException(e);
 124         }
 125 
 126         throw new IllegalStateException("No command to execute");
 127     }
 128 
 129     private Result runExecutable() throws IOException, InterruptedException {
 130         List<String> command = new ArrayList<>();
 131         command.add(executable);
 132         command.addAll(args);
 133         Path outputFile = null;
 134         ProcessBuilder builder = new ProcessBuilder(command);
 135         StringBuilder sb = new StringBuilder(getPrintableCommandLine());
 136         if (saveOutputType != SaveOutputType.NONE) {
 137             outputFile = Test.createTempFile(".out");
 138             builder.redirectErrorStream(true);
 139             builder.redirectOutput(outputFile.toFile());
 140             sb.append(String.format("; redirect output to [%s]", outputFile));
 141         }
 142         if (directory != null) {
 143             builder.directory(directory.toFile());
 144             sb.append(String.format("; in directory [%s]", directory));
 145         }
 146 
 147         try {
 148             Test.trace("Execute " + sb.toString() + "...");
 149             Process process = builder.start();
 150             Result reply = new Result(process.waitFor());
 151             Test.trace("Done. Exit code: " + reply.exitCode);
 152             if (saveOutputType == SaveOutputType.FIRST_LINE) {
 153                 reply.output = Arrays.asList(
 154                         Files.readAllLines(outputFile).stream().findFirst().get());
 155             } else if (saveOutputType == SaveOutputType.FULL) {
 156                 reply.output = Collections.unmodifiableList(Files.readAllLines(
 157                         outputFile));
 158             }
 159             return reply;
 160         } finally {
 161             if (outputFile != null) {
 162                 Files.deleteIfExists(outputFile);
 163             }
 164         }
 165     }
 166 
 167     private Result runToolProvider() {
 168         StringWriter writer = new StringWriter();
 169         PrintWriter pw = new PrintWriter(writer);
 170 
 171         Test.trace("Execute " + getPrintableCommandLine() + "...");
 172         Result reply = new Result(toolProvider.run(pw, pw, args.toArray(
 173                 String[]::new)));
 174         Test.trace("Done. Exit code: " + reply.exitCode);
 175 
 176         List lines = List.of(writer.toString().split("\\R", -1));
 177 
 178         if (saveOutputType == SaveOutputType.FIRST_LINE) {
 179             reply.output = Stream.of(lines).findFirst().get();
 180         } else if (saveOutputType == SaveOutputType.FULL) {
 181             reply.output = Collections.unmodifiableList(lines);
 182         }
 183         return reply;
 184     }
 185 
 186     public String getPrintableCommandLine() {
 187         String argsStr = String.format("; args(%d)=%s", args.size(),
 188                 Arrays.toString(args.toArray()));
 189 
 190         if (toolProvider == null && executable == null) {
 191             return "[null]; " + argsStr;
 192         }
 193 
 194         if (toolProvider != null) {
 195             return String.format("tool provider=[%s]; ", toolProvider.name()) + argsStr;
 196         }
 197 
 198         return String.format("[%s]; ", executable) + argsStr;
 199     }
 200 
 201     private ToolProvider toolProvider;
 202     private String executable;
 203     private SaveOutputType saveOutputType;
 204     private Path directory;
 205 
 206     private static enum SaveOutputType {
 207         NONE, FULL, FIRST_LINE
 208     };
 209 }