Util.java

  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.  *      http://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. package org.apache.commons.net.io;

  18. import java.io.Closeable;
  19. import java.io.IOException;
  20. import java.io.InputStream;
  21. import java.io.OutputStream;
  22. import java.io.OutputStreamWriter;
  23. import java.io.PrintStream;
  24. import java.io.PrintWriter;
  25. import java.io.Reader;
  26. import java.io.Writer;
  27. import java.net.Socket;
  28. import java.nio.charset.Charset;

  29. import org.apache.commons.net.util.NetConstants;

  30. /**
  31.  * The Util class cannot be instantiated and stores short static convenience methods that are often quite useful.
  32.  *
  33.  *
  34.  * @see CopyStreamException
  35.  * @see CopyStreamListener
  36.  * @see CopyStreamAdapter
  37.  */

  38. public final class Util {

  39.     /**
  40.      * The default buffer size ({@value}) used by {@link #copyStream copyStream } and {@link #copyReader copyReader} and by the copyReader/copyStream methods if
  41.      * a zero or negative buffer size is supplied.
  42.      */
  43.     public static final int DEFAULT_COPY_BUFFER_SIZE = 1024;

  44.     /**
  45.      * Closes the object quietly, catching rather than throwing IOException. Intended for use from finally blocks.
  46.      *
  47.      * @param closeable the object to close, may be {@code null}
  48.      * @since 3.0
  49.      */
  50.     public static void closeQuietly(final Closeable closeable) {
  51.         if (closeable != null) {
  52.             try {
  53.                 closeable.close();
  54.             } catch (final IOException e) {
  55.                 // Ignored
  56.             }
  57.         }
  58.     }

  59.     /**
  60.      * Closes the socket quietly, catching rather than throwing IOException. Intended for use from finally blocks.
  61.      *
  62.      * @param socket the socket to close, may be {@code null}
  63.      * @since 3.0
  64.      */
  65.     public static void closeQuietly(final Socket socket) {
  66.         if (socket != null) {
  67.             try {
  68.                 socket.close();
  69.             } catch (final IOException e) {
  70.                 // Ignored
  71.             }
  72.         }
  73.     }

  74.     /**
  75.      * Same as <code>copyReader(source, dest, DEFAULT_COPY_BUFFER_SIZE);</code>
  76.      *
  77.      * @param source where to copy from
  78.      * @param dest   where to copy to
  79.      * @return number of bytes copied
  80.      * @throws CopyStreamException on error
  81.      */
  82.     public static long copyReader(final Reader source, final Writer dest) throws CopyStreamException {
  83.         return copyReader(source, dest, DEFAULT_COPY_BUFFER_SIZE);
  84.     }

  85.     /**
  86.      * 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
  87.      * neither the source nor the destination are closed. You must do this yourself outside the method call. The number of characters read/written is
  88.      * returned.
  89.      *
  90.      * @param source     The source Reader.
  91.      * @param dest       The destination writer.
  92.      * @param bufferSize The number of characters to buffer during the copy. A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}.
  93.      * @return The number of characters read/written in the copy operation.
  94.      * @throws CopyStreamException If an error occurs while reading from the source or writing to the destination. The CopyStreamException will contain the
  95.      *                             number of bytes confirmed to have been transferred before an IOException occurred, and it will also contain the IOException
  96.      *                             that caused the error. These values can be retrieved with the CopyStreamException getTotalBytesTransferred() and
  97.      *                             getIOException() methods.
  98.      */
  99.     public static long copyReader(final Reader source, final Writer dest, final int bufferSize) throws CopyStreamException {
  100.         return copyReader(source, dest, bufferSize, CopyStreamEvent.UNKNOWN_STREAM_SIZE, null);
  101.     }

  102.     /**
  103.      * 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
  104.      * 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
  105.      * use a CopyStreamAdapter as the listener and register the additional listeners with the CopyStreamAdapter.
  106.      * <p>
  107.      * 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
  108.      * the method call. The number of characters read/written is returned.
  109.      *
  110.      * @param source     The source Reader.
  111.      * @param dest       The destination writer.
  112.      * @param bufferSize The number of characters to buffer during the copy. A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}.
  113.      * @param streamSize The number of characters in the stream being copied. Should be set to CopyStreamEvent.UNKNOWN_STREAM_SIZE if unknown. Not currently
  114.      *                   used (though it is passed to {@link CopyStreamListener#bytesTransferred(long, int, long)}
  115.      * @param listener   The CopyStreamListener to notify of progress. If this parameter is null, notification is not attempted.
  116.      * @return The number of characters read/written in the copy operation.
  117.      * @throws CopyStreamException If an error occurs while reading from the source or writing to the destination. The CopyStreamException will contain the
  118.      *                             number of bytes confirmed to have been transferred before an IOException occurred, and it will also contain the IOException
  119.      *                             that caused the error. These values can be retrieved with the CopyStreamException getTotalBytesTransferred() and
  120.      *                             getIOException() methods.
  121.      */
  122.     public static long copyReader(final Reader source, final Writer dest, final int bufferSize, final long streamSize, final CopyStreamListener listener)
  123.             throws CopyStreamException {
  124.         int numChars;
  125.         long total = 0;
  126.         final char[] buffer = new char[bufferSize > 0 ? bufferSize : DEFAULT_COPY_BUFFER_SIZE];

  127.         try {
  128.             while ((numChars = source.read(buffer)) != NetConstants.EOS) {
  129.                 // Technically, some read(char[]) methods may return 0, and we cannot
  130.                 // accept that as an indication of EOF.
  131.                 if (numChars == 0) {
  132.                     final int singleChar = source.read();
  133.                     if (singleChar < 0) {
  134.                         break;
  135.                     }
  136.                     dest.write(singleChar);
  137.                     dest.flush();
  138.                     ++total;
  139.                     if (listener != null) {
  140.                         listener.bytesTransferred(total, 1, streamSize);
  141.                     }
  142.                     continue;
  143.                 }

  144.                 dest.write(buffer, 0, numChars);
  145.                 dest.flush();
  146.                 total += numChars;
  147.                 if (listener != null) {
  148.                     listener.bytesTransferred(total, numChars, streamSize);
  149.                 }
  150.             }
  151.         } catch (final IOException e) {
  152.             throw new CopyStreamException("IOException caught while copying.", total, e);
  153.         }

  154.         return total;
  155.     }

  156.     /**
  157.      * Same as <code>copyStream(source, dest, DEFAULT_COPY_BUFFER_SIZE);</code>
  158.      *
  159.      * @param source where to copy from
  160.      * @param dest   where to copy to
  161.      * @return number of bytes copied
  162.      * @throws CopyStreamException on error
  163.      */
  164.     public static long copyStream(final InputStream source, final OutputStream dest) throws CopyStreamException {
  165.         return copyStream(source, dest, DEFAULT_COPY_BUFFER_SIZE);
  166.     }

  167.     /**
  168.      * 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
  169.      * 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
  170.      * read/written is returned.
  171.      *
  172.      * @param source     The source InputStream.
  173.      * @param dest       The destination OutputStream.
  174.      * @param bufferSize The number of bytes to buffer during the copy. A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}.
  175.      * @return The number of bytes read/written in the copy operation.
  176.      * @throws CopyStreamException If an error occurs while reading from the source or writing to the destination. The CopyStreamException will contain the
  177.      *                             number of bytes confirmed to have been transferred before an IOException occurred, and it will also contain the IOException
  178.      *                             that caused the error. These values can be retrieved with the CopyStreamException getTotalBytesTransferred() and
  179.      *                             getIOException() methods.
  180.      */
  181.     public static long copyStream(final InputStream source, final OutputStream dest, final int bufferSize) throws CopyStreamException {
  182.         return copyStream(source, dest, bufferSize, CopyStreamEvent.UNKNOWN_STREAM_SIZE, null);
  183.     }

  184.     /**
  185.      * 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
  186.      * 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
  187.      * you should use a CopyStreamAdapter as the listener and register the additional listeners with the CopyStreamAdapter.
  188.      * <p>
  189.      * 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
  190.      * yourself outside the method call. The number of bytes read/written is returned.
  191.      *
  192.      * @param source     The source InputStream.
  193.      * @param dest       The destination OutputStream.
  194.      * @param bufferSize The number of bytes to buffer during the copy. A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}.
  195.      * @param streamSize The number of bytes in the stream being copied. Should be set to CopyStreamEvent.UNKNOWN_STREAM_SIZE if unknown. Not currently used
  196.      *                   (though it is passed to {@link CopyStreamListener#bytesTransferred(long, int, long)}
  197.      * @param listener   The CopyStreamListener to notify of progress. If this parameter is null, notification is not attempted.
  198.      * @return number of bytes read/written
  199.      * @throws CopyStreamException If an error occurs while reading from the source or writing to the destination. The CopyStreamException will contain the
  200.      *                             number of bytes confirmed to have been transferred before an IOException occurred, and it will also contain the IOException
  201.      *                             that caused the error. These values can be retrieved with the CopyStreamException getTotalBytesTransferred() and
  202.      *                             getIOException() methods.
  203.      */
  204.     public static long copyStream(final InputStream source, final OutputStream dest, final int bufferSize, final long streamSize,
  205.             final CopyStreamListener listener) throws CopyStreamException {
  206.         return copyStream(source, dest, bufferSize, streamSize, listener, true);
  207.     }

  208.     /**
  209.      * 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
  210.      * 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
  211.      * you should use a CopyStreamAdapter as the listener and register the additional listeners with the CopyStreamAdapter.
  212.      * <p>
  213.      * 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
  214.      * yourself outside the method call. The number of bytes read/written is returned.
  215.      *
  216.      * @param source     The source InputStream.
  217.      * @param dest       The destination OutputStream.
  218.      * @param bufferSize The number of bytes to buffer during the copy. A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}.
  219.      * @param streamSize The number of bytes in the stream being copied. Should be set to CopyStreamEvent.UNKNOWN_STREAM_SIZE if unknown. Not currently used
  220.      *                   (though it is passed to {@link CopyStreamListener#bytesTransferred(long, int, long)}
  221.      * @param listener   The CopyStreamListener to notify of progress. If this parameter is null, notification is not attempted.
  222.      * @param flush      Whether to flush the output stream after every write. This is necessary for interactive sessions that rely on buffered streams. If you
  223.      *                   don't flush, the data will stay in the stream buffer.
  224.      * @return number of bytes read/written
  225.      * @throws CopyStreamException If an error occurs while reading from the source or writing to the destination. The CopyStreamException will contain the
  226.      *                             number of bytes confirmed to have been transferred before an IOException occurred, and it will also contain the IOException
  227.      *                             that caused the error. These values can be retrieved with the CopyStreamException getTotalBytesTransferred() and
  228.      *                             getIOException() methods.
  229.      */
  230.     public static long copyStream(final InputStream source, final OutputStream dest, final int bufferSize, final long streamSize,
  231.             final CopyStreamListener listener, final boolean flush) throws CopyStreamException {
  232.         int numBytes;
  233.         long total = 0;
  234.         final byte[] buffer = new byte[bufferSize > 0 ? bufferSize : DEFAULT_COPY_BUFFER_SIZE];

  235.         try {
  236.             while ((numBytes = source.read(buffer)) != NetConstants.EOS) {
  237.                 // Technically, some read(byte[]) methods may return 0, and we cannot
  238.                 // accept that as an indication of EOF.

  239.                 if (numBytes == 0) {
  240.                     final int singleByte = source.read();
  241.                     if (singleByte < 0) {
  242.                         break;
  243.                     }
  244.                     dest.write(singleByte);
  245.                     if (flush) {
  246.                         dest.flush();
  247.                     }
  248.                     ++total;
  249.                     if (listener != null) {
  250.                         listener.bytesTransferred(total, 1, streamSize);
  251.                     }
  252.                     continue;
  253.                 }

  254.                 dest.write(buffer, 0, numBytes);
  255.                 if (flush) {
  256.                     dest.flush();
  257.                 }
  258.                 total += numBytes;
  259.                 if (listener != null) {
  260.                     listener.bytesTransferred(total, numBytes, streamSize);
  261.                 }
  262.             }
  263.         } catch (final IOException e) {
  264.             throw new CopyStreamException("IOException caught while copying.", total, e);
  265.         }

  266.         return total;
  267.     }

  268.     /**
  269.      * Creates a new PrintWriter using the default encoding.
  270.      *
  271.      * @param printStream the target PrintStream.
  272.      * @return a new PrintWriter.
  273.      * @since 3.11.0
  274.      */
  275.     public static PrintWriter newPrintWriter(final PrintStream printStream) {
  276.         return new PrintWriter(new OutputStreamWriter(printStream, Charset.defaultCharset()));
  277.     }

  278.     /** Cannot be instantiated. */
  279.     private Util() {
  280.     }
  281. }