    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}