|
From: <jrb...@us...> - 2012-01-10 23:51:26
|
Revision: 1300
http://cishell.svn.sourceforge.net/cishell/?rev=1300&view=rev
Author: jrbibers
Date: 2012-01-10 23:51:20 +0000 (Tue, 10 Jan 2012)
Log Message:
-----------
New "process" subpackage for CIShell utilities.
Three new classes for working with java.lang.Process. Core functionality is (1) emptying the standard output and standard error streams as a running process fills them since the process would otherwise block indefinitely and (2) capturing the exit value and standard output and standard error messages of the completed process as a ProcessResult.
Manifest updated to export this new package.
Reviewed by David.
Modified Paths:
--------------
trunk/core/org.cishell.utilities/META-INF/MANIFEST.MF
Added Paths:
-----------
trunk/core/org.cishell.utilities/src/org/cishell/utilities/process/
trunk/core/org.cishell.utilities/src/org/cishell/utilities/process/OutputGobblingProcessRunner.java
trunk/core/org.cishell.utilities/src/org/cishell/utilities/process/ProcessResult.java
trunk/core/org.cishell.utilities/src/org/cishell/utilities/process/StreamGobbler.java
Modified: trunk/core/org.cishell.utilities/META-INF/MANIFEST.MF
===================================================================
--- trunk/core/org.cishell.utilities/META-INF/MANIFEST.MF 2012-01-09 16:19:32 UTC (rev 1299)
+++ trunk/core/org.cishell.utilities/META-INF/MANIFEST.MF 2012-01-10 23:51:20 UTC (rev 1300)
@@ -36,4 +36,5 @@
org.cishell.utilities.mutateParameter,
org.cishell.utilities.mutateParameter.defaultvalue,
org.cishell.utilities.mutateParameter.dropdown,
- org.cishell.utilities.osgi.logging
+ org.cishell.utilities.osgi.logging,
+ org.cishell.utilities.process
Added: trunk/core/org.cishell.utilities/src/org/cishell/utilities/process/OutputGobblingProcessRunner.java
===================================================================
--- trunk/core/org.cishell.utilities/src/org/cishell/utilities/process/OutputGobblingProcessRunner.java (rev 0)
+++ trunk/core/org.cishell.utilities/src/org/cishell/utilities/process/OutputGobblingProcessRunner.java 2012-01-10 23:51:20 UTC (rev 1300)
@@ -0,0 +1,64 @@
+package org.cishell.utilities.process;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import com.google.common.base.Objects;
+
+/**
+ * {@link Process#waitFor()} may wait indefinitely if the running process's stdout and stderr
+ * streams are not emptied as they fill.
+ *
+ * See
+ * <a href="http://stackoverflow.com/questions/2150723/process-waitfor-threads-and-inputstreams">here</a>
+ * or
+ * <a href="http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html">here</a> for details.
+ *
+ */
+public class OutputGobblingProcessRunner {
+ private ProcessBuilder processBuilder;
+ private String charsetName;
+
+ public OutputGobblingProcessRunner(ProcessBuilder processBuilder, String charsetName) {
+ this.processBuilder = processBuilder;
+ this.charsetName = charsetName;
+ }
+
+ public ProcessResult run() throws IOException, InterruptedException {
+ // Start the process
+ Process process = processBuilder.start();
+
+ // Set up and start the stdout and stderr gobblers
+ ByteArrayOutputStream stdoutStream = new ByteArrayOutputStream();
+ StreamGobbler stdoutGobbler = new StreamGobbler(process.getInputStream(), stdoutStream);
+
+ ByteArrayOutputStream stderrStream = new ByteArrayOutputStream();
+ StreamGobbler stderrGobbler = new StreamGobbler(process.getErrorStream(), stderrStream);
+
+ stdoutGobbler.start();
+ stderrGobbler.start();
+
+ // Wait for the process to finish
+ int exitValue = process.waitFor();
+
+ // Interrupt the gobblers and wait for both to die
+ stdoutGobbler.interrupt();
+ stderrGobbler.interrupt();
+ stdoutGobbler.join();
+ stderrGobbler.join();
+
+ // Dump gobbler messages
+ String stdoutMessage = stdoutStream.toString(charsetName).trim();
+ String stderrMessage = stderrStream.toString(charsetName).trim();
+
+ return new ProcessResult(exitValue, stdoutMessage, stderrMessage);
+ }
+
+ @Override
+ public String toString() {
+ return Objects.toStringHelper(this)
+ .add("processBuilder", processBuilder)
+ .add("charsetName", charsetName)
+ .toString();
+ }
+}
Added: trunk/core/org.cishell.utilities/src/org/cishell/utilities/process/ProcessResult.java
===================================================================
--- trunk/core/org.cishell.utilities/src/org/cishell/utilities/process/ProcessResult.java (rev 0)
+++ trunk/core/org.cishell.utilities/src/org/cishell/utilities/process/ProcessResult.java 2012-01-10 23:51:20 UTC (rev 1300)
@@ -0,0 +1,98 @@
+package org.cishell.utilities.process;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Objects;
+
+/**
+ * Value class for the result of {@link OutputGobblingProcessRunner#run()}.
+ * Represents a process's exit value and messages to standard out and standard error,
+ * both as a single String.
+ */
+public class ProcessResult {
+ private int exitValue;
+ private String stdoutMessage;
+ private String stderrMessage;
+
+ /**
+ * @param exitValue A completed {@link Process}'s exit value.
+ * @param stdoutMessage A completed {@link Process}'s messages to standard output as one String.
+ * @param stderrMessage A completed {@link Process}'s messages to standard error as one String.
+ */
+ public ProcessResult(int exitValue, String stdoutMessage, String stderrMessage) {
+ this.exitValue = exitValue;
+ this.stdoutMessage = stdoutMessage;
+ this.stderrMessage = stderrMessage;
+ }
+
+ public int getExitValue() {
+ return exitValue;
+ }
+
+ /**
+ * @return The message to standard output.
+ */
+ public String getStdoutMessage() {
+ return stdoutMessage;
+ }
+
+ /**
+ * @return The message to standard error.
+ */
+ public String getStderrMessage() {
+ return stderrMessage;
+ }
+
+ /**
+ * @return True if and only if the exit value is zero.
+ */
+ public boolean isExitNormal() {
+ return (exitValue == 0);
+ }
+
+ /**
+ * @return A plain-text report of the exit value and standard output/error messages.
+ */
+ public String report() {
+ return Joiner.on(" ").join(
+ String.format("The program returned exit value %d.", exitValue),
+ reportStreamContents("standard output", stdoutMessage),
+ reportStreamContents("standard error", stderrMessage));
+ }
+
+ private static String reportStreamContents(String streamName, String contents) {
+ if (contents.isEmpty()) {
+ return String.format("No messages to %s.", streamName);
+ } else {
+ return String.format("Message to %s: [[%s]].", streamName, contents);
+ }
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (!(thatObject instanceof ProcessResult)) {
+ return false;
+ }
+
+ ProcessResult that = (ProcessResult) thatObject;
+
+ return (Objects.equal(this.exitValue,
+ that.exitValue)
+ && Objects.equal(this.stdoutMessage,
+ that.stdoutMessage)
+ && Objects.equal(this.stderrMessage,
+ that.stderrMessage));
+ }
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(exitValue, stdoutMessage, stderrMessage);
+ }
+
+ @Override
+ public String toString() {
+ return Objects.toStringHelper(this)
+ .add("exitValue", exitValue)
+ .add("stdoutMessage", stdoutMessage)
+ .add("stderrMessage", stderrMessage)
+ .toString();
+ }
+}
\ No newline at end of file
Added: trunk/core/org.cishell.utilities/src/org/cishell/utilities/process/StreamGobbler.java
===================================================================
--- trunk/core/org.cishell.utilities/src/org/cishell/utilities/process/StreamGobbler.java (rev 0)
+++ trunk/core/org.cishell.utilities/src/org/cishell/utilities/process/StreamGobbler.java 2012-01-10 23:51:20 UTC (rev 1300)
@@ -0,0 +1,60 @@
+package org.cishell.utilities.process;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+
+import com.google.common.base.Objects;
+
+/**
+ * Running this thread empties {@link #inputStream} into {@link #outputStream}.
+ * <p/>
+ * Adapted from
+ * <a href="http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html?page=4">this guide</a>.
+ */
+public class StreamGobbler extends Thread {
+ private InputStream inputStream;
+ private OutputStream outputStream;
+
+ /**
+ * @param inputStream Stream for reading.
+ * @param outputStream Stream for writing.
+ */
+ public StreamGobbler(InputStream inputStream, OutputStream outputStream) {
+ this.inputStream = inputStream;
+ this.outputStream = outputStream;
+ }
+
+ /**
+ * Empties this gobbler's {@link InputStream} into its {@link OutputStream}.
+ */
+ @Override
+ public void run() {
+ try {
+ PrintWriter printWriter = new PrintWriter(outputStream);
+
+ InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
+ BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
+ String line = null;
+ while ((line = bufferedReader.readLine()) != null) {
+ printWriter.println(line);
+ }
+
+ printWriter.flush();
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new RuntimeException("Problem reading messages from process.", e);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return Objects.toStringHelper(this)
+ .add("inputStream", inputStream)
+ .add("outputStream", outputStream)
+ .toString();
+ }
+}
\ No newline at end of file
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|