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

org.xnio.channels.FixedLengthStreamSourceChannel Maven / Gradle / Ivy

There is a newer version: 3.8.16.Final
Show newest version
/*
 * JBoss, Home of Professional Open Source.
 *
 * Copyright 2012 Red Hat, Inc. and/or its affiliates, and individual
 * contributors as indicated by the @author tags.
 *
 * 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.channels;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.concurrent.TimeUnit;
import org.xnio.ChannelListener;
import org.xnio.ChannelListeners;
import org.xnio.Option;
import org.xnio.XnioExecutor;
import org.xnio.XnioIoThread;
import org.xnio.XnioWorker;

import static java.lang.Math.min;
import static org.xnio.Bits.*;
import static org.xnio._private.Messages.msg;

/**
 * A channel which reads data of a fixed length and calls a finish listener.  When the finish listener is called,
 * it should examine the result of {@link #getRemaining()} to see if more bytes were pending when the channel was
 * closed.
 *
 * @author David M. Lloyd
 */
public final class FixedLengthStreamSourceChannel implements StreamSourceChannel, ProtectedWrappedChannel, ReadListenerSettable, CloseListenerSettable {
    private final StreamSourceChannel delegate;
    private final Object guard;

    private final ChannelListener finishListener;
    private ChannelListener readListener;
    private ChannelListener closeListener;

    private int state;
    private long remaining;

    private static final int FLAG_CLOSED = 1 << 0;
    private static final int FLAG_FINISHED = 1 << 1;
    private static final int FLAG_CONFIGURABLE = 1 << 2;
    private static final int FLAG_PASS_CLOSE = 1 << 3;

    /**
     * Construct a new instance.  The given listener is called once all the bytes are read from the stream
     * or the stream is closed.  This listener should cause the remaining data to be drained from the
     * underlying stream via the {@link #drain()} method if the underlying stream is to be reused.
     * 

* Calling this constructor will replace the read listener of the underlying channel. The listener should be * restored from the {@code finishListener} object. The underlying stream should not be closed while this wrapper * stream is active. * * @param delegate the stream source channel to read from * @param contentLength the amount of content to read * @param finishListener the listener to call once the stream is exhausted or closed * @param guard the guard object to use */ public FixedLengthStreamSourceChannel(final StreamSourceChannel delegate, final long contentLength, final ChannelListener finishListener, final Object guard) { this(delegate, contentLength, false, finishListener, guard); } /** * Construct a new instance. The given listener is called once all the bytes are read from the stream * or the stream is closed. This listener should cause the remaining data to be drained from the * underlying stream via the {@link #drain()} method if the underlying stream is to be reused. *

* Calling this constructor will replace the read listener of the underlying channel. The listener should be * restored from the {@code finishListener} object. The underlying stream should not be closed while this wrapper * stream is active. * * @param delegate the stream source channel to read from * @param contentLength the amount of content to read * @param configurable {@code true} to allow options to pass through to the delegate, {@code false} otherwise * @param finishListener the listener to call once the stream is exhausted or closed * @param guard the guard object to use */ public FixedLengthStreamSourceChannel(final StreamSourceChannel delegate, final long contentLength, final boolean configurable, final ChannelListener finishListener, final Object guard) { this(delegate, contentLength, configurable, false, finishListener, guard); } /** * Construct a new instance. The given listener is called once all the bytes are read from the stream * or the stream is closed. This listener should cause the remaining data to be drained from the * underlying stream via the {@link #drain()} method if the underlying stream is to be reused. *

* Calling this constructor will replace the read listener of the underlying channel. The listener should be * restored from the {@code finishListener} object. The underlying stream should not be closed while this wrapper * stream is active. * * @param delegate the stream source channel to read from * @param contentLength the amount of content to read * @param configurable {@code true} to allow options to pass through to the delegate, {@code false} otherwise * @param propagateClose {@code true} to propagate close/shutdown to the delegate, {@code false} otherwise * @param finishListener the listener to call once the stream is exhausted or closed * @param guard the guard object to use */ public FixedLengthStreamSourceChannel(final StreamSourceChannel delegate, final long contentLength, final boolean configurable, final boolean propagateClose, final ChannelListener finishListener, final Object guard) { this.guard = guard; this.finishListener = finishListener; if (contentLength < 0L) { throw msg.parameterOutOfRange("contentLength"); } this.delegate = delegate; remaining = contentLength; delegate.getReadSetter().set(new ChannelListener() { public void handleEvent(final StreamSourceChannel channel) { ChannelListeners.invokeChannelListener(FixedLengthStreamSourceChannel.this, readListener); } }); state = (configurable ? FLAG_CONFIGURABLE : 0) | (propagateClose ? FLAG_PASS_CLOSE : 0); } public void setReadListener(final ChannelListener readListener) { this.readListener = readListener; } public ChannelListener getReadListener() { return readListener; } public void setCloseListener(final ChannelListener closeListener) { this.closeListener = closeListener; } public ChannelListener getCloseListener() { return closeListener; } public ChannelListener.Setter getReadSetter() { return new ReadListenerSettable.Setter(this); } public ChannelListener.Setter getCloseSetter() { return new CloseListenerSettable.Setter(this); } public long transferTo(final long position, final long count, final FileChannel target) throws IOException { final long remaining = this.remaining; if (anyAreSet(state, FLAG_CLOSED | FLAG_FINISHED) || remaining == 0L || count == 0L) { return 0L; } long res = 0L; try { return res = delegate.transferTo(position, min(count, remaining), target); } finally { if (res > 0L) { if ((this.remaining = remaining - res) == 0L) { state |= FLAG_FINISHED; callFinish(); } } } } public long transferTo(final long count, final ByteBuffer throughBuffer, final StreamSinkChannel target) throws IOException { final long remaining = this.remaining; if (anyAreSet(state, FLAG_CLOSED | FLAG_FINISHED) || remaining == 0L) { return -1L; } if (count == 0L) { return 0L; } long res = 0L; try { return res = delegate.transferTo(min(count, remaining), throughBuffer, target); } finally { if (res > 0L) { if ((this.remaining = remaining - res) == 0L) { state |= FLAG_FINISHED; callFinish(); } } } } public long read(final ByteBuffer[] dsts, final int offset, final int length) throws IOException { final long remaining = this.remaining; if (anyAreSet(state, FLAG_CLOSED | FLAG_FINISHED) || remaining == 0L) { return -1L; } if (length == 0) { return 0L; } else if (length == 1) { return read(dsts[offset]); } long res = 0L; try { int lim; // The total amount of buffer space discovered so far. long t = 0L; for (int i = 0; i < length; i ++) { final ByteBuffer buffer = dsts[i + offset]; // Grow the discovered buffer space by the remaining size of the current buffer. // We want to capture the limit so we calculate "remaining" ourselves. t += (lim = buffer.limit()) - buffer.position(); if (t > remaining) { // only read up to this point, and trim the last buffer by the number of extra bytes buffer.limit(lim - (int) (t - remaining)); try { return res = delegate.read(dsts, offset, i + 1); } finally { // restore the original limit buffer.limit(lim); } } } // the total buffer space is less than the remaining count. return res = t == 0L ? 0L : delegate.read(dsts, offset, length); } finally { if (res > 0L) { if ((this.remaining = remaining - res) == 0L) { state |= FLAG_FINISHED; callFinish(); } } } } public long read(final ByteBuffer[] dsts) throws IOException { return read(dsts, 0, dsts.length); } public int read(final ByteBuffer dst) throws IOException { final long remaining = this.remaining; if (anyAreSet(state, FLAG_CLOSED | FLAG_FINISHED) || remaining == 0L) { return -1; } int res = 0; try { final int lim = dst.limit(); final int pos = dst.position(); if (lim - pos > remaining) { dst.limit((int) (remaining - (long) pos)); try { return res = delegate.read(dst); } finally { dst.limit(lim); } } else { return res = delegate.read(dst); } } finally { if (res > 0) { if ((this.remaining = remaining - res) == 0L) { state |= FLAG_FINISHED; callFinish(); } } } } public void suspendReads() { if (allAreClear(state, FLAG_CLOSED | FLAG_FINISHED)) { delegate.suspendReads(); } } public void resumeReads() { if (allAreClear(state, FLAG_CLOSED | FLAG_FINISHED)) { delegate.resumeReads(); } else { delegate.getIoThread().execute(ChannelListeners.getChannelListenerTask(this, readListener)); } } public boolean isReadResumed() { return allAreClear(state, FLAG_CLOSED | FLAG_FINISHED) && delegate.isReadResumed(); } public void wakeupReads() { if (allAreClear(state, FLAG_CLOSED | FLAG_FINISHED)) { delegate.wakeupReads(); } else { delegate.getIoThread().execute(ChannelListeners.getChannelListenerTask(this, readListener)); } } public void shutdownReads() throws IOException { final int state = this.state; if (allAreClear(state, FLAG_CLOSED)) try { this.state = state | FLAG_CLOSED | FLAG_FINISHED; if (allAreSet(state, FLAG_PASS_CLOSE)) { delegate.shutdownReads(); } } finally { if (allAreClear(state, FLAG_FINISHED)) callFinish(); callClosed(); } } public void awaitReadable() throws IOException { if (anyAreSet(state, FLAG_CLOSED | FLAG_FINISHED)) { return; } delegate.awaitReadable(); } public void awaitReadable(final long time, final TimeUnit timeUnit) throws IOException { if (anyAreSet(state, FLAG_CLOSED | FLAG_FINISHED)) { return; } delegate.awaitReadable(time, timeUnit); } @Deprecated public XnioExecutor getReadThread() { return delegate.getReadThread(); } public XnioIoThread getIoThread() { return delegate.getIoThread(); } public XnioWorker getWorker() { return delegate.getWorker(); } public boolean isOpen() { return allAreClear(state, FLAG_CLOSED); } public void close() throws IOException { shutdownReads(); } public boolean supportsOption(final Option option) { return allAreSet(state, FLAG_CONFIGURABLE) && delegate.supportsOption(option); } public T getOption(final Option option) throws IOException { return allAreSet(state, FLAG_CONFIGURABLE) ? delegate.getOption(option) : null; } public T setOption(final Option option, final T value) throws IllegalArgumentException, IOException { return allAreSet(state, FLAG_CONFIGURABLE) ? delegate.setOption(option, value) : null; } public StreamSourceChannel getChannel(final Object guard) { final Object ourGuard = this.guard; if (ourGuard == null || guard == ourGuard) { return delegate; } else { return null; } } /** * Get the number of remaining bytes. * * @return the number of remaining bytes */ public long getRemaining() { return remaining; } private void callFinish() { ChannelListeners.invokeChannelListener(this, finishListener); } private void callClosed() { ChannelListeners.invokeChannelListener(this, closeListener); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy