diff --git a/core/deployment/src/main/java/io/quarkus/deployment/util/ExecUtil.java b/core/deployment/src/main/java/io/quarkus/deployment/util/ExecUtil.java index 2d61e0831e376..e94e507b620a6 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/util/ExecUtil.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/util/ExecUtil.java @@ -7,6 +7,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.time.Duration; +import java.util.Optional; import java.util.concurrent.TimeUnit; import java.util.function.Function; @@ -16,43 +17,58 @@ public class ExecUtil { private static final Logger LOG = Logger.getLogger(ExecUtil.class); - private static final Function PRINT_OUTPUT = i -> new HandleOutput(i); - private static final Function SILENT = i -> new HandleOutput(i, Logger.Level.DEBUG); + private static final Function INFO_LOGGING = i -> new HandleOutput(i); + private static final Function DEBUG_LOGGING = i -> new HandleOutput(i, Logger.Level.DEBUG); + private static final Function SYSTEM_LOGGING = i -> new HandleOutput(i); + + private static Function SELECTED_LOGGING = INFO_LOGGING; private static final int PROCESS_CHECK_INTERVAL = 500; private static class HandleOutput implements Runnable { private final InputStream is; - private final Logger.Level logLevel; + private final Optional logLevel; HandleOutput(InputStream is) { - this(is, Logger.Level.INFO); + this(is, null); } HandleOutput(InputStream is, Logger.Level logLevel) { this.is = is; - this.logLevel = LOG.isEnabled(logLevel) ? logLevel : null; + this.logLevel = Optional.ofNullable(logLevel); } @Override public void run() { - try (InputStreamReader isr = new InputStreamReader(is); - BufferedReader reader = new BufferedReader(isr)) { - + try (InputStreamReader isr = new InputStreamReader(is); BufferedReader reader = new BufferedReader(isr)) { for (String line = reader.readLine(); line != null; line = reader.readLine()) { - if (logLevel != null) { - LOG.log(logLevel, line); - } + final String l = line; + logLevel.ifPresentOrElse(level -> LOG.log(level, l), () -> System.out.println(l)); } } catch (IOException e) { - if (logLevel != null) { - LOG.log(logLevel, "Failed to handle output", e); - } + logLevel.ifPresentOrElse(level -> LOG.log(level, "Failed to handle output", e), () -> e.printStackTrace()); } } } + public static void useInfoLogging() { + ExecUtil.SELECTED_LOGGING = INFO_LOGGING; + } + + public static void useDebugLogging() { + ExecUtil.SELECTED_LOGGING = DEBUG_LOGGING; + } + + /** + * There are cases where its preferable to just write to System.out. + * For example from maven-invoker verify scripts, logging can trigger Stack Overflow. + * For such cases its preferable to use this method. + */ + public static void useSystemLogging() { + ExecUtil.SELECTED_LOGGING = SYSTEM_LOGGING; + } + /** * Execute the specified command from within the current directory. * @@ -77,125 +93,131 @@ public static boolean execWithTimeout(Duration timeout, String command, String.. } /** - * Execute the specified command from within the current directory and hide the output. + * Execute the specified command from within the specified directory. * + * @param directory The directory * @param command The command * @param args The command arguments * @return true if commands where executed successfully */ - public static boolean execSilent(String command, String... args) { - return execSilent(new File("."), command, args); + public static boolean exec(File directory, String command, String... args) { + return exec(directory, SELECTED_LOGGING, command, args); } /** - * Execute the specified command until the given timeout from within the current directory and hide the output. + * Execute the specified command until the given timeout from within the specified directory. * + * @param directory The directory * @param timeout The timeout * @param command The command * @param args The command arguments * @return true if commands where executed successfully */ - public static boolean execSilentWithTimeout(Duration timeout, String command, String... args) { - return execSilentWithTimeout(new File("."), timeout, command, args); + public static boolean execWithTimeout(File directory, Duration timeout, String command, String... args) { + return execWithTimeout(directory, SELECTED_LOGGING, timeout, command, args); } /** * Execute the specified command from within the specified directory. + * The method allows specifying an output filter that processes the command output. * * @param directory The directory + * @param outputFilterFunction A {@link Function} that gets an {@link InputStream} and returns an outputFilter. * @param command The command * @param args The command arguments * @return true if commands where executed successfully */ - public static boolean exec(File directory, String command, String... args) { - return exec(directory, PRINT_OUTPUT, command, args); + public static boolean exec(File directory, Function outputFilterFunction, String command, + String... args) { + try { + Function loggingFunction = outputFilterFunction != null ? outputFilterFunction + : INFO_LOGGING; + Process process = startProcess(directory, command, args); + Thread t = new Thread(loggingFunction.apply(process.getInputStream())); + t.setName("Process stdout"); + t.setDaemon(true); + t.start(); + process.waitFor(); + destroyProcess(process); + return process.exitValue() == 0; + } catch (InterruptedException e) { + return false; + } } /** * Execute the specified command until the given timeout from within the specified directory. + * The method allows specifying an output filter that processes the command output. * * @param directory The directory + * @param outputFilterFunction A {@link Function} that gets an {@link InputStream} and returns an outputFilter. * @param timeout The timeout * @param command The command * @param args The command arguments * @return true if commands where executed successfully */ - public static boolean execWithTimeout(File directory, Duration timeout, String command, String... args) { - return execWithTimeout(directory, PRINT_OUTPUT, timeout, command, args); + public static boolean execWithTimeout(File directory, Function outputFilterFunction, + Duration timeout, String command, String... args) { + try { + Function loggingFunction = outputFilterFunction != null ? outputFilterFunction + : INFO_LOGGING; + Process process = startProcess(directory, command, args); + Thread t = new Thread(loggingFunction.apply(process.getInputStream())); + t.setName("Process stdout"); + t.setDaemon(true); + t.start(); + process.waitFor(timeout.toMillis(), TimeUnit.MILLISECONDS); + destroyProcess(process); + return process.exitValue() == 0; + } catch (InterruptedException e) { + return false; + } } /** - * Execute the specified command from within the specified directory and hide the output. + * Execute the specified command from within the current directory using debug logging. * - * @param directory The directory * @param command The command * @param args The command arguments * @return true if commands where executed successfully */ - public static boolean execSilent(File directory, String command, String... args) { - return exec(directory, SILENT, command, args); + public static boolean execWithDebugLogging(String command, String... args) { + return execWithDebugLogging(new File("."), command, args); } /** - * Execute the specified command until the given timeout from within the specified directory and hide the output. + * Execute the specified command from within the specified directory using debug logging. * * @param directory The directory - * @param timeout The timeout * @param command The command * @param args The command arguments * @return true if commands where executed successfully */ - public static boolean execSilentWithTimeout(File directory, Duration timeout, String command, String... args) { - return execWithTimeout(directory, SILENT, timeout, command, args); + public static boolean execWithDebugLogging(File directory, String command, String... args) { + return exec(directory, DEBUG_LOGGING, command, args); } /** - * Execute the specified command from within the specified directory. - * The method allows specifying an output filter that processes the command output. + * Execute the specified command from within the current directory using system logging. * - * @param directory The directory - * @param outputFilterFunction A {@link Function} that gets an {@link InputStream} and returns an outputFilter. * @param command The command * @param args The command arguments * @return true if commands where executed successfully */ - public static boolean exec(File directory, Function outputFilterFunction, String command, - String... args) { - try { - Process process = startProcess(directory, command, args); - outputFilterFunction.apply(process.getInputStream()).run(); - process.waitFor(); - return process.exitValue() == 0; - } catch (InterruptedException e) { - return false; - } + public static boolean execWithSystemLogging(String command, String... args) { + return execWithSystemLogging(new File("."), command, args); } /** - * Execute the specified command until the given timeout from within the specified directory. - * The method allows specifying an output filter that processes the command output. + * Execute the specified command from within the specified directory using system logging. * * @param directory The directory - * @param outputFilterFunction A {@link Function} that gets an {@link InputStream} and returns an outputFilter. - * @param timeout The timeout * @param command The command * @param args The command arguments * @return true if commands where executed successfully */ - public static boolean execWithTimeout(File directory, Function outputFilterFunction, - Duration timeout, String command, String... args) { - try { - Process process = startProcess(directory, command, args); - Thread t = new Thread(outputFilterFunction.apply(process.getInputStream())); - t.setName("Process stdout"); - t.setDaemon(true); - t.start(); - process.waitFor(timeout.toMillis(), TimeUnit.MILLISECONDS); - destroyProcess(process); - return process.exitValue() == 0; - } catch (InterruptedException e) { - return false; - } + public static boolean execWithSystemLogging(File directory, String command, String... args) { + return exec(directory, SYSTEM_LOGGING, command, args); } /** @@ -243,5 +265,4 @@ public static void destroyProcess(Process process) { process.destroyForcibly(); } } - } diff --git a/integration-tests/container-image/maven-invoker-way/src/it/container-build-docker/verify.groovy b/integration-tests/container-image/maven-invoker-way/src/it/container-build-docker/verify.groovy index 508f36d8c387b..4508a3f377090 100644 --- a/integration-tests/container-image/maven-invoker-way/src/it/container-build-docker/verify.groovy +++ b/integration-tests/container-image/maven-invoker-way/src/it/container-build-docker/verify.groovy @@ -1,5 +1,7 @@ import io.quarkus.deployment.util.ExecUtil +ExecUtil.useSystemLogging() //prevents stack overflow issues + try { ExecUtil.exec("docker", "version", "--format", "'{{.Server.Version}}'") } catch (Exception ignored) { @@ -9,4 +11,4 @@ try { String group = System.getProperty("user.name") assert ExecUtil.exec("docker", "images", group + "/container-build-docker") -assert ExecUtil.exec("docker", "rmi", group + "/container-build-docker:0.1-SNAPSHOT") \ No newline at end of file +assert ExecUtil.exec("docker", "rmi", group + "/container-build-docker:0.1-SNAPSHOT") diff --git a/integration-tests/container-image/maven-invoker-way/src/it/container-build-jib-appcds/verify.groovy b/integration-tests/container-image/maven-invoker-way/src/it/container-build-jib-appcds/verify.groovy index 9787c6f7f8335..357496be9f092 100644 --- a/integration-tests/container-image/maven-invoker-way/src/it/container-build-jib-appcds/verify.groovy +++ b/integration-tests/container-image/maven-invoker-way/src/it/container-build-jib-appcds/verify.groovy @@ -2,6 +2,7 @@ import io.quarkus.deployment.util.ExecUtil import java.util.concurrent.ThreadLocalRandom +ExecUtil.useSystemLogging() //prevents stack overflow issues try { ExecUtil.exec("docker", "version", "--format", "'{{.Server.Version}}'") } catch (Exception ignored) { diff --git a/integration-tests/container-image/maven-invoker-way/src/it/container-build-jib-inherit/verify.groovy b/integration-tests/container-image/maven-invoker-way/src/it/container-build-jib-inherit/verify.groovy index 9997de49e3f8c..f8bf500bc48a0 100644 --- a/integration-tests/container-image/maven-invoker-way/src/it/container-build-jib-inherit/verify.groovy +++ b/integration-tests/container-image/maven-invoker-way/src/it/container-build-jib-inherit/verify.groovy @@ -2,6 +2,8 @@ import io.quarkus.deployment.util.ExecUtil import java.util.concurrent.ThreadLocalRandom +ExecUtil.useSystemLogging() //prevents stack overflow issues + try { ExecUtil.exec("docker", "version", "--format", "'{{.Server.Version}}'") } catch (Exception ignored) { diff --git a/integration-tests/container-image/maven-invoker-way/src/it/container-build-jib/verify.groovy b/integration-tests/container-image/maven-invoker-way/src/it/container-build-jib/verify.groovy index 5e11627791e4e..46ad840deef04 100644 --- a/integration-tests/container-image/maven-invoker-way/src/it/container-build-jib/verify.groovy +++ b/integration-tests/container-image/maven-invoker-way/src/it/container-build-jib/verify.groovy @@ -4,6 +4,7 @@ import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths +ExecUtil.useSystemLogging() //prevents stack overflow issues try { ExecUtil.exec("docker", "version", "--format", "'{{.Server.Version}}'") } catch (Exception ignored) { diff --git a/integration-tests/container-image/maven-invoker-way/src/it/container-build-multiple-tags-docker/verify.groovy b/integration-tests/container-image/maven-invoker-way/src/it/container-build-multiple-tags-docker/verify.groovy index 2e3040e79d073..7ce1459a76536 100644 --- a/integration-tests/container-image/maven-invoker-way/src/it/container-build-multiple-tags-docker/verify.groovy +++ b/integration-tests/container-image/maven-invoker-way/src/it/container-build-multiple-tags-docker/verify.groovy @@ -1,5 +1,6 @@ import io.quarkus.deployment.util.ExecUtil +ExecUtil.useSystemLogging() //prevents stack overflow issues try { ExecUtil.exec("docker", "version", "--format", "'{{.Server.Version}}'") } catch (Exception ignored) { diff --git a/integration-tests/container-image/maven-invoker-way/src/it/container-build-multiple-tags-jib/verify.groovy b/integration-tests/container-image/maven-invoker-way/src/it/container-build-multiple-tags-jib/verify.groovy index d3914dd083039..4198a8b3fdbb8 100644 --- a/integration-tests/container-image/maven-invoker-way/src/it/container-build-multiple-tags-jib/verify.groovy +++ b/integration-tests/container-image/maven-invoker-way/src/it/container-build-multiple-tags-jib/verify.groovy @@ -1,5 +1,6 @@ import io.quarkus.deployment.util.ExecUtil +ExecUtil.useSystemLogging() //prevents stack overflow issues try { ExecUtil.exec("docker", "version", "--format", "'{{.Server.Version}}'") } catch (Exception ignored) { diff --git a/integration-tests/container-image/maven-invoker-way/src/it/container-image-push/setup.groovy b/integration-tests/container-image/maven-invoker-way/src/it/container-image-push/setup.groovy index 36d1a0f17e7a2..10e1af8bc32c3 100644 --- a/integration-tests/container-image/maven-invoker-way/src/it/container-image-push/setup.groovy +++ b/integration-tests/container-image/maven-invoker-way/src/it/container-image-push/setup.groovy @@ -1,5 +1,6 @@ import io.quarkus.deployment.util.ExecUtil +ExecUtil.useSystemLogging() //prevents stack overflow issues try { ExecUtil.exec("docker", "version", "--format", "'{{.Server.Version}}'") } catch (Exception ignored) { @@ -7,4 +8,4 @@ try { return } -assert ExecUtil.exec("docker", "run", "--rm", "-p", "5000:5000", "-d", "--name", "registry" ,"registry:2"); \ No newline at end of file +assert ExecUtil.exec("docker", "run", "--rm", "-p", "5000:5000", "-d", "--name", "registry" ,"registry:2"); diff --git a/integration-tests/container-image/maven-invoker-way/src/it/container-image-push/verify.groovy b/integration-tests/container-image/maven-invoker-way/src/it/container-image-push/verify.groovy index 9651c3ddae472..4e7e2642de850 100644 --- a/integration-tests/container-image/maven-invoker-way/src/it/container-image-push/verify.groovy +++ b/integration-tests/container-image/maven-invoker-way/src/it/container-image-push/verify.groovy @@ -3,6 +3,7 @@ import io.quarkus.deployment.util.ExecUtil import static io.restassured.RestAssured.get import static org.hamcrest.Matchers.containsString +ExecUtil.useSystemLogging() //prevents stack overflow issues try { ExecUtil.exec("docker", "version", "--format", "'{{.Server.Version}}'") } catch (Exception ignored) { @@ -14,4 +15,4 @@ get("http://localhost:5000/v2/_catalog") .then() .body(containsString("container-image-push")) -assert ExecUtil.exec("docker", "stop", "registry") \ No newline at end of file +assert ExecUtil.exec("docker", "stop", "registry")