Move ProcessHelper and its friends to the processing-app, so they can be used by the unit tests

This commit is contained in:
jdf
2010-03-17 18:55:58 +00:00
parent 48c3cf30f9
commit fb6aa9712c
11 changed files with 22 additions and 14 deletions
@@ -0,0 +1,5 @@
package processing.util.exec;
public interface LineProcessor {
void processLine(final String line);
}
@@ -0,0 +1,86 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2009-10 Ben Fry and Casey Reas
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.util.exec;
import java.io.IOException;
import java.io.StringWriter;
/**
* Class to handle calling Runtime.exec() and stuffing output and error streams
* into Strings that can be dealt with more easily.
*
* @author Jonathan Feinberg <jdf@pobox.com>
*/
public class ProcessHelper {
private final String[] cmd;
public ProcessHelper(final String... cmd) {
this.cmd = cmd;
}
@Override
public String toString() {
final StringBuffer buffer = new StringBuffer();
for (int i = 0; i < cmd.length; i++) {
if (i != 0) {
buffer.append(" ");
}
buffer.append(cmd[i]);
}
return buffer.toString();
}
/**
* Blocking execution.
* @return exit value of process
* @throws InterruptedException
* @throws IOException
*/
public ProcessResult execute() throws InterruptedException, IOException {
final StringWriter outWriter = new StringWriter();
final StringWriter errWriter = new StringWriter();
final long startTime = System.currentTimeMillis();
final String prettyCommand = toString();
// System.err.println("ProcessHelper: >>>>> " + Thread.currentThread().getId()
// + " " + prettyCommand);
final Process process = Runtime.getRuntime().exec(cmd);
ProcessRegistry.watch(process);
try {
new StreamPump(process.getInputStream()).addTarget(outWriter).start();
new StreamPump(process.getErrorStream()).addTarget(errWriter).start();
try {
final int result = process.waitFor();
final long time = System.currentTimeMillis() - startTime;
// System.err.println("ProcessHelper: <<<<< "
// + Thread.currentThread().getId() + " " + cmd[0] + " (" + time
// + "ms)");
return new ProcessResult(prettyCommand, result, outWriter.toString(),
errWriter.toString(), time);
} catch (final InterruptedException e) {
System.err.println("Interrupted: " + prettyCommand);
throw e;
}
} finally {
process.destroy();
ProcessRegistry.unwatch(process);
}
}
}
@@ -0,0 +1,40 @@
package processing.util.exec;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
public class ProcessRegistry {
private static final Set<Process> REGISTRY = Collections
.synchronizedSet(new HashSet<Process>());
static {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
synchronized (REGISTRY) {
for (final Process p : REGISTRY) {
try {
// System.err.println("Cleaning up rogue process " + p);
p.destroy();
} catch (final Exception drop) {
}
}
}
}
});
}
/**
* When starting up a process
* @param p
*/
public static void watch(final Process p) {
REGISTRY.add(p);
}
public static void unwatch(final Process p) {
REGISTRY.remove(p);
}
}
@@ -0,0 +1,60 @@
package processing.util.exec;
import java.util.Arrays;
import java.util.Iterator;
public class ProcessResult implements Iterable<String> {
private final String cmd;
private final long time;
private final String output;
private final String error;
private final int result;
public ProcessResult(final String cmd, final int result, final String output,
final String error, final long time) {
this.cmd = cmd;
this.output = output;
this.error = error;
this.result = result;
this.time = time;
}
public Iterator<String> iterator() {
return Arrays.asList(output.split("\r?\n")).iterator();
}
public String getCmd() {
return cmd;
}
public int getResult() {
return result;
}
public boolean succeeded() {
return result == 0;
}
public String getStderr() {
return error;
}
public String getStdout() {
return output;
}
public long getTime() {
return time;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder(cmd).append("\n");
sb.append(" status: ").append(result).append("\n");
sb.append(" ").append(time).append("ms").append("\n");
sb.append(" stdout:\n").append(output).append("\n");
sb.append(" stderr:\n").append(error);
return sb.toString();
}
}
@@ -0,0 +1,101 @@
package processing.util.exec;
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 java.io.Writer;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
/**
* <p>A StreamPump reads lines of text from its given InputStream
* and informs its LineProcessors
* until the InputStream is exhausted. <b>It is useful only for pumping lines of
* text, and not for arbitrary binary cruft.</b> It's handy for reading
* the output of processes that emit textual data, for example.
*
* @author Jonathan Feinberg &lt;jdf@pobox.com&gt;
*
*/
public class StreamPump implements Runnable {
private static final ExecutorService threads = Executors
.newCachedThreadPool(new ThreadFactory() {
public Thread newThread(final Runnable r) {
final Thread t = new Thread(r);
t.setDaemon(true);
t.setName("StreamPump " + t.getId());
return t;
}
});
private final BufferedReader reader;
private final List<LineProcessor> outs = new CopyOnWriteArrayList<LineProcessor>();
public StreamPump(final InputStream in) {
this.reader = new BufferedReader(new InputStreamReader(in));
}
public StreamPump addTarget(final OutputStream out) {
outs.add(new WriterLineProcessor(out));
return this;
}
public StreamPump addTarget(final Writer out) {
outs.add(new WriterLineProcessor(out));
return this;
}
public StreamPump addTarget(final LineProcessor out) {
outs.add(out);
return this;
}
public void start() {
threads.execute(this);
}
public void run() {
try {
String line;
while ((line = reader.readLine()) != null) {
for (final LineProcessor out : outs) {
try {
out.processLine(line);
} catch (final Exception e) {
}
}
}
} catch (final IOException e) {
e.printStackTrace(System.err);
}
}
private static class WriterLineProcessor implements LineProcessor {
private final PrintWriter writer;
private WriterLineProcessor(final OutputStream out) {
this.writer = new PrintWriter(out, true);
}
private WriterLineProcessor(final Writer writer) {
this.writer = new PrintWriter(writer, true);
}
public void processLine(final String line) {
writer.println(line);
}
}
public static final LineProcessor DEVNULL = new LineProcessor() {
public void processLine(final String line) {
// noop
}
};
}