001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.internal; 014 015import java.io.*; 016import java.util.*; 017 018/** 019 * Output stream that can send output to multiple output streams. 020 */ 021public class TeeOutputStream extends OutputStream { 022 private OutputStream[] outputStreams = new OutputStream[0]; 023 private Map<String,OutputStream> outputStreamMap; 024 025 /** 026 * Constructor. 027 * 028 * @param outputStreams The list of output streams. 029 */ 030 public TeeOutputStream(OutputStream...outputStreams) { 031 this.outputStreams = outputStreams; 032 } 033 034 /** 035 * Constructor. 036 * 037 * @param outputStreams The list of output streams. 038 */ 039 public TeeOutputStream(Collection<OutputStream> outputStreams) { 040 this.outputStreams = outputStreams.toArray(new OutputStream[outputStreams.size()]); 041 } 042 043 /** 044 * Adds an output stream to this tee output stream. 045 * 046 * @param os The output stream to add to this tee output stream. 047 * @param close 048 * If <jk>false</jk>, then calling {@link #close()} on this stream will not filter to the specified output stream. 049 * @return This object (for method chaining). 050 */ 051 public TeeOutputStream add(OutputStream os, boolean close) { 052 if (os == null) 053 return this; 054 if (! close) 055 os = new NoCloseOutputStream(os); 056 if (os == this) 057 throw new RuntimeException("Cannot add this output stream to itself."); 058 for (OutputStream os2 : outputStreams) 059 if (os2 == os) 060 throw new RuntimeException("Cannot add this output stream again."); 061 if (os instanceof TeeOutputStream) { 062 for (OutputStream os2 : ((TeeOutputStream)os).outputStreams) 063 add(os2, true); 064 } else { 065 outputStreams = ArrayUtils.append(outputStreams, os); 066 } 067 return this; 068 } 069 070 /** 071 * Returns the output stream identified through the <code>id</code> parameter passed in through the 072 * {@link #add(String, OutputStream, boolean)} method. 073 * 074 * @param id The ID associated with the output stream. 075 * @return The output stream, or <jk>null</jk> if no identifier was specified when the output stream was added. 076 */ 077 public OutputStream getOutputStream(String id) { 078 if (outputStreamMap != null) 079 return outputStreamMap.get(id); 080 return null; 081 } 082 083 /** 084 * Same as {@link #add(OutputStream, boolean)} but associates the stream with an identifier so the stream can be 085 * retrieved through {@link #getOutputStream(String)}. 086 * 087 * @param id The ID to associate the output stream with. 088 * @param os The output stream to add. 089 * @param close Close the specified stream afterwards. 090 * @return This object (for method chaining). 091 */ 092 public TeeOutputStream add(String id, OutputStream os, boolean close) { 093 if (id != null) { 094 if (outputStreamMap == null) 095 outputStreamMap = new TreeMap<>(); 096 outputStreamMap.put(id, os); 097 } 098 return add(os, close); 099 } 100 101 /** 102 * Returns the number of inner streams in this tee stream. 103 * 104 * @return The number of streams in this tee stream. 105 */ 106 public int size() { 107 return outputStreams.length; 108 } 109 110 @Override /* OutputStream */ 111 public void write(int b) throws IOException { 112 for (OutputStream os : outputStreams) 113 os.write(b); 114 } 115 116 @Override /* OutputStream */ 117 public void write(byte b[], int off, int len) throws IOException { 118 for (OutputStream os : outputStreams) 119 os.write(b, off, len); 120 } 121 122 @Override /* OutputStream */ 123 public void flush() throws IOException { 124 for (OutputStream os : outputStreams) 125 os.flush(); 126 } 127 128 @Override /* OutputStream */ 129 public void close() throws IOException { 130 for (OutputStream os : outputStreams) 131 os.close(); 132 } 133}