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 028import org.apache.commons.net.util.NetConstants; 029 030/** 031 * The Util class cannot be instantiated and stores short static convenience methods that are often quite useful. 032 * 033 * 034 * @see CopyStreamException 035 * @see CopyStreamListener 036 * @see CopyStreamAdapter 037 */ 038 039public final class Util { 040 /** 041 * The default buffer size ({@value}) used by {@link #copyStream copyStream } and {@link #copyReader copyReader} and by the copyReader/copyStream methods if 042 * a zero or negative buffer size is supplied. 043 */ 044 public static final int DEFAULT_COPY_BUFFER_SIZE = 1024; 045 046 /** 047 * Closes the object quietly, catching rather than throwing IOException. Intended for use from finally blocks. 048 * 049 * @param closeable the object to close, may be {@code null} 050 * @since 3.0 051 */ 052 public static void closeQuietly(final Closeable closeable) { 053 if (closeable != null) { 054 try { 055 closeable.close(); 056 } catch (final IOException e) { 057 // Ignored 058 } 059 } 060 } 061 062 /** 063 * Closes the socket quietly, catching rather than throwing IOException. Intended for use from finally blocks. 064 * 065 * @param socket the socket to close, may be {@code null} 066 * @since 3.0 067 */ 068 public static void closeQuietly(final Socket socket) { 069 if (socket != null) { 070 try { 071 socket.close(); 072 } catch (final IOException e) { 073 // Ignored 074 } 075 } 076 } 077 078 /** 079 * Same as <code> copyReader(source, dest, DEFAULT_COPY_BUFFER_SIZE); </code> 080 * 081 * @param source where to copy from 082 * @param dest where to copy to 083 * @return number of bytes copied 084 * @throws CopyStreamException on error 085 */ 086 public static long copyReader(final Reader source, final Writer dest) throws CopyStreamException { 087 return copyReader(source, dest, DEFAULT_COPY_BUFFER_SIZE); 088 } 089 090 /** 091 * 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 092 * neither the source nor the destination are closed. You must do this yourself outside the method call. The number of characters read/written is 093 * returned. 094 * 095 * @param source The source Reader. 096 * @param dest The destination writer. 097 * @param bufferSize The number of characters to buffer during the copy. A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}. 098 * @return The number of characters read/written in the copy operation. 099 * @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}