Verified Commit aab5063a authored by Matteo Melli's avatar Matteo Melli
Browse files

Update 20200820-java_process_and_streams/README.md

parent 4233b334
![Java, Processes and Streams](java-process-and-streams.jpg "Java, Processes and Streams")
# Java, Processes and Streams examples
Those are the examples from the blog post "Java, Processes and Streams" you can find out on the
[ongres website](https://www.ongres.com/blog). Run them by executing:
```shell
cd examples
mvn clean verify
```
<?xml version="1.0" encoding="UTF-8"?>
<fileset-config file-format-version="1.2.0" simple-config="false" sync-formatter="true">
<local-check-config name="maven-checkstyle-plugin validate" location="file:/home/matteom/Repositories/gitlab.com/ongresinc/build-tools/build-resources/target/classes/com/ongres/checkstyle/checkstyle.xml" type="remote" description="maven-checkstyle-plugin configuration validate">
<property name="checkstyle.header.file" value="/home/matteom/eclipse/stackgres/.metadata/.plugins/org.eclipse.core.resources/.projects/com.ongres.java-process-examples/com.basistech.m2e.code.quality.checkstyleConfigurator/checkstyle-header-validate.txt"/>
<property name="config_loc" value="/home/matteom/Repositories/gitlab.com/ongresinc/blog-posts-src/20200820-java_process_and_streams/examples/target/build-resources/com/ongres/checkstyle/"/>
<property name="checkstyle.cache.file" value="${project_loc}/target/checkstyle-cachefile"/>
<property name="checkstyle.suppressions.file" value="/home/matteom/eclipse/stackgres/.metadata/.plugins/org.eclipse.core.resources/.projects/com.ongres.java-process-examples/com.basistech.m2e.code.quality.checkstyleConfigurator/checkstyle-suppressions-validate.xml"/>
</local-check-config>
<fileset name="java-sources-validate" enabled="true" check-config-name="maven-checkstyle-plugin validate" local="true">
<file-match-pattern match-pattern="^src/main/java.*\/.*\.java" include-pattern="true"/>
<file-match-pattern match-pattern="^src/test/java/.*\/.*\.java" include-pattern="true"/>
<file-match-pattern match-pattern="^src/main/resources.*\.properties" include-pattern="true"/>
<file-match-pattern match-pattern="^src/test/resources.*\.properties" include-pattern="true"/>
</fileset>
</fileset-config>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.ongres</groupId>
<artifactId>build-parent</artifactId>
<version>1.0.8</version>
</parent>
<artifactId>java-process-examples</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>java-process-examples</name>
<url>https://gitlab.com/teoincontatto/java-process-examples</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-exec</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.zeroturnaround</groupId>
<artifactId>zt-exec</artifactId>
<version>1.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.buildobjects</groupId>
<artifactId>jproc</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.ongres</groupId>
<artifactId>fluent-process</artifactId>
<version>1.0.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
package com.ongres.javaprocessexamples;
import java.io.BufferedReader;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.ongres.process.FluentProcess;
import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecuteResultHandler;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.Executor;
import org.apache.commons.exec.PumpStreamHandler;
import org.buildobjects.process.ProcBuilder;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.zeroturnaround.exec.ProcessExecutor;
import org.zeroturnaround.exec.ProcessResult;
import org.zeroturnaround.exec.StartedProcess;
public class ComplexExamplesTest {
@BeforeEach
public void beforeEach() {
System.out.println();
}
@Test
public void jdkExample() throws Exception {
ProcessBuilder shBuilder = new ProcessBuilder("sh", "-c",
Stream.of("cat", "exit 79").collect(Collectors.joining("\n")));
ProcessBuilder sedBuilder = new ProcessBuilder("sed", "s/world/process/");
Stream<String> inputStream = Stream.of(
"hello",
"world"
);
Process sh = shBuilder.start();
Process sed = sedBuilder.start();
CompletableFuture<Void> shInput = CompletableFuture.runAsync(() -> {
try {
try {
Iterator<byte[]> iterator = inputStream
.map(line -> (line + "\n").getBytes(StandardCharsets.UTF_8))
.iterator();
while (iterator.hasNext()) {
sh.getOutputStream().write(iterator.next());
}
} finally {
sh.getOutputStream().close();
}
} catch (IOException ex) {
throw new RuntimeException(ex);
}
});
CompletableFuture<Void> sedInput = CompletableFuture.runAsync(() -> {
try {
try {
byte[] buffer = new byte[8192];
while (true) {
int size = sh.getInputStream().read(buffer);
if (size < 0) {
break;
}
if (size > 0) {
sed.getOutputStream().write(buffer, 0, size);
}
}
} finally {
sed.getOutputStream().close();
}
} catch (IOException ex) {
throw new RuntimeException(ex);
}
});
try (BufferedReader sedReader = new BufferedReader(
new InputStreamReader(sed.getInputStream(), StandardCharsets.UTF_8))) {
sedReader.lines()
.peek(System.out::println)
.collect(Collectors.toList());
sh.waitFor();
sed.waitFor();
if (sh.exitValue() != 79) {
throw new Exception(
"Process " + shBuilder.command() + " has failed with exit code " + sh.exitValue());
}
if (sed.exitValue() != 0) {
throw new Exception(
"Process " + sedBuilder.command() + " has failed with exit code " + sed.exitValue());
}
shInput.join();
sedInput.join();
}
}
@Test
public void commonsExecExample() throws Exception {
final CommandLine sh = new CommandLine("sh")
.addArgument("-c")
.addArgument(Stream.of(
"cat",
"exit 79"
)
.collect(Collectors.joining("\n")));
final CommandLine sed = new CommandLine("sed")
.addArgument("s/world/process/");
Stream<String> inputStream = Stream.of(
"hello",
"world"
);
InputStream shInputStream = new InputStream() {
final Iterator<Byte> iterator = inputStream
.map(line -> line.getBytes(StandardCharsets.UTF_8))
.flatMap(bytes -> {
List<Byte> byteList = new ArrayList<>(bytes.length);
for (byte value : bytes) {
byteList.add(value);
}
return byteList.stream();
})
.iterator();
@Override
public int read() throws IOException {
if (!iterator.hasNext()) {
return -1;
}
return iterator.next().byteValue();
}
};
DefaultExecuteResultHandler shExecutionHandler = new DefaultExecuteResultHandler();
Executor shExecutor = new DefaultExecutor();
PipedOutputStream shOutputStream = new PipedOutputStream();
PipedInputStream sedInputStream = new PipedInputStream(shOutputStream);
shExecutor.setStreamHandler(new PumpStreamHandler(shOutputStream, null, shInputStream));
DefaultExecuteResultHandler sedExecutionHandler = new DefaultExecuteResultHandler();
Executor sedExecutor = new DefaultExecutor();
PipedOutputStream sedOutputStream = new PipedOutputStream();
PipedInputStream sedInputStreamForOutput = new PipedInputStream(sedOutputStream);
sedExecutor.setStreamHandler(new PumpStreamHandler(sedOutputStream, null, sedInputStream));
shExecutor.execute(sh, shExecutionHandler);
sedExecutor.execute(sed, sedExecutionHandler);
try (BufferedReader sedReader = new BufferedReader(
new InputStreamReader(sedInputStreamForOutput, StandardCharsets.UTF_8))) {
sedReader.lines()
.peek(System.out::println)
.collect(Collectors.toList());
shExecutionHandler.waitFor();
if ((shExecutionHandler.getExitValue() != 79)
|| shExecutionHandler.getException() != null) {
if (shExecutionHandler.getException() != null) {
throw new Exception(
"Process " + sh + " has failed with exit code " + shExecutionHandler.getExitValue(),
shExecutionHandler.getException());
}
throw new Exception(
"Process " + sh + " has failed with exit code " + shExecutionHandler.getExitValue());
}
sedExecutionHandler.waitFor();
if ((sedExecutionHandler.getExitValue() != 0)
|| sedExecutionHandler.getException() != null) {
if (sedExecutionHandler.getException() != null) {
throw new Exception(
"Process " + sed + " has failed with exit code " + sedExecutionHandler.getExitValue(),
sedExecutionHandler.getException());
}
throw new Exception(
"Process " + sed + " failed with exit code " + sedExecutionHandler.getExitValue());
}
}
}
@Test
public void ztExecExample() throws Exception {
Stream<String> inputStream = Stream.of(
"hello",
"world"
);
InputStream shInputStream = new InputStream() {
final Iterator<Byte> iterator = inputStream
.map(line -> (line + "\n").getBytes(StandardCharsets.UTF_8))
.flatMap(bytes -> {
List<Byte> byteList = new ArrayList<>(bytes.length);
for (byte value : bytes) {
byteList.add(value);
}
return byteList.stream();
})
.iterator();
@Override
public int read() throws IOException {
if (!iterator.hasNext()) {
return -1;
}
return iterator.next().byteValue();
}
};
PipedOutputStream shOutputStream = new PipedOutputStream();
PipedInputStream sedInputStream = new PipedInputStream(shOutputStream);
ProcessExecutor shExecutor = new ProcessExecutor("sh", "-c",
Stream.of("cat", "exit 79")
.collect(Collectors.joining("\n")))
.exitValue(79)
.redirectInput(shInputStream)
.redirectOutput(shOutputStream);
PipedOutputStream sedOutputStream = new PipedOutputStream();
PipedInputStream sedInputStreamForOutput = new PipedInputStream(sedOutputStream);
InputStream endProtectedInputStream = new FilterInputStream(sedInputStreamForOutput) {
@Override
public int read() throws IOException {
return checkEndDead(() -> super.read());
}
@Override
public int read(byte[] b) throws IOException {
return checkEndDead(() -> super.read(b));
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
return checkEndDead(() -> super.read(b, off, len));
}
private int checkEndDead(Callable<Integer> readCall) throws IOException {
try {
return readCall.call();
} catch (IOException ex) {
if (ex.getMessage().equals("Write end dead")) {
return -1;
}
throw ex;
} catch (RuntimeException ex) {
throw ex;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
};
ProcessExecutor sedExecutor = new ProcessExecutor("sed", "s/world/process/")
.redirectInput(sedInputStream)
.redirectOutput(sedOutputStream);
StartedProcess sh = shExecutor
.start();
StartedProcess sed = sedExecutor
.start();
try (BufferedReader sedReader = new BufferedReader(
new InputStreamReader(endProtectedInputStream, StandardCharsets.UTF_8))) {
sedReader.lines()
.peek(System.out::println)
.collect(Collectors.toList());
ProcessResult shResult = sh.getFuture().get();
ProcessResult sedResult = sed.getFuture().get();
if (shResult.getExitValue() != 79) {
throw new Exception(
"Process " + shExecutor.getCommand()
+ " has failed with exit code " + shResult.getExitValue());
}
if (sedResult.getExitValue() != 0) {
throw new Exception(
"Process " + sedExecutor.getCommand()
+ " has failed with exit code " + sedResult.getExitValue());
}
}
}
@Test
public void jprocExample() throws Exception {
Stream<String> inputStream = Stream.of(
"hello",
"world"
);
InputStream shInputStream = new InputStream() {
final Iterator<Byte> iterator = inputStream
.map(line -> (line + "\n").getBytes(StandardCharsets.UTF_8))
.flatMap(bytes -> {
List<Byte> byteList = new ArrayList<>(bytes.length);
for (byte value : bytes) {
byteList.add(value);
}
return byteList.stream();
})
.iterator();
@Override
public int read() throws IOException {
if (!iterator.hasNext()) {
return -1;
}
return iterator.next().byteValue();
}
};
CompletableFuture<List<String>> futureOutput = new CompletableFuture<>();
new ProcBuilder("sh", "-c",
Stream.of("cat", "exit 79")
.collect(Collectors.joining("\n")))
.withExpectedExitStatuses(79)
.withInputStream(shInputStream)
.withOutputConsumer(sedInputStream -> {
new ProcBuilder("sed", "s/world/process/")
.withInputStream(sedInputStream)
.withOutputConsumer(sedInputStreamForOutput -> {
try (BufferedReader sedReader = new BufferedReader(
new InputStreamReader(sedInputStreamForOutput, StandardCharsets.UTF_8))) {
futureOutput.complete(sedReader.lines()
.peek(System.out::println)
.collect(Collectors.toList()));
} catch (Exception ex) {
futureOutput.completeExceptionally(ex);
}
})
.run();
})
.run();
futureOutput.join();
}
@Test
public void fluentProcessExample() {
FluentProcess.builder("sh").arg("-c")
.multilineArg(
"cat",
"exit 79")
.allowedExitCodes(Arrays.asList(79))
.start()
.inputStream(Stream.of("hello", "world"))
.pipe("sed", "s/world/process/")
.stream()
.peek(System.out::println)
.collect(Collectors.toList());
}
}
package com.ongres.javaprocessexamples;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.CompletableFuture;
import com.ongres.process.FluentProcess;
import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecuteResultHandler;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.Executor;
import org.apache.commons.exec.PumpStreamHandler;
import org.buildobjects.process.ProcBuilder;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.zeroturnaround.exec.ProcessExecutor;
import org.zeroturnaround.exec.ProcessResult;
import org.zeroturnaround.exec.StartedProcess;
public class SimpleExamplesTest {
@BeforeEach
public void beforeEach() {
System.out.println();
}
@Test
public void jdkExample() throws Exception {
ProcessBuilder sedBuilder = new ProcessBuilder("sed", "s/world/process/");
Process sed = sedBuilder.start();
InputStream inputStream = new ByteArrayInputStream(
"hello\nworld".getBytes(StandardCharsets.UTF_8));
final CompletableFuture<Void> sedInput = CompletableFuture.runAsync(() -> {
try {
try {
byte[] buffer = new byte[8192];
while (true) {
int size = inputStream.read(buffer);
if (size < 0) {
break;
}
if (size > 0) {
sed.getOutputStream().write(buffer, 0, size);
}
}
} finally {
sed.getOutputStream().close();
}
} catch (IOException ex) {
throw new RuntimeException(ex);
}
});
try {
byte[] buffer = new byte[8192];
while (true) {
int size = sed.getInputStream().read(buffer);
if (size < 0) {
break;
}
if (size > 0) {
System.out.write(buffer, 0, size);
}
}
} catch (IOException ex) {
throw new RuntimeException(ex);
}
sed.waitFor();
if (sed.exitValue() != 0) {
throw new Exception(
"Process " + sedBuilder.command() + " has failed with exit code " + sed.exitValue());
}
sedInput.join();
}
@Test
public void commonsExecExample() throws Exception {
CommandLine sed = new CommandLine("sed")
.addArgument("s/world/process/");
DefaultExecuteResultHandler sedExecutionHandler = new DefaultExecuteResultHandler();
Executor sedExecutor = new DefaultExecutor();
sedExecutor.setStreamHandler(new PumpStreamHandler(System.out, null,
new ByteArrayInputStream("hello\nworld".getBytes(StandardCharsets.UTF_8))));
sedExecutor.execute(sed, sedExecutionHandler);
sedExecutionHandler.waitFor();
if ((sedExecutionHandler.getExitValue() != 0)
|| sedExecutionHandler.getException() != null) {
if (sedExecutionHandler.getException() != null) {
throw new Exception(
"Process " + sed + " has failed with exit code " + sedExecutionHandler.getExitValue(),
sedExecutionHandler.getException());
}
throw new Exception(
"Process " + sed + " failed with exit code " + sedExecutionHandler.getExitValue());
}
}
@Test
public void ztExecExample() throws Exception {
ProcessExecutor sedExecutor = new ProcessExecutor("sed", "s/world/process/")
.redirectInput(new ByteArrayInputStream("hello\nworld".getBytes(StandardCharsets.UTF_8)))
.redirectOutput(System.out);
StartedProcess sed = sedExecutor
.