src/jdk.jpackage/share/classes/jdk/jpackage/internal/Executor.java
branchJDK-8200758-branch
changeset 58994 b09ba68c6a19
parent 58993 b5e1baa9d2c3
child 58995 de1413ae214c
equal deleted inserted replaced
58993:b5e1baa9d2c3 58994:b09ba68c6a19
     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.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 package jdk.jpackage.internal;
       
    26 
       
    27 import java.io.BufferedReader;
       
    28 import java.io.IOException;
       
    29 import java.io.InputStreamReader;
       
    30 import java.util.List;
       
    31 import java.util.function.Consumer;
       
    32 import java.util.function.Supplier;
       
    33 import java.util.stream.Collectors;
       
    34 import java.util.stream.Stream;
       
    35 
       
    36 final public class Executor {
       
    37 
       
    38     Executor() {
       
    39     }
       
    40 
       
    41     Executor setOutputConsumer(Consumer<Stream<String>> v) {
       
    42         outputConsumer = v;
       
    43         return this;
       
    44     }
       
    45 
       
    46     Executor saveOutput(boolean v) {
       
    47         saveOutput = v;
       
    48         return this;
       
    49     }
       
    50 
       
    51     Executor setProcessBuilder(ProcessBuilder v) {
       
    52         pb = v;
       
    53         return this;
       
    54     }
       
    55 
       
    56     Executor setCommandLine(String... cmdline) {
       
    57         return setProcessBuilder(new ProcessBuilder(cmdline));
       
    58     }
       
    59 
       
    60     List<String> getOutput() {
       
    61         return output;
       
    62     }
       
    63 
       
    64     Executor executeExpectSuccess() throws IOException {
       
    65         int ret = execute();
       
    66         if (0 != ret) {
       
    67             throw new IOException(
       
    68                     String.format("Command %s exited with %d code",
       
    69                             createLogMessage(pb), ret));
       
    70         }
       
    71         return this;
       
    72     }
       
    73 
       
    74     int execute() throws IOException {
       
    75         output = null;
       
    76 
       
    77         boolean needProcessOutput = outputConsumer != null || Log.isVerbose() || saveOutput;
       
    78         if (needProcessOutput) {
       
    79             pb.redirectErrorStream(true);
       
    80         } else {
       
    81             // We are not going to read process output, so need to notify
       
    82             // ProcessBuilder about this. Otherwise some processes might just
       
    83             // hang up (`ldconfig -p`).
       
    84             pb.redirectError(ProcessBuilder.Redirect.DISCARD);
       
    85             pb.redirectOutput(ProcessBuilder.Redirect.DISCARD);
       
    86         }
       
    87 
       
    88         Log.verbose(String.format("Running %s", createLogMessage(pb)));
       
    89         Process p = pb.start();
       
    90 
       
    91         if (needProcessOutput) {
       
    92             try (var br = new BufferedReader(new InputStreamReader(
       
    93                     p.getInputStream()))) {
       
    94                 final List<String> savedOutput;
       
    95                 // Need to save output if explicitely requested (saveOutput=true) or
       
    96                 // if will be used used by multiple consumers
       
    97                 if ((outputConsumer != null && Log.isVerbose()) || saveOutput) {
       
    98                     savedOutput = br.lines().collect(Collectors.toList());
       
    99                     if (saveOutput) {
       
   100                         output = savedOutput;
       
   101                     }
       
   102                 } else {
       
   103                     savedOutput = null;
       
   104                 }
       
   105 
       
   106                 Supplier<Stream<String>> outputStream = () -> {
       
   107                     if (savedOutput != null) {
       
   108                         return savedOutput.stream();
       
   109                     }
       
   110                     return br.lines();
       
   111                 };
       
   112 
       
   113                 if (Log.isVerbose()) {
       
   114                     outputStream.get().forEach(Log::verbose);
       
   115                 }
       
   116 
       
   117                 if (outputConsumer != null) {
       
   118                     outputConsumer.accept(outputStream.get());
       
   119                 }
       
   120 
       
   121                 if (savedOutput == null) {
       
   122                     // For some processes on Linux if the output stream
       
   123                     // of the process is opened but not consumed, the process
       
   124                     // would exit with code 141.
       
   125                     // It turned out that reading just a single line of process
       
   126                     // output fixes the problem, but let's process
       
   127                     // all of the output, just in case.
       
   128                     br.lines().forEach(x -> {});
       
   129                 }
       
   130             }
       
   131         }
       
   132 
       
   133         try {
       
   134             return p.waitFor();
       
   135         } catch (InterruptedException ex) {
       
   136             Log.verbose(ex);
       
   137             throw new RuntimeException(ex);
       
   138         }
       
   139     }
       
   140 
       
   141     static Executor of(String... cmdline) {
       
   142         return new Executor().setCommandLine(cmdline);
       
   143     }
       
   144 
       
   145     static Executor of(ProcessBuilder pb) {
       
   146         return new Executor().setProcessBuilder(pb);
       
   147     }
       
   148 
       
   149     private static String createLogMessage(ProcessBuilder pb) {
       
   150         StringBuilder sb = new StringBuilder();
       
   151         sb.append(String.format("%s", pb.command()));
       
   152         if (pb.directory() != null) {
       
   153             sb.append(String.format("in %s", pb.directory().getAbsolutePath()));
       
   154         }
       
   155         return sb.toString();
       
   156     }
       
   157 
       
   158     private ProcessBuilder pb;
       
   159     private boolean saveOutput;
       
   160     private List<String> output;
       
   161     private Consumer<Stream<String>> outputConsumer;
       
   162 }