/* * This file is part of muCommander, http://www.mucommander.com * Copyright (C) 2002-2016 Maxence Bernard * * muCommander is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * muCommander is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.mucommander.commons.io; import java.io.IOException; import java.io.PipedInputStream; import java.io.PipedOutputStream; /** * SafePipedInputStream is a <code>PipedInputStream</code> that can be safely interrupted when the thread that * pushes data to this stream has encountered an error. This can be done by calling * {@link #setExternalFailure(java.io.IOException)} with an <code>IOException</code> that will be thrown by * read/skip/available methods thereafter. This method ensures that the <code>IOException</code> will be propagated * to any thread that is currently executing a read/skip/available method of this class. * * <p>This class overcomes a limitation of <code>PipedInputStream</code> whose read/skip/available methods do not * throw an <code>IOException</code> when the stream is closed from another thread in the midst of their execution. * </p> * * @author Maxence Bernard */ public class FailSafePipedInputStream extends PipedInputStream { /** An IOException to be thrown by read/skip/available methods */ private volatile IOException failure; public FailSafePipedInputStream() { super(); } public FailSafePipedInputStream(int pipeSize) { super(pipeSize); } public FailSafePipedInputStream(PipedOutputStream src) throws IOException { super(src); } public FailSafePipedInputStream(PipedOutputStream src, int pipeSize) throws IOException { super(src, pipeSize); } /** * Sets an IOException to be subsequently thrown by <code>read</code>, <code>skip</code> and * <code>available</code> methods. This method calls {@link #close()} to have any other thread blocked in a * read/skip/available return immediately and throw the specified exception. * * @param failure the IOException to be thrown by read, skip and available methods, <code>null</code> for none */ public void setExternalFailure(IOException failure) { this.failure = failure; if(failure!=null) { // Close the PipedInputStream to have any other thread blocked in a read/skip/available return immediately try { super.close(); } catch(IOException e) { // Swallow the exception } } } /** * Checks whether an external failure (IOException) has been registered and if has, throws it. * * @throws java.io.IOException if an external failure has been registered */ protected void checkExternalFailure() throws IOException { if(failure!=null) throw failure; } //////////////////////// // Overridden methods // //////////////////////// @Override public synchronized int read() throws IOException { int ret = super.read(); checkExternalFailure(); return ret; } @Override public synchronized int read(byte b[], int off, int len) throws IOException { int ret = super.read(b, off, len); checkExternalFailure(); return ret; } @Override public synchronized int read(byte b[]) throws IOException { int ret = super.read(b); checkExternalFailure(); return ret; } @Override public long skip(long n) throws IOException { long ret = super.skip(n); checkExternalFailure(); return ret; } @Override public synchronized int available() throws IOException { int ret = super.available(); checkExternalFailure(); return ret; } @Override public void close() throws IOException { super.close(); checkExternalFailure(); } }