/* * Copyright (C) 2014 Indeed Inc. * * 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 com.indeed.imhotep.io; import org.apache.log4j.Logger; import java.io.Closeable; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; /** * Class for piping bytes from one thread to another on top of a circular buffer, while minimizing the number of syncs needed */ public final class CircularIOStream { private static final Logger log = Logger.getLogger(CircularIOStream.class); private volatile boolean inputClosed = false; private volatile boolean outputClosed = false; private final InputStream inputStream; private final OutputStream outputStream; public CircularIOStream(int bufferSize) throws IOException { if (bufferSize <= 0) throw new IllegalArgumentException("bufferSize must be greater than zero"); final CircularInputStream circularInputStream = new CircularInputStream(bufferSize); outputStream = new OutputStream() { public void write(final int b) throws IOException { circularInputStream.write(b); } public void write(final byte[] b, final int off, final int len) throws IOException { circularInputStream.write(b, off, len); } public void close() throws IOException { outputClosed = true; circularInputStream.end(); } }; inputStream = new FilterInputStream(circularInputStream) { public void close() throws IOException { inputClosed = true; super.close(); } }; } public InputStream getInputStream() { return inputStream; } public OutputStream getOutputStream() { return outputStream; } protected void finalize() throws Throwable { if (!inputClosed) { log.error("input was not closed, closing in finalizer"); closeQuietly(inputStream); } if (!outputClosed) { log.error("output was not closed, closing in finalizer"); closeQuietly(outputStream); } } /** * this is very similar to guava's {#link Closeables.closeQuietly()}, except with logging * unlike guava this swallows all Exceptions, not just IOExceptions. Error is still propagated. * @param closeable closeable to close */ private static void closeQuietly(final Closeable closeable) { try { if (null != closeable) { closeable.close(); } } catch (Exception e) { log.error("Exception during cleanup of a Closeable, ignoring", e); } } }