summaryrefslogtreecommitdiffstats
path: root/vespajlib
diff options
context:
space:
mode:
authorgjoranv <gv@yahoo-inc.com>2017-03-02 00:15:38 +0100
committergjoranv <gv@yahoo-inc.com>2017-03-02 00:29:09 +0100
commitf238d6793d32e139b1c3f17857da6ea492ed2c70 (patch)
tree10b971bdecfa4265f90edcc44fc3b0a134a307ab /vespajlib
parentd37abf742d3fa48ad564ab72689c91a1a1c81209 (diff)
Add a configurable process executor based on commons-exec.
Diffstat (limited to 'vespajlib')
-rw-r--r--vespajlib/pom.xml4
-rw-r--r--vespajlib/src/main/java/com/yahoo/system/execution/ProcessExecutor.java79
-rw-r--r--vespajlib/src/main/java/com/yahoo/system/execution/ProcessResult.java19
-rw-r--r--vespajlib/src/main/java/com/yahoo/system/execution/package-info.java5
-rw-r--r--vespajlib/src/test/java/com/yahoo/system/execution/ProcessExecutorTest.java25
5 files changed, 132 insertions, 0 deletions
diff --git a/vespajlib/pom.xml b/vespajlib/pom.xml
index 0282cc94ff6..1d9bdfb452d 100644
--- a/vespajlib/pom.xml
+++ b/vespajlib/pom.xml
@@ -79,6 +79,10 @@
<artifactId>jackson-databind</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-exec</artifactId>
+ </dependency>
</dependencies>
<build>
<plugins>
diff --git a/vespajlib/src/main/java/com/yahoo/system/execution/ProcessExecutor.java b/vespajlib/src/main/java/com/yahoo/system/execution/ProcessExecutor.java
new file mode 100644
index 00000000000..564ee5ea3ac
--- /dev/null
+++ b/vespajlib/src/main/java/com/yahoo/system/execution/ProcessExecutor.java
@@ -0,0 +1,79 @@
+// Copyright 2017 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.system.execution;
+
+import org.apache.commons.exec.CommandLine;
+import org.apache.commons.exec.DefaultExecutor;
+import org.apache.commons.exec.ExecuteWatchdog;
+import org.apache.commons.exec.PumpStreamHandler;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author gjoranv
+ * @author bjorncs
+ */
+public class ProcessExecutor {
+
+ public static class Builder {
+ private final int timeoutSeconds;
+ private int[] successExitCodes;
+
+ public Builder(int timeoutSeconds) {
+ this.timeoutSeconds = timeoutSeconds;
+ }
+
+ public Builder setSuccessExitCodes(int... successExitCodes) {
+ this.successExitCodes = successExitCodes;
+ return this;
+ }
+
+ public ProcessExecutor build() {
+ return new ProcessExecutor(timeoutSeconds, successExitCodes);
+ }
+ }
+
+ private ProcessExecutor(int timeoutSeconds, int[] successExitCodes) {
+ this.timeoutSeconds = timeoutSeconds;
+ this.successExitCodes = successExitCodes;
+ }
+
+ public final int timeoutSeconds;
+ private final int[] successExitCodes;
+
+ public Optional<ProcessResult> execute(String command) throws IOException {
+ return execute(command, null);
+ }
+
+ public Optional<ProcessResult> execute(String command, String processInput) throws IOException {
+ ByteArrayOutputStream processErr = new ByteArrayOutputStream();
+ ByteArrayOutputStream processOut = new ByteArrayOutputStream();
+
+ DefaultExecutor executor = new DefaultExecutor();
+ executor.setStreamHandler(createStreamHandler(processOut, processErr, processInput));
+ ExecuteWatchdog watchDog = new ExecuteWatchdog(TimeUnit.SECONDS.toMillis(timeoutSeconds));
+ executor.setWatchdog(watchDog);
+ executor.setExitValues(successExitCodes);
+
+ int exitCode = executor.execute(CommandLine.parse(command));
+ return (watchDog.killedProcess()) ?
+ Optional.empty() : Optional.of(new ProcessResult(exitCode, processOut.toString(), processErr.toString()));
+ }
+
+ private static PumpStreamHandler createStreamHandler(ByteArrayOutputStream processOut,
+ ByteArrayOutputStream processErr,
+ String input) {
+ if (input != null) {
+ InputStream processInput = new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_8));
+ return new PumpStreamHandler(processOut, processErr, processInput);
+ } else {
+ return new PumpStreamHandler(processOut, processErr);
+ }
+ }
+
+}
diff --git a/vespajlib/src/main/java/com/yahoo/system/execution/ProcessResult.java b/vespajlib/src/main/java/com/yahoo/system/execution/ProcessResult.java
new file mode 100644
index 00000000000..2f277f83a8c
--- /dev/null
+++ b/vespajlib/src/main/java/com/yahoo/system/execution/ProcessResult.java
@@ -0,0 +1,19 @@
+// Copyright 2017 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.system.execution;
+
+/**
+ * @author bjorncs
+ * @author gjoranv
+ */
+public class ProcessResult {
+ public final String stdOut;
+ public final String stdErr;
+ public final int exitCode;
+
+ public ProcessResult(int exitCode, String stdOut, String stdErr) {
+ this.exitCode = exitCode;
+ this.stdOut = stdOut;
+ this.stdErr = stdErr;
+ }
+
+}
diff --git a/vespajlib/src/main/java/com/yahoo/system/execution/package-info.java b/vespajlib/src/main/java/com/yahoo/system/execution/package-info.java
new file mode 100644
index 00000000000..1f88163d23d
--- /dev/null
+++ b/vespajlib/src/main/java/com/yahoo/system/execution/package-info.java
@@ -0,0 +1,5 @@
+// Copyright 2017 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+@ExportPackage
+package com.yahoo.system.execution;
+
+import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/vespajlib/src/test/java/com/yahoo/system/execution/ProcessExecutorTest.java b/vespajlib/src/test/java/com/yahoo/system/execution/ProcessExecutorTest.java
new file mode 100644
index 00000000000..b12d66853fe
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/system/execution/ProcessExecutorTest.java
@@ -0,0 +1,25 @@
+// Copyright 2017 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.system.execution;
+
+import org.junit.Test;
+
+import java.util.Optional;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author gjoranv
+ */
+public class ProcessExecutorTest {
+
+ @Test
+ public void echo_can_be_executed() throws Exception {
+ final String message = "Hello from executor!";
+ ProcessExecutor executor = new ProcessExecutor.Builder(10).build();
+ Optional<ProcessResult> result = executor.execute("echo " + message);
+ assertTrue(result.isPresent());
+ assertEquals(message, result.get().stdOut.trim());
+ assertEquals("", result.get().stdErr);
+ }
+}