001/*-------------------------------------------------------------------------+ 002| | 003| Copyright 2005-2011 The ConQAT Project | 004| | 005| Licensed under the Apache License, Version 2.0 (the "License"); | 006| you may not use this file except in compliance with the License. | 007| You may obtain a copy of the License at | 008| | 009| http://www.apache.org/licenses/LICENSE-2.0 | 010| | 011| Unless required by applicable law or agreed to in writing, software | 012| distributed under the License is distributed on an "AS IS" BASIS, | 013| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 014| See the License for the specific language governing permissions and | 015| limitations under the License. | 016+-------------------------------------------------------------------------*/ 017package org.conqat.lib.commons.io; 018 019import java.io.IOException; 020import java.io.OutputStream; 021import java.util.ArrayList; 022import java.util.Collection; 023import java.util.List; 024 025import org.conqat.lib.commons.assertion.CCSMAssert; 026import org.conqat.lib.commons.collections.CollectionUtils; 027import org.conqat.lib.commons.collections.UnmodifiableList; 028import org.conqat.lib.commons.string.StringUtils; 029 030/** 031 * This class enables multiplexing of output streams. It can be e.g. used to 032 * output content to multiple files. 033 */ 034public class MultiplexedOutputStream extends OutputStream { 035 036 /** The underlying output streams. */ 037 private final OutputStream[] streams; 038 039 /** 040 * Create new multiplexed output streams. 041 * 042 * @param streams 043 * any number of output streams. 044 */ 045 public MultiplexedOutputStream(OutputStream... streams) { 046 this.streams = streams; 047 } 048 049 /** 050 * Forwards close operation to all underlying output streams. 051 * 052 * @throws MultiIOException 053 * if one or more of the underlying streams raised an exception 054 */ 055 @Override 056 public void close() throws MultiIOException { 057 List<IOException> exceptions = new ArrayList<IOException>(); 058 for (OutputStream stream : streams) { 059 try { 060 stream.close(); 061 } catch (IOException e) { 062 exceptions.add(e); 063 } 064 } 065 checkExceptions(exceptions); 066 } 067 068 /** 069 * Forwards flush operation to all underlying output streams. 070 * 071 * @throws MultiIOException 072 * if one or more of the underlying streams raised an exception 073 */ 074 @Override 075 public void flush() throws MultiIOException { 076 List<IOException> exceptions = new ArrayList<IOException>(); 077 for (OutputStream stream : streams) { 078 try { 079 stream.flush(); 080 } catch (IOException e) { 081 exceptions.add(e); 082 } 083 } 084 checkExceptions(exceptions); 085 } 086 087 /** 088 * Forwards write operation to all underlying output streams. 089 * 090 * @throws MultiIOException 091 * if one or more of the underlying streams raised an exception 092 */ 093 @Override 094 public void write(int b) throws MultiIOException { 095 List<IOException> exceptions = new ArrayList<IOException>(); 096 for (OutputStream stream : streams) { 097 try { 098 stream.write(b); 099 } catch (IOException e) { 100 exceptions.add(e); 101 } 102 } 103 checkExceptions(exceptions); 104 } 105 106 /** 107 * Raises an {@link MultiIOException} if the provided collection is not empty. 108 */ 109 private static void checkExceptions(Collection<IOException> exceptions) throws MultiIOException { 110 if (!exceptions.isEmpty()) { 111 throw new MultiIOException(exceptions); 112 } 113 } 114 115 /** Exception class that encapsulates multiple {@link IOException}s. */ 116 public static class MultiIOException extends IOException { 117 118 /** Serial version UID. */ 119 private static final long serialVersionUID = 1; 120 121 /** The exceptions. */ 122 private final List<IOException> exceptions = new ArrayList<IOException>(); 123 124 /** 125 * Create exception 126 * 127 * @throws AssertionError 128 * if provided collection is empty. 129 */ 130 public MultiIOException(Collection<? extends IOException> exceptions) { 131 CCSMAssert.isFalse(exceptions.isEmpty(), "Must have at least one exception."); 132 this.exceptions.addAll(exceptions); 133 } 134 135 /** Returns messages of all exceptions. */ 136 @Override 137 public String getMessage() { 138 StringBuilder result = new StringBuilder(); 139 for (IOException ex : exceptions) { 140 result.append(ex.getMessage()); 141 result.append(StringUtils.LINE_SEPARATOR); 142 } 143 return result.toString(); 144 } 145 146 /** Get exceptions. */ 147 public UnmodifiableList<IOException> getExceptions() { 148 return CollectionUtils.asUnmodifiable(exceptions); 149 } 150 } 151}