001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      https://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.commons.net.io;
019
020import java.io.Closeable;
021import java.io.IOException;
022import java.io.InputStream;
023import java.io.OutputStream;
024import java.io.OutputStreamWriter;
025import java.io.PrintStream;
026import java.io.PrintWriter;
027import java.io.Reader;
028import java.io.Writer;
029import java.net.Socket;
030import java.nio.charset.Charset;
031import java.util.concurrent.atomic.AtomicLong;
032
033import org.apache.commons.io.IOUtils;
034import org.apache.commons.io.output.ProxyOutputStream;
035import org.apache.commons.io.output.ProxyWriter;
036
037/**
038 * The Util class cannot be instantiated and stores short static convenience methods that are often quite useful.
039 *
040 * @see CopyStreamException
041 * @see CopyStreamListener
042 * @see CopyStreamAdapter
043 */
044public final class Util {
045
046    /**
047     * The default buffer size ({@value}) used by {@link #copyStream copyStream} and {@link #copyReader copyReader} and by the copyReader/copyStream methods if
048     * a zero or negative buffer size is supplied.
049     */
050    public static final int DEFAULT_COPY_BUFFER_SIZE = IOUtils.DEFAULT_BUFFER_SIZE;
051
052    /**
053     * Closes the object quietly, catching rather than throwing IOException. Intended for use from finally blocks.
054     *
055     * @param closeable the object to close, may be {@code null}
056     * @since 3.0
057     * @deprecated Use {@link IOUtils#closeQuietly(Closeable)}.
058     */
059    @Deprecated
060    public static void closeQuietly(final Closeable closeable) {
061        IOUtils.closeQuietly(closeable);
062    }
063
064    /**
065     * Closes the socket quietly, catching rather than throwing IOException. Intended for use from finally blocks.
066     *
067     * @param socket the socket to close, may be {@code null}
068     * @since 3.0
069     * @deprecated Use {@link IOUtils#closeQuietly(Socket)}.
070     */
071    @Deprecated
072    public static void closeQuietly(final Socket socket) {
073        IOUtils.closeQuietly(socket);
074    }
075
076    /**
077     * Same as {@code copyReader(source, dest, DEFAULT_COPY_BUFFER_SIZE);}
078     *
079     * @param source where to copy from
080     * @param dest   where to copy to
081     * @return number of bytes copied
082     * @throws CopyStreamException on error
083     * @deprecated Use {@link IOUtils#copy(Reader, Writer)}.
084     */
085    @Deprecated
086    public static long copyReader(final Reader source, final Writer dest) throws CopyStreamException {
087        return copyReader(source, dest, DEFAULT_COPY_BUFFER_SIZE);
088    }
089
090    /**
091     * 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
092     * neither the source nor the destination are closed. You must do this yourself outside the method call. The number of characters read/written is
093     * returned.
094     *
095     * @param source     The source Reader.
096     * @param dest       The destination writer.
097     * @param bufferSize The number of characters to buffer during the copy. A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}.
098     * @return The number of characters read/written in the copy operation.
099     * @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}