From 69cd6f586418291c980831cd92508c549c84ce94 Mon Sep 17 00:00:00 2001 From: Christian Halstrick Date: Wed, 28 Oct 2015 13:24:28 +0100 Subject: [PATCH] Introduce FS.execute() to execute a command defined by a ProcessBuilder Change-Id: I2ad2c71ad30b969455bdea89637b8e996b1dad8c Signed-off-by: Matthias Sohn --- .../jgit/util/RunExternalScriptTest.java | 40 ++++++++++ .../src/org/eclipse/jgit/util/FS.java | 78 ++++++++++++++++++- 2 files changed, 115 insertions(+), 3 deletions(-) diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RunExternalScriptTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RunExternalScriptTest.java index 82beab2dc..b6a2519c5 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RunExternalScriptTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RunExternalScriptTest.java @@ -52,6 +52,7 @@ import java.io.InputStream; import org.eclipse.jgit.junit.JGitTestUtil; +import org.eclipse.jgit.util.FS.ExecutionResult; import org.junit.Before; import org.junit.Test; @@ -163,6 +164,45 @@ public void testWrongScript() throws IOException, InterruptedException { assertEquals(127, rc); } + @Test + public void testCopyStdInExecute() + throws IOException, InterruptedException { + String inputStr = "a\nb\rc\r\nd"; + File script = writeTempFile("cat -"); + ProcessBuilder pb = new ProcessBuilder("/bin/sh", script.getPath()); + ExecutionResult res = FS.DETECTED.execute(pb, + new ByteArrayInputStream(inputStr.getBytes())); + assertEquals(0, res.getRc()); + assertEquals(inputStr, new String(res.getStdout().toByteArray())); + assertEquals("", new String(res.getStderr().toByteArray())); + } + + @Test + public void testStdErrExecute() throws IOException, InterruptedException { + File script = writeTempFile("echo hi >&2"); + ProcessBuilder pb = new ProcessBuilder("/bin/sh", script.getPath()); + ExecutionResult res = FS.DETECTED.execute(pb, null); + assertEquals(0, res.getRc()); + assertEquals("", new String(res.getStdout().toByteArray())); + assertEquals("hi" + sep, new String(res.getStderr().toByteArray())); + } + + @Test + public void testAllTogetherBinExecute() + throws IOException, InterruptedException { + String inputStr = "a\nb\rc\r\nd"; + File script = writeTempFile( + "echo $#,$1,$2,$3,$4,$5,$6 >&2 ; cat -; exit 5"); + ProcessBuilder pb = new ProcessBuilder("/bin/sh", script.getPath(), "a", + "b", "c"); + ExecutionResult res = FS.DETECTED.execute(pb, + new ByteArrayInputStream(inputStr.getBytes())); + assertEquals(5, res.getRc()); + assertEquals(inputStr, new String(res.getStdout().toByteArray())); + assertEquals("3,a,b,c,,," + sep, + new String(res.getStderr().toByteArray())); + } + private File writeTempFile(String body) throws IOException { File f = File.createTempFile("RunProcessTestScript_", ""); JGitTestUtil.write(f, body); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java index e407dd8be..b61e47f5f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java @@ -110,7 +110,54 @@ public FS detect(Boolean cygwinUsed) { } } - final static Logger LOG = LoggerFactory.getLogger(FS.class); + /** + * Result of an executed process. The caller is responsible to close the + * contained {@link TemporaryBuffer}s + * + * @since 4.2 + */ + public static class ExecutionResult { + private TemporaryBuffer stdout; + + private TemporaryBuffer stderr; + + private int rc; + + /** + * @param stdout + * @param stderr + * @param rc + */ + public ExecutionResult(TemporaryBuffer stdout, TemporaryBuffer stderr, + int rc) { + this.stdout = stdout; + this.stderr = stderr; + this.rc = rc; + } + + /** + * @return buffered standard output stream + */ + public TemporaryBuffer getStdout() { + return stdout; + } + + /** + * @return buffered standard error stream + */ + public TemporaryBuffer getStderr() { + return stderr; + } + + /** + * @return the return code of the process + */ + public int getRc() { + return rc; + } + } + + private final static Logger LOG = LoggerFactory.getLogger(FS.class); /** The auto-detected implementation selected for this operating system and JRE. */ public static final FS DETECTED = detect(); @@ -1004,10 +1051,10 @@ private static boolean shutdownAndAwaitTermination(ExecutorService pool) { pool.shutdown(); // Disable new tasks from being submitted try { // Wait a while for existing tasks to terminate - if (!pool.awaitTermination(5, TimeUnit.SECONDS)) { + if (!pool.awaitTermination(60, TimeUnit.SECONDS)) { pool.shutdownNow(); // Cancel currently executing tasks // Wait a while for tasks to respond to being canceled - if (!pool.awaitTermination(5, TimeUnit.SECONDS)) + if (!pool.awaitTermination(60, TimeUnit.SECONDS)) hasShutdown = false; } } catch (InterruptedException ie) { @@ -1034,6 +1081,31 @@ private static boolean shutdownAndAwaitTermination(ExecutorService pool) { */ public abstract ProcessBuilder runInShell(String cmd, String[] args); + /** + * Execute a command defined by a {@link ProcessBuilder}. + * + * @param pb + * The command to be executed + * @param in + * The standard input stream passed to the process + * @return The result of the executed command + * @throws InterruptedException + * @throws IOException + * @since 4.2 + */ + public ExecutionResult execute(ProcessBuilder pb, InputStream in) + throws IOException, InterruptedException { + TemporaryBuffer stdout = new TemporaryBuffer.LocalFile(null); + TemporaryBuffer stderr = new TemporaryBuffer.Heap(1024, 1024 * 1024); + try { + int rc = runProcess(pb, stdout, stderr, in); + return new ExecutionResult(stdout, stderr, rc); + } finally { + stdout.close(); + stderr.close(); + } + } + private static class Holder { final V value;