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    *      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  /***
29   * The Util class cannot be instantiated and stores short static convenience
30   * methods that are often quite useful.
31   *
32   *
33   * @see CopyStreamException
34   * @see CopyStreamListener
35   * @see CopyStreamAdapter
36   ***/
37  
38  public final class Util
39  {
40      /**
41       * The default buffer size ({@value}) used by
42       * {@link #copyStream  copyStream } and {@link #copyReader  copyReader}
43       * and by the copyReader/copyStream methods if a zero or negative buffer size is supplied.
44       */
45      public static final int DEFAULT_COPY_BUFFER_SIZE = 1024;
46  
47      // Cannot be instantiated
48      private Util()
49      { }
50  
51  
52      /***
53       * Copies the contents of an InputStream to an OutputStream using a
54       * copy buffer of a given size and notifies the provided
55       * CopyStreamListener of the progress of the copy operation by calling
56       * its bytesTransferred(long, int) method after each write to the
57       * destination.  If you wish to notify more than one listener you should
58       * use a CopyStreamAdapter as the listener and register the additional
59       * listeners with the CopyStreamAdapter.
60       * <p>
61       * The contents of the InputStream are
62       * read until the end of the stream is reached, but neither the
63       * source nor the destination are closed.  You must do this yourself
64       * outside of the method call.  The number of bytes read/written is
65       * returned.
66       *
67       * @param source  The source InputStream.
68       * @param dest    The destination OutputStream.
69       * @param bufferSize  The number of bytes to buffer during the copy.
70       *            A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}.
71       * @param streamSize  The number of bytes in the stream being copied.
72       *          Should be set to CopyStreamEvent.UNKNOWN_STREAM_SIZE if unknown.
73       * Not currently used (though it is passed to {@link CopyStreamListener#bytesTransferred(long, int, long)}
74       * @param listener  The CopyStreamListener to notify of progress.  If
75       *      this parameter is null, notification is not attempted.
76       * @param flush Whether to flush the output stream after every
77       *        write.  This is necessary for interactive sessions that rely on
78       *        buffered streams.  If you don't flush, the data will stay in
79       *        the stream buffer.
80       * @return number of bytes read/written
81       * @exception CopyStreamException  If an error occurs while reading from the
82       *            source or writing to the destination.  The CopyStreamException
83       *            will contain the number of bytes confirmed to have been
84       *            transferred before an
85       *            IOException occurred, and it will also contain the IOException
86       *            that caused the error.  These values can be retrieved with
87       *            the CopyStreamException getTotalBytesTransferred() and
88       *            getIOException() methods.
89       ***/
90      public static final long copyStream(InputStream source, OutputStream dest,
91                                          int bufferSize, long streamSize,
92                                          CopyStreamListener listener,
93                                          boolean flush)
94      throws CopyStreamException
95      {
96          int numBytes;
97          long total = 0;
98          byte[] buffer = new byte[bufferSize > 0 ? bufferSize : DEFAULT_COPY_BUFFER_SIZE];
99  
100         try
101         {
102             while ((numBytes = source.read(buffer)) != -1)
103             {
104                 // Technically, some read(byte[]) methods may return 0 and we cannot
105                 // accept that as an indication of EOF.
106 
107                 if (numBytes == 0)
108                 {
109                     int singleByte = source.read();
110                     if (singleByte < 0) {
111                         break;
112                     }
113                     dest.write(singleByte);
114                     if(flush) {
115                         dest.flush();
116                     }
117                     ++total;
118                     if (listener != null) {
119                         listener.bytesTransferred(total, 1, streamSize);
120                     }
121                     continue;
122                 }
123 
124                 dest.write(buffer, 0, numBytes);
125                 if(flush) {
126                     dest.flush();
127                 }
128                 total += numBytes;
129                 if (listener != null) {
130                     listener.bytesTransferred(total, numBytes, streamSize);
131                 }
132             }
133         }
134         catch (IOException e)
135         {
136             throw new CopyStreamException("IOException caught while copying.",
137                                           total, e);
138         }
139 
140         return total;
141     }
142 
143 
144     /***
145      * Copies the contents of an InputStream to an OutputStream using a
146      * copy buffer of a given size and notifies the provided
147      * CopyStreamListener of the progress of the copy operation by calling
148      * its bytesTransferred(long, int) method after each write to the
149      * destination.  If you wish to notify more than one listener you should
150      * use a CopyStreamAdapter as the listener and register the additional
151      * listeners with the CopyStreamAdapter.
152      * <p>
153      * The contents of the InputStream are
154      * read until the end of the stream is reached, but neither the
155      * source nor the destination are closed.  You must do this yourself
156      * outside of the method call.  The number of bytes read/written is
157      * returned.
158      *
159      * @param source  The source InputStream.
160      * @param dest    The destination OutputStream.
161      * @param bufferSize  The number of bytes to buffer during the copy.
162      *            A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}.
163      * @param streamSize  The number of bytes in the stream being copied.
164      *          Should be set to CopyStreamEvent.UNKNOWN_STREAM_SIZE if unknown.
165      * Not currently used (though it is passed to {@link CopyStreamListener#bytesTransferred(long, int, long)}
166      * @param listener  The CopyStreamListener to notify of progress.  If
167      *      this parameter is null, notification is not attempted.
168      * @return number of bytes read/written
169      * @exception CopyStreamException  If an error occurs while reading from the
170      *            source or writing to the destination.  The CopyStreamException
171      *            will contain the number of bytes confirmed to have been
172      *            transferred before an
173      *            IOException occurred, and it will also contain the IOException
174      *            that caused the error.  These values can be retrieved with
175      *            the CopyStreamException getTotalBytesTransferred() and
176      *            getIOException() methods.
177      ***/
178     public static final long copyStream(InputStream source, OutputStream dest,
179                                         int bufferSize, long streamSize,
180                                         CopyStreamListener listener)
181     throws CopyStreamException
182     {
183       return copyStream(source, dest, bufferSize, streamSize, listener,
184                         true);
185     }
186 
187 
188     /***
189      * Copies the contents of an InputStream to an OutputStream using a
190      * copy buffer of a given size.  The contents of the InputStream are
191      * read until the end of the stream is reached, but neither the
192      * source nor the destination are closed.  You must do this yourself
193      * outside of the method call.  The number of bytes read/written is
194      * returned.
195      *
196      * @param source  The source InputStream.
197      * @param dest    The destination OutputStream.
198      * @param bufferSize  The number of bytes to buffer during the copy.
199      *            A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}.
200      * @return  The number of bytes read/written in the copy operation.
201      * @exception CopyStreamException  If an error occurs while reading from the
202      *            source or writing to the destination.  The CopyStreamException
203      *            will contain the number of bytes confirmed to have been
204      *            transferred before an
205      *            IOException occurred, and it will also contain the IOException
206      *            that caused the error.  These values can be retrieved with
207      *            the CopyStreamException getTotalBytesTransferred() and
208      *            getIOException() methods.
209      ***/
210     public static final long copyStream(InputStream source, OutputStream dest,
211                                         int bufferSize)
212     throws CopyStreamException
213     {
214         return copyStream(source, dest, bufferSize,
215                           CopyStreamEvent.UNKNOWN_STREAM_SIZE, null);
216     }
217 
218 
219     /***
220      * Same as <code> copyStream(source, dest, DEFAULT_COPY_BUFFER_SIZE); </code>
221      * @param source where to copy from
222      * @param dest  where to copy to
223      * @return number of bytes copied
224      * @throws CopyStreamException on error
225      ***/
226     public static final long copyStream(InputStream source, OutputStream dest)
227     throws CopyStreamException
228     {
229         return copyStream(source, dest, DEFAULT_COPY_BUFFER_SIZE);
230     }
231 
232 
233     /***
234      * Copies the contents of a Reader to a Writer using a
235      * copy buffer of a given size and notifies the provided
236      * CopyStreamListener of the progress of the copy operation by calling
237      * its bytesTransferred(long, int) method after each write to the
238      * destination.  If you wish to notify more than one listener you should
239      * use a CopyStreamAdapter as the listener and register the additional
240      * listeners with the CopyStreamAdapter.
241      * <p>
242      * The contents of the Reader are
243      * read until its end is reached, but neither the source nor the
244      * destination are closed.  You must do this yourself outside of the
245      * method call.  The number of characters read/written is returned.
246      *
247      * @param source  The source Reader.
248      * @param dest    The destination writer.
249      * @param bufferSize  The number of characters to buffer during the copy.
250      *            A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}.
251      * @param streamSize  The number of characters in the stream being copied.
252      *          Should be set to CopyStreamEvent.UNKNOWN_STREAM_SIZE if unknown.
253      * Not currently used (though it is passed to {@link CopyStreamListener#bytesTransferred(long, int, long)}
254      * @param listener  The CopyStreamListener to notify of progress.  If
255      *      this parameter is null, notification is not attempted.
256      * @return  The number of characters read/written in the copy operation.
257      * @exception CopyStreamException  If an error occurs while reading from the
258      *            source or writing to the destination.  The CopyStreamException
259      *            will contain the number of bytes confirmed to have been
260      *            transferred before an
261      *            IOException occurred, and it will also contain the IOException
262      *            that caused the error.  These values can be retrieved with
263      *            the CopyStreamException getTotalBytesTransferred() and
264      *            getIOException() methods.
265      ***/
266     public static final long copyReader(Reader source, Writer dest,
267                                         int bufferSize, long streamSize,
268                                         CopyStreamListener listener)
269     throws CopyStreamException
270     {
271         int numChars;
272         long total = 0;
273         char[] buffer = new char[bufferSize > 0 ? bufferSize : DEFAULT_COPY_BUFFER_SIZE];
274 
275         try
276         {
277             while ((numChars = source.read(buffer)) != -1)
278             {
279                 // Technically, some read(char[]) methods may return 0 and we cannot
280                 // accept that as an indication of EOF.
281                 if (numChars == 0)
282                 {
283                     int singleChar = source.read();
284                     if (singleChar < 0) {
285                         break;
286                     }
287                     dest.write(singleChar);
288                     dest.flush();
289                     ++total;
290                     if (listener != null) {
291                         listener.bytesTransferred(total, 1, streamSize);
292                     }
293                     continue;
294                 }
295 
296                 dest.write(buffer, 0, numChars);
297                 dest.flush();
298                 total += numChars;
299                 if (listener != null) {
300                     listener.bytesTransferred(total, numChars, streamSize);
301                 }
302             }
303         }
304         catch (IOException e)
305         {
306             throw new CopyStreamException("IOException caught while copying.",
307                                           total, e);
308         }
309 
310         return total;
311     }
312 
313 
314     /***
315      * Copies the contents of a Reader to a Writer using a
316      * copy buffer of a given size.  The contents of the Reader are
317      * read until its end is reached, but neither the source nor the
318      * destination are closed.  You must do this yourself outside of the
319      * method call.  The number of characters read/written is returned.
320      *
321      * @param source  The source Reader.
322      * @param dest    The destination writer.
323      * @param bufferSize  The number of characters to buffer during the copy.
324      *            A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}.
325      * @return  The number of characters read/written in the copy operation.
326      * @exception CopyStreamException  If an error occurs while reading from the
327      *            source or writing to the destination.  The CopyStreamException
328      *            will contain the number of bytes confirmed to have been
329      *            transferred before an
330      *            IOException occurred, and it will also contain the IOException
331      *            that caused the error.  These values can be retrieved with
332      *            the CopyStreamException getTotalBytesTransferred() and
333      *            getIOException() methods.
334      ***/
335     public static final long copyReader(Reader source, Writer dest,
336                                         int bufferSize)
337     throws CopyStreamException
338     {
339         return copyReader(source, dest, bufferSize,
340                           CopyStreamEvent.UNKNOWN_STREAM_SIZE, null);
341     }
342 
343 
344     /***
345      * Same as <code> copyReader(source, dest, DEFAULT_COPY_BUFFER_SIZE); </code>
346      * @param source where to copy from
347      * @param dest  where to copy to
348      * @return number of bytes copied
349      * @throws CopyStreamException on error
350      ***/
351     public static final long copyReader(Reader source, Writer dest)
352     throws CopyStreamException
353     {
354         return copyReader(source, dest, DEFAULT_COPY_BUFFER_SIZE);
355     }
356 
357     /**
358      * Closes the object quietly, catching rather than throwing IOException.
359      * Intended for use from finally blocks.
360      *
361      * @param closeable the object to close, may be {@code null}
362      * @since 3.0
363      */
364     public static void closeQuietly(Closeable closeable) {
365         if (closeable != null) {
366             try {
367                 closeable.close();
368             } catch (IOException e) {
369                 // Ignored
370             }
371         }
372     }
373 
374     /**
375      * Closes the socket quietly, catching rather than throwing IOException.
376      * Intended for use from finally blocks.
377      *
378      * @param socket the socket to close, may be {@code null}
379      * @since 3.0
380      */
381     public static void closeQuietly(Socket socket) {
382         if (socket != null) {
383             try {
384                 socket.close();
385             } catch (IOException e) {
386                 // Ignored
387             }
388         }
389     }
390 }