From a92da836c6490267815c3a7373d18c2ec41c0ee5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 9 Nov 2025 12:15:17 +0000 Subject: [PATCH 1/4] Initial plan From 53d7b109cc763717fecccd91d39f43c34733e03e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 9 Nov 2025 12:22:51 +0000 Subject: [PATCH 2/4] Add StreamPollFeeder implementation and test Co-authored-by: slachiewicz <6705942+slachiewicz@users.noreply.github.com> --- .../plexus/util/cli/CommandLineUtils.java | 10 +- .../plexus/util/cli/StreamPollFeeder.java | 130 ++++++++++++++++++ .../plexus/util/cli/StreamPollFeederTest.java | 57 ++++++++ 3 files changed, 192 insertions(+), 5 deletions(-) create mode 100644 src/main/java/org/codehaus/plexus/util/cli/StreamPollFeeder.java create mode 100644 src/test/java/org/codehaus/plexus/util/cli/StreamPollFeederTest.java diff --git a/src/main/java/org/codehaus/plexus/util/cli/CommandLineUtils.java b/src/main/java/org/codehaus/plexus/util/cli/CommandLineUtils.java index b2ac489e..f7cf36d5 100644 --- a/src/main/java/org/codehaus/plexus/util/cli/CommandLineUtils.java +++ b/src/main/java/org/codehaus/plexus/util/cli/CommandLineUtils.java @@ -146,13 +146,13 @@ public void run() { @Override public Integer call() throws CommandLineException { - StreamFeeder inputFeeder = null; + StreamPollFeeder inputFeeder = null; StreamPumper outputPumper = null; StreamPumper errorPumper = null; boolean success = false; try { if (systemIn != null) { - inputFeeder = new StreamFeeder(systemIn, p.getOutputStream()); + inputFeeder = new StreamPollFeeder(systemIn, p.getOutputStream()); inputFeeder.start(); } @@ -288,11 +288,11 @@ private static void handleException(final StreamPumper streamPumper, final Strin } } - private static void handleException(final StreamFeeder streamFeeder, final String streamName) + private static void handleException(final StreamPollFeeder streamPollFeeder, final String streamName) throws CommandLineException { - if (streamFeeder.getException() != null) { + if (streamPollFeeder.getException() != null) { throw new CommandLineException( - String.format("Failure processing %s.", streamName), streamFeeder.getException()); + String.format("Failure processing %s.", streamName), streamPollFeeder.getException()); } } diff --git a/src/main/java/org/codehaus/plexus/util/cli/StreamPollFeeder.java b/src/main/java/org/codehaus/plexus/util/cli/StreamPollFeeder.java new file mode 100644 index 00000000..24ad32a2 --- /dev/null +++ b/src/main/java/org/codehaus/plexus/util/cli/StreamPollFeeder.java @@ -0,0 +1,130 @@ +package org.codehaus.plexus.util.cli; + +/* + * Copyright The Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * Poll InputStream for available data and write the output to an OutputStream. + * + * @author Trygve Laugstøl + */ +public class StreamPollFeeder extends AbstractStreamHandler { + + public static final int BUF_LEN = 80; + + private InputStream input; + + private OutputStream output; + + private volatile Throwable exception = null; + + private final Object lock = new Object(); + + /** + * Create a new StreamPollFeeder + * + * @param input Stream to read from + * @param output Stream to write to + */ + public StreamPollFeeder(InputStream input, OutputStream output) { + super(); + this.input = input; + this.output = output; + } + + @Override + public void run() { + byte[] buf = new byte[BUF_LEN]; + + try { + while (!isDone()) { + if (input.available() > 0) { + int i = input.read(buf); + if (i > 0) { + output.write(buf, 0, i); + output.flush(); + } else { + setDone(); + } + } else { + synchronized (lock) { + if (!isDone()) { + lock.wait(100); + } + } + } + } + } catch (IOException e) { + exception = e; + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } finally { + close(); + } + } + + public void close() { + if (input != null) { + synchronized (input) { + try { + input.close(); + } catch (IOException ex) { + if (exception == null) { + exception = ex; + } + } + + input = null; + } + } + + if (output != null) { + synchronized (output) { + try { + output.close(); + } catch (IOException ex) { + if (exception == null) { + exception = ex; + } + } + + output = null; + } + } + } + + /** + * @since 3.1.0 + * @return the Exception + */ + public Throwable getException() { + return exception; + } + + @Override + public synchronized void waitUntilDone() throws InterruptedException { + synchronized (lock) { + setDone(); + lock.notifyAll(); + } + + join(); + } +} diff --git a/src/test/java/org/codehaus/plexus/util/cli/StreamPollFeederTest.java b/src/test/java/org/codehaus/plexus/util/cli/StreamPollFeederTest.java new file mode 100644 index 00000000..19ea6376 --- /dev/null +++ b/src/test/java/org/codehaus/plexus/util/cli/StreamPollFeederTest.java @@ -0,0 +1,57 @@ +package org.codehaus.plexus.util.cli; + +/* + * Copyright The Codehaus Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class StreamPollFeederTest { + + @Test + public void dataShouldBeCopied() throws InterruptedException, IOException { + + StringBuilder TEST_DATA = new StringBuilder(); + for (int i = 0; i < 100; i++) { + TEST_DATA.append("TestData"); + } + + ByteArrayInputStream inputStream = + new ByteArrayInputStream(TEST_DATA.toString().getBytes()); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + + StreamPollFeeder streamPollFeeder = new StreamPollFeeder(inputStream, outputStream); + + streamPollFeeder.start(); + + // wait until all data from steam will be read + while (outputStream.size() < TEST_DATA.length()) { + Thread.sleep(100); + } + + // wait until process finish + streamPollFeeder.waitUntilDone(); + assertNull(streamPollFeeder.getException()); + + assertEquals(TEST_DATA.toString(), outputStream.toString()); + } +} From 364e1f6cb579a272abbf823c8289db1beac31670 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 9 Nov 2025 12:24:30 +0000 Subject: [PATCH 3/4] Deprecate StreamFeeder in favor of StreamPollFeeder Co-authored-by: slachiewicz <6705942+slachiewicz@users.noreply.github.com> --- src/main/java/org/codehaus/plexus/util/cli/StreamFeeder.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/codehaus/plexus/util/cli/StreamFeeder.java b/src/main/java/org/codehaus/plexus/util/cli/StreamFeeder.java index 81289044..f5e11451 100644 --- a/src/main/java/org/codehaus/plexus/util/cli/StreamFeeder.java +++ b/src/main/java/org/codehaus/plexus/util/cli/StreamFeeder.java @@ -24,8 +24,9 @@ * Read from an InputStream and write the output to an OutputStream. * * @author Trygve Laugstøl - * + * @deprecated Use {@link StreamPollFeeder} instead. This class can block when used with System.in */ +@Deprecated public class StreamFeeder extends AbstractStreamHandler { private InputStream input; From 268f05c7713e550aab377b258b91c361b602080d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 9 Nov 2025 12:29:20 +0000 Subject: [PATCH 4/4] Add documentation for StreamPollFeeder Co-authored-by: slachiewicz <6705942+slachiewicz@users.noreply.github.com> --- .../java/org/codehaus/plexus/util/cli/StreamPollFeeder.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/org/codehaus/plexus/util/cli/StreamPollFeeder.java b/src/main/java/org/codehaus/plexus/util/cli/StreamPollFeeder.java index 24ad32a2..8bd4f97a 100644 --- a/src/main/java/org/codehaus/plexus/util/cli/StreamPollFeeder.java +++ b/src/main/java/org/codehaus/plexus/util/cli/StreamPollFeeder.java @@ -22,6 +22,9 @@ /** * Poll InputStream for available data and write the output to an OutputStream. + *
+ * This class is designed to avoid blocking when reading from streams like System.in. + * It polls the input stream for available data instead of blocking on read operations. * * @author Trygve Laugstøl */