Skip to content

Commit

Permalink
add Processes.Builder#withInput()
Browse files Browse the repository at this point in the history
  • Loading branch information
sebthom committed Jun 20, 2024
1 parent a5f1b8e commit d2a7f4f
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 16 deletions.
80 changes: 65 additions & 15 deletions jstuff-core/src/main/java/net/sf/jstuff/core/io/Processes.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,21 +47,17 @@ public abstract class Processes {

public static class Builder {

private final String executable;
private final List<Object> args = new ArrayList<>(2);
@Nullable
private Map<String, Object> env;
private Function<Object, String> stringifier = Objects::toString;
@Nullable
private Consumer<ProcessWrapper> onExit;
@Nullable
private File workDir;

private final String executable;
private final List<Object> args = new ArrayList<>(2);
private @Nullable Map<String, Object> env;
private @Nullable File workDir;
private @Nullable Consumer<ProcessWrapper> onExit;
private boolean redirectErrorToOutput;
@Nullable
private Object redirectError;
@Nullable
private Object redirectOutput;
private @Nullable Object redirectError;
private @Nullable Object input;
private @Nullable Object redirectOutput;

protected Builder(final String exe) {
executable = exe;
Expand All @@ -83,10 +79,10 @@ public Builder onExit(final @Nullable ThrowingConsumer<ProcessWrapper, Throwable
}

@SuppressWarnings("null")
private CompletableFuture<Void> redirect(final InputStream in, final Appendable consumer) {
private CompletableFuture<Void> redirect(final InputStream in, final Appendable out) {
return CompletableFuture.runAsync(() -> {
try {
IOUtils.copy(new InputStreamReader(in, StandardCharsets.UTF_8), consumer);
IOUtils.copy(new InputStreamReader(in, StandardCharsets.UTF_8), out);
} catch (final IOException ex) {
throw new RuntimeIOException(ex);
}
Expand Down Expand Up @@ -115,6 +111,30 @@ private CompletableFuture<Void> redirect(final InputStream in, final OutputStrea
}, BACKGROUND_THREADS);
}

@SuppressWarnings("null")
private CompletableFuture<Void> writeToStdIn(final CharSequence in, final Process proc) {
return CompletableFuture.runAsync(() -> {
try (var out = proc.getOutputStream()) {
IOUtils.write(in, out, StandardCharsets.UTF_8);
} catch (final IOException ex) {
throw new RuntimeIOException(ex);
}
}, BACKGROUND_THREADS);
}

@SuppressWarnings("null")
private CompletableFuture<Void> writeToStdIn(final InputStream in, final Process proc) {
return CompletableFuture.runAsync(() -> {
try (var out = proc.getOutputStream()) {
IOUtils.copy(in, out);
} catch (final IOException ex) {
throw new RuntimeIOException(ex);
} finally {
IOUtils.closeQuietly(in);
}
}, BACKGROUND_THREADS);
}

/**
* Runs the command in the background and immediately returns.
*/
Expand All @@ -139,6 +159,17 @@ public ProcessWrapper start() throws IOException {
}

final Process proc = pb.start();

if (input != null) {
final var input = this.input;
if (input instanceof File) {
pb.redirectInput((File) input);
} else if (input instanceof InputStream) {
writeToStdIn((InputStream) input, proc);
} else if (input instanceof CharSequence) {
writeToStdIn((CharSequence) input, proc);
}
}
if (redirectErrorToOutput) {
pb.redirectError();
} else if (redirectError != null) {
Expand Down Expand Up @@ -166,6 +197,7 @@ public ProcessWrapper start() throws IOException {
redirect(proc.getInputStream(), (Consumer<String>) redirectOutput);
}
}

return new ProcessWrapper(proc) //
.onExit(onExit);
}
Expand Down Expand Up @@ -214,6 +246,16 @@ public Builder withEnvironment(final Consumer<Map<String, Object>> envConfigurer
return this;
}

public Builder withInput(final @Nullable CharSequence input) {
this.input = input;
return this;
}

public Builder withInput(final @Nullable InputStream input) {
this.input = input;
return this;
}

public Builder withRedirectError(final @Nullable Appendable target) {
assertRedirectErrorToOuputNotConfigured();
redirectError = target;
Expand Down Expand Up @@ -242,6 +284,10 @@ public Builder withRedirectError(final @Nullable Path target) {
return withRedirectError(target == null ? null : target.toFile());
}

/**
* @deprecated use {@link #withRedirectError(OutputStream)}
*/
@Deprecated
public Builder withRedirectError(final @Nullable PrintStream target) {
return withRedirectError((OutputStream) target);
}
Expand Down Expand Up @@ -277,6 +323,10 @@ public Builder withRedirectOutput(final @Nullable Path target) {
return this;
}

/**
* @deprecated use {@link #withRedirectOutput(OutputStream)}
*/
@Deprecated
public Builder withRedirectOutput(final @Nullable PrintStream target) {
return withRedirectOutput((OutputStream) target);
}
Expand Down Expand Up @@ -516,7 +566,7 @@ public static Builder builder(final String executable) {
* @return false if the given process was not alive, true if it was destroyed by this method.
*/
public static boolean destroy(final Process process, final int gracePeriod, final TimeUnit gracePeriodTimeUnit)
throws InterruptedException {
throws InterruptedException {
Args.notNull("process", process);
Args.notNegative("gracePeriod", gracePeriod);
Args.notNull("gracePeriodTimeUnit", gracePeriodTimeUnit);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*/
package net.sf.jstuff.core.io;

import static org.assertj.core.api.Assertions.*;
import static org.assertj.core.api.Assertions.assertThat;

import java.io.IOException;
import java.util.concurrent.CountDownLatch;
Expand All @@ -19,8 +19,29 @@
/**
* @author <a href="https://sebthom.de/">Sebastian Thomschke</a>
*/
@SuppressWarnings("deprecation")
public class ProcessesTest {

@Test
public void testWithStdInput() throws IOException, InterruptedException {
final var pwb = SystemUtils.IS_OS_WINDOWS ? Processes.builder("findstr").withArg("x*") : Processes.builder("cat");

final var stdout = new StringBuilder();
pwb.withInput("Hello World!\n");
pwb.withRedirectOutput(stdout);
pwb.withRedirectErrorToOutput();

final var pw = pwb.start();

// Wait for the process to exit
pw.waitForExit(2, TimeUnit.SECONDS);

// Assert the output and exit code
assertThat(pw.exitStatus()).isZero();
assertThat(stdout).hasToString("Hello World!\n");

}

@Test
public void testCaptureOutput() throws IOException, InterruptedException {
final var out = new StringBuilder();
Expand Down

0 comments on commit d2a7f4f

Please sign in to comment.