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

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

There is a newer version: 3.8.16.Final
Show newest version
/*
 * JBoss, Home of Professional Open Source
 * Copyright 2011, JBoss Inc., and individual contributors as indicated
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This 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 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software 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 software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.xnio.channels;

import java.io.EOFException;
import java.io.IOException;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import org.jboss.logging.Logger;
import org.xnio.Buffers;
import org.xnio.Pooled;

/**
 * A connected message channel providing a SASL-style framing layer over a stream channel where each message is prepended
 * by a four-byte length field.
 *
 * @author David M. Lloyd
 */
public class FramedMessageChannel extends TranslatingSuspendableChannel implements ConnectedMessageChannel {

    private static final Logger log = Logger.getLogger("org.xnio.channels.framed");

    private final Pooled receiveBuffer;
    private final Pooled transmitBuffer;
    private boolean readsDone;
    private boolean writesDone;

    /**
     * Construct a new instance.
     *
     * @param channel the channel to wrap
     * @param receiveBuffer the receive buffer (should be direct)
     * @param transmitBuffer the send buffer (should be direct)
     */
    public FramedMessageChannel(final ConnectedStreamChannel channel, final ByteBuffer receiveBuffer, final ByteBuffer transmitBuffer) {
        super(channel);
        this.receiveBuffer = Buffers.pooledWrapper(receiveBuffer);
        this.transmitBuffer = Buffers.pooledWrapper(transmitBuffer);
        log.tracef("Created new framed message channel around %s, receive buffer %s, transmit buffer %s", channel, receiveBuffer, transmitBuffer);
    }

    /**
     * Construct a new instance.
     *
     * @param channel the channel to wrap
     * @param receiveBuffer the receive buffer (should be direct)
     * @param transmitBuffer the send buffer (should be direct)
     */
    public FramedMessageChannel(final ConnectedStreamChannel channel, final Pooled receiveBuffer, final Pooled transmitBuffer) {
        super(channel);
        this.receiveBuffer = receiveBuffer;
        this.transmitBuffer = transmitBuffer;
        log.tracef("Created new framed message channel around %s, receive buffer %s, transmit buffer %s", channel, receiveBuffer, transmitBuffer);
    }

    /** {@inheritDoc} */
    protected Readiness isReadable() {
        if (readsDone) return Readiness.NEVER;
        final ByteBuffer buffer = receiveBuffer.getResource();
        final int size = buffer.position();
        return size >= 4 && size >= buffer.getInt(0) + 4 ? Readiness.ALWAYS : Readiness.OKAY;
    }

    /** {@inheritDoc} */
    protected Object getReadLock() {
        return receiveBuffer;
    }

    /** {@inheritDoc} */
    protected Readiness isWritable() {
        return writesDone ? Readiness.NEVER : Readiness.OKAY;
    }

    /** {@inheritDoc} */
    protected Object getWriteLock() {
        return transmitBuffer;
    }

    /** {@inheritDoc} */
    public int receive(final ByteBuffer buffer) throws IOException {
        synchronized (receiveBuffer) {
            if (readsDone) {
                return -1;
            }
            final ByteBuffer receiveBuffer = this.receiveBuffer.getResource();
            int res;
            final ConnectedStreamChannel channel = (ConnectedStreamChannel) this.channel;
            do {
                res = channel.read(receiveBuffer);
            } while (res > 0);
            if (receiveBuffer.position() < 4) {
                if (res == -1) {
                    receiveBuffer.clear();
                }
                // must be <= 0
                return res;
            }
            receiveBuffer.flip();
            try {
                final int length = receiveBuffer.getInt();
                if (length < 0 || length > receiveBuffer.capacity() - 4) {
                    Buffers.unget(receiveBuffer, 4);
                    throw new IOException("Received an invalid message length of " + length);
                }
                if (receiveBuffer.remaining() < length) {
                    if (res == -1) {
                        receiveBuffer.clear();
                    } else {
                        Buffers.unget(receiveBuffer, 4);
                    }
                    // must be <= 0
                    return res;
                }
                if (buffer.hasRemaining()) {
                    return Buffers.copy(buffer, Buffers.slice(receiveBuffer, length));
                } else {
                    Buffers.skip(receiveBuffer, length);
                    return 0;
                }
            } finally {
                receiveBuffer.compact();
            }
        }
    }

    /** {@inheritDoc} */
    public long receive(final ByteBuffer[] buffers) throws IOException {
        return receive(buffers, 0, buffers.length);
    }

    /** {@inheritDoc} */
    public long receive(final ByteBuffer[] buffers, final int offs, final int len) throws IOException {
        synchronized (receiveBuffer) {
            if (readsDone) {
                return -1;
            }
            final ByteBuffer receiveBuffer = this.receiveBuffer.getResource();
            int res;
            final ConnectedStreamChannel channel = (ConnectedStreamChannel) this.channel;
            do {
                res = channel.read(receiveBuffer);
            } while (res > 0);
            if (receiveBuffer.remaining() < 4) {
                if (res == -1) {
                    receiveBuffer.clear();
                }
                return res;
            }
            final int length = receiveBuffer.getInt();
            if (receiveBuffer.remaining() < length) {
                if (res == -1) {
                    receiveBuffer.clear();
                } else {
                    Buffers.unget(receiveBuffer, 4);
                }
                return res;
            }
            receiveBuffer.flip();
            try {
                if (Buffers.hasRemaining(buffers)) {
                    return Buffers.copy(buffers, offs, len, Buffers.slice(receiveBuffer, length));
                } else {
                    Buffers.skip(receiveBuffer, length);
                    return 0;
                }
            } finally {
                receiveBuffer.compact();
            }
        }
    }

    /** {@inheritDoc} */
    public void shutdownReads() throws IOException {
        synchronized (receiveBuffer) {
            if (readsDone) return;
            final ByteBuffer receiveBuffer = this.receiveBuffer.getResource();
            receiveBuffer.clear();
        }
        super.shutdownReads();
    }


    /** {@inheritDoc} */
    public boolean send(final ByteBuffer buffer) throws IOException {
        synchronized (transmitBuffer) {
            if (writesDone) {
                throw new EOFException("Writes have been shut down");
            }
            final ByteBuffer transmitBuffer = this.transmitBuffer.getResource();
            if (buffer.remaining() > transmitBuffer.capacity() - 4) {
                throw new IOException("Transmitted message is too large");
            }
            final int remaining = buffer.remaining();
            if (transmitBuffer.remaining() < 4 + remaining && ! doFlush()) {
                return false;
            }
            // todo check max transmit size
            transmitBuffer.putInt(remaining);
            transmitBuffer.put(buffer);
            doFlush();
            return true;
        }
    }

    /** {@inheritDoc} */
    public boolean send(final ByteBuffer[] buffers) throws IOException {
        return send(buffers, 0, buffers.length);
    }

    /** {@inheritDoc} */
    public boolean send(final ByteBuffer[] buffers, final int offs, final int len) throws IOException {
        synchronized (transmitBuffer) {
            if (writesDone) {
                throw new EOFException("Writes have been shut down");
            }
            final ByteBuffer transmitBuffer = this.transmitBuffer.getResource();
            if (Buffers.remaining(buffers) > transmitBuffer.capacity() - 4L) {
                throw new IOException("Transmitted message is too large");
            }
            final long remaining = Buffers.remaining(buffers);
            if (transmitBuffer.remaining() < 4 + remaining && ! doFlush()) {
                return false;
            }
            // todo check max transmit size
            transmitBuffer.putInt((int) remaining);
            Buffers.copy(transmitBuffer, buffers, offs, len);
            doFlush();
            return true;
        }
    }

    /** {@inheritDoc} */
    public boolean shutdownWrites() throws IOException {
        synchronized (transmitBuffer) {
            return writesDone || doFlush() && super.shutdownWrites() && (writesDone = true);
        }
    }

    /** {@inheritDoc} */
    public boolean flush() throws IOException {
        synchronized (transmitBuffer) {
            return writesDone || doFlush();
        }
    }

    private boolean doFlush() throws IOException {
        final ByteBuffer buffer = transmitBuffer.getResource();
        buffer.flip();
        try {
            while (buffer.hasRemaining()) {
                final int res = channel.write(buffer);
                if (res == 0) {
                    return false;
                }
            }
            return channel.flush();
        } finally {
            buffer.compact();
        }
    }

    /** {@inheritDoc} */
    public void close() throws IOException {
        synchronized (transmitBuffer) {
            if (! writesDone) {
                writesDone = true;
                transmitBuffer.getResource().clear();
                transmitBuffer.free();
            }
        }
        synchronized (receiveBuffer) {
            if (! readsDone) {
                readsDone = true;
                receiveBuffer.getResource().clear();
                receiveBuffer.free();
            }
        }
        super.close();
    }

    /** {@inheritDoc} */
    public SocketAddress getPeerAddress() {
        return channel.getPeerAddress();
    }

    /** {@inheritDoc} */
    public  A getPeerAddress(final Class type) {
        return channel.getPeerAddress(type);
    }

    /** {@inheritDoc} */
    public SocketAddress getLocalAddress() {
        return channel.getLocalAddress();
    }

    /** {@inheritDoc} */
    public  A getLocalAddress(final Class type) {
        return channel.getLocalAddress(type);
    }

    /**
     * Get the underlying channel.
     *
     * @return the underlying channel
     */
    public ConnectedStreamChannel getChannel() {
        return channel;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy