diff --git a/pom.xml b/pom.xml
index b2a21fe4..64092168 100644
--- a/pom.xml
+++ b/pom.xml
@@ -45,6 +45,7 @@
1.0.4
5.0.3
1.0.2
+ 1.11
diff --git a/releaser-core/pom.xml b/releaser-core/pom.xml
index cdbb2bdd..bdfd71aa 100644
--- a/releaser-core/pom.xml
+++ b/releaser-core/pom.xml
@@ -135,6 +135,11 @@
${javax.json.version}
compile
+
+ org.zeroturnaround
+ zt-exec
+ ${zt.exec.version}
+
diff --git a/releaser-core/src/main/java/releaser/internal/project/ProjectCommandExecutor.java b/releaser-core/src/main/java/releaser/internal/project/ProjectCommandExecutor.java
index 4eb40a20..5278d900 100644
--- a/releaser-core/src/main/java/releaser/internal/project/ProjectCommandExecutor.java
+++ b/releaser-core/src/main/java/releaser/internal/project/ProjectCommandExecutor.java
@@ -18,7 +18,6 @@ package releaser.internal.project;
import java.io.File;
import java.io.IOException;
-import java.io.InputStream;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -28,8 +27,8 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
-import java.util.Scanner;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
@@ -37,6 +36,9 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.zeroturnaround.exec.ProcessExecutor;
+import org.zeroturnaround.exec.ProcessResult;
+import org.zeroturnaround.exec.stream.slf4j.Slf4jStream;
import releaser.internal.ReleaserProperties;
import releaser.internal.ReleaserPropertiesAware;
@@ -179,8 +181,8 @@ public class ProjectCommandExecutor implements ReleaserPropertiesAware {
return executor(projectRoot).runCommand(substitutedCommands, waitTimeInMinutes);
}
- ProcessExecutor executor(String workDir) {
- return new ProcessExecutor(workDir);
+ ReleaserProcessExecutor executor(String workDir) {
+ return new ReleaserProcessExecutor(workDir);
}
public void publishDocs(ProjectVersion originalVersion,
@@ -264,63 +266,64 @@ public class ProjectCommandExecutor implements ReleaserPropertiesAware {
}
-class ProcessExecutor implements ReleaserPropertiesAware {
+class ReleaserProcessExecutor implements ReleaserPropertiesAware {
- private static final Logger log = LoggerFactory.getLogger(ProcessExecutor.class);
+ private static final Logger log = LoggerFactory
+ .getLogger(ReleaserProcessExecutor.class);
private static String[] OS_OPERATORS = { "|", "<", ">", "||", "&&" };
private String workingDir;
- ProcessExecutor(String workingDir) {
+ ReleaserProcessExecutor(String workingDir) {
this.workingDir = workingDir;
}
String runCommand(String[] commands, long waitTimeInMinutes) {
+ String workingDir = this.workingDir;
+ log.info("Will run the command from [{}] and wait for result for [{}] minutes",
+ workingDir, waitTimeInMinutes);
+
try {
- String workingDir = this.workingDir;
- log.info(
- "Will run the command from [{}] and wait for result for [{}] minutes",
- workingDir, waitTimeInMinutes);
- ProcessBuilder builder = builder(commands, workingDir);
- Process process = startProcess(builder);
- boolean finished = process.waitFor(waitTimeInMinutes, TimeUnit.MINUTES);
- if (!finished) {
- log.error("The command hasn't managed to finish in [{}] minutes",
- waitTimeInMinutes);
- process.destroyForcibly();
- throw new IllegalStateException("Process waiting time of ["
- + waitTimeInMinutes + "] minutes exceeded");
- }
- if (process.exitValue() != 0) {
+ ProcessExecutor processExecutor = processExecutor(commands, workingDir)
+ .timeout(waitTimeInMinutes, TimeUnit.MINUTES);
+ final ProcessResult processResult = doExecute(processExecutor);
+
+ int processExitValue = processResult.getExitValue();
+ if (processExitValue != 0) {
throw new IllegalStateException("The process has exited with exit code ["
- + process.exitValue() + "]");
+ + processExitValue + "]");
}
- return convertStreamToString(process.getInputStream());
+ return processResult.outputUTF8();
}
catch (InterruptedException | IOException e) {
- throw new IllegalStateException(e);
+ throw new IllegalStateException("Process execution failed", e);
+ }
+ catch (TimeoutException e) {
+ log.error("The command hasn't managed to finish in [{}] minutes",
+ waitTimeInMinutes);
+ throw new IllegalStateException("Process waiting time of ["
+ + waitTimeInMinutes + "] minutes exceeded", e);
}
}
- private String convertStreamToString(InputStream is) {
- Scanner scanner = new Scanner(is).useDelimiter("\\A");
- return scanner.hasNext() ? scanner.next() : "";
+ ProcessResult doExecute(ProcessExecutor processExecutor)
+ throws IOException, InterruptedException, TimeoutException {
+ return processExecutor.execute();
}
- Process startProcess(ProcessBuilder builder) throws IOException {
- return builder.start();
- }
-
- ProcessBuilder builder(String[] commands, String workingDir) {
+ ProcessExecutor processExecutor(String[] commands, String workingDir) {
String[] commandsToRun = commands;
String lastArg = String.join(" ", commands);
if (Arrays.stream(OS_OPERATORS).anyMatch(lastArg::contains)) {
commandsToRun = commandToExecute(lastArg);
}
log.info("Will run the command [{}]", Arrays.toString(commandsToRun));
- return new ProcessBuilder(commandsToRun).directory(new File(workingDir))
- .inheritIO();
+ return new ProcessExecutor().command(commandsToRun).destroyOnExit()
+ .readOutput(true)
+ .redirectOutputAlsoTo(
+ Slf4jStream.of(ReleaserProcessExecutor.class).asInfo())
+ .directory(new File(workingDir));
}
String[] commandToExecute(String lastArg) {
diff --git a/releaser-core/src/test/java/releaser/internal/project/ProjectCommandExecutorTests.java b/releaser-core/src/test/java/releaser/internal/project/ProjectCommandExecutorTests.java
index 5ce671c2..1de56957 100644
--- a/releaser-core/src/test/java/releaser/internal/project/ProjectCommandExecutorTests.java
+++ b/releaser-core/src/test/java/releaser/internal/project/ProjectCommandExecutorTests.java
@@ -16,18 +16,22 @@
package releaser.internal.project;
+import java.io.BufferedOutputStream;
import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.InputStream;
import java.io.OutputStream;
import java.net.URISyntaxException;
import java.nio.file.Files;
+import java.util.concurrent.TimeoutException;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
+import org.zeroturnaround.exec.ProcessExecutor;
+import org.zeroturnaround.exec.ProcessResult;
import releaser.internal.PomUpdateAcceptanceTests;
import releaser.internal.ReleaserProperties;
import releaser.internal.buildsystem.TestUtils;
@@ -62,7 +66,7 @@ public class ProjectCommandExecutorTests {
ProjectCommandExecutor projectBuilder(ReleaserProperties properties) {
return new ProjectCommandExecutor(properties) {
@Override
- ProcessExecutor executor(String workingDir) {
+ ReleaserProcessExecutor executor(String workingDir) {
return testExecutor(workingDir);
}
};
@@ -281,7 +285,7 @@ public class ProjectCommandExecutorTests {
ReleaserProperties properties = new ReleaserProperties();
properties.getMaven().setDeployCommand("echo foo");
properties.setWorkingDir(tmpFile("/builder/resolved").getPath());
- new ProcessExecutor(properties.getWorkingDir())
+ new ReleaserProcessExecutor(properties.getWorkingDir())
.runCommand(new String[] { "touch", "pom.xml" }, 1);
ProjectCommandExecutor builder = projectBuilder(properties);
@@ -311,7 +315,7 @@ public class ProjectCommandExecutorTests {
ReleaserProperties properties = new ReleaserProperties();
properties.getMaven().setDeployCommand("echo foo");
properties.setWorkingDir(tmpFile("/builder/resolved").getPath());
- new ProcessExecutor(properties.getWorkingDir())
+ new ReleaserProcessExecutor(properties.getWorkingDir())
.runCommand(new String[] { "touch", "pom.xml" }, 1);
ProjectCommandExecutor builder = projectBuilder(properties);
@@ -340,7 +344,7 @@ public class ProjectCommandExecutorTests {
ReleaserProperties properties = new ReleaserProperties();
properties.getMaven().setDeployCommand("echo foo");
properties.setWorkingDir(tmpFile("/builder/resolved").getPath());
- new ProcessExecutor(properties.getWorkingDir())
+ new ReleaserProcessExecutor(properties.getWorkingDir())
.runCommand(new String[] { "touch", "pom.xml" }, 1);
ProjectCommandExecutor builder = projectBuilder(properties);
@@ -412,10 +416,10 @@ public class ProjectCommandExecutorTests {
properties.getBash().setPublishDocsCommands(new String[] { "ls -al",
"echo {{version}} {{oldVersion}} {{nextVersion}}" });
properties.setWorkingDir(tmpFile("/builder/resolved").getPath());
- TestProcessExecutor executor = testExecutor(properties.getWorkingDir());
+ TestReleaserProcessExecutor executor = testExecutor(properties.getWorkingDir());
ProjectCommandExecutor builder = new ProjectCommandExecutor(properties) {
@Override
- ProcessExecutor executor(String workingDir) {
+ ReleaserProcessExecutor executor(String workingDir) {
return executor;
}
};
@@ -435,10 +439,10 @@ public class ProjectCommandExecutorTests {
new String[] { "echo {{systemProps}} 1", "echo {{systemProps}} 2" });
properties.getBash().setSystemProperties("-Dhello=world -Dfoo=bar");
properties.setWorkingDir(tmpFile("/builder/resolved").getPath());
- TestProcessExecutor executor = testExecutor(properties.getWorkingDir());
+ TestReleaserProcessExecutor executor = testExecutor(properties.getWorkingDir());
ProjectCommandExecutor builder = new ProjectCommandExecutor(properties) {
@Override
- ProcessExecutor executor(String workingDir) {
+ ReleaserProcessExecutor executor(String workingDir) {
return executor;
}
};
@@ -457,10 +461,10 @@ public class ProjectCommandExecutorTests {
properties.getBash()
.setPublishDocsCommands(new String[] { "echo '{{version}}'" });
properties.setWorkingDir(tmpFile("/builder/resolved").getPath());
- TestProcessExecutor executor = testExecutor(properties.getWorkingDir());
+ TestReleaserProcessExecutor executor = testExecutor(properties.getWorkingDir());
ProjectCommandExecutor builder = new ProjectCommandExecutor(properties) {
@Override
- ProcessExecutor executor(String workingDir) {
+ ReleaserProcessExecutor executor(String workingDir) {
return executor;
}
};
@@ -478,10 +482,10 @@ public class ProjectCommandExecutorTests {
properties.getBash().setGenerateReleaseTrainDocsCommand("echo '{{version}}'");
File resolved = tmpFile("/builder/resolved");
properties.setWorkingDir(resolved.getPath());
- TestProcessExecutor executor = testExecutor(properties.getWorkingDir());
+ TestReleaserProcessExecutor executor = testExecutor(properties.getWorkingDir());
ProjectCommandExecutor builder = new ProjectCommandExecutor(properties) {
@Override
- ProcessExecutor executor(String workingDir) {
+ ReleaserProcessExecutor executor(String workingDir) {
return executor;
}
};
@@ -513,11 +517,12 @@ public class ProjectCommandExecutorTests {
properties.setWorkingDir(tmpFile("/builder/unresolved").getPath());
ProjectCommandExecutor builder = new ProjectCommandExecutor(properties) {
@Override
- ProcessExecutor executor(String workingDir) {
- return new ProcessExecutor(properties.getWorkingDir()) {
+ ReleaserProcessExecutor executor(String workingDir) {
+ return new ReleaserProcessExecutor(properties.getWorkingDir()) {
@Override
- Process startProcess(ProcessBuilder builder) {
- return processWithInvalidExitCode();
+ ProcessResult doExecute(ProcessExecutor processExecutor)
+ throws IOException, InterruptedException, TimeoutException {
+ return new ProcessResult(1, null);
}
};
}
@@ -528,42 +533,8 @@ public class ProjectCommandExecutorTests {
"The process has exited with exit code [1]");
}
- private Process processWithInvalidExitCode() {
- return new Process() {
- @Override
- public OutputStream getOutputStream() {
- return null;
- }
-
- @Override
- public InputStream getInputStream() {
- return null;
- }
-
- @Override
- public InputStream getErrorStream() {
- return null;
- }
-
- @Override
- public int waitFor() {
- return 0;
- }
-
- @Override
- public int exitValue() {
- return 1;
- }
-
- @Override
- public void destroy() {
-
- }
- };
- }
-
- private TestProcessExecutor testExecutor(String workingDir) {
- return new TestProcessExecutor(workingDir);
+ private TestReleaserProcessExecutor testExecutor(String workingDir) {
+ return new TestReleaserProcessExecutor(workingDir);
}
private File tmpFile(String relativePath) {
@@ -582,19 +553,35 @@ public class ProjectCommandExecutorTests {
return new ProjectVersion("foo", "0.100.0.BUILD-SNAPSHOT");
}
- class TestProcessExecutor extends ProcessExecutor {
+ class TestReleaserProcessExecutor extends ReleaserProcessExecutor {
int counter = 0;
- TestProcessExecutor(String workingDir) {
+ TestReleaserProcessExecutor(String workingDir) {
super(workingDir);
}
@Override
- ProcessBuilder builder(String[] commands, String workingDir) {
+ ProcessExecutor processExecutor(String[] commands, String workingDir) {
this.counter++;
- return super.builder(commands, workingDir)
- .redirectOutput(tmpFile("/builder/resolved/resolved.log"));
+ final ProcessExecutor processExecutor = super.processExecutor(commands,
+ workingDir);
+
+ File tempFile = tmpFile("/builder/resolved/resolved.log");
+ try {
+ tempFile.createNewFile();
+ OutputStream fos = new BufferedOutputStream(
+ new FileOutputStream(tempFile));
+
+ return processExecutor
+ // use redirectOutputAlsoTo to avoid overriding all output
+ // destinations
+ .redirectOutput(fos);
+ }
+ catch (IOException e) {
+ e.printStackTrace();
+ return processExecutor;
+ }
}
}