001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. 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 */ 017 018package org.apache.commons.net.io; 019 020import java.io.Closeable; 021import java.io.IOException; 022import java.io.InputStream; 023import java.io.OutputStream; 024import java.io.Reader; 025import java.io.Writer; 026import java.net.Socket; 027 028/*** 029 * The Util class cannot be instantiated and stores short static convenience 030 * methods that are often quite useful. 031 * 032 * 033 * @see CopyStreamException 034 * @see CopyStreamListener 035 * @see CopyStreamAdapter 036 ***/ 037 038public final class Util 039{ 040 /** 041 * The default buffer size ({@value}) used by 042 * {@link #copyStream copyStream } and {@link #copyReader copyReader} 043 * and by the copyReader/copyStream methods if a zero or negative buffer size is supplied. 044 */ 045 public static final int DEFAULT_COPY_BUFFER_SIZE = 1024; 046 047 // Cannot be instantiated 048 private Util() 049 { } 050 051 052 /*** 053 * Copies the contents of an InputStream to an OutputStream using a 054 * copy buffer of a given size and notifies the provided 055 * CopyStreamListener of the progress of the copy operation by calling 056 * its bytesTransferred(long, int) method after each write to the 057 * destination. If you wish to notify more than one listener you should 058 * use a CopyStreamAdapter as the listener and register the additional 059 * listeners with the CopyStreamAdapter. 060 * <p> 061 * The contents of the InputStream are 062 * read until the end of the stream is reached, but neither the 063 * source nor the destination are closed. You must do this yourself 064 * outside of the method call. The number of bytes read/written is 065 * returned. 066 * 067 * @param source The source InputStream. 068 * @param dest The destination OutputStream. 069 * @param bufferSize The number of bytes to buffer during the copy. 070 * A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}. 071 * @param streamSize The number of bytes in the stream being copied. 072 * Should be set to CopyStreamEvent.UNKNOWN_STREAM_SIZE if unknown. 073 * Not currently used (though it is passed to {@link CopyStreamListener#bytesTransferred(long, int, long)} 074 * @param listener The CopyStreamListener to notify of progress. If 075 * this parameter is null, notification is not attempted. 076 * @param flush Whether to flush the output stream after every 077 * write. This is necessary for interactive sessions that rely on 078 * buffered streams. If you don't flush, the data will stay in 079 * the stream buffer. 080 * @return number of bytes read/written 081 * @throws CopyStreamException If an error occurs while reading from the 082 * source or writing to the destination. The CopyStreamException 083 * will contain the number of bytes confirmed to have been 084 * transferred before an 085 * IOException occurred, and it will also contain the IOException 086 * that caused the error. These values can be retrieved with 087 * the CopyStreamException getTotalBytesTransferred() and 088 * getIOException() methods. 089 ***/ 090 public static final long copyStream(InputStream source, OutputStream dest, 091 int bufferSize, long streamSize, 092 CopyStreamListener listener, 093 boolean flush) 094 throws CopyStreamException 095 { 096 int numBytes; 097 long total = 0; 098 byte[] buffer = new byte[bufferSize > 0 ? bufferSize : DEFAULT_COPY_BUFFER_SIZE]; 099 100 try 101 { 102 while ((numBytes = source.read(buffer)) != -1) 103 { 104 // Technically, some read(byte[]) methods may return 0 and we cannot 105 // accept that as an indication of EOF. 106 107 if (numBytes == 0) 108 { 109 int singleByte = source.read(); 110 if (singleByte < 0) { 111 break; 112 } 113 dest.write(singleByte); 114 if(flush) { 115 dest.flush(); 116 } 117 ++total; 118 if (listener != null) { 119 listener.bytesTransferred(total, 1, streamSize); 120 } 121 continue; 122 } 123 124 dest.write(buffer, 0, numBytes); 125 if(flush) { 126 dest.flush(); 127 } 128 total += numBytes; 129 if (listener != null) { 130 listener.bytesTransferred(total, numBytes, streamSize); 131 } 132 } 133 } 134 catch (IOException e) 135 { 136 throw new CopyStreamException("IOException caught while copying.", 137 total, e); 138 } 139 140 return total; 141 } 142 143 144 /*** 145 * Copies the contents of an InputStream to an OutputStream using a 146 * copy buffer of a given size and notifies the provided 147 * CopyStreamListener of the progress of the copy operation by calling 148 * its bytesTransferred(long, int) method after each write to the 149 * destination. If you wish to notify more than one listener you should 150 * use a CopyStreamAdapter as the listener and register the additional 151 * listeners with the CopyStreamAdapter. 152 * <p> 153 * The contents of the InputStream are 154 * read until the end of the stream is reached, but neither the 155 * source nor the destination are closed. You must do this yourself 156 * outside of the method call. The number of bytes read/written is 157 * returned. 158 * 159 * @param source The source InputStream. 160 * @param dest The destination OutputStream. 161 * @param bufferSize The number of bytes to buffer during the copy. 162 * A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}. 163 * @param streamSize The number of bytes in the stream being copied. 164 * Should be set to CopyStreamEvent.UNKNOWN_STREAM_SIZE if unknown. 165 * Not currently used (though it is passed to {@link CopyStreamListener#bytesTransferred(long, int, long)} 166 * @param listener The CopyStreamListener to notify of progress. If 167 * this parameter is null, notification is not attempted. 168 * @return number of bytes read/written 169 * @throws CopyStreamException If an error occurs while reading from the 170 * source or writing to the destination. The CopyStreamException 171 * will contain the number of bytes confirmed to have been 172 * transferred before an 173 * IOException occurred, and it will also contain the IOException 174 * that caused the error. These values can be retrieved with 175 * the CopyStreamException getTotalBytesTransferred() and 176 * getIOException() methods. 177 ***/ 178 public static final long copyStream(InputStream source, OutputStream dest, 179 int bufferSize, long streamSize, 180 CopyStreamListener listener) 181 throws CopyStreamException 182 { 183 return copyStream(source, dest, bufferSize, streamSize, listener, 184 true); 185 } 186 187 188 /*** 189 * Copies the contents of an InputStream to an OutputStream using a 190 * copy buffer of a given size. The contents of the InputStream are 191 * read until the end of the stream is reached, but neither the 192 * source nor the destination are closed. You must do this yourself 193 * outside of the method call. The number of bytes read/written is 194 * returned. 195 * 196 * @param source The source InputStream. 197 * @param dest The destination OutputStream. 198 * @param bufferSize The number of bytes to buffer during the copy. 199 * A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}. 200 * @return The number of bytes read/written in the copy operation. 201 * @throws CopyStreamException If an error occurs while reading from the 202 * source or writing to the destination. The CopyStreamException 203 * will contain the number of bytes confirmed to have been 204 * transferred before an 205 * IOException occurred, and it will also contain the IOException 206 * that caused the error. These values can be retrieved with 207 * the CopyStreamException getTotalBytesTransferred() and 208 * getIOException() methods. 209 ***/ 210 public static final long copyStream(InputStream source, OutputStream dest, 211 int bufferSize) 212 throws CopyStreamException 213 { 214 return copyStream(source, dest, bufferSize, 215 CopyStreamEvent.UNKNOWN_STREAM_SIZE, null); 216 } 217 218 219 /*** 220 * Same as <code> copyStream(source, dest, DEFAULT_COPY_BUFFER_SIZE); </code> 221 * @param source where to copy from 222 * @param dest where to copy to 223 * @return number of bytes copied 224 * @throws CopyStreamException on error 225 ***/ 226 public static final long copyStream(InputStream source, OutputStream dest) 227 throws CopyStreamException 228 { 229 return copyStream(source, dest, DEFAULT_COPY_BUFFER_SIZE); 230 } 231 232 233 /*** 234 * Copies the contents of a Reader to a Writer using a 235 * copy buffer of a given size and notifies the provided 236 * CopyStreamListener of the progress of the copy operation by calling 237 * its bytesTransferred(long, int) method after each write to the 238 * destination. If you wish to notify more than one listener you should 239 * use a CopyStreamAdapter as the listener and register the additional 240 * listeners with the CopyStreamAdapter. 241 * <p> 242 * The contents of the Reader are 243 * read until its end is reached, but neither the source nor the 244 * destination are closed. You must do this yourself outside of the 245 * method call. The number of characters read/written is returned. 246 * 247 * @param source The source Reader. 248 * @param dest The destination writer. 249 * @param bufferSize The number of characters to buffer during the copy. 250 * A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}. 251 * @param streamSize The number of characters in the stream being copied. 252 * Should be set to CopyStreamEvent.UNKNOWN_STREAM_SIZE if unknown. 253 * Not currently used (though it is passed to {@link CopyStreamListener#bytesTransferred(long, int, long)} 254 * @param listener The CopyStreamListener to notify of progress. If 255 * this parameter is null, notification is not attempted. 256 * @return The number of characters read/written in the copy operation. 257 * @throws CopyStreamException If an error occurs while reading from the 258 * source or writing to the destination. The CopyStreamException 259 * will contain the number of bytes confirmed to have been 260 * transferred before an 261 * IOException occurred, and it will also contain the IOException 262 * that caused the error. These values can be retrieved with 263 * the CopyStreamException getTotalBytesTransferred() and 264 * getIOException() methods. 265 ***/ 266 public static final long copyReader(Reader source, Writer dest, 267 int bufferSize, long streamSize, 268 CopyStreamListener listener) 269 throws CopyStreamException 270 { 271 int numChars; 272 long total = 0; 273 char[] buffer = new char[bufferSize > 0 ? bufferSize : DEFAULT_COPY_BUFFER_SIZE]; 274 275 try 276 { 277 while ((numChars = source.read(buffer)) != -1) 278 { 279 // Technically, some read(char[]) methods may return 0 and we cannot 280 // accept that as an indication of EOF. 281 if (numChars == 0) 282 { 283 int singleChar = source.read(); 284 if (singleChar < 0) { 285 break; 286 } 287 dest.write(singleChar); 288 dest.flush(); 289 ++total; 290 if (listener != null) { 291 listener.bytesTransferred(total, 1, streamSize); 292 } 293 continue; 294 } 295 296 dest.write(buffer, 0, numChars); 297 dest.flush(); 298 total += numChars; 299 if (listener != null) { 300 listener.bytesTransferred(total, numChars, streamSize); 301 } 302 } 303 } 304 catch (IOException e) 305 { 306 throw new CopyStreamException("IOException caught while copying.", 307 total, e); 308 } 309 310 return total; 311 } 312 313 314 /*** 315 * Copies the contents of a Reader to a Writer using a 316 * copy buffer of a given size. The contents of the Reader are 317 * read until its end is reached, but neither the source nor the 318 * destination are closed. You must do this yourself outside of the 319 * method call. The number of characters read/written is returned. 320 * 321 * @param source The source Reader. 322 * @param dest The destination writer. 323 * @param bufferSize The number of characters to buffer during the copy. 324 * A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}. 325 * @return The number of characters read/written in the copy operation. 326 * @throws CopyStreamException If an error occurs while reading from the 327 * source or writing to the destination. The CopyStreamException 328 * will contain the number of bytes confirmed to have been 329 * transferred before an 330 * IOException occurred, and it will also contain the IOException 331 * that caused the error. These values can be retrieved with 332 * the CopyStreamException getTotalBytesTransferred() and 333 * getIOException() methods. 334 ***/ 335 public static final long copyReader(Reader source, Writer dest, 336 int bufferSize) 337 throws CopyStreamException 338 { 339 return copyReader(source, dest, bufferSize, 340 CopyStreamEvent.UNKNOWN_STREAM_SIZE, null); 341 } 342 343 344 /*** 345 * Same as <code> copyReader(source, dest, DEFAULT_COPY_BUFFER_SIZE); </code> 346 * @param source where to copy from 347 * @param dest where to copy to 348 * @return number of bytes copied 349 * @throws CopyStreamException on error 350 ***/ 351 public static final long copyReader(Reader source, Writer dest) 352 throws CopyStreamException 353 { 354 return copyReader(source, dest, DEFAULT_COPY_BUFFER_SIZE); 355 } 356 357 /** 358 * Closes the object quietly, catching rather than throwing IOException. 359 * Intended for use from finally blocks. 360 * 361 * @param closeable the object to close, may be {@code null} 362 * @since 3.0 363 */ 364 public static void closeQuietly(Closeable closeable) { 365 if (closeable != null) { 366 try { 367 closeable.close(); 368 } catch (IOException e) { 369 // Ignored 370 } 371 } 372 } 373 374 /** 375 * Closes the socket quietly, catching rather than throwing IOException. 376 * Intended for use from finally blocks. 377 * 378 * @param socket the socket to close, may be {@code null} 379 * @since 3.0 380 */ 381 public static void closeQuietly(Socket socket) { 382 if (socket != null) { 383 try { 384 socket.close(); 385 } catch (IOException e) { 386 // Ignored 387 } 388 } 389 } 390}