/******************************************************************************* * $Id$ * $Author$ * $Date$ * * Copyright 2002 - YAJUL Developers, Joshua Davis, Kent Vogel. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * ******************************************************************************/ package org.yajul.io; import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.logging.Level; import java.util.logging.Logger; import static org.yajul.juli.LogHelper.unexpected; /** * An output stream filter that writes to multiple underlying output streams. * * @author josh */ public class TeeOutputStream extends FilterOutputStream { private static Logger log = Logger.getLogger(TeeOutputStream.class.getName()); private OutputStream[] teeOut; // Array of streams to write to. /** * Creates a new output stream that echoes output to both * of the specified streams. * * @param streams streams to write to. */ public TeeOutputStream(OutputStream ... streams) { super(streams[0]); teeOut = new OutputStream[streams.length - 1]; System.arraycopy(streams, 1, teeOut, 0, teeOut.length); } /** * Closes this output stream and releases any system resources * associated with the stream. * <p/> * The <code>close</code> method of <code>FilterOutputStream</code> * calls its <code>flush</code> method, and then calls the * <code>close</code> method of its underlying output stream. * <p/> * NOTE: This implementation also closes the 'tee' streams as well. * * @throws IOException if an I/O error occurs. * @see FilterOutputStream#flush() * @see FilterOutputStream#out */ public void close() throws IOException { IOException e = null; super.close(); // Close the main underlying output stream. for (OutputStream aTeeOut : teeOut) { try { aTeeOut.close(); } catch (IOException ioe) { unexpected(log, e); e = ioe; } } teeOut = null; if (e != null) throw e; } /** * Flushes this output stream and forces any buffered output bytes * to be written out to the stream. * <p/> * The <code>flush</code> method of <code>FilterOutputStream</code> * calls the <code>flush</code> method of its underlying output stream. * * @throws IOException if an I/O error occurs. * @see FilterOutputStream#out */ public void flush() throws IOException { IOException e = null; super.flush(); for (OutputStream aTeeOut : teeOut) { try { aTeeOut.flush(); } catch (IOException ioe) { unexpected(log, e); e = ioe; } } if (e != null) throw e; } /** * Writes <code>b.length</code> bytes to this output stream. * <p/> * The <code>write</code> method of <code>FilterOutputStream</code> * calls its <code>write</code> method of three arguments with the * arguments <code>b</code>, <code>0</code>, and * <code>b.length</code>. * <p/> * Note that this method does not call the one-argument * <code>write</code> method of its underlying stream with the single * argument <code>b</code>. * <p/> * NOTE: This implementation also writes to the 'tee' streams as well. * * @param b the data to be written. * @throws IOException if an I/O error occurs. * @see FilterOutputStream#write(byte[],int,int) */ public void write(byte b[]) throws IOException { IOException e = null; // NOTE: If super.write(b) is called, FilterOutputStream // will invoke write(b) in a loop. So the output stream is being // invoked directly here. super.out.write(b); for (OutputStream aTeeOut : teeOut) { try { aTeeOut.write(b); } catch (IOException ioe) { unexpected(log, e); e = ioe; } } if (e != null) throw e; } /** * Writes <code>len</code> bytes from the specified * <code>byte</code> array starting at offset <code>off</code> to * this output stream. * <p/> * The <code>write</code> method of <code>FilterOutputStream</code> * calls the <code>write</code> method of one argument on each * <code>byte</code> to output. * <p/> * Note that this method does not call the <code>write</code> method * of its underlying input stream with the same arguments. Subclasses * of <code>FilterOutputStream</code> should provide a more efficient * implementation of this method. * <p/> * NOTE: This implementation also writes to the 'tee' streams as well. * * @param b the data. * @param off the start offset in the data. * @param len the number of bytes to write. * @throws IOException if an I/O error occurs. * @see FilterOutputStream#write(int) */ public void write(byte b[], int off, int len) throws IOException { IOException e = null; // NOTE: If super.write(b,off,len) is called, FilterOutputStream // will invoke write(b) in a loop. So the output stream is being // invoked directly here. super.out.write(b, off, len); for (OutputStream aTeeOut : teeOut) { try { aTeeOut.write(b, off, len); } catch (IOException ioe) { unexpected(log, e); e = ioe; } } if (e != null) throw e; } /** * Writes the specified <code>byte</code> to this output stream. * <p/> * The <code>write</code> method of <code>FilterOutputStream</code> * calls the <code>write</code> method of its underlying output stream, * that is, it performs <tt>out.write(b)</tt>. * <p/> * Implements the abstract <tt>write</tt> method of <tt>OutputStream</tt>. * <p/> * NOTE: This implementation also writes to the 'tee' streams as well. * * @param b the <code>byte</code>. * @throws IOException if an I/O error occurs. */ public void write(int b) throws IOException { IOException e = null; super.write(b); for (OutputStream aTeeOut : teeOut) { try { aTeeOut.write(b); } catch (IOException ioe) { unexpected(log, e); e = ioe; } } if (e != null) throw e; } }