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 }