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