001/* 002 * Copyright 2011-2019 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2011-2019 Ping Identity Corporation 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<>(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(new ArrayList<>(targetStreams)); 092 } 093 } 094 095 096 097 /** 098 * Writes the provided byte of data to each of the target output streams. 099 * 100 * @param b The byte of data to be written. Only the lower eight bits 101 * of the provided value will be written. 102 * 103 * @throws IOException If a problem occurs while writing the provided byte 104 * to any of the target output streams. 105 */ 106 @Override() 107 public void write(final int b) 108 throws IOException 109 { 110 for (final OutputStream s : streams) 111 { 112 s.write(b); 113 } 114 } 115 116 117 118 /** 119 * Writes the entire contents of the provided byte array to each of the target 120 * output streams. 121 * 122 * @param b The byte array containing the data to be written. 123 * 124 * @throws IOException If a problem occurs while writing the provided data 125 * to any of the target output streams. 126 */ 127 @Override() 128 public void write(final byte[] b) 129 throws IOException 130 { 131 for (final OutputStream s : streams) 132 { 133 s.write(b); 134 } 135 } 136 137 138 139 /** 140 * Writes a portion of the contents of the provided byte array to each of the 141 * target output streams. 142 * 143 * @param b The byte array containing the data to be written. 144 * @param off The offset within the array at which the data should start 145 * being written. 146 * @param len The number of bytes from the array that should be written. 147 * 148 * @throws IOException If a problem occurs while writing the provided data 149 * to any of the target output streams. 150 */ 151 @Override() 152 public void write(final byte[] b, final int off, final int len) 153 throws IOException 154 { 155 for (final OutputStream s : streams) 156 { 157 s.write(b, off, len); 158 } 159 } 160 161 162 163 /** 164 * Flushes each of the target output streams to force any buffered content to 165 * be written out. 166 * 167 * @throws IOException If a problem occurs while flushing any of the target 168 * output streams. 169 */ 170 @Override() 171 public void flush() 172 throws IOException 173 { 174 for (final OutputStream s : streams) 175 { 176 s.flush(); 177 } 178 } 179 180 181 182 /** 183 * Closes each of the target output streams. 184 * 185 * @throws IOException If a problem occurs while closing any of the target 186 * output streams. Note that even if an exception is 187 * thrown, an attempt will be made to close all target 188 * streams. If multiple target streams throw an 189 * exception, then the first exception encountered will 190 * be thrown. 191 */ 192 @Override() 193 public void close() 194 throws IOException 195 { 196 IOException exceptionToThrow = null; 197 198 for (final OutputStream s : streams) 199 { 200 try 201 { 202 s.close(); 203 } 204 catch (final IOException ioe) 205 { 206 Debug.debugException(ioe); 207 if (exceptionToThrow == null) 208 { 209 exceptionToThrow = ioe; 210 } 211 } 212 } 213 214 if (exceptionToThrow != null) 215 { 216 throw exceptionToThrow; 217 } 218 } 219}