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

org.xnio.streams.BufferPipeOutputStream Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

There is a newer version: 34.0.0.Final
Show newest version
/*
 * JBoss, Home of Professional Open Source
 *
 * Copyright 2010 Red Hat, Inc. and/or its affiliates.
 *
 * 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.xnio.streams;

import java.io.Flushable;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;

import org.xnio.BrokenPipeException;
import org.xnio.Buffers;
import org.xnio.Pooled;

/**
 * An {@code OutputStream} implementation which writes out {@code ByteBuffer}s to a consumer.
 */
public class BufferPipeOutputStream extends OutputStream {

    // internal buffer
    private Pooled buffer;
    // indicates this stream is closed
    private boolean closed;

    private final BufferWriter bufferWriterTask;

    /**
     * Construct a new instance.  The internal buffers will have a capacity of {@code bufferSize}.  The
     * given {@code bufferWriterTask} will be called to send buffers, flush the output stream, and handle the
     * end-of-file condition.
     *
     * @param bufferWriterTask the writer task
     * @throws IOException if an error occurs while initializing the stream
     */
    public BufferPipeOutputStream(final BufferWriter bufferWriterTask) throws IOException {
        this.bufferWriterTask = bufferWriterTask;
        synchronized (this) {
            buffer = bufferWriterTask.getBuffer(true);
        }
    }

    private static IOException closed() {
        return new IOException("Stream is closed");
    }

    private void checkClosed() throws IOException {
        assert Thread.holdsLock(this);
        if (closed) {
            throw closed();
        }
    }

    private Pooled getBuffer() throws IOException {
        assert Thread.holdsLock(this);
        final Pooled buffer = this.buffer;
        if (buffer != null && buffer.getResource().hasRemaining()) {
            return buffer;
        } else {
            if (buffer != null) send(false);
            return this.buffer = bufferWriterTask.getBuffer(false);
        }
    }

    /** {@inheritDoc} */
    public void write(final int b) throws IOException {
        synchronized (this) {
            checkClosed();
            getBuffer().getResource().put((byte) b);
        }
    }

    /** {@inheritDoc} */
    public void write(final byte[] b, int off, int len) throws IOException {
        synchronized (this) {
            checkClosed();
            while (len > 0) {
                final ByteBuffer buffer = getBuffer().getResource();
                final int cnt = Math.min(len, buffer.remaining());
                buffer.put(b, off, cnt);
                len -= cnt;
                off += cnt;
            }
        }
    }

    // call with lock held
    private void send(boolean eof) throws IOException {
        assert Thread.holdsLock(this);
        assert !closed;
        final Pooled pooledBuffer = buffer;
        final ByteBuffer buffer = pooledBuffer == null ? null : pooledBuffer.getResource();
        this.buffer =  null;
        if (buffer != null && buffer.position() > 0) {
            buffer.flip();
            send(pooledBuffer, eof);
        } else if (eof) {
            Pooled pooledBuffer1 = getBuffer();
            final ByteBuffer buffer1 = pooledBuffer1.getResource();
            buffer1.flip();
            send(pooledBuffer1, eof);
        }
    }

    private void send(Pooled buffer, boolean eof) throws IOException {
        assert Thread.holdsLock(this);
        try {
            bufferWriterTask.accept(buffer, eof);
        } catch (IOException e) {
            this.closed = true;
            throw e;
        }
    }

    /** {@inheritDoc} */
    public void flush() throws IOException {
        flush(false);
    }

    private void flush(boolean eof) throws IOException {
        synchronized (this) {
            if (closed) {
                return;
            }
            send(eof);
            try {
                bufferWriterTask.flush();
            } catch (IOException e) {
                closed = true;
                buffer = null;
                throw e;
            }
        }
    }

    /** {@inheritDoc} */
    public void close() throws IOException {
        synchronized (this) {
            if (closed) {
                return;
            }
            try {
                flush(true);
            } finally {
                closed = true;
            }
        }
    }

    /**
     * Break the pipe and return any filling pooled buffer.  Sets the stream to an EOF condition.  Callers to this
     * method should ensure that any threads blocked on {@link BufferWriter#accept(org.xnio.Pooled, boolean)} are
     * unblocked, preferably with a {@link BrokenPipeException}.
     *
     * @return the current pooled buffer, or {@code null} if none was pending
     */
    public Pooled breakPipe() {
        synchronized (this) {
            if (closed) {
                return null;
            }
            closed = true;
            try {
                return buffer;
            } finally {
                buffer = null;
            }
        }
    }

    /**
     * A buffer writer for an {@link BufferPipeOutputStream}.
     */
    public interface BufferWriter extends Flushable {

        /**
         * Get a new buffer to be filled.  The new buffer may, for example, include a prepended header.  This method
         * may block until a buffer is available or until some other condition, such as flow control, is met.
         *
         * @param firstBuffer {@code true} if this is the first buffer in the stream, {@code false} otherwise
         * @return the new buffer
         * @throws IOException if an I/O error occurs
         */
        Pooled getBuffer(boolean firstBuffer) throws IOException;

        /**
         * Accept a buffer.  If this is the last buffer that will be sent, the {@code eof} flag will be set to {@code true}.
         * This method should block until the entire buffer is consumed, or an error occurs.  This method may also block
         * until some other condition, such as flow control, is met.
         *
         * @param pooledBuffer the buffer to send
         * @param eof {@code true} if this is the last buffer which will be sent
         * @throws IOException if an I/O error occurs
         */
        void accept(Pooled pooledBuffer, boolean eof) throws IOException;

        /**
         * Flushes this stream by writing any buffered output to the underlying stream.  This method should block until
         * the data is fully flushed.  This method may also block until some other condition, such as flow control, is
         * met.
         *
         * @throws IOException If an I/O error occurs
         */
        void flush() throws IOException;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy