maven-mvnd icon indicating copy to clipboard operation
maven-mvnd copied to clipboard

Can only read 1 byte from stdin

Open SapiensAnatis opened this issue 2 months ago • 3 comments

Affected version

Apache Maven Daemon (mvnd) 2.0.0-rc-4-SNAPSHOT linux-amd64 native client (6734804eedc68f0f30aca315e842f3ec015def73)
Terminal: org.jline.terminal.impl.PosixSysTerminal with pty org.jline.terminal.impl.jni.linux.LinuxNativePty
Apache Maven 4.0.0-rc-4 (bed0f8174bf728978f86fac533aa38a9511f3872)
Maven home: /home/jay/Projects/maven-mvnd/dist/target/maven-mvnd-2.0.0-rc-4-SNAPSHOT-linux-amd64/mvn
Java version: 24.0.2, vendor: GraalVM Community, runtime: /home/jay/.local/jvm/jdk24-graalvm
Default locale: en_GB, platform encoding: UTF-8
OS name: "linux", version: "6.17.0-2-default", arch: "amd64", family: "unix"

Bug description

I am looking to use mvnd as a way to run an auto-formatter in an IDE plugin, so far it looks much faster than mvn so I'm very keen to get it working.

The auto-formatter mojo tries to read the document from stdin. I have noticed this works fine in mvn but on mvnd it is only able to read 1 byte. I made the following mojo to demonstrate the issue:

package sample.plugin;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Mojo;
import java.nio.charset.StandardCharsets;

@Mojo(name = "sayhi")
public class GreetingMojo extends AbstractMojo {
    @Override
    public void execute() throws MojoExecutionException {
        byte[] bytes = new byte[] {};

        try {
            System.out.println("Available bytes: " + System.in.available());
            bytes = System.in.readAllBytes();
        } catch (Exception ex) {
            getLog().error("Failed to read from stdin", ex);
        }

        System.out.println("Read " + bytes.length + " bytes from stdin.");
        System.out.println("Stdin content: \"" + new String(bytes, StandardCharsets.UTF_8) + "\"");
    }
}

If I run this with mvn as follows:

$ echo -n "Hello" | mvn sample.plugin:hello-maven-plugin:1.0-SNAPSHOT:sayhi

then I get the expected output:

[INFO] --- hello:1.0-SNAPSHOT:sayhi (default-cli) @ hello-maven-plugin ---
[INFO] [stdout] Available bytes: 5
[INFO] [stdout] Read 5 bytes from stdin.
[INFO] [stdout] Stdin content: "Hello"

However if I use mvnd:

$ echo -n "Hello" | mvnd sample.plugin:hello-maven-plugin:1.0-SNAPSHOT:sayhi

then I get this output:

[INFO] --- hello:1.0-SNAPSHOT:sayhi (default-cli) @ hello-maven-plugin ---
[INFO] [stdout] Available bytes: 0
[INFO] [stdout] Read 1 bytes from stdin.
[INFO] [stdout] Stdin content: "H"

or sometimes I get [INFO] [stdout] Stdin content: "". I have found if I ignore System.in.available() and keep trying to read 1 character at a time I can eventually get the whole stdin content, but it would be nice if readAllBytes just worked.

Could this be a delay in the daemon receiving the standard input from the terminal interface? I did try adding a sleep for 1s at the start of my mojo but this didn't change the behaviour at all.

SapiensAnatis avatar Oct 12 '25 16:10 SapiensAnatis

In an attempt to be helpful I thought I would look at the code and see what I could find, I could be completely wrong but I suspect the cause may be sending an EOF before sending the text in TerminalInputHandler::handleProjectInput. I made this change to add some debug logging and move the EOF to after the response:

index c4071cbe..74f0aa62 100644
--- a/common/src/main/java/org/mvndaemon/mvnd/common/logging/TerminalInputHandler.java
+++ b/common/src/main/java/org/mvndaemon/mvnd/common/logging/TerminalInputHandler.java
@@ -127,6 +127,8 @@ public class TerminalInputHandler implements AutoCloseable {
     }
 
     private void handleProjectInput(String projectId, int bytesToRead) throws IOException {
+        System.err.println("Project " + projectId + " requests to read " + bytesToRead + " bytes.");
+
         if (daemonReceive == null) {
             return;
         }
@@ -138,17 +140,22 @@ public class TerminalInputHandler implements AutoCloseable {
             int c = terminal.reader().read(timeout);
             if (c < 0) {
                 // End of stream reached
-                daemonReceive.accept(Message.inputEof());
+                System.out.println("Reached end of stream");
                 break;
             }
+
+            System.out.println("Read character: " + (char)c);
             buf[idx++] = (char) c;
             timeout = idx > 0 ? 1 : 10; // Shorter timeout after first char
         }
 
         if (idx > 0) {
             String data = String.valueOf(buf, 0, idx);
+            System.out.println("Sending response: " + data);
             daemonReceive.accept(Message.inputResponse(data));
         }
+
+        daemonReceive.accept(Message.inputEof());
     }
 
     private void handleControlKeys() throws IOException {

then my example sort of works, but I have to remove my call to System.in.available(). It comes through like this:

[INFO] --- hello:1.0-SNAPSHOT:sayhi (default-cli) @ hello-maven-plugin ---
Project hello-maven-plugin requests to read 16384 bytes.
Read character: H
Read character: e
Read character: l
Read character: l
Read character: o
Reached end of stream
Sending response: Hello
Project hello-maven-plugin requests to read 16379 bytes.
Reached end of stream
[INFO] [stdout] Read 5 bytes from stdin.
[INFO] [stdout] Stdin content: "Hello"

If I keep the System.in.available() it seems like that comes through as a request to read 1 byte, which breaks under both master and my change:

[INFO] --- hello:1.0-SNAPSHOT:sayhi (default-cli) @ hello-maven-plugin ---
Project hello-maven-plugin requests to read 1 bytes.
Read character: H
Sending response: H
[INFO] [stdout] Available bytes: 0
Project hello-maven-plugin requests to read 16383 bytes.
[INFO] [stdout] Read 1 bytes from stdin.
[INFO] [stdout] Stdin content: "H"
[INFO] ----------------------------------------------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ----------------------------------------------------------------------------------------------------------------
[INFO] Total time:  0.044 s (Wall Clock)
[INFO] Finished at: 2025-10-12T18:05:50+01:00
[INFO] ----------------------------------------------------------------------------------------------------------------
Read character: e
Read character: l
Read character: l
Read character: o
Reached end of stream
Sending response: ello

SapiensAnatis avatar Oct 12 '25 17:10 SapiensAnatis

Could you setup a simple reproducer project ?

gnodet avatar Oct 12 '25 18:10 gnodet

Yeah sure, I've just pushed up what I have been using to test locally: https://github.com/SapiensAnatis/maven-stdin-issue

You should just be able to use mvn install from the repo root and then use the commands from the post to see the issue.

SapiensAnatis avatar Oct 12 '25 18:10 SapiensAnatis