diff options
author | Christian Halstrick <christian.halstrick@sap.com> | 2015-06-17 14:54:11 +0200 |
---|---|---|
committer | Matthias Sohn <matthias.sohn@sap.com> | 2015-11-02 22:19:47 +0100 |
commit | 67a77d402aa4bab609cb639fee9d18f42aed1d59 (patch) | |
tree | a9cd5551bd22b9f12c813ed06facf4c450e18721 /org.eclipse.jgit | |
parent | 8473bc4f8d2d69ca8aa3b4889660ed17d08f2270 (diff) | |
download | jgit-67a77d402aa4bab609cb639fee9d18f42aed1d59.tar.gz jgit-67a77d402aa4bab609cb639fee9d18f42aed1d59.zip |
Enhance FS.runProcess() to support stdin-redirection and binary data
In order to support filters in gitattributes FS.runProcess() is made
public. Support for stdin redirection has been added. Support for binary
data on stdin/stdout (as used be clean/smudge filters) has been added.
Change-Id: Ice2c152e9391368dc5748d7b825a838e3eb755f9
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
Diffstat (limited to 'org.eclipse.jgit')
-rw-r--r-- | org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java | 111 |
1 files changed, 71 insertions, 40 deletions
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 4e4371e8db..bcaf62a0c4 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java @@ -44,15 +44,13 @@ package org.eclipse.jgit.util; import java.io.BufferedReader; -import java.io.BufferedWriter; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; -import java.io.OutputStreamWriter; import java.io.PrintStream; -import java.io.PrintWriter; import java.nio.charset.Charset; import java.security.AccessController; import java.security.PrivilegedAction; @@ -864,52 +862,88 @@ public abstract class FS { * Runs the given process until termination, clearing its stdout and stderr * streams on-the-fly. * - * @param hookProcessBuilder - * The process builder configured for this hook. + * @param processBuilder + * The process builder configured for this process. * @param outRedirect - * A print stream on which to redirect the hook's stdout. Can be - * <code>null</code>, in which case the hook's standard output - * will be lost. + * A OutputStream on which to redirect the processes stdout. Can + * be <code>null</code>, in which case the processes standard + * output will be lost. * @param errRedirect - * A print stream on which to redirect the hook's stderr. Can be - * <code>null</code>, in which case the hook's standard error - * will be lost. + * A OutputStream on which to redirect the processes stderr. Can + * be <code>null</code>, in which case the processes standard + * error will be lost. * @param stdinArgs * A string to pass on to the standard input of the hook. Can be * <code>null</code>. - * @return the exit value of this hook. + * @return the exit value of this process. * @throws IOException - * if an I/O error occurs while executing this hook. + * if an I/O error occurs while executing this process. * @throws InterruptedException * if the current thread is interrupted while waiting for the * process to end. - * @since 3.7 + * @since 4.2 */ - protected int runProcess(ProcessBuilder hookProcessBuilder, + public int runProcess(ProcessBuilder processBuilder, OutputStream outRedirect, OutputStream errRedirect, String stdinArgs) throws IOException, InterruptedException { + InputStream in = (stdinArgs == null) ? null : new ByteArrayInputStream( + stdinArgs.getBytes(Constants.CHARACTER_ENCODING)); + return runProcess(processBuilder, outRedirect, errRedirect, in); + } + + /** + * Runs the given process until termination, clearing its stdout and stderr + * streams on-the-fly. + * + * @param processBuilder + * The process builder configured for this process. + * @param outRedirect + * An OutputStream on which to redirect the processes stdout. Can + * be <code>null</code>, in which case the processes standard + * output will be lost. If binary is set to <code>false</code> + * then it is expected that the process emits text data which + * should be processed line by line. + * @param errRedirect + * An OutputStream on which to redirect the processes stderr. Can + * be <code>null</code>, in which case the processes standard + * error will be lost. + * @param inRedirect + * An InputStream from which to redirect the processes stdin. Can + * be <code>null</code>, in which case the process doesn't get + * any data over stdin. If binary is set to + * <code>false</code> then it is expected that the process + * expects text data which should be processed line by line. + * @return the return code of this process. + * @throws IOException + * if an I/O error occurs while executing this process. + * @throws InterruptedException + * if the current thread is interrupted while waiting for the + * process to end. + * @since 4.2 + */ + public int runProcess(ProcessBuilder processBuilder, + OutputStream outRedirect, OutputStream errRedirect, + InputStream inRedirect) throws IOException, + InterruptedException { final ExecutorService executor = Executors.newFixedThreadPool(2); Process process = null; // We'll record the first I/O exception that occurs, but keep on trying // to dispose of our open streams and file handles IOException ioException = null; try { - process = hookProcessBuilder.start(); + process = processBuilder.start(); final Callable<Void> errorGobbler = new StreamGobbler( process.getErrorStream(), errRedirect); final Callable<Void> outputGobbler = new StreamGobbler( process.getInputStream(), outRedirect); executor.submit(errorGobbler); executor.submit(outputGobbler); - if (stdinArgs != null) { - final PrintWriter stdinWriter = new PrintWriter( - process.getOutputStream()); - stdinWriter.print(stdinArgs); - stdinWriter.flush(); - // We are done with this hook's input. Explicitly close its - // stdin now to kick off any blocking read the hook might have. - stdinWriter.close(); + OutputStream outputStream = process.getOutputStream(); + if (inRedirect != null) { + new StreamGobbler(inRedirect, outputStream) + .call(); } + outputStream.close(); return process.waitFor(); } catch (IOException e) { ioException = e; @@ -1187,30 +1221,27 @@ public abstract class FS { * </p> */ private static class StreamGobbler implements Callable<Void> { - private final BufferedReader reader; + private InputStream in; - private final BufferedWriter writer; + private OutputStream out; public StreamGobbler(InputStream stream, OutputStream output) { - this.reader = new BufferedReader(new InputStreamReader(stream)); - if (output == null) - this.writer = null; - else - this.writer = new BufferedWriter(new OutputStreamWriter(output)); + this.in = stream; + this.out = output; } public Void call() throws IOException { boolean writeFailure = false; - - String line = null; - while ((line = reader.readLine()) != null) { - // Do not try to write again after a failure, but keep reading - // as long as possible to prevent the input stream from choking. - if (!writeFailure && writer != null) { + byte buffer[] = new byte[4096]; + int readBytes; + while ((readBytes = in.read(buffer)) != -1) { + // Do not try to write again after a failure, but keep + // reading as long as possible to prevent the input stream + // from choking. + if (!writeFailure && out != null) { try { - writer.write(line); - writer.newLine(); - writer.flush(); + out.write(buffer, 0, readBytes); + out.flush(); } catch (IOException e) { writeFailure = true; } |