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    package org.apache.commons.io.output;
018    
019    import java.io.IOException;
020    import java.io.OutputStream;
021    import java.io.Serializable;
022    import java.util.UUID;
023    
024    import org.apache.commons.io.TaggedIOException;
025    
026    /**
027     * An output stream decorator that tags potential exceptions so that the
028     * stream that caused the exception can easily be identified. This is
029     * done by using the {@link TaggedIOException} class to wrap all thrown
030     * {@link IOException}s. See below for an example of using this class.
031     * <pre>
032     * TaggedOutputStream stream = new TaggedOutputStream(...);
033     * try {
034     *     // Processing that may throw an IOException either from this stream
035     *     // or from some other IO activity like temporary files, etc.
036     *     writeToStream(stream);
037     * } catch (IOException e) {
038     *     if (stream.isCauseOf(e)) {
039     *         // The exception was caused by this stream.
040     *         // Use e.getCause() to get the original exception.
041     *     } else {
042     *         // The exception was caused by something else.
043     *     }
044     * }
045     * </pre>
046     * <p>
047     * Alternatively, the {@link #throwIfCauseOf(Exception)} method can be
048     * used to let higher levels of code handle the exception caused by this
049     * stream while other processing errors are being taken care of at this
050     * lower level.
051     * <pre>
052     * TaggedOutputStream stream = new TaggedOutputStream(...);
053     * try {
054     *     writeToStream(stream);
055     * } catch (IOException e) {
056     *     stream.throwIfCauseOf(e);
057     *     // ... or process the exception that was caused by something else
058     * }
059     * </pre>
060     *
061     * @see TaggedIOException
062     * @since 2.0
063     */
064    public class TaggedOutputStream extends ProxyOutputStream {
065    
066        /**
067         * The unique tag associated with exceptions from stream.
068         */
069        private final Serializable tag = UUID.randomUUID();
070    
071        /**
072         * Creates a tagging decorator for the given output stream.
073         *
074         * @param proxy output stream to be decorated
075         */
076        public TaggedOutputStream(OutputStream proxy) {
077            super(proxy);
078        }
079    
080        /**
081         * Tests if the given exception was caused by this stream.
082         *
083         * @param exception an exception
084         * @return {@code true} if the exception was thrown by this stream,
085         *         {@code false} otherwise
086         */
087        public boolean isCauseOf(Exception exception) {
088            return TaggedIOException.isTaggedWith(exception, tag);
089        }
090    
091        /**
092         * Re-throws the original exception thrown by this stream. This method
093         * first checks whether the given exception is a {@link TaggedIOException}
094         * wrapper created by this decorator, and then unwraps and throws the
095         * original wrapped exception. Returns normally if the exception was
096         * not thrown by this stream.
097         *
098         * @param exception an exception
099         * @throws IOException original exception, if any, thrown by this stream
100         */
101        public void throwIfCauseOf(Exception exception) throws IOException {
102            TaggedIOException.throwCauseIfTaggedWith(exception, tag);
103        }
104    
105        /**
106         * Tags any IOExceptions thrown, wrapping and re-throwing.
107         *
108         * @param e The IOException thrown
109         * @throws IOException if an I/O error occurs
110         */
111        @Override
112        protected void handleIOException(IOException e) throws IOException {
113            throw new TaggedIOException(e, tag);
114        }
115    
116    }