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