View Javadoc
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    *      https://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.OutputStreamWriter;
25  import java.io.PrintStream;
26  import java.io.PrintWriter;
27  import java.io.Reader;
28  import java.io.Writer;
29  import java.net.Socket;
30  import java.nio.charset.Charset;
31  
32  import org.apache.commons.io.IOUtils;
33  import org.apache.commons.net.util.NetConstants;
34  
35  /**
36   * The Util class cannot be instantiated and stores short static convenience methods that are often quite useful.
37   *
38   *
39   * @see CopyStreamException
40   * @see CopyStreamListener
41   * @see CopyStreamAdapter
42   */
43  public final class Util {
44  
45      /**
46       * The default buffer size ({@value}) used by {@link #copyStream copyStream} and {@link #copyReader copyReader} and by the copyReader/copyStream methods if
47       * a zero or negative buffer size is supplied.
48       */
49      public static final int DEFAULT_COPY_BUFFER_SIZE = 1024;
50  
51      /**
52       * Closes the object quietly, catching rather than throwing IOException. Intended for use from finally blocks.
53       *
54       * @param closeable the object to close, may be {@code null}
55       * @since 3.0
56       * @deprecated Use {@link IOUtils#closeQuietly(Closeable)}.
57       */
58      @Deprecated
59      public static void closeQuietly(final Closeable closeable) {
60          IOUtils.closeQuietly(closeable);
61      }
62  
63      /**
64       * Closes the socket quietly, catching rather than throwing IOException. Intended for use from finally blocks.
65       *
66       * @param socket the socket to close, may be {@code null}
67       * @since 3.0
68       * @deprecated Use {@link IOUtils#closeQuietly(Socket)}.
69       */
70      @Deprecated
71      public static void closeQuietly(final Socket socket) {
72          IOUtils.closeQuietly(socket);
73      }
74  
75      /**
76       * Same as {@code copyReader(source, dest, DEFAULT_COPY_BUFFER_SIZE);}
77       *
78       * @param source where to copy from
79       * @param dest   where to copy to
80       * @return number of bytes copied
81       * @throws CopyStreamException on error
82       */
83      public static long copyReader(final Reader source, final Writer dest) throws CopyStreamException {
84          return copyReader(source, dest, DEFAULT_COPY_BUFFER_SIZE);
85      }
86  
87      /**
88       * 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
89       * neither the source nor the destination are closed. You must do this yourself outside the method call. The number of characters read/written is
90       * returned.
91       *
92       * @param source     The source Reader.
93       * @param dest       The destination writer.
94       * @param bufferSize The number of characters to buffer during the copy. A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}.
95       * @return The number of characters read/written in the copy operation.
96       * @throws CopyStreamException If an error occurs while reading from the source or writing to the destination. The CopyStreamException will contain the
97       *                             number of bytes confirmed to have been transferred before an IOException occurred, and it will also contain the IOException
98       *                             that caused the error. These values can be retrieved with the CopyStreamException getTotalBytesTransferred() and
99       *                             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 }