org.xnio.channels.FramedMessageChannel Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xnio-api Show documentation
Show all versions of xnio-api Show documentation
The API JAR of the XNIO project
/*
* JBoss, Home of Professional Open Source
*
* Copyright 2013 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 static org.xnio._private.Messages.msg;
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.IoUtils;
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
*
* @deprecated This class is deprecated; use conduits instead.
*/
@SuppressWarnings("unused")
@Deprecated
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 final Object readLock = new Object();
private final Object writeLock = new Object();
/**
* 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} */
public int receive(final ByteBuffer buffer) throws IOException {
synchronized (readLock) {
if (isReadShutDown()) {
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();
}
log.tracef("Did not read a length");
clearReadReady();
// 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 msg.recvInvalidMsgLength(length);
}
if (receiveBuffer.remaining() < length) {
if (res == -1) {
receiveBuffer.clear();
} else {
Buffers.unget(receiveBuffer, 4);
receiveBuffer.compact();
}
log.tracef("Did not read enough bytes for a full message");
clearReadReady();
// must be <= 0
return res;
}
if (buffer.hasRemaining()) {
log.tracef("Copying message from %s into %s", receiveBuffer, buffer);
Buffers.copy(buffer, Buffers.slice(receiveBuffer, length));
} else {
log.tracef("Not copying message from %s into full buffer %s", receiveBuffer, buffer);
Buffers.skip(receiveBuffer, length);
}
// move on to next message
receiveBuffer.compact();
return length;
} finally {
if (res != -1) {
if (receiveBuffer.position() >= 4 && receiveBuffer.position() >= 4 + receiveBuffer.getInt(0)) {
// there's another packet ready to go
setReadReady();
}
}
}
}
}
/** {@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 (readLock) {
if (isReadShutDown()) {
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();
}
log.tracef("Did not read a length");
clearReadReady();
return res;
}
receiveBuffer.flip();
try {
final int length = receiveBuffer.getInt();
if (length < 0 || length > receiveBuffer.capacity() - 4) {
Buffers.unget(receiveBuffer, 4);
throw msg.recvInvalidMsgLength(length);
}
if (receiveBuffer.remaining() < length) {
if (res == -1) {
receiveBuffer.clear();
} else {
Buffers.unget(receiveBuffer, 4);
receiveBuffer.compact();
}
log.tracef("Did not read enough bytes for a full message");
clearReadReady();
// must be <= 0
return res;
}
if (Buffers.hasRemaining(buffers)) {
log.tracef("Copying message from %s into multiple buffers", receiveBuffer);
Buffers.copy(buffers, offs, len, Buffers.slice(receiveBuffer, length));
} else {
log.tracef("Not copying message from %s into multiple full buffers", receiveBuffer);
Buffers.skip(receiveBuffer, length);
}
// move on to next message
receiveBuffer.compact();
return length;
} finally {
if (res != -1) {
if (receiveBuffer.position() >= 4 && receiveBuffer.position() >= 4 + receiveBuffer.getInt(0)) {
// there's another packet ready to go
setReadReady();
}
}
}
}
}
protected void shutdownReadsAction(final boolean writeComplete) throws IOException {
synchronized (readLock) {
log.tracef("Shutting down reads on %s", this);
try {
receiveBuffer.getResource().clear();
} catch (Throwable t) {
}
try {
receiveBuffer.free();
} catch (Throwable t) {
}
}
channel.shutdownReads();
}
/** {@inheritDoc} */
public boolean send(final ByteBuffer buffer) throws IOException {
synchronized (writeLock) {
if (isWriteShutDown()) {
throw msg.writeShutDown();
}
if (!buffer.hasRemaining()) {
return true;
}
final ByteBuffer transmitBuffer = this.transmitBuffer.getResource();
final int remaining = buffer.remaining();
if (remaining > transmitBuffer.capacity() - 4) {
throw msg.txMsgTooLarge();
}
log.tracef("Accepting %s into %s", buffer, transmitBuffer);
if (transmitBuffer.remaining() < 4 + remaining && ! doFlushBuffer()) {
log.tracef("Insufficient room to accept %s into %s", buffer, transmitBuffer);
return false;
}
transmitBuffer.putInt(remaining);
transmitBuffer.put(buffer);
log.tracef("Accepted a message into %s", transmitBuffer);
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 (writeLock) {
if (isWriteShutDown()) {
throw msg.writeShutDown();
}
if (!Buffers.hasRemaining(buffers, offs, len)) {
return true;
}
final ByteBuffer transmitBuffer = this.transmitBuffer.getResource();
final long remaining = Buffers.remaining(buffers, offs, len);
if (remaining > transmitBuffer.capacity() - 4L) {
throw msg.txMsgTooLarge();
}
log.tracef("Accepting multiple buffers into %s", transmitBuffer);
if (transmitBuffer.remaining() < 4 + remaining && ! doFlushBuffer()) {
log.tracef("Insufficient room to accept multiple buffers into %s", transmitBuffer);
return false;
}
transmitBuffer.putInt((int) remaining);
Buffers.copy(transmitBuffer, buffers, offs, len);
log.tracef("Accepted a message into %s", transmitBuffer);
return true;
}
}
@Override
public boolean sendFinal(ByteBuffer buffer) throws IOException {
if(send(buffer)) {
shutdownWrites();
return true;
}
return false;
}
@Override
public boolean sendFinal(ByteBuffer[] buffers) throws IOException {
if(send(buffers)) {
shutdownWrites();
return true;
}
return false;
}
@Override
public boolean sendFinal(ByteBuffer[] buffers, int offs, int len) throws IOException {
if(send(buffers, offs, len)) {
shutdownWrites();
return true;
}
return false;
}
protected boolean flushAction(final boolean shutDown) throws IOException {
synchronized (writeLock) {
return (doFlushBuffer()) && channel.flush();
}
}
protected void shutdownWritesComplete(final boolean readShutDown) throws IOException {
synchronized (writeLock) {
log.tracef("Finished shutting down writes on %s", this);
try {
transmitBuffer.free();
} catch (Throwable t) {}
}
channel.shutdownWrites();
}
private boolean doFlushBuffer() throws IOException {
assert Thread.holdsLock(writeLock);
final ByteBuffer buffer = transmitBuffer.getResource();
buffer.flip();
try {
while (buffer.hasRemaining()) {
final int res = channel.write(buffer);
if (res == 0) {
log.tracef("Did not fully flush %s", this);
return false;
}
}
log.tracef("Fully flushed %s", this);
return true;
} finally {
buffer.compact();
}
}
private boolean doFlush() throws IOException {
return doFlushBuffer() && channel.flush();
}
protected void closeAction(final boolean readShutDown, final boolean writeShutDown) throws IOException {
boolean error = false;
if (! writeShutDown) {
synchronized (writeLock) {
try {
if (! doFlush()) error = true;
} catch (Throwable t) {
error = true;
}
try {
transmitBuffer.free();
} catch (Throwable t) {
}
}
}
if (! readShutDown) {
synchronized (readLock) {
try {
receiveBuffer.free();
} catch (Throwable t) {
}
}
}
try {
if (error) throw msg.unflushedData();
channel.close();
} finally {
IoUtils.safeClose(channel);
}
}
/** {@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;
}
}