/********************************************************************************
* CruiseControl, a Continuous Integration Toolkit
* Copyright (c) 2001, ThoughtWorks, Inc.
* 200 E. Randolph, 25th Floor
* Chicago, IL 60601 USA
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* + Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* + Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
* names of its contributors may be used to endorse or promote
* products derive from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
********************************************************************************/
package net.sourceforge.cruisecontrol.testutil;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.*;
import java.util.regex.Pattern;
/* Non system classes, needs to be added to SysUtilMock#java */
import net.sourceforge.cruisecontrol.builders.PipedExecBuilder.Script;
import net.sourceforge.cruisecontrol.util.MainArgs;
import net.sourceforge.cruisecontrol.util.StreamConsumer;
import net.sourceforge.cruisecontrol.util.StreamPumper;
import org.apache.log4j.Logger; // required by StreamPumper
/**
* Class implementing some system utilities, usually found on Unix systems, in order to
* run tests on platforms not shipping those utilities.
*
* The class is not intended to be instantiated from the CruiseControl. Instead, it is
* expected to called from the separate Java process invoked to simulate the call of a
* system command.
*
* Note that the "utilities" implemented here do not follow the exact behavior of the
* real utilities they are replacement for. Neither the options are the same in many cases.
* All the "utilities" also expect textual data only!
*/
public class SysUtilMock {
/** The java command used to invoke this class. The "classpath" path is tried to
* be built automatically */
private static final String javaArgs = "-cp " + getClassPath(new Class[]{SysUtilMock.class,
MainArgs.class,
StreamConsumer.class,
StreamPumper.class,
Logger.class})
+ " " + SysUtilMock.class.getCanonicalName();
/**
* <code>cat</code> command with some information/debugging messages printed to
* STDERR. It has signature <code>[options] [file1 file2 ... fileN] [>[>]output]</code>.
*
* The options are:
* <code>[-V]</code> prints the data written to he output to the STDERR as well
* <code>[-P text]</code> text prefixing the messages printed to STDERR
* <code>[-D ms]</code> waits <i>ms</i> milliseconds before each line is written
* to the output.
*
* The space-separated list if files to join together can follow the options. The file
* may be string "-" to read from STDIN, or <i>ZERO</i> to read from infinite source
* returning newlines only (similar to <code>/dev/zero</code>). If there is no file set,
* the data are read from STDIN
*
* The output is STDOUT by default, or to file when redirected using "> filename" (to
* new file) or ">> filename" (to append to the existing file). The filename can be
* text <i>NULL</i> to simulate printing to <code>/dev/null</code>.
*
* Use SysUtilMock#cat[0] as argument for {@link Script#setCommand(String)},
* and SysUtilMock#cat[1] + <code>"required options list"</code> as argument
* for {@link Script#setArgs(String)}.
*/
public static final String[] cat = {"java", javaArgs + " -C cat " };
/**
* <code>grep</code> command with some information/debugging messages printed to
* STDERR. Contrary to the standard linux <code>grep</code>, this command expects
* regular expression in Java {@link Pattern} compatible form. It has signature
* <code>[options] pattern [file]</code>.
*
* The options are:
* <code>[-V]</code> prints the data written to he output to the STDERR as well
* <code>[-P text]</code> text prefixing the messages printed to STDERR
* <code>[-v]</code> revert match, lines not matching the pattern will be printed
* to the output.
*
* The <code>pattern</code> is {@link Pattern} compatible regular expression
*
* If <code>file</code> is set, the data are read from it. Otherwise, the data are read
* from STDIN. The command does not support either "-" or <i>ZERO</i> as source.
*
* The input filtered through the pattern is written to STDOUT, the redirection is not
* supported (pipe it with {@link #cat} if required).
*
* Use SysUtilMock#grep[0] as argument for {@link Script#setCommand(String)},
* and SysUtilMock#grep[1] + <code>"required options list"</code> as argument
* for {@link Script#setArgs(String)}.
*/
public static final String[] grep = {"java", javaArgs + " -C grep "};
/**
* <code>sort</code> command with some information/debugging messages printed to
* STDERR. It has signature <code>[options] [file]</code>.
*
* The options are:
* <code>[-V]</code> prints the data written to he output to the STDERR as well
* <code>[-P text]</code> text prefixing the messages printed to STDERR
* <code>[-u]</code> make the output unique ()
*
* If <code>file</code> is set, the data are read from it. Otherwise, the data are read
* from STDIN. The command does not support either "-" or <i>ZERO</i> as source.
*
* The sorted (and unique, is set) input is written to STDOUT, the redirection is not
* supported (pipe it with {@link #cat} if required).
*
* Use SysUtilMock#sort[0] as argument for {@link Script#setCommand(String)},
* and SysUtilMock#sort[1] + <code>"required options list"</code> as argument
* for {@link Script#setArgs(String)}.
*/
public static final String[] sort = {"java", javaArgs + " -C sort "};
/**
* <code>unique</code> command with some information/debugging messages printed to
* STDERR. It has signature <code>[options] [file]</code>.
*
* The options are:
* <code>[-V]</code> prints the data written to he output to the STDERR as well
* <code>[-P text]</code> text prefixing the messages printed to STDERR
*
* If <code>file</code> is set, the data are read from it. Otherwise, the data are read
* from STDIN. The command does not support either "-" or <i>ZERO</i> as source.
*
* The unique items from the input is written to STDOUT, the redirection is not supported
* (pipe it with {@link #cat} if required).
*
* Use SysUtilMock#uniq[0] as argument for {@link Script#setCommand(String)},
* and SysUtilMock#uniq[1] + <code>"required options list"</code> as argument
* for {@link Script#setArgs(String)}.
*/
public static final String[] uniq = {"java", javaArgs + " -C uniq "};
/**
* <code>shuf</code> command with some information/debugging messages printed to
* STDERR. It has signature <code>[options] [file]</code>.
*
* The options are:
* <code>[-V]</code> prints the data written to he output to the STDERR as well
* <code>[-P text]</code> text prefixing the messages printed to STDERR
*
* If <code>file</code> is set, the data are read from it. Otherwise, the data are read
* from STDIN. The command does not support either "-" or <i>ZERO</i> as source.
*
* The shuffled input is written to STDOUT, the redirection is not supported (pipe it
* with {@link #cat} if required).
*
* Use SysUtilMock#shuf[0] as argument for {@link Script#setCommand(String)},
* and SysUtilMock#shuf[1] + <code>"required options list"</code> as argument
* for {@link Script#setArgs(String)}.
*/
public static final String[] shuf = {"java", javaArgs + " -C shuf "};
/**
* Special command prefixing each input line by "NL NT NC ", where <i>NL</i> is the
* index of line, <i>NT</i> is the number of tokens (space separated fields) in the line,
* and <i>NC</i> is the numbed of characters in the line. The prefix can then be removed
* by {@link #del} command. Some information/debugging messages are printed to STDERR.
* The command has signature <code>[options] [file]</code>.
*
* The options are:
* <code>[-V]</code> prints the data written to he output to the STDERR as well
* <code>[-P text]</code> text prefixing the messages printed to STDERR
*
* If <code>file</code> is set, the data are read from it. Otherwise, the data are read
* from STDIN. The command does not support either "-" or <i>ZERO</i> as source.
*
* The prefixed input is written to STDOUT, the redirection is not supported (pipe it
* with {@link #cat} if required).
*
* Use SysUtilMock#add[0] as argument for {@link Script#setCommand(String)},
* and SysUtilMock#add[1] + <code>"required options list"</code> as argument
* for {@link Script#setArgs(String)}.
*/
public static final String[] add = {"java", javaArgs + " -C add "};
/**
* Special command deleting the prefix added by {@link #add} command. Some
* information/debugging messages are printed to STDERR. The command has signature
* <code>[options] [file]</code>.
*
* The options are:
* <code>[-V]</code> prints the data written to he output to the STDERR as well
* <code>[-P text]</code> text prefixing the messages printed to STDERR
*
* If <code>file</code> is set, the data are read from it. Otherwise, the data are read
* from STDIN. The command does not support either "-" or <i>ZERO</i> as source.
*
* The modified input is written to STDOUT, the redirection is not supported (pipe it
* with {@link #cat} if required).
*
* Use SysUtilMock#del[0] as argument for {@link Script#setCommand(String)},
* and SysUtilMock#del[1] + <code>"required options list"</code> as argument
* for {@link Script#setArgs(String)}.
*/
public static final String[] del = {"java", javaArgs + " -C del "};
/**
* Special command representing not existing command (the attempt to invoke it fails
* on system level by <i>command not found</i> error).
*
* Use SysUtilMock#BAD[0] as argument for {@link Script#setCommand(String)},
* and SysUtilMock#BAD[1] + <code>"whatever"</code> as argument
* for {@link Script#setArgs(String)}.
*/
public static final String[] BAD = {"BAD", ""};
/** Should the data printed to STDOUT (or file) be printed to STDERR as well? Se to
* <code>true</code> to do so. */
private static boolean verbose = false;
/**
* Main method, calls individual utilities according to the <code>-C command</code>
* arguments passed.
* @param args the arguments passed to the method
*/
public static void main(String[] args) {
try {
/* Get the prefix as the very first action */
final String prefix;
if ((prefix = MainArgs.parseArgument(args, "P", null, null)) != null) {
args = removeArgs(args, MainArgs.findIndex(args, "P"), 2);
System.setErr(new PrefixedStream(prefix, System.err));
}
/* Print how the command was called */
System.err.print("Executing command: ");
System.err.print("java[" + SysUtilMock.class.getCanonicalName() + "]");
for (String a : args) {
System.err.print(" ");
System.err.print(a);
}
System.err.println();
/* Get the command */
final String command;
if ((command = MainArgs.parseArgument(args, "C", null, null)) == null) {
throw new IllegalArgumentException("Command (-C option) not set");
}
/* Remove the command from the array. It will contain only the command
* options now */
args = removeArgs(args, MainArgs.findIndex(args, "C"), 2);
/* Get the verbose output flag */
if (MainArgs.argumentPresent(args, "V")) {
verbose = true;
args = removeArgs(args, MainArgs.findIndex(args, "V"), 1);
}
/* Call the command */
if ("cat".equals(command)) {
cat(args);
System.exit(0);
}
if ("grep".equals(command)) {
grep(args);
System.exit(0);
}
if ("sort".equals(command)) {
sort(args);
System.exit(0);
}
if ("uniq".equals(command)) {
uniq(args);
System.exit(0);
}
if ("shuf".equals(command)) {
shuf(args);
System.exit(0);
}
if ("add".equals(command)) {
modify(args, true);
System.exit(0);
}
if ("del".equals(command)) {
modify(args, false);
System.exit(0);
}
/* Command not found */
throw new IllegalArgumentException("Command '" + command + "' not supported");
} catch(Throwable e) {
System.err.println("Command failed with exception:");
/* Print the exception */
e.printStackTrace(System.err);
System.exit(1);
}
}
/**
* The implementation of {@link #cat} command.
* @param args the arguments passed to the {@link #cat} command. Options not
* expected cause error.
* @throws IOException If an io error occurs
*/
private static void cat(String[] args) throws IOException {
/* Is -D set? */
final int hold = MainArgs.parseInt(args, "D", -1, -1);
if (hold > -1) {
args = removeArgs(args, MainArgs.findIndex(args, "D"), 2);
}
/* Create the array of inputs - process argument-by-argument */
final List<InputStream> inputs = new ArrayList<InputStream>();
for (final String src : args) {
/* Redirection found, leave */
if (src.startsWith(">")) {
break;
}
/* Read from STDIO */
if ("-".equals(src)) {
inputs.add(System.in);
continue;
}
/* Read from "ZERO". It returns '\n' instead of an empty character to prevent
* "line overflow" (one line cannot be read as it would be infinite ...) */
if ("ZERO".equals(src)) {
inputs.add(new InputStream() {
/* Returns '\n' */
@Override
public int read() {
return '\n';
}
});
continue;
}
/* Read from "classic" file */
inputs.add(new FileInputStream(src));
}
/* Create the output. If all attributes were not processed, the output is redirected
* to a file */
StreamFlusher output = null;
if (args.length > inputs.size()) {
final String ofname;
final boolean append;
/* Just one remaining: ">file" */
if (args.length == inputs.size() +1) {
append = args[args.length -1].startsWith(">>");
ofname = args[args.length -1].replaceFirst("^>+", "");
}
/* more remaining "> file"*/
else {
append = args[args.length -2].equals(">>");
ofname = args[args.length -1];
}
/* Create output stream. If output filename is "NULL", ignore the output */
if ("NULL".equals(ofname)) {
output = new StreamFlusher(null, hold);
}
else {
output = new StreamFlusher(new FileOutputStream(ofname, append), hold);
}
}
/* If not output stream was set, print to STDOUT */
if (output == null) {
output = new StreamFlusher(System.out, hold);
}
/* If not input was set, read from STDIN */
if (inputs.size() == 0) {
inputs.add(System.in);
}
/* Process source-by-source */
for (InputStream i : inputs) {
new StreamPumper(i, output).run();
i.close();
}
/* Close the output */
output.close();
} /* cat */
/**
* The implementation of {@link #grep} command.
* @param args the arguments passed to the command. Options not expected cause error.
* @throws IOException if an IO error occurs.
*/
private static void grep(String[] args) throws IOException {
final int invert = MainArgs.findIndex(args, "v");
/* Is -v set? */
if (invert != MainArgs.NOT_FOUND) {
args = removeArgs(args, invert, 1);
}
/* No pattern set? Error */
if (args.length == 0) {
throw new IllegalArgumentException("Not enough arguments for 'grep'");
}
System.err.println("Grepping by pattern " + args[0] + ", lines " +
(invert != MainArgs.NOT_FOUND ? "not " : "") + "matching the pattern are printed");
/* Compile the pattern */
final Pattern regexp = Pattern.compile(args[0]);
/* Read the data from STDIO, if no file was set (in argument 1)
* Store data to STDOUT (implements filter prior to storig the data) */
final InputStream input = args.length == 1 ? System.in : new FileInputStream(args[1]);
final StreamFlusher output = new StreamFlusher(System.out, 0);
final AbstractConsumer filter = new AbstractConsumer(output) {
/** The implementation of {@link net.sourceforge.cruisecontrol.util.StreamConsumer#consumeLine(String)}
* filtering the lines */
@Override
public void consumeLine(final String line) {
final boolean f = regexp.matcher(line).find();
if (f && invert == MainArgs.NOT_FOUND) {
super.consumeLine(line);
}
if (!f && invert != MainArgs.NOT_FOUND) {
super.consumeLine(line);
}
}
};
/* Filer the input and write it to output */
new StreamPumper(input, filter).run();
input.close();
output.close();
System.err.println(filter.getNumLines() + " lines checked");
System.err.println(output.getNumLines() + " lines written");
} /* grep */
/**
* The implementation of {@link #sort} command.
* @param args the arguments passed to the command. Options not expected cause error.
* @throws IOException if an IO error occurs.
*/
private static void sort(String[] args) throws IOException {
final int uniq;
/* Is -u set? */
if((uniq = MainArgs.findIndex(args, "u")) != MainArgs.NOT_FOUND) {
args = removeArgs(args, uniq, 1);
}
System.err.println("Sorting " + (uniq != MainArgs.NOT_FOUND ? "and makign unique " :
"") + "lines in the input unique");
/* Read the data from STDIO, if no file was set
* Store data to STDOUT */
final InputStream input = args.length == 0 ? System.in : new FileInputStream(args[0]);
final StreamFlusher output = new StreamFlusher(System.out, 0);
ArrayStoreConsumer lines = new ArrayStoreConsumer();
new StreamPumper(input, lines).run();
System.err.println(lines.size() + " lines read");
/* Make the data unique (and sorted), if required */
if (uniq != MainArgs.NOT_FOUND) {
lines = new ArrayStoreConsumer(new TreeSet<String>(lines));
}
/* Sort them only, otherwise */
else {
Collections.sort(lines);
}
/* Write unique (and sorted) lines to the output */
for (final String s : lines) {
output.consumeLine(s);
}
input.close();
output.close();
System.err.println(output.getNumLines() + (uniq != MainArgs.NOT_FOUND ? " unique" : "")
+ " lines written");
} /* sort */
/**
* The implementation of {@link #uniq} command.
* @param args the arguments passed to the command. Options not expected cause error.
* @throws IOException if an IO error occurs.
*/
private static void uniq(final String[] args) throws IOException {
System.err.println("Making lines in the input unique");
/* Read the data from STDIO, if no file was set
* Store data to STDOUT */
final InputStream input = args.length == 0 ? System.in : new FileInputStream(args[0]);
final StreamFlusher output = new StreamFlusher(System.out, 0);
final ArrayStoreConsumer lines = new ArrayStoreConsumer();
new StreamPumper(input, lines).run();
System.err.println(lines.size() + " lines read");
/* Write unique (and sorted) lines to the output */
for (final String s : new TreeSet<String>(lines)) {
output.consumeLine(s);
}
input.close();
output.close();
System.err.println(output.getNumLines() + " unique lines written");
} /* uniq */
/**
* The implementation of {@link #shuf} command.
* @param args the arguments passed to the command. Options not expected cause error.
* @throws IOException if an IO error occurs.
*/
private static void shuf(final String[] args) throws IOException {
System.err.println("Shuffling lines in the input");
/* Read the data from STDIO, if no file was set
* Store data to STDOUT */
final InputStream input = args.length == 0 ? System.in : new FileInputStream(args[0]);
final StreamFlusher output = new StreamFlusher(System.out, 0);
final ArrayStoreConsumer lines = new ArrayStoreConsumer();
new StreamPumper(input, lines).run();
/* Shuffle the lines */
Collections.shuffle(lines);
/* Write to the output */
for (final String s : lines) {
output.consumeLine(s);
}
input.close();
output.close();
System.err.println(output.getNumLines() + " lines were read/shuffled/written");
} /* shuff */
/**
* The implementation of {@link #add} and {@link #del} commands.
* @param args the arguments passed to the command. Options not expected cause error.
* @param add <code>true</code> if to all {@link #add}, <code>false</code> if to call
* {@link #del}
* @throws IOException if an IO error occurs.
*/
private static void modify(final String[] args, final boolean add) throws IOException {
System.err.println("Modifying input by " + (add ? "adding some items"
: "removing items added before"));
/* Read the data from STDIO, if no file was set
* Store data to STDOUT */
final InputStream input = args.length == 0 ? System.in : new FileInputStream(args[0]);
final StreamFlusher output = new StreamFlusher(System.out, 0) {
/** The implementation of {@link net.sourceforge.cruisecontrol.util.StreamConsumer#consumeLine(String)}
* filtering the lines */
@Override
public void consumeLine(String line) {
/* Add:
* - the index of the line,
* - the number of items in the line
* - the length of the line */
if (add) {
line = getNumLines() + " " + line.split("\\s+").length + " " +
line.length() + " " + line;
}
/* Remove the 3 items added */
else {
line = line.replaceAll("^\\s*\\d+\\s+\\d+\\s+\\d+\\s+", "");
}
/* Pass it to the parent */
super.consumeLine(line);
}
};
/* Filter the input and write it to the output */
new StreamPumper(input, output).run();
input.close();
output.close();
System.err.println(output.getNumLines() + " lines were read/modified/written");
} /* add */
/**
* Removes the given range of arguments from the input array of arguments.
*
* @param args the array of arguments
* @param from the index of the first argument to remove
* @param num the number of arguments to remove
* @return the copy of array without the indexes specified
*/
private static String[] removeArgs(final String[] args, final int from, final int num) {
final List<String> arglist = new ArrayList<String>(args.length);
/* Add the first part up to from */
arglist.addAll(Arrays.asList(args).subList(0, from));
/* and the second part */
arglist.addAll(Arrays.asList(args).subList(from + num, args.length));
/* Get the reduced array */
return arglist.toArray(new String[arglist.size()]);
}
/**
* Gets the string with classpath for <code>java</code> command (<code>-cp</code> option).
* The string is build from <code>CLASSPATH</code> environment variable, and from
* directories where the <code>.class</code> files are located for the given classes.
*
* @param clazz the classes for which to find the classpath direcory
* @return the string to be set as "classpath"
*/
private static String getClassPath(final Class<?>[] clazz) {
final HashSet<String> cpset = new HashSet<String>();
final StringBuilder cpopt = new StringBuilder();
/* Environment variable */
if (System.getenv("CLASSPATH") != null) {
Collections.addAll(cpset, System.getenv("CLASSPATH").split(File.pathSeparator));
}
/* Individual classes */
for (final Class<?> c : clazz) {
try {
final String classFile = c.getCanonicalName().replace('.', '/') + ".class";
URL classPath = c.getClassLoader().getResource(classFile);
/* The URL points to JAR file */
if ("jar".equals(classPath.getProtocol())) {
classPath = ((JarURLConnection) classPath.openConnection()).getJarFileURL();
}
/* Add the basic directory (without class namespace) to the set */
cpset.add(classPath.getPath().replace(classFile, ""));
} catch(Exception e) {
System.err.println("Cannot build classpath for class " + c.getCanonicalName());
e.printStackTrace(System.err);
}
}
/* Format to the classpath option */
for(final String s : cpset) {
cpopt.append(cpopt.lastIndexOf(File.pathSeparator) < cpopt.length() -1 ?
File.pathSeparator : ""); // endswith(":")
cpopt.append(s);
}
/* Get the result */
return cpopt.toString();
}
/**
* The parent of all the consumers.
*/
private static class AbstractConsumer implements StreamConsumer
{
/** Constructor. Creates the consumer suppressing all the messages. */
public AbstractConsumer() {
this(null);
}
/** Constructor. Creates consumer passing the data to the given consumer.
* @param out the consumer to write to (can be <code>null</code> to suppress the
* output) */
public AbstractConsumer(final AbstractConsumer out) {
this.lines = 0;
this.out = out;
}
/** The implementation of {@link StreamConsumer#consumeLine(String)}. It is highly
* recommended to call the method, if overriding! */
//@Override
public void consumeLine(final String line) {
this.lines++;
if (this.out != null) {
this.out.consumeLine(line);
}
}
/** @return the number of lines written */
public long getNumLines() {
return lines;
}
/** Closes the output stream. This implementation just calls parent */
public void close() {
if (this.out != null) {
this.out.close();
}
}
/** The consumer to write to */
private final AbstractConsumer out;
/** The number of lines passed through the consumer */
private long lines;
}
/**
* The implementation of {@link StreamConsumer} which stores the lines in the array
* represented by this class
*/
private static class ArrayStoreConsumer extends ArrayList<String> implements StreamConsumer
{
/** Serialization UID */
private static final long serialVersionUID = -3226412369210939346L;
/** Default constructor */
public ArrayStoreConsumer() {
/* Just call parent */
}
/** Copy constructor. Fills the class by items from the given collection
* @param parent the collection to fill from */
public ArrayStoreConsumer(final Collection<String> parent) {
super(parent);
}
/** The implementation of {@link StreamConsumer#consumeLine(String)} */
//@Override
public void consumeLine(final String line) {
add(line);
}
}
/**
* The implementation of {@link AbstractConsumer} which passes the lines into the given
* output stream
*/
private static class StreamFlusher extends AbstractConsumer
{
/** Constructor
* @param outStream the stream to write to, or <code>null</code> not to write the
* lines anywhere
* @param waitMSec the time[ms] to wait before flushing each line to the output
* stream; if value is <= 0, line is flushed immediately.
*/
public StreamFlusher(final OutputStream outStream, final int waitMSec) {
waitMS = waitMSec;
out = outStream != null ? new PrintStream(outStream)
: null;
}
/** The implementation of {@link StreamConsumer#consumeLine(String)} */
@Override
public void consumeLine(final String line) {
/* Call the parent */
super.consumeLine(line);
/* if wait, wait */
if (waitMS > 10) {
try {
Thread.sleep(waitMS);
} catch (InterruptedException e) {
System.err.println("Exception caught when waiting for " + waitMS + "[msec]." +
"Message is: " + e.getMessage());
}
}
/* Flush the line */
if (out != null) {
out.println(line);
}
/* Print the line to STDERR in verbose mode */
if (verbose) {
System.err.println(line);
}
}
/** Closes the output stream */
@Override
public void close() {
out.close();
}
/** Wait time[ms] before flushing it to output stream (if > 10) */
private final int waitMS;
/** The stream to write to */
private final PrintStream out;
}
/**
* Overriding of {@link PrintStream} prefixing each line by the given string. The stream
* is used as the replacement of {@link System#err}, prefixing the output by the ID of
* the command (passed through <code>-P</code> option, set as the prefix) for debugging
* purposes.
*/
private static class PrefixedStream extends PrintStream
{
/** Constructor
* @param prefix the string print before each line
* @param out the stream to which values and objects will be printed */
public PrefixedStream(final String prefix, final PrintStream out) {
super(out);
this.prefix = (prefix + ": ").getBytes();
printPrefix = true;
}
/** The overriding of {@link PrintStream#write(byte[], int, int)} */
@Override
public void write(final byte[] buf, final int off, final int len) {
/* Possibly performace killer, but acceptabe for now ... */
for (int i = 0; i < len; i++) {
write(buf[i]);
}
}
/** The overriding of {@link PrintStream#write(int)} */
@Override
public void write(final int b) {
/* Print prefix, if scheduled */
if (printPrefix) {
printPrefix = false;
for (byte p : this.prefix) {
super.write(p);
}
}
super.write(b);
/* Schedule prefix printing, if newline was found */
if (b == '\n') {
printPrefix = true;
}
}
/** The prefix */
private final byte[] prefix;
/** Should the prefix be printed after new call? */
private boolean printPrefix;
}
}