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