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.OutputStreamWriter; 025import java.io.PrintStream; 026import java.io.PrintWriter; 027import java.io.Reader; 028import java.io.Writer; 029import java.net.Socket; 030import java.nio.charset.Charset; 031 032import org.apache.commons.net.util.NetConstants; 033 034/** 035 * The Util class cannot be instantiated and stores short static convenience methods that are often quite useful. 036 * 037 * 038 * @see CopyStreamException 039 * @see CopyStreamListener 040 * @see CopyStreamAdapter 041 */ 042 043public final class Util { 044 045 /** 046 * The default buffer size ({@value}) used by {@link #copyStream copyStream } and {@link #copyReader copyReader} and by the copyReader/copyStream methods if 047 * a zero or negative buffer size is supplied. 048 */ 049 public static final int DEFAULT_COPY_BUFFER_SIZE = 1024; 050 051 /** 052 * Closes the object quietly, catching rather than throwing IOException. Intended for use from finally blocks. 053 * 054 * @param closeable the object to close, may be {@code null} 055 * @since 3.0 056 */ 057 public static void closeQuietly(final Closeable closeable) { 058 if (closeable != null) { 059 try { 060 closeable.close(); 061 } catch (final IOException e) { 062 // Ignored 063 } 064 } 065 } 066 067 /** 068 * Closes the socket quietly, catching rather than throwing IOException. Intended for use from finally blocks. 069 * 070 * @param socket the socket to close, may be {@code null} 071 * @since 3.0 072 */ 073 public static void closeQuietly(final Socket socket) { 074 if (socket != null) { 075 try { 076 socket.close(); 077 } catch (final IOException e) { 078 // Ignored 079 } 080 } 081 } 082 083 /** 084 * Same as <code>copyReader(source, dest, DEFAULT_COPY_BUFFER_SIZE);</code> 085 * 086 * @param source where to copy from 087 * @param dest where to copy to 088 * @return number of bytes copied 089 * @throws CopyStreamException on error 090 */ 091 public static long copyReader(final Reader source, final Writer dest) throws CopyStreamException { 092 return copyReader(source, dest, DEFAULT_COPY_BUFFER_SIZE); 093 } 094 095 /** 096 * 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 097 * neither the source nor the destination are closed. You must do this yourself outside the method call. The number of characters read/written is 098 * returned. 099 * 100 * @param source The source Reader. 101 * @param dest The destination writer. 102 * @param bufferSize The number of characters to buffer during the copy. A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}. 103 * @return The number of characters read/written in the copy operation. 104 * @throws CopyStreamException If an error occurs while reading from the source or writing to the destination. The CopyStreamException will contain the 105 * number of bytes confirmed to have been transferred before an IOException occurred, and it will also contain the IOException 106 * that caused the error. These values can be retrieved with the CopyStreamException getTotalBytesTransferred() and 107 * getIOException() methods. 108 */ 109 public static long copyReader(final Reader source, final Writer dest, final int bufferSize) throws CopyStreamException { 110 return copyReader(source, dest, bufferSize, CopyStreamEvent.UNKNOWN_STREAM_SIZE, null); 111 } 112 113 /** 114 * 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 115 * 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 116 * use a CopyStreamAdapter as the listener and register the additional listeners with the CopyStreamAdapter. 117 * <p> 118 * 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 119 * the method call. The number of characters read/written is returned. 120 * 121 * @param source The source Reader. 122 * @param dest The destination writer. 123 * @param bufferSize The number of characters to buffer during the copy. A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}. 124 * @param streamSize The number of characters in the stream being copied. Should be set to CopyStreamEvent.UNKNOWN_STREAM_SIZE if unknown. Not currently 125 * used (though it is passed to {@link CopyStreamListener#bytesTransferred(long, int, long)} 126 * @param listener The CopyStreamListener to notify of progress. If this parameter is null, notification is not attempted. 127 * @return The number of characters read/written in the copy operation. 128 * @throws CopyStreamException If an error occurs while reading from the source or writing to the destination. The CopyStreamException will contain the 129 * number of bytes confirmed to have been transferred before an IOException occurred, and it will also contain the IOException 130 * that caused the error. These values can be retrieved with the CopyStreamException getTotalBytesTransferred() and 131 * getIOException() methods. 132 */ 133 public static long copyReader(final Reader source, final Writer dest, final int bufferSize, final long streamSize, final CopyStreamListener listener) 134 throws CopyStreamException { 135 int numChars; 136 long total = 0; 137 final char[] buffer = new char[bufferSize > 0 ? bufferSize : DEFAULT_COPY_BUFFER_SIZE]; 138 139 try { 140 while ((numChars = source.read(buffer)) != NetConstants.EOS) { 141 // Technically, some read(char[]) methods may return 0, and we cannot 142 // accept that as an indication of EOF. 143 if (numChars == 0) { 144 final int singleChar = source.read(); 145 if (singleChar < 0) { 146 break; 147 } 148 dest.write(singleChar); 149 dest.flush(); 150 ++total; 151 if (listener != null) { 152 listener.bytesTransferred(total, 1, streamSize); 153 } 154 continue; 155 } 156 157 dest.write(buffer, 0, numChars); 158 dest.flush(); 159 total += numChars; 160 if (listener != null) { 161 listener.bytesTransferred(total, numChars, streamSize); 162 } 163 } 164 } catch (final IOException e) { 165 throw new CopyStreamException("IOException caught while copying.", total, e); 166 } 167 168 return total; 169 } 170 171 /** 172 * Same as <code>copyStream(source, dest, DEFAULT_COPY_BUFFER_SIZE);</code> 173 * 174 * @param source where to copy from 175 * @param dest where to copy to 176 * @return number of bytes copied 177 * @throws CopyStreamException on error 178 */ 179 public static long copyStream(final InputStream source, final OutputStream dest) throws CopyStreamException { 180 return copyStream(source, dest, DEFAULT_COPY_BUFFER_SIZE); 181 } 182 183 /** 184 * 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 185 * 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 186 * read/written is returned. 187 * 188 * @param source The source InputStream. 189 * @param dest The destination OutputStream. 190 * @param bufferSize The number of bytes to buffer during the copy. A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}. 191 * @return The number of bytes read/written in the copy operation. 192 * @throws CopyStreamException If an error occurs while reading from the source or writing to the destination. The CopyStreamException will contain the 193 * number of bytes confirmed to have been transferred before an IOException occurred, and it will also contain the IOException 194 * that caused the error. These values can be retrieved with the CopyStreamException getTotalBytesTransferred() and 195 * getIOException() methods. 196 */ 197 public static long copyStream(final InputStream source, final OutputStream dest, final int bufferSize) throws CopyStreamException { 198 return copyStream(source, dest, bufferSize, CopyStreamEvent.UNKNOWN_STREAM_SIZE, null); 199 } 200 201 /** 202 * 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 203 * 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 204 * you should use a CopyStreamAdapter as the listener and register the additional listeners with the CopyStreamAdapter. 205 * <p> 206 * 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 207 * yourself outside the method call. The number of bytes read/written is returned. 208 * 209 * @param source The source InputStream. 210 * @param dest The destination OutputStream. 211 * @param bufferSize The number of bytes to buffer during the copy. A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}. 212 * @param streamSize The number of bytes in the stream being copied. Should be set to CopyStreamEvent.UNKNOWN_STREAM_SIZE if unknown. Not currently used 213 * (though it is passed to {@link CopyStreamListener#bytesTransferred(long, int, long)} 214 * @param listener The CopyStreamListener to notify of progress. If this parameter is null, notification is not attempted. 215 * @return number of bytes read/written 216 * @throws CopyStreamException If an error occurs while reading from the source or writing to the destination. The CopyStreamException will contain the 217 * number of bytes confirmed to have been transferred before an IOException occurred, and it will also contain the IOException 218 * that caused the error. These values can be retrieved with the CopyStreamException getTotalBytesTransferred() and 219 * getIOException() methods. 220 */ 221 public static long copyStream(final InputStream source, final OutputStream dest, final int bufferSize, final long streamSize, 222 final CopyStreamListener listener) throws CopyStreamException { 223 return copyStream(source, dest, bufferSize, streamSize, listener, true); 224 } 225 226 /** 227 * 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 228 * 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 229 * you should use a CopyStreamAdapter as the listener and register the additional listeners with the CopyStreamAdapter. 230 * <p> 231 * 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 232 * yourself outside the method call. The number of bytes read/written is returned. 233 * 234 * @param source The source InputStream. 235 * @param dest The destination OutputStream. 236 * @param bufferSize The number of bytes to buffer during the copy. A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}. 237 * @param streamSize The number of bytes in the stream being copied. Should be set to CopyStreamEvent.UNKNOWN_STREAM_SIZE if unknown. Not currently used 238 * (though it is passed to {@link CopyStreamListener#bytesTransferred(long, int, long)} 239 * @param listener The CopyStreamListener to notify of progress. If this parameter is null, notification is not attempted. 240 * @param flush Whether to flush the output stream after every write. This is necessary for interactive sessions that rely on buffered streams. If you 241 * don't flush, the data will stay in the stream buffer. 242 * @return number of bytes read/written 243 * @throws CopyStreamException If an error occurs while reading from the source or writing to the destination. The CopyStreamException will contain the 244 * number of bytes confirmed to have been transferred before an IOException occurred, and it will also contain the IOException 245 * that caused the error. These values can be retrieved with the CopyStreamException getTotalBytesTransferred() and 246 * getIOException() methods. 247 */ 248 public static long copyStream(final InputStream source, final OutputStream dest, final int bufferSize, final long streamSize, 249 final CopyStreamListener listener, final boolean flush) throws CopyStreamException { 250 int numBytes; 251 long total = 0; 252 final byte[] buffer = new byte[bufferSize > 0 ? bufferSize : DEFAULT_COPY_BUFFER_SIZE]; 253 254 try { 255 while ((numBytes = source.read(buffer)) != NetConstants.EOS) { 256 // Technically, some read(byte[]) methods may return 0, and we cannot 257 // accept that as an indication of EOF. 258 259 if (numBytes == 0) { 260 final int singleByte = source.read(); 261 if (singleByte < 0) { 262 break; 263 } 264 dest.write(singleByte); 265 if (flush) { 266 dest.flush(); 267 } 268 ++total; 269 if (listener != null) { 270 listener.bytesTransferred(total, 1, streamSize); 271 } 272 continue; 273 } 274 275 dest.write(buffer, 0, numBytes); 276 if (flush) { 277 dest.flush(); 278 } 279 total += numBytes; 280 if (listener != null) { 281 listener.bytesTransferred(total, numBytes, streamSize); 282 } 283 } 284 } catch (final IOException e) { 285 throw new CopyStreamException("IOException caught while copying.", total, e); 286 } 287 288 return total; 289 } 290 291 /** 292 * Creates a new PrintWriter using the default encoding. 293 * 294 * @param printStream the target PrintStream. 295 * @return a new PrintWriter. 296 * @since 3.11.0 297 */ 298 public static PrintWriter newPrintWriter(final PrintStream printStream) { 299 return new PrintWriter(new OutputStreamWriter(printStream, Charset.defaultCharset())); 300 } 301 302 /** Cannot be instantiated. */ 303 private Util() { 304 } 305}