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 */ 017package org.apache.commons.io.output; 018 019import java.io.IOException; 020import java.io.OutputStream; 021import java.io.Serializable; 022import java.util.UUID; 023 024import 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 * </p> 052 * <pre> 053 * TaggedOutputStream stream = new TaggedOutputStream(...); 054 * try { 055 * writeToStream(stream); 056 * } catch (IOException e) { 057 * stream.throwIfCauseOf(e); 058 * // ... or process the exception that was caused by something else 059 * } 060 * </pre> 061 * 062 * @see TaggedIOException 063 * @since 2.0 064 */ 065public class TaggedOutputStream extends ProxyOutputStream { 066 067 /** 068 * The unique tag associated with exceptions from stream. 069 */ 070 private final Serializable tag = UUID.randomUUID(); 071 072 /** 073 * Constructs a tagging decorator for the given output stream. 074 * 075 * @param proxy output stream to be decorated 076 */ 077 public TaggedOutputStream(final OutputStream proxy) { 078 super(proxy); 079 } 080 081 /** 082 * Tags any IOExceptions thrown, wrapping and re-throwing. 083 * 084 * @param e The IOException thrown 085 * @throws IOException if an I/O error occurs. 086 */ 087 @Override 088 protected void handleIOException(final IOException e) throws IOException { 089 throw new TaggedIOException(e, tag); 090 } 091 092 /** 093 * Tests if the given exception was caused by this stream. 094 * 095 * @param exception an exception 096 * @return {@code true} if the exception was thrown by this stream, 097 * {@code false} otherwise 098 */ 099 public boolean isCauseOf(final Exception exception) { 100 return TaggedIOException.isTaggedWith(exception, tag); 101 } 102 103 /** 104 * Re-throws the original exception thrown by this stream. This method 105 * first checks whether the given exception is a {@link TaggedIOException} 106 * wrapper created by this decorator, and then unwraps and throws the 107 * original wrapped exception. Returns normally if the exception was 108 * not thrown by this stream. 109 * 110 * @param exception an exception 111 * @throws IOException original exception, if any, thrown by this stream 112 */ 113 public void throwIfCauseOf(final Exception exception) throws IOException { 114 TaggedIOException.throwCauseIfTaggedWith(exception, tag); 115 } 116 117}