001 /*
002 * Copyright 2011-2016 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2011-2016 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 }