/* * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package jdk.incubator.jpackage.internal; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.List; import java.util.function.Consumer; import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; final public class Executor { Executor() { } Executor setOutputConsumer(Consumer> v) { outputConsumer = v; return this; } Executor saveOutput(boolean v) { saveOutput = v; return this; } Executor setProcessBuilder(ProcessBuilder v) { pb = v; return this; } Executor setCommandLine(String... cmdline) { return setProcessBuilder(new ProcessBuilder(cmdline)); } List getOutput() { return output; } Executor executeExpectSuccess() throws IOException { int ret = execute(); if (0 != ret) { throw new IOException( String.format("Command %s exited with %d code", createLogMessage(pb), ret)); } return this; } int execute() throws IOException { output = null; boolean needProcessOutput = outputConsumer != null || Log.isVerbose() || saveOutput; if (needProcessOutput) { pb.redirectErrorStream(true); } else { // We are not going to read process output, so need to notify // ProcessBuilder about this. Otherwise some processes might just // hang up (`ldconfig -p`). pb.redirectError(ProcessBuilder.Redirect.DISCARD); pb.redirectOutput(ProcessBuilder.Redirect.DISCARD); } Log.verbose(String.format("Running %s", createLogMessage(pb))); Process p = pb.start(); if (needProcessOutput) { try (var br = new BufferedReader(new InputStreamReader( p.getInputStream()))) { final List savedOutput; // Need to save output if explicitely requested (saveOutput=true) or // if will be used used by multiple consumers if ((outputConsumer != null && Log.isVerbose()) || saveOutput) { savedOutput = br.lines().collect(Collectors.toList()); if (saveOutput) { output = savedOutput; } } else { savedOutput = null; } Supplier> outputStream = () -> { if (savedOutput != null) { return savedOutput.stream(); } return br.lines(); }; if (Log.isVerbose()) { outputStream.get().forEach(Log::verbose); } if (outputConsumer != null) { outputConsumer.accept(outputStream.get()); } if (savedOutput == null) { // For some processes on Linux if the output stream // of the process is opened but not consumed, the process // would exit with code 141. // It turned out that reading just a single line of process // output fixes the problem, but let's process // all of the output, just in case. br.lines().forEach(x -> {}); } } } try { return p.waitFor(); } catch (InterruptedException ex) { Log.verbose(ex); throw new RuntimeException(ex); } } static Executor of(String... cmdline) { return new Executor().setCommandLine(cmdline); } static Executor of(ProcessBuilder pb) { return new Executor().setProcessBuilder(pb); } private static String createLogMessage(ProcessBuilder pb) { StringBuilder sb = new StringBuilder(); sb.append(String.format("%s", pb.command())); if (pb.directory() != null) { sb.append(String.format("in %s", pb.directory().getAbsolutePath())); } return sb.toString(); } private ProcessBuilder pb; private boolean saveOutput; private List output; private Consumer> outputConsumer; }