All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.dataconservancy.pass.deposit.assembler.shared.ExHandingPipedInputStream Maven / Gradle / Ivy

/*
 * Copyright 2018 Johns Hopkins University
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.dataconservancy.pass.deposit.assembler.shared;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;

import static java.lang.Integer.toHexString;
import static java.lang.System.identityHashCode;
import static org.dataconservancy.pass.deposit.assembler.shared.ArchivingPackageStream.STREAMING_IO_LOG;

/**
 * Re-throws the {@code Throwable} set by {@link #setWriterEx(Throwable)} when any {@code public} or {@code protected}
 * method of {@link PipedInputStream} is invoked.
 * 

* Upon invocation of any {@code public} or {@code protected} methods of {@code PipedInputStream}, the presence of a * {@link Throwable} (stored in a member {@code volatile} variable) is checked. If a {@code Throwable} is present, it * indicates that the writing side of the pipe encountered an exception. The writer is executing in a * separate thread, and cannot report exceptions "up the stack" to the caller. Instead, the writer (via a {@link * Thread.UncaughtExceptionHandler}) sets any caught exceptions on the reading side of the pipe, and the * reading side of the pipe will re-throw them to readers if {@link #setWriterEx(Throwable)} is called with a non-{@code * null Throwable}. *

* * @author Elliot Metsger ([email protected]) */ public class ExHandingPipedInputStream extends PipedInputStream { private static final Logger LOG = LoggerFactory.getLogger(ExHandingPipedInputStream.class); /** * If non-null, represents an exception that was thrown on the writing side of the pipe. It should be * re-thrown to callers of {@link PipedInputStream} {@code public} or {@code protected} methods. */ private volatile Throwable writerEx; public ExHandingPipedInputStream(int pipeSize) { super(pipeSize); } @Override public void connect(PipedOutputStream src) throws IOException { handleEx(); super.connect(src); } @Override protected synchronized void receive(int b) throws IOException { handleEx(); super.receive(b); } @Override public synchronized int read() throws IOException { handleEx(); return super.read(); } @Override public synchronized int read(byte[] b, int off, int len) throws IOException { handleEx(); return super.read(b, off, len); } @Override public synchronized int available() throws IOException { handleEx(); return super.available(); } @Override public void close() throws IOException { // Close the stream, regardless of whether or not there is an exception waiting for us STREAMING_IO_LOG.debug(">>>> {}@{} close() invoked: ", this.getClass().getSimpleName(), toHexString(identityHashCode(this)), new Exception("close() invoked")); try { super.close(); } finally { handleEx(); } } /** * Obtain the {@code Throwable} that presumably occurred on the writing side of this pipe. It will be re- * thrown as an {@link IOException} the next time a {@code public} or {@code protected} method of {@link * PipedInputStream} is invoked. * * @return a {@code Throwable} that occurred while writing to the pipe, or {@code null} if no exception has occurred */ public Throwable getWriterEx() { return writerEx; } /** * Set the {@code Throwable} that presumably occurred on the writing side of this pipe. It will be re- * thrown as an {@link IOException} the next time a {@code public} or {@code protected} method of {@link * PipedInputStream} is invoked. * * @param writerEx a {@code Throwable} that occurred while writing to the pipe */ public void setWriterEx(Throwable writerEx) { this.writerEx = writerEx; } /** * Checks for a non-null {@link #writerEx}, and re-throws it as an {@link IOException}. * * @throws IOException the wrapped {@link #writerEx} */ private void handleEx() throws IOException { if (writerEx == null) { return; } LOG.error("The writing side of this PipedInputStream encountered an exception: {}", writerEx.getMessage(), writerEx); throw new IOException("The writing side of this PipedInputStream encountered an exception: " + writerEx.getMessage(), writerEx); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy