1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package org.apache.commons.net.io; 19 20 import java.io.Closeable; 21 import java.io.IOException; 22 import java.io.InputStream; 23 import java.io.OutputStream; 24 import java.io.Reader; 25 import java.io.Writer; 26 import java.net.Socket; 27 28 import org.apache.commons.net.util.NetConstants; 29 30 /** 31 * The Util class cannot be instantiated and stores short static convenience methods that are often quite useful. 32 * 33 * 34 * @see CopyStreamException 35 * @see CopyStreamListener 36 * @see CopyStreamAdapter 37 */ 38 39 public final class Util { 40 /** 41 * The default buffer size ({@value}) used by {@link #copyStream copyStream } and {@link #copyReader copyReader} and by the copyReader/copyStream methods if 42 * a zero or negative buffer size is supplied. 43 */ 44 public static final int DEFAULT_COPY_BUFFER_SIZE = 1024; 45 46 /** 47 * Closes the object quietly, catching rather than throwing IOException. Intended for use from finally blocks. 48 * 49 * @param closeable the object to close, may be {@code null} 50 * @since 3.0 51 */ 52 public static void closeQuietly(final Closeable closeable) { 53 if (closeable != null) { 54 try { 55 closeable.close(); 56 } catch (final IOException e) { 57 // Ignored 58 } 59 } 60 } 61 62 /** 63 * Closes the socket quietly, catching rather than throwing IOException. Intended for use from finally blocks. 64 * 65 * @param socket the socket to close, may be {@code null} 66 * @since 3.0 67 */ 68 public static void closeQuietly(final Socket socket) { 69 if (socket != null) { 70 try { 71 socket.close(); 72 } catch (final IOException e) { 73 // Ignored 74 } 75 } 76 } 77 78 /** 79 * Same as <code> copyReader(source, dest, DEFAULT_COPY_BUFFER_SIZE); </code> 80 * 81 * @param source where to copy from 82 * @param dest where to copy to 83 * @return number of bytes copied 84 * @throws CopyStreamException on error 85 */ 86 public static long copyReader(final Reader source, final Writer dest) throws CopyStreamException { 87 return copyReader(source, dest, DEFAULT_COPY_BUFFER_SIZE); 88 } 89 90 /** 91 * Copies the contents of a Reader to a Writer using a copy buffer of a given size. The contents of the Reader are read until its end is reached, but 92 * neither the source nor the destination are closed. You must do this yourself outside the method call. The number of characters read/written is 93 * returned. 94 * 95 * @param source The source Reader. 96 * @param dest The destination writer. 97 * @param bufferSize The number of characters to buffer during the copy. A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}. 98 * @return The number of characters read/written in the copy operation. 99 * @throws CopyStreamException If an error occurs while reading from the source or writing to the destination. The CopyStreamException will contain the 100 * number of bytes confirmed to have been transferred before an IOException occurred, and it will also contain the IOException 101 * that caused the error. These values can be retrieved with the CopyStreamException getTotalBytesTransferred() and 102 * getIOException() methods. 103 */ 104 public static long copyReader(final Reader source, final Writer dest, final int bufferSize) throws CopyStreamException { 105 return copyReader(source, dest, bufferSize, CopyStreamEvent.UNKNOWN_STREAM_SIZE, null); 106 } 107 108 /** 109 * Copies the contents of a Reader to a Writer using a copy buffer of a given size and notifies the provided CopyStreamListener of the progress of the copy 110 * operation by calling its bytesTransferred(long, int) method after each write to the destination. If you wish to notify more than one listener you should 111 * use a CopyStreamAdapter as the listener and register the additional listeners with the CopyStreamAdapter. 112 * <p> 113 * The contents of the Reader are read until its end is reached, but neither the source nor the destination are closed. You must do this yourself outside 114 * the method call. The number of characters read/written is returned. 115 * 116 * @param source The source Reader. 117 * @param dest The destination writer. 118 * @param bufferSize The number of characters to buffer during the copy. A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}. 119 * @param streamSize The number of characters in the stream being copied. Should be set to CopyStreamEvent.UNKNOWN_STREAM_SIZE if unknown. Not currently 120 * used (though it is passed to {@link CopyStreamListener#bytesTransferred(long, int, long)} 121 * @param listener The CopyStreamListener to notify of progress. If this parameter is null, notification is not attempted. 122 * @return The number of characters read/written in the copy operation. 123 * @throws CopyStreamException If an error occurs while reading from the source or writing to the destination. The CopyStreamException will contain the 124 * number of bytes confirmed to have been transferred before an IOException occurred, and it will also contain the IOException 125 * that caused the error. These values can be retrieved with the CopyStreamException getTotalBytesTransferred() and 126 * getIOException() methods. 127 */ 128 public static long copyReader(final Reader source, final Writer dest, final int bufferSize, final long streamSize, final CopyStreamListener listener) 129 throws CopyStreamException { 130 int numChars; 131 long total = 0; 132 final char[] buffer = new char[bufferSize > 0 ? bufferSize : DEFAULT_COPY_BUFFER_SIZE]; 133 134 try { 135 while ((numChars = source.read(buffer)) != NetConstants.EOS) { 136 // Technically, some read(char[]) methods may return 0, and we cannot 137 // accept that as an indication of EOF. 138 if (numChars == 0) { 139 final int singleChar = source.read(); 140 if (singleChar < 0) { 141 break; 142 } 143 dest.write(singleChar); 144 dest.flush(); 145 ++total; 146 if (listener != null) { 147 listener.bytesTransferred(total, 1, streamSize); 148 } 149 continue; 150 } 151 152 dest.write(buffer, 0, numChars); 153 dest.flush(); 154 total += numChars; 155 if (listener != null) { 156 listener.bytesTransferred(total, numChars, streamSize); 157 } 158 } 159 } catch (final IOException e) { 160 throw new CopyStreamException("IOException caught while copying.", total, e); 161 } 162 163 return total; 164 } 165 166 /** 167 * Same as <code> copyStream(source, dest, DEFAULT_COPY_BUFFER_SIZE); </code> 168 * 169 * @param source where to copy from 170 * @param dest where to copy to 171 * @return number of bytes copied 172 * @throws CopyStreamException on error 173 */ 174 public static long copyStream(final InputStream source, final OutputStream dest) throws CopyStreamException { 175 return copyStream(source, dest, DEFAULT_COPY_BUFFER_SIZE); 176 } 177 178 /** 179 * Copies the contents of an InputStream to an OutputStream using a copy buffer of a given size. The contents of the InputStream are read until the end of 180 * the stream is reached, but neither the source nor the destination are closed. You must do this yourself outside the method call. The number of bytes 181 * read/written is returned. 182 * 183 * @param source The source InputStream. 184 * @param dest The destination OutputStream. 185 * @param bufferSize The number of bytes to buffer during the copy. A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}. 186 * @return The number of bytes read/written in the copy operation. 187 * @throws CopyStreamException If an error occurs while reading from the source or writing to the destination. The CopyStreamException will contain the 188 * number of bytes confirmed to have been transferred before an IOException occurred, and it will also contain the IOException 189 * that caused the error. These values can be retrieved with the CopyStreamException getTotalBytesTransferred() and 190 * getIOException() methods. 191 */ 192 public static long copyStream(final InputStream source, final OutputStream dest, final int bufferSize) throws CopyStreamException { 193 return copyStream(source, dest, bufferSize, CopyStreamEvent.UNKNOWN_STREAM_SIZE, null); 194 } 195 196 /** 197 * Copies the contents of an InputStream to an OutputStream using a copy buffer of a given size and notifies the provided CopyStreamListener of the progress 198 * of the copy operation by calling its bytesTransferred(long, int) method after each write to the destination. If you wish to notify more than one listener 199 * you should use a CopyStreamAdapter as the listener and register the additional listeners with the CopyStreamAdapter. 200 * <p> 201 * The contents of the InputStream are read until the end of the stream is reached, but neither the source nor the destination are closed. You must do this 202 * yourself outside the method call. The number of bytes read/written is returned. 203 * 204 * @param source The source InputStream. 205 * @param dest The destination OutputStream. 206 * @param bufferSize The number of bytes to buffer during the copy. A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}. 207 * @param streamSize The number of bytes in the stream being copied. Should be set to CopyStreamEvent.UNKNOWN_STREAM_SIZE if unknown. Not currently used 208 * (though it is passed to {@link CopyStreamListener#bytesTransferred(long, int, long)} 209 * @param listener The CopyStreamListener to notify of progress. If this parameter is null, notification is not attempted. 210 * @return number of bytes read/written 211 * @throws CopyStreamException If an error occurs while reading from the source or writing to the destination. The CopyStreamException will contain the 212 * number of bytes confirmed to have been transferred before an IOException occurred, and it will also contain the IOException 213 * that caused the error. These values can be retrieved with the CopyStreamException getTotalBytesTransferred() and 214 * getIOException() methods. 215 */ 216 public static long copyStream(final InputStream source, final OutputStream dest, final int bufferSize, final long streamSize, 217 final CopyStreamListener listener) throws CopyStreamException { 218 return copyStream(source, dest, bufferSize, streamSize, listener, true); 219 } 220 221 /** 222 * Copies the contents of an InputStream to an OutputStream using a copy buffer of a given size and notifies the provided CopyStreamListener of the progress 223 * of the copy operation by calling its bytesTransferred(long, int) method after each write to the destination. If you wish to notify more than one listener 224 * you should use a CopyStreamAdapter as the listener and register the additional listeners with the CopyStreamAdapter. 225 * <p> 226 * The contents of the InputStream are read until the end of the stream is reached, but neither the source nor the destination are closed. You must do this 227 * yourself outside the method call. The number of bytes read/written is returned. 228 * 229 * @param source The source InputStream. 230 * @param dest The destination OutputStream. 231 * @param bufferSize The number of bytes to buffer during the copy. A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}. 232 * @param streamSize The number of bytes in the stream being copied. Should be set to CopyStreamEvent.UNKNOWN_STREAM_SIZE if unknown. Not currently used 233 * (though it is passed to {@link CopyStreamListener#bytesTransferred(long, int, long)} 234 * @param listener The CopyStreamListener to notify of progress. If this parameter is null, notification is not attempted. 235 * @param flush Whether to flush the output stream after every write. This is necessary for interactive sessions that rely on buffered streams. If you 236 * don't flush, the data will stay in the stream buffer. 237 * @return number of bytes read/written 238 * @throws CopyStreamException If an error occurs while reading from the source or writing to the destination. The CopyStreamException will contain the 239 * number of bytes confirmed to have been transferred before an IOException occurred, and it will also contain the IOException 240 * that caused the error. These values can be retrieved with the CopyStreamException getTotalBytesTransferred() and 241 * getIOException() methods. 242 */ 243 public static long copyStream(final InputStream source, final OutputStream dest, final int bufferSize, final long streamSize, 244 final CopyStreamListener listener, final boolean flush) throws CopyStreamException { 245 int numBytes; 246 long total = 0; 247 final byte[] buffer = new byte[bufferSize > 0 ? bufferSize : DEFAULT_COPY_BUFFER_SIZE]; 248 249 try { 250 while ((numBytes = source.read(buffer)) != NetConstants.EOS) { 251 // Technically, some read(byte[]) methods may return 0, and we cannot 252 // accept that as an indication of EOF. 253 254 if (numBytes == 0) { 255 final int singleByte = source.read(); 256 if (singleByte < 0) { 257 break; 258 } 259 dest.write(singleByte); 260 if (flush) { 261 dest.flush(); 262 } 263 ++total; 264 if (listener != null) { 265 listener.bytesTransferred(total, 1, streamSize); 266 } 267 continue; 268 } 269 270 dest.write(buffer, 0, numBytes); 271 if (flush) { 272 dest.flush(); 273 } 274 total += numBytes; 275 if (listener != null) { 276 listener.bytesTransferred(total, numBytes, streamSize); 277 } 278 } 279 } catch (final IOException e) { 280 throw new CopyStreamException("IOException caught while copying.", total, e); 281 } 282 283 return total; 284 } 285 286 /** Cannot be instantiated. */ 287 private Util() { 288 } 289 }