    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     *      http://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
    018    package org.apache.commons.net.io;
    019
    020    import java.io.Closeable;
    021    import java.io.IOException;
    022    import java.io.InputStream;
    023    import java.io.OutputStream;
    024    import java.io.Reader;
    025    import java.io.Writer;
    026    import java.net.Socket;
    027
    028    /***
    029     * The Util class cannot be instantiated and stores short static convenience
    030     * methods that are often quite useful.
    031     * <p>
    032     * <p>
    033     * @see CopyStreamException
    034     * @see CopyStreamListener
    035     * @see CopyStreamAdapter
    036     ***/
    037
    038    public final class Util
    039    {
    040        /***
    041         * The default buffer size used by {@link #copyStream  copyStream }
    042         * and {@link #copyReader  copyReader }. It's value is 1024.
    043         ***/
    044        public static final int DEFAULT_COPY_BUFFER_SIZE = 1024;
    045
    046        // Cannot be instantiated
    047        private Util()
    048        { }
    049
    050
    051        /***
    052         * Copies the contents of an InputStream to an OutputStream using a
    053         * copy buffer of a given size and notifies the provided
    054         * CopyStreamListener of the progress of the copy operation by calling
    055         * its bytesTransferred(long, int) method after each write to the
    056         * destination.  If you wish to notify more than one listener you should
    057         * use a CopyStreamAdapter as the listener and register the additional
    058         * listeners with the CopyStreamAdapter.
    059         * <p>
    060         * The contents of the InputStream are
    061         * read until the end of the stream is reached, but neither the
    062         * source nor the destination are closed.  You must do this yourself
    063         * outside of the method call.  The number of bytes read/written is
    064         * returned.
    065         * <p>
    066         * @param source  The source InputStream.
    067         * @param dest    The destination OutputStream.
    068         * @param bufferSize  The number of bytes to buffer during the copy.
    069         * @param streamSize  The number of bytes in the stream being copied.
    070         *          Should be set to CopyStreamEvent.UNKNOWN_STREAM_SIZE if unknown.
    071         * @param listener  The CopyStreamListener to notify of progress.  If
    072         *      this parameter is null, notification is not attempted.
    073         * @param flush Whether to flush the output stream after every
    074         *        write.  This is necessary for interactive sessions that rely on
    075         *        buffered streams.  If you don't flush, the data will stay in
    076         *        the stream buffer.
    077         * @exception CopyStreamException  If an error occurs while reading from the
    078         *            source or writing to the destination.  The CopyStreamException
    079         *            will contain the number of bytes confirmed to have been
    080         *            transferred before an
    081         *            IOException occurred, and it will also contain the IOException
    082         *            that caused the error.  These values can be retrieved with
    083         *            the CopyStreamException getTotalBytesTransferred() and
    084         *            getIOException() methods.
    085         ***/
    086        public static final long copyStream(InputStream source, OutputStream dest,
    087                                            int bufferSize, long streamSize,
    088                                            CopyStreamListener listener,
    089                                            boolean flush)
    090        throws CopyStreamException
    091        {
    092            int bytes;
    093            long total;
    094            byte[] buffer;
    095
    096            buffer = new byte[bufferSize];
    097            total = 0;
    098
    099            try
    100            {
    101                while ((bytes = source.read(buffer)) != -1)
    102                {
    103                    // Technically, some read(byte[]) methods may return 0 and we cannot
    104                    // accept that as an indication of EOF.
    105
    106                    if (bytes == 0)
    107                    {
    108                        bytes = source.read();
    109                        if (bytes < 0) {
    110                            break;
    111                        }
    112                        dest.write(bytes);
    113                        if(flush) {
    114                            dest.flush();
    115                        }
    116                        ++total;
    117                        if (listener != null) {
    118                            listener.bytesTransferred(total, 1, streamSize);
    119                        }
    120                        continue;
    121                    }
    122
    123                    dest.write(buffer, 0, bytes);
    124                    if(flush) {
    125                        dest.flush();
    126                    }
    127                    total += bytes;
    128                    if (listener != null) {
    129                        listener.bytesTransferred(total, bytes, streamSize);
    130                    }
    131                }
    132            }
    133            catch (IOException e)
    134            {
    135                throw new CopyStreamException("IOException caught while copying.",
    136                                              total, e);
    137            }
    138
    139            return total;
    140        }
    141
    142
    143        /***
    144         * Copies the contents of an InputStream to an OutputStream using a
    145         * copy buffer of a given size and notifies the provided
    146         * CopyStreamListener of the progress of the copy operation by calling
    147         * its bytesTransferred(long, int) method after each write to the
    148         * destination.  If you wish to notify more than one listener you should
    149         * use a CopyStreamAdapter as the listener and register the additional
    150         * listeners with the CopyStreamAdapter.
    151         * <p>
    152         * The contents of the InputStream are
    153         * read until the end of the stream is reached, but neither the
    154         * source nor the destination are closed.  You must do this yourself
    155         * outside of the method call.  The number of bytes read/written is
    156         * returned.
    157         * <p>
    158         * @param source  The source InputStream.
    159         * @param dest    The destination OutputStream.
    160         * @param bufferSize  The number of bytes to buffer during the copy.
    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         * @return  The number of bytes read/written in the copy operation.
    195         * @exception CopyStreamException  If an error occurs while reading from the
    196         *            source or writing to the destination.  The CopyStreamException
    197         *            will contain the number of bytes confirmed to have been
    198         *            transferred before an
    199         *            IOException occurred, and it will also contain the IOException
    200         *            that caused the error.  These values can be retrieved with
    201         *            the CopyStreamException getTotalBytesTransferred() and
    202         *            getIOException() methods.
    203         ***/
    204        public static final long copyStream(InputStream source, OutputStream dest,
    205                                            int bufferSize)
    206        throws CopyStreamException
    207        {
    208            return copyStream(source, dest, bufferSize,
    209                              CopyStreamEvent.UNKNOWN_STREAM_SIZE, null);
    210        }
    211
    212
    213        /***
    214         * Same as <code> copyStream(source, dest, DEFAULT_COPY_BUFFER_SIZE); </code>
    215         ***/
    216        public static final long copyStream(InputStream source, OutputStream dest)
    217        throws CopyStreamException
    218        {
    219            return copyStream(source, dest, DEFAULT_COPY_BUFFER_SIZE);
    220        }
    221
    222
    223        /***
    224         * Copies the contents of a Reader to a Writer using a
    225         * copy buffer of a given size and notifies the provided
    226         * CopyStreamListener of the progress of the copy operation by calling
    227         * its bytesTransferred(long, int) method after each write to the
    228         * destination.  If you wish to notify more than one listener you should
    229         * use a CopyStreamAdapter as the listener and register the additional
    230         * listeners with the CopyStreamAdapter.
    231         * <p>
    232         * The contents of the Reader are
    233         * read until its end is reached, but neither the source nor the
    234         * destination are closed.  You must do this yourself outside of the
    235         * method call.  The number of characters read/written is returned.
    236         * <p>
    237         * @param source  The source Reader.
    238         * @param dest    The destination writer.
    239         * @param bufferSize  The number of characters to buffer during the copy.
    240         * @param streamSize  The number of characters in the stream being copied.
    241         *          Should be set to CopyStreamEvent.UNKNOWN_STREAM_SIZE if unknown.
    242         * @param listener  The CopyStreamListener to notify of progress.  If
    243         *      this parameter is null, notification is not attempted.
    244         * @return  The number of characters read/written in the copy operation.
    245         * @exception CopyStreamException  If an error occurs while reading from the
    246         *            source or writing to the destination.  The CopyStreamException
    247         *            will contain the number of bytes confirmed to have been
    248         *            transferred before an
    249         *            IOException occurred, and it will also contain the IOException
    250         *            that caused the error.  These values can be retrieved with
    251         *            the CopyStreamException getTotalBytesTransferred() and
    252         *            getIOException() methods.
    253         ***/
    254        public static final long copyReader(Reader source, Writer dest,
    255                                            int bufferSize, long streamSize,
    256                                            CopyStreamListener listener)
    257        throws CopyStreamException
    258        {
    259            int chars;
    260            long total;
    261            char[] buffer;
    262
    263            buffer = new char[bufferSize];
    264            total = 0;
    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         * @return  The number of characters read/written in the copy operation.
    316         * @exception CopyStreamException  If an error occurs while reading from the
    317         *            source or writing to the destination.  The CopyStreamException
    318         *            will contain the number of bytes confirmed to have been
    319         *            transferred before an
    320         *            IOException occurred, and it will also contain the IOException
    321         *            that caused the error.  These values can be retrieved with
    322         *            the CopyStreamException getTotalBytesTransferred() and
    323         *            getIOException() methods.
    324         ***/
    325        public static final long copyReader(Reader source, Writer dest,
    326                                            int bufferSize)
    327        throws CopyStreamException
    328        {
    329            return copyReader(source, dest, bufferSize,
    330                              CopyStreamEvent.UNKNOWN_STREAM_SIZE, null);
    331        }
    332
    333
    334        /***
    335         * Same as <code> copyReader(source, dest, DEFAULT_COPY_BUFFER_SIZE); </code>
    336         ***/
    337        public static final long copyReader(Reader source, Writer dest)
    338        throws CopyStreamException
    339        {
    340            return copyReader(source, dest, DEFAULT_COPY_BUFFER_SIZE);
    341        }
    342
    343        /**
    344         * Closes the object quietly, catching rather than throwing IOException.
    345         * Intended for use from finally blocks.
    346         *
    347         * @param closeable the object to close, may be {@code null}
    348         * @since 3.0
    349         */
    350        public static void closeQuietly(Closeable closeable) {
    351            if (closeable != null) {
    352                try {
    353                    closeable.close();
    354                } catch (IOException e) {
    355                }
    356            }
    357        }
    358
    359        /**
    360         * Closes the socket quietly, catching rather than throwing IOException.
    361         * Intended for use from finally blocks.
    362         *
    363         * @param socket the socket to close, may be {@code null}
    364         * @since 3.0
    365         */
    366        public static void closeQuietly(Socket socket) {
    367            if (socket != null) {
    368                try {
    369                    socket.close();
    370                } catch (IOException e) {
    371                }
    372            }
    373        }
    374    }