ProxyOutputStream.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.io.output;

  18. import java.io.FilterOutputStream;
  19. import java.io.IOException;
  20. import java.io.OutputStream;

  21. import org.apache.commons.io.IOUtils;
  22. import org.apache.commons.io.build.AbstractStreamBuilder;

  23. /**
  24.  * A Proxy stream which acts as expected, that is it passes the method
  25.  * calls on to the proxied stream and doesn't change which methods are
  26.  * being called. It is an alternative base class to FilterOutputStream
  27.  * to increase reusability.
  28.  * <p>
  29.  * See the protected methods for ways in which a subclass can easily decorate
  30.  * a stream with custom pre-, post- or error processing functionality.
  31.  * </p>
  32.  */
  33. public class ProxyOutputStream extends FilterOutputStream {

  34.     /**
  35.      * Builds instances of {@link ProxyOutputStream}.
  36.      * <p>
  37.      * This class does not provide a convenience static {@code builder()} method so that subclasses can.
  38.      * </p>
  39.      *
  40.      * @since 2.19.0
  41.      */
  42.     public static class Builder extends AbstractStreamBuilder<ProxyOutputStream, Builder> {

  43.         /**
  44.          * Constructs a new builder of {@link ProxyOutputStream}.
  45.          */
  46.         public Builder() {
  47.             // empty
  48.         }

  49.         /**
  50.          * Builds a new {@link ProxyOutputStream}.
  51.          * <p>
  52.          * This builder uses the following aspects:
  53.          * </p>
  54.          * <ul>
  55.          * <li>{@link #getOutputStream()} is the target aspect.</li>
  56.          * </ul>
  57.          *
  58.          * @return a new instance.
  59.          * @throws IllegalStateException         if the {@code origin} is {@code null}.
  60.          * @throws UnsupportedOperationException if the origin cannot be converted to an {@link OutputStream}.
  61.          * @throws IOException                   if an I/O error occurs converting to an {@link OutputStream} using {@link #getOutputStream()}.
  62.          * @see #getOutputStream()
  63.          * @see #getUnchecked()
  64.          */
  65.         @SuppressWarnings("resource") // caller closes
  66.         @Override
  67.         public ProxyOutputStream get() throws IOException {
  68.             return new ProxyOutputStream(getOutputStream());
  69.         }

  70.     }

  71.     /**
  72.      * Constructs a new ProxyOutputStream.
  73.      *
  74.      * @param delegate  the OutputStream to delegate to
  75.      */
  76.     public ProxyOutputStream(final OutputStream delegate) {
  77.         // the delegate is stored in a protected superclass variable named 'out'
  78.         super(delegate);
  79.     }

  80.     /**
  81.      * Invoked by the write methods after the proxied call has returned
  82.      * successfully. The number of bytes written (1 for the
  83.      * {@link #write(int)} method, buffer length for {@link #write(byte[])},
  84.      * etc.) is given as an argument.
  85.      * <p>
  86.      * Subclasses can override this method to add common post-processing
  87.      * functionality without having to override all the write methods.
  88.      * The default implementation does nothing.
  89.      *
  90.      * @param n number of bytes written
  91.      * @throws IOException if the post-processing fails
  92.      * @since 2.0
  93.      */
  94.     @SuppressWarnings("unused") // Possibly thrown from subclasses.
  95.     protected void afterWrite(final int n) throws IOException {
  96.         // noop
  97.     }

  98.     /**
  99.      * Invoked by the write methods before the call is proxied. The number
  100.      * of bytes to be written (1 for the {@link #write(int)} method, buffer
  101.      * length for {@link #write(byte[])}, etc.) is given as an argument.
  102.      * <p>
  103.      * Subclasses can override this method to add common pre-processing
  104.      * functionality without having to override all the write methods.
  105.      * The default implementation does nothing.
  106.      *
  107.      * @param n number of bytes to be written
  108.      * @throws IOException if the pre-processing fails
  109.      * @since 2.0
  110.      */
  111.     @SuppressWarnings("unused") // Possibly thrown from subclasses.
  112.     protected void beforeWrite(final int n) throws IOException {
  113.         // noop
  114.     }

  115.     /**
  116.      * Invokes the delegate's {@code close()} method.
  117.      * @throws IOException if an I/O error occurs.
  118.      */
  119.     @Override
  120.     public void close() throws IOException {
  121.         IOUtils.close(out, this::handleIOException);
  122.     }

  123.     /**
  124.      * Invokes the delegate's {@code flush()} method.
  125.      * @throws IOException if an I/O error occurs.
  126.      */
  127.     @Override
  128.     public void flush() throws IOException {
  129.         try {
  130.             out.flush();
  131.         } catch (final IOException e) {
  132.             handleIOException(e);
  133.         }
  134.     }

  135.     /**
  136.      * Handle any IOExceptions thrown.
  137.      * <p>
  138.      * This method provides a point to implement custom exception
  139.      * handling. The default behavior is to re-throw the exception.
  140.      * @param e The IOException thrown
  141.      * @throws IOException if an I/O error occurs.
  142.      * @since 2.0
  143.      */
  144.     protected void handleIOException(final IOException e) throws IOException {
  145.         throw e;
  146.     }

  147.     /**
  148.      * Sets the underlying output stream.
  149.      *
  150.      * @param out the underlying output stream.
  151.      * @return this instance.
  152.      * @since 2.19.0
  153.      */
  154.     public ProxyOutputStream setReference(final OutputStream out) {
  155.         this.out = out;
  156.         return this;
  157.     }

  158.     /**
  159.      * Unwraps this instance by returning the underlying {@link OutputStream}.
  160.      * <p>
  161.      * Use with caution; useful to query the underlying {@link OutputStream}.
  162.      * </p>
  163.      *
  164.      * @return the underlying {@link OutputStream}.
  165.      */
  166.     OutputStream unwrap() {
  167.         return out;
  168.     }

  169.     /**
  170.      * Invokes the delegate's {@code write(byte[])} method.
  171.      * @param bts the bytes to write
  172.      * @throws IOException if an I/O error occurs.
  173.      */
  174.     @Override
  175.     public void write(final byte[] bts) throws IOException {
  176.         try {
  177.             final int len = IOUtils.length(bts);
  178.             beforeWrite(len);
  179.             out.write(bts);
  180.             afterWrite(len);
  181.         } catch (final IOException e) {
  182.             handleIOException(e);
  183.         }
  184.     }

  185.     /**
  186.      * Invokes the delegate's {@code write(byte[])} method.
  187.      * @param bts the bytes to write
  188.      * @param st The start offset
  189.      * @param end The number of bytes to write
  190.      * @throws IOException if an I/O error occurs.
  191.      */
  192.     @Override
  193.     public void write(final byte[] bts, final int st, final int end) throws IOException {
  194.         try {
  195.             beforeWrite(end);
  196.             out.write(bts, st, end);
  197.             afterWrite(end);
  198.         } catch (final IOException e) {
  199.             handleIOException(e);
  200.         }
  201.     }

  202.     /**
  203.      * Invokes the delegate's {@code write(int)} method.
  204.      * @param idx the byte to write
  205.      * @throws IOException if an I/O error occurs.
  206.      */
  207.     @Override
  208.     public void write(final int idx) throws IOException {
  209.         try {
  210.             beforeWrite(1);
  211.             out.write(idx);
  212.             afterWrite(1);
  213.         } catch (final IOException e) {
  214.             handleIOException(e);
  215.         }
  216.     }

  217. }