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