/******************************************************************************** * CruiseControl, a Continuous Integration Toolkit * Copyright (c) 2001-2003, 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 derived 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. ********************************************************************************/ /* * The Apache Software License, Version 1.1 * * Copyright (c) 2000,2002 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. 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. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Ant", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR * ITS 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. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. */ package net.sourceforge.cruisecontrol.util; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PipedInputStream; import java.io.PipedOutputStream; import org.apache.log4j.Logger; /** * Class to pump the error stream during Process's runtime. Originally copied from * the Ant built-in task, but changed to work with both text and binary data. * * @since June 11, 2001 * @author <a href="mailto:fvancea@maxiq.com">Florin Vancea</a> * @author <a href="mailto:pj@thoughtworks.com">Paul Julius</a> */ public class StreamPumper implements Runnable { private final InputStream in; private final boolean isBinary; private final OutputStream binConsumer; private final StreamConsumer txtConsumer; private static final int SIZE = 1024; private static final Logger LOG = Logger.getLogger(StreamPumper.class); /** * Constructor. Assigns the stream to read <b>text data</b> from with the consumer being * fed with the texts. * * @param in the stream to read texts from * @param consumer the consumer to feed texts into */ public StreamPumper(final InputStream in, final StreamConsumer consumer) { this.in = in; this.isBinary = false; this.txtConsumer = consumer; this.binConsumer = null; } /** * Constructor. Assigns the stream to read <b>text or binary data</b> from with the * consumers being fed with data. If the data are marked as binary, summary message only * is written into {@link StreamConsumer}. * * @param in the stream to read data from * @param isBinary the consumer to feed texts into * @param txtConsumer the consumer to feed texts into * @param binConsumer the consumer to feed data into */ public StreamPumper(final InputStream in, boolean isBinary, final StreamConsumer txtConsumer, final OutputStream binConsumer) { this.in = in; this.isBinary = isBinary; this.txtConsumer = txtConsumer; this.binConsumer = binConsumer; } /** * Reads data from the input stream and writes them to the consumers assigned. */ public void run() { int bytesread = 0; try { // No stream, do not read (close outputs in finally {}) if (this.in == null) { return; } // If only text stream consumer is set, pass the input stream directly through // Reader which will convert it to the strings ... if (this.binConsumer == null && !this.isBinary) { final BufferedReader reader = new BufferedReader(new InputStreamReader(this.in)); String s; while ((s = reader.readLine()) != null) { consumeLine(s, this.txtConsumer); } // Well, we have binary reader defined as well ... // So, chunks of binary data must be read from the input stream and directly passed to the // binary output. Moreover, they must be passed to the text output (for non-binary only data) // which is done through pipe: byte[] -> PipeOutputStream -> PipeInputStream -> BufferedReader } else { // For non-binary data create the binary->text conversion pipe // Use the StreamPumper class final OutputStream tostrOutput; final Thread tostrReader; if (!this.isBinary) { tostrOutput = new PipedOutputStream(); tostrReader = new Thread(new StreamPumper(new PipedInputStream((PipedOutputStream) tostrOutput), this.txtConsumer)); tostrReader.start(); } else { tostrOutput = null; tostrReader = new Thread(); } // Read binary data final byte[] binBuff = new byte[SIZE]; int numread; try { while ((numread = this.in.read(binBuff)) >= 0) { bytesread += numread; // Pass them to the binary consumer and to binary->text conversion pipe consumeBytes(binBuff, numread, this.binConsumer); consumeBytes(binBuff, numread, tostrOutput); } } finally { // Must close, otherwise the binary->text conversion thread will never end IO.close(tostrOutput); } tostrReader.join(); // Print summary in binary mode if (this.isBinary) { consumeLine("Read " + bytesread + " Bytes", this.txtConsumer); } } } catch (Exception e) { LOG.error("Problem consuming input stream [" + this.in + "], " + bytesread + " Bytes passed", e); } finally { IO.close(this.in); IO.close(this.binConsumer); } } private void consumeLine(String line, final StreamConsumer consumer) { if (consumer != null) { try { // Remove VT100 terminal escape sequences line = line.replaceAll("\\e(\\[[^a-zA-Z]*[a-zA-Z]|[^\\[])", ""); // Remove other control characters apart from tab, newline and carriage return line = line.replaceAll("[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f]", ""); consumer.consumeLine(line); } catch (RuntimeException e) { LOG.error("Problem consuming line [" + line + "]", e); } } } private void consumeBytes(final byte[] bytes, int len, final OutputStream consumer) throws IOException { if (consumer != null) { consumer.write(bytes, 0, len); } } }