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  import java.util.concurrent.atomic.AtomicLong;
32  
33  import org.apache.commons.io.IOUtils;
34  import org.apache.commons.io.output.ProxyOutputStream;
35  import org.apache.commons.io.output.ProxyWriter;
36  
37  /**
38   * The Util class cannot be instantiated and stores short static convenience methods that are often quite useful.
39   *
40   * @see CopyStreamException
41   * @see CopyStreamListener
42   * @see CopyStreamAdapter
43   */
44  public final class Util {
45  
46      /**
47       * The default buffer size ({@value}) used by {@link #copyStream copyStream} and {@link #copyReader copyReader} and by the copyReader/copyStream methods if
48       * a zero or negative buffer size is supplied.
49       */
50      public static final int DEFAULT_COPY_BUFFER_SIZE = IOUtils.DEFAULT_BUFFER_SIZE;
51  
52      /**
53       * Closes the object quietly, catching rather than throwing IOException. Intended for use from finally blocks.
54       *
55       * @param closeable the object to close, may be {@code null}
56       * @since 3.0
57       * @deprecated Use {@link IOUtils#closeQuietly(Closeable)}.
58       */
59      @Deprecated
60      public static void closeQuietly(final Closeable closeable) {
61          IOUtils.closeQuietly(closeable);
62      }
63  
64      /**
65       * Closes the socket quietly, catching rather than throwing IOException. Intended for use from finally blocks.
66       *
67       * @param socket the socket to close, may be {@code null}
68       * @since 3.0
69       * @deprecated Use {@link IOUtils#closeQuietly(Socket)}.
70       */
71      @Deprecated
72      public static void closeQuietly(final Socket socket) {
73          IOUtils.closeQuietly(socket);
74      }
75  
76      /**
77       * Same as {@code copyReader(source, dest, DEFAULT_COPY_BUFFER_SIZE);}
78       *
79       * @param source where to copy from
80       * @param dest   where to copy to
81       * @return number of bytes copied
82       * @throws CopyStreamException on error
83       * @deprecated Use {@link IOUtils#copy(Reader, Writer)}.
84       */
85      @Deprecated
86      public static long copyReader(final Reader source, final Writer dest) throws CopyStreamException {
87          return copyReader(source, dest, DEFAULT_COPY_BUFFER_SIZE);
88      }
89  
90      /**
91       * 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
92       * neither the source nor the destination are closed. You must do this yourself outside the method call. The number of characters read/written is
93       * returned.
94       *
95       * @param source     The source Reader.
96       * @param dest       The destination writer.
97       * @param bufferSize The number of characters to buffer during the copy. A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}.
98       * @return The number of characters read/written in the copy operation.
99       * @throws CopyStreamException If an error occurs while reading from the source or writing to the destination. The CopyStreamException will contain the
100      *                             number of bytes confirmed to have been transferred before an IOException occurred, and it will also contain the IOException
101      *                             that caused the error. These values can be retrieved with the CopyStreamException getTotalBytesTransferred() and
102      *                             getIOException() methods.
103      */
104     public static long copyReader(final Reader source, final Writer dest, final int bufferSize) throws CopyStreamException {
105         return copyReader(source, dest, bufferSize, CopyStreamEvent.UNKNOWN_STREAM_SIZE, null);
106     }
107 
108     /**
109      * 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
110      * 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
111      * use a CopyStreamAdapter as the listener and register the additional listeners with the CopyStreamAdapter.
112      * <p>
113      * 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
114      * the method call. The number of characters read/written is returned.
115      * </p>
116      *
117      * @param source     The source Reader.
118      * @param dest       The destination writer.
119      * @param bufferSize The number of characters to buffer during the copy. A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}.
120      * @param streamSize The number of characters in the stream being copied. Should be set to CopyStreamEvent.UNKNOWN_STREAM_SIZE if unknown. Not currently
121      *                   used (though it is passed to {@link CopyStreamListener#bytesTransferred(long, int, long)}
122      * @param listener   The CopyStreamListener to notify of progress. If this parameter is null, notification is not attempted.
123      * @return The number of characters read/written in the copy operation.
124      * @throws CopyStreamException If an error occurs while reading from the source or writing to the destination. The CopyStreamException will contain the
125      *                             number of bytes confirmed to have been transferred before an IOException occurred, and it will also contain the IOException
126      *                             that caused the error. These values can be retrieved with the CopyStreamException getTotalBytesTransferred() and
127      *                             getIOException() methods.
128      */
129     public static long copyReader(final Reader source, final Writer dest, final int bufferSize, final long streamSize, final CopyStreamListener listener)
130             throws CopyStreamException {
131         final AtomicLong total = new AtomicLong();
132         try {
133             return IOUtils.copyLarge(source, listener == null ? dest : new ProxyWriter(dest) {
134 
135                 @Override
136                 protected void afterWrite(int n) throws IOException {
137                     dest.flush();
138                     listener.bytesTransferred(total.addAndGet(n), n, streamSize);
139                 }
140             }, new char[bufferSize > 0 ? bufferSize : DEFAULT_COPY_BUFFER_SIZE]);
141         } catch (IOException e) {
142             throw new CopyStreamException("IOException caught while copying.", total.get(), e);
143         }
144     }
145 
146     /**
147      * Same as {@code copyStream(source, dest, DEFAULT_COPY_BUFFER_SIZE);}
148      *
149      * @param source where to copy from
150      * @param dest   where to copy to
151      * @return number of bytes copied
152      * @throws CopyStreamException on error
153      */
154     public static long copyStream(final InputStream source, final OutputStream dest) throws CopyStreamException {
155         return copyStream(source, dest, DEFAULT_COPY_BUFFER_SIZE);
156     }
157 
158     /**
159      * 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
160      * 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
161      * read/written is returned.
162      *
163      * @param source     The source InputStream.
164      * @param dest       The destination OutputStream.
165      * @param bufferSize The number of bytes to buffer during the copy. A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}.
166      * @return The number of bytes read/written in the copy operation.
167      * @throws CopyStreamException If an error occurs while reading from the source or writing to the destination. The CopyStreamException will contain the
168      *                             number of bytes confirmed to have been transferred before an IOException occurred, and it will also contain the IOException
169      *                             that caused the error. These values can be retrieved with the CopyStreamException getTotalBytesTransferred() and
170      *                             getIOException() methods.
171      */
172     public static long copyStream(final InputStream source, final OutputStream dest, final int bufferSize) throws CopyStreamException {
173         return copyStream(source, dest, bufferSize, CopyStreamEvent.UNKNOWN_STREAM_SIZE, null);
174     }
175 
176     /**
177      * 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
178      * 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
179      * you should use a CopyStreamAdapter as the listener and register the additional listeners with the CopyStreamAdapter.
180      * <p>
181      * 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
182      * yourself outside the method call. The number of bytes read/written is returned.
183      *
184      * @param source     The source InputStream.
185      * @param dest       The destination OutputStream.
186      * @param bufferSize The number of bytes to buffer during the copy. A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}.
187      * @param streamSize The number of bytes in the stream being copied. Should be set to CopyStreamEvent.UNKNOWN_STREAM_SIZE if unknown. Not currently used
188      *                   (though it is passed to {@link CopyStreamListener#bytesTransferred(long, int, long)}
189      * @param listener   The CopyStreamListener to notify of progress. If this parameter is null, notification is not attempted.
190      * @return number of bytes read/written
191      * @throws CopyStreamException If an error occurs while reading from the source or writing to the destination. The CopyStreamException will contain the
192      *                             number of bytes confirmed to have been transferred before an IOException occurred, and it will also contain the IOException
193      *                             that caused the error. These values can be retrieved with the CopyStreamException getTotalBytesTransferred() and
194      *                             getIOException() methods.
195      */
196     public static long copyStream(final InputStream source, final OutputStream dest, final int bufferSize, final long streamSize,
197             final CopyStreamListener listener) throws CopyStreamException {
198         return copyStream(source, dest, bufferSize, streamSize, listener, true);
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      * @param flush      Whether to flush the output stream after every write. This is necessary for interactive sessions that rely on buffered streams. If you
216      *                   don't flush, the data will stay in the stream buffer.
217      * @return number of bytes read/written
218      * @throws CopyStreamException If an error occurs while reading from the source or writing to the destination. The CopyStreamException will contain the
219      *                             number of bytes confirmed to have been transferred before an IOException occurred, and it will also contain the IOException
220      *                             that caused the error. These values can be retrieved with the CopyStreamException getTotalBytesTransferred() and
221      *                             getIOException() methods.
222      */
223     public static long copyStream(final InputStream source, final OutputStream dest, final int bufferSize, final long streamSize,
224             final CopyStreamListener listener, final boolean flush) throws CopyStreamException {
225         final AtomicLong total = new AtomicLong();
226         try {
227             return IOUtils.copyLarge(source, listener == null ? dest : new ProxyOutputStream(dest) {
228 
229                 @Override
230                 protected void afterWrite(int n) throws IOException {
231                     if (flush) {
232                         dest.flush();
233                     }
234                     listener.bytesTransferred(total.addAndGet(n), n, streamSize);
235                 }
236 
237             }, new byte[bufferSize > 0 ? bufferSize : DEFAULT_COPY_BUFFER_SIZE]);
238         } catch (IOException e) {
239             throw new CopyStreamException("IOException caught while copying.", total.get(), e);
240         }
241     }
242 
243     /**
244      * Creates a new PrintWriter using the default encoding.
245      *
246      * @param printStream the target PrintStream.
247      * @return a new PrintWriter.
248      * @since 3.11.0
249      */
250     public static PrintWriter newPrintWriter(final PrintStream printStream) {
251         return new PrintWriter(new OutputStreamWriter(printStream, Charset.defaultCharset()));
252     }
253 
254     /** Cannot be instantiated. */
255     private Util() {
256     }
257 }