001    /*
002     * Copyright 2011-2015 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2011-2015 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     */
021    package com.unboundid.util;
022    
023    
024    
025    import java.io.OutputStream;
026    import java.io.IOException;
027    import java.util.ArrayList;
028    import java.util.Arrays;
029    import java.util.Collection;
030    import java.util.Collections;
031    import 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)
043    public 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    }