001/*
002 * Copyright 2011-2014 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2011-2014 UnboundID Corp.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021package com.unboundid.util;
022
023
024
025import java.io.OutputStream;
026import java.io.IOException;
027import java.util.ArrayList;
028import java.util.Arrays;
029import java.util.Collection;
030import java.util.Collections;
031import java.util.List;
032
033
034
035/**
036 * This class provides an {@code OutputStream} implementation that can cause
037 * everything provided to it to be written to multiple output streams (e.g.,
038 * to both a file and to standard output, or to both a file and a network
039 * socket).  Any number of destination streams (including zero, if desired) may
040 * be specified.
041 */
042@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
043public final class TeeOutputStream
044       extends OutputStream
045{
046  // The set of target output streams to which any data received will be
047  // written.
048  private final List<OutputStream> streams;
049
050
051
052  /**
053   * Creates a new instance of this output stream that will write any data
054   * received to each of the provided target streams.
055   *
056   * @param  targetStreams  The set of output streams to which any data received
057   *                        will be written.  If it is {@code null} or empty,
058   *                        then any data received will simply be discarded.
059   */
060  public TeeOutputStream(final OutputStream... targetStreams)
061  {
062    if (targetStreams == null)
063    {
064      streams = Collections.emptyList();
065    }
066    else
067    {
068      streams = Collections.unmodifiableList(
069           new ArrayList<OutputStream>(Arrays.asList(targetStreams)));
070    }
071  }
072
073
074
075  /**
076   * Creates a new instance of this output stream that will write any data
077   * received to each of the provided target streams.
078   *
079   * @param  targetStreams  The set of output streams to which any data received
080   *                        will be written.  If it is {@code null} or empty,
081   *                        then any data received will simply be discarded.
082   */
083  public TeeOutputStream(final Collection<? extends OutputStream> targetStreams)
084  {
085    if (targetStreams == null)
086    {
087      streams = Collections.emptyList();
088    }
089    else
090    {
091      streams = Collections.unmodifiableList(
092           new ArrayList<OutputStream>(targetStreams));
093    }
094  }
095
096
097
098  /**
099   * Writes the provided byte of data to each of the target output streams.
100   *
101   * @param  b  The byte of data to be written.  Only the lower eight bits
102   *            of the provided value will be written.
103   *
104   * @throws  IOException  If a problem occurs while writing the provided byte
105   *                       to any of the target output streams.
106   */
107  @Override()
108  public void write(final int b)
109         throws IOException
110  {
111    for (final OutputStream s : streams)
112    {
113      s.write(b);
114    }
115  }
116
117
118
119  /**
120   * Writes the entire contents of the provided byte array to each of the target
121   * output streams.
122   *
123   * @param  b  The byte array containing the data to be written.
124   *
125   * @throws  IOException  If a problem occurs while writing the provided data
126   *                       to any of the target output streams.
127   */
128  @Override()
129  public void write(final byte[] b)
130         throws IOException
131  {
132    for (final OutputStream s : streams)
133    {
134      s.write(b);
135    }
136  }
137
138
139
140  /**
141   * Writes a portion of the contents of the provided byte array to each of the
142   * target output streams.
143   *
144   * @param  b    The byte array containing the data to be written.
145   * @param  off  The offset within the array at which the data should start
146   *              being written.
147   * @param  len  The number of bytes from the array that should be written.
148   *
149   * @throws  IOException  If a problem occurs while writing the provided data
150   *                       to any of the target output streams.
151   */
152  @Override()
153  public void write(final byte[] b, final int off, final int len)
154         throws IOException
155  {
156    for (final OutputStream s : streams)
157    {
158      s.write(b, off, len);
159    }
160  }
161
162
163
164  /**
165   * Flushes each of the target output streams to force any buffered content to
166   * be written out.
167   *
168   * @throws  IOException  If a problem occurs while flushing any of the target
169   *                       output streams.
170   */
171  @Override()
172  public void flush()
173         throws IOException
174  {
175    for (final OutputStream s : streams)
176    {
177      s.flush();
178    }
179  }
180
181
182
183  /**
184   * Closes each of the target output streams.
185   *
186   * @throws  IOException  If a problem occurs while closing any of the target
187   *                       output streams.  Note that even if an exception is
188   *                       thrown, an attempt will be made to close all target
189   *                       streams.  If multiple target streams throw an
190   *                       exception, then the first exception encountered will
191   *                       be thrown.
192   */
193  @Override()
194  public void close()
195         throws IOException
196  {
197    IOException exceptionToThrow = null;
198
199    for (final OutputStream s : streams)
200    {
201      try
202      {
203        s.close();
204      }
205      catch (final IOException ioe)
206      {
207        Debug.debugException(ioe);
208        if (exceptionToThrow == null)
209        {
210          exceptionToThrow = ioe;
211        }
212      }
213    }
214
215    if (exceptionToThrow != null)
216    {
217      throw exceptionToThrow;
218    }
219  }
220}