From 8e6ce5ebe6dfa2fd2baab645c844d0e05ddd2873 Mon Sep 17 00:00:00 2001 From: Burak KALAYCI Date: Tue, 4 Aug 2026 16:39:21 +0300 Subject: [PATCH] [#2514] Flush out/err PrintWriters after CommandLine.execute print() does not trigger autoFlush on PrintWriter. Flush the root and parsed command writers in a finally block so buffered output is not lost when the JVM exits immediately after execute(). --- src/main/java/picocli/CommandLine.java | 19 ++++++++++++ src/test/java/picocli/ExecuteTest.java | 41 ++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/src/main/java/picocli/CommandLine.java b/src/main/java/picocli/CommandLine.java index ab95179f1..a2cba9984 100644 --- a/src/main/java/picocli/CommandLine.java +++ b/src/main/java/picocli/CommandLine.java @@ -2187,6 +2187,25 @@ public int execute(String... args) { } } catch (Exception ex) { return handleUnhandled(ex, this, getCommandSpec().exitCodeOnExecutionException()); + } finally { + // #2514 print() does not auto-flush; ensure out/err buffers are flushed after execute + flush(this); + if (parseResult[0] != null) { + for (CommandLine parsed : parseResult[0].asCommandLineList()) { + flush(parsed); + } + } + } + } + private static void flush(CommandLine cmd) { + if (cmd == null) { + return; + } + if (cmd.out != null) { + cmd.out.flush(); + } + if (cmd.err != null) { + cmd.err.flush(); } } private static int handleUnhandled(Exception ex, CommandLine cmd, int defaultExitCode) { diff --git a/src/test/java/picocli/ExecuteTest.java b/src/test/java/picocli/ExecuteTest.java index 96c0f19b5..f1d7355dc 100644 --- a/src/test/java/picocli/ExecuteTest.java +++ b/src/test/java/picocli/ExecuteTest.java @@ -1626,5 +1626,46 @@ public int handleParseException(ParameterException ex, String[] args) throws Exc } assertEquals(expected, lines); } + + @Command(name = "demo") + static class Issue2514Command implements Callable { + @Spec CommandSpec spec; + private final boolean writeErr; + + Issue2514Command(boolean writeErr) { + this.writeErr = writeErr; + } + + public Integer call() { + if (writeErr) { + spec.commandLine().getErr().print("err-hello"); + } else { + spec.commandLine().getOut().print("hello"); + } + return 0; + } + } + + @Test + public void testExecuteFlushesOutAfterPrintWithoutNewline() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + PrintWriter out = CommandLine.newPrintWriter(baos, getStdoutEncoding()); + + int exit = new CommandLine(new Issue2514Command(false)).setOut(out).execute(); + + assertEquals(ExitCode.OK, exit); + assertEquals("hello", baos.toString()); + } + + @Test + public void testExecuteFlushesErrAfterPrintWithoutNewline() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + PrintWriter err = CommandLine.newPrintWriter(baos, getStdoutEncoding()); + + int exit = new CommandLine(new Issue2514Command(true)).setErr(err).execute(); + + assertEquals(ExitCode.OK, exit); + assertEquals("err-hello", baos.toString()); + } }