
org.jboss.remoting3.remote.RemoteConnectionChannel Maven / Gradle / Ivy
The 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.jboss.remoting3.remote;
import static org.jboss.remoting3.remote.RemoteLogger.log;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import org.jboss.remoting3.Attachments;
import org.jboss.remoting3.Channel;
import org.jboss.remoting3.ChannelBusyException;
import org.jboss.remoting3.Connection;
import org.jboss.remoting3.MessageCancelledException;
import org.jboss.remoting3.MessageOutputStream;
import org.jboss.remoting3.NotOpenException;
import org.jboss.remoting3.RemotingOptions;
import org.jboss.remoting3.spi.AbstractHandleableCloseable;
import org.jboss.remoting3.spi.ConnectionHandlerContext;
import org.xnio.Bits;
import org.xnio.Option;
import org.xnio.Pooled;
import org.xnio.channels.Channels;
import org.xnio.channels.ConnectedMessageChannel;
/**
* @author David M. Lloyd
*/
final class RemoteConnectionChannel extends AbstractHandleableCloseable implements Channel {
static final IntIndexer INDEXER = new IntIndexer() {
public int getKey(final RemoteConnectionChannel argument) {
return argument.channelId;
}
public boolean equals(final RemoteConnectionChannel argument, final int index) {
return argument.channelId == index;
}
};
private final RemoteConnectionHandler connectionHandler;
private final ConnectionHandlerContext connectionHandlerContext;
private final RemoteConnection connection;
private final int channelId;
private final IntIndexMap outboundMessages = new IntIndexHashMap(OutboundMessage.INDEXER, Equaller.IDENTITY, 512, 0.5f);
private final IntIndexMap inboundMessages = new IntIndexHashMap(InboundMessage.INDEXER, Equaller.IDENTITY, 512, 0.5f);
private final int outboundWindow;
private final int inboundWindow;
private final Attachments attachments = new Attachments();
private final Queue inboundMessageQueue = new ArrayDeque();
private final int maxOutboundMessages;
private final int maxInboundMessages;
private final long maxOutboundMessageSize;
private final long maxInboundMessageSize;
private volatile int channelState = 0;
private static final AtomicIntegerFieldUpdater channelStateUpdater = AtomicIntegerFieldUpdater.newUpdater(RemoteConnectionChannel.class, "channelState");
private Receiver nextReceiver;
private static final int WRITE_CLOSED = (1 << 31);
private static final int READ_CLOSED = (1 << 30);
private static final int OUTBOUND_MESSAGES_MASK = (1 << 15) - 1;
private static final int ONE_OUTBOUND_MESSAGE = 1;
private static final int INBOUND_MESSAGES_MASK = ((1 << 30) - 1) & ~OUTBOUND_MESSAGES_MASK;
private static final int ONE_INBOUND_MESSAGE = (1 << 15);
RemoteConnectionChannel(final RemoteConnectionHandler connectionHandler, final RemoteConnection connection, final int channelId, final int outboundWindow, final int inboundWindow, final int maxOutboundMessages, final int maxInboundMessages, final long maxOutboundMessageSize, final long maxInboundMessageSize) {
super(connectionHandler.getConnectionContext().getConnectionProviderContext().getExecutor(), true);
this.maxOutboundMessageSize = maxOutboundMessageSize;
this.maxInboundMessageSize = maxInboundMessageSize;
connectionHandlerContext = connectionHandler.getConnectionContext();
this.connectionHandler = connectionHandler;
this.connection = connection;
this.channelId = channelId;
this.outboundWindow = outboundWindow;
this.inboundWindow = inboundWindow;
this.maxOutboundMessages = maxOutboundMessages;
this.maxInboundMessages = maxInboundMessages;
}
void openOutboundMessage() throws IOException {
int oldState, newState;
do {
oldState = channelState;
if ((oldState & WRITE_CLOSED) != 0) {
throw new NotOpenException("Writes closed");
}
final int outboundCount = oldState & OUTBOUND_MESSAGES_MASK;
if (outboundCount == maxOutboundMessages) {
throw new ChannelBusyException("Too many open outbound writes");
}
newState = oldState + ONE_OUTBOUND_MESSAGE;
} while (!casState(oldState, newState));
log.tracef("Opened outbound message on %s", this);
}
private int incrementState(final int count) {
final int oldState = channelStateUpdater.getAndAdd(this, count);
if (log.isTraceEnabled()) {
final int newState = oldState + count;
log.tracef("CAS %s\n\told: RS=%s WS=%s IM=%d OM=%d\n\tnew: RS=%s WS=%s IM=%d OM=%d", this,
Boolean.valueOf((oldState & READ_CLOSED) != 0),
Boolean.valueOf((oldState & WRITE_CLOSED) != 0),
Integer.valueOf((oldState & INBOUND_MESSAGES_MASK) >> Integer.numberOfTrailingZeros(ONE_INBOUND_MESSAGE)),
Integer.valueOf((oldState & OUTBOUND_MESSAGES_MASK) >> Integer.numberOfTrailingZeros(ONE_OUTBOUND_MESSAGE)),
Boolean.valueOf((newState & READ_CLOSED) != 0),
Boolean.valueOf((newState & WRITE_CLOSED) != 0),
Integer.valueOf((newState & INBOUND_MESSAGES_MASK) >> Integer.numberOfTrailingZeros(ONE_INBOUND_MESSAGE)),
Integer.valueOf((newState & OUTBOUND_MESSAGES_MASK) >> Integer.numberOfTrailingZeros(ONE_OUTBOUND_MESSAGE))
);
}
return oldState;
}
private boolean casState(final int oldState, final int newState) {
final boolean result = channelStateUpdater.compareAndSet(this, oldState, newState);
if (result && log.isTraceEnabled()) {
log.tracef("CAS %s\n\told: RS=%s WS=%s IM=%d OM=%d\n\tnew: RS=%s WS=%s IM=%d OM=%d", this,
Boolean.valueOf((oldState & READ_CLOSED) != 0),
Boolean.valueOf((oldState & WRITE_CLOSED) != 0),
Integer.valueOf((oldState & INBOUND_MESSAGES_MASK) >> Integer.numberOfTrailingZeros(ONE_INBOUND_MESSAGE)),
Integer.valueOf((oldState & OUTBOUND_MESSAGES_MASK) >> Integer.numberOfTrailingZeros(ONE_OUTBOUND_MESSAGE)),
Boolean.valueOf((newState & READ_CLOSED) != 0),
Boolean.valueOf((newState & WRITE_CLOSED) != 0),
Integer.valueOf((newState & INBOUND_MESSAGES_MASK) >> Integer.numberOfTrailingZeros(ONE_INBOUND_MESSAGE)),
Integer.valueOf((newState & OUTBOUND_MESSAGES_MASK) >> Integer.numberOfTrailingZeros(ONE_OUTBOUND_MESSAGE))
);
}
return result;
}
void closeOutboundMessage() {
int oldState = incrementState(-ONE_OUTBOUND_MESSAGE);
if (oldState == (WRITE_CLOSED | READ_CLOSED)) {
// no messages left and read & write closed
log.tracef("Closed outbound message on %s (unregistering)", this);
unregister();
} else {
log.tracef("Closed outbound message on %s", this);
}
}
boolean openInboundMessage() {
int oldState, newState;
do {
oldState = channelState;
if ((oldState & READ_CLOSED) != 0) {
log.tracef("Refusing inbound message on %s (reads closed)", this);
return false;
}
final int inboundCount = oldState & INBOUND_MESSAGES_MASK;
if (inboundCount == maxInboundMessages) {
log.tracef("Refusing inbound message on %s (too many concurrent reads)", this);
return false;
}
newState = oldState + ONE_INBOUND_MESSAGE;
} while (!casState(oldState, newState));
log.tracef("Opened inbound message on %s", this);
return true;
}
void closeInboundMessage() {
int oldState = incrementState(-ONE_INBOUND_MESSAGE);
if (oldState == (WRITE_CLOSED | READ_CLOSED)) {
// no messages left and read & write closed
log.tracef("Closed inbound message on %s (unregistering)", this);
unregister();
} else {
log.tracef("Closed inbound message on %s", this);
}
}
void closeReads() {
int oldState, newState;
do {
oldState = channelState;
if ((oldState & READ_CLOSED) != 0) {
return;
}
newState = oldState | READ_CLOSED;
} while (!casState(oldState, newState));
if (oldState == WRITE_CLOSED) {
// no channels
log.tracef("Closed channel reads on %s (unregistering)", this);
unregister();
} else {
log.tracef("Closed channel reads on %s", this);
}
notifyEnd();
}
boolean closeWrites() {
int oldState, newState;
do {
oldState = channelState;
if ((oldState & WRITE_CLOSED) != 0) {
return false;
}
newState = oldState | WRITE_CLOSED;
} while (!casState(oldState, newState));
if (oldState == READ_CLOSED) {
// no channels and read was closed
log.tracef("Closed channel writes on %s (unregistering)", this);
unregister();
} else {
log.tracef("Closed channel writes on %s", this);
}
return true;
}
boolean closeReadsAndWrites() {
int oldState, newState;
do {
oldState = channelState;
if ((oldState & (READ_CLOSED | WRITE_CLOSED)) == (READ_CLOSED | WRITE_CLOSED)) {
return false;
}
newState = oldState | READ_CLOSED | WRITE_CLOSED;
} while (!casState(oldState, newState));
if ((oldState & WRITE_CLOSED) == 0) {
// we're sending the write close request asynchronously
Pooled pooled = connection.allocate();
boolean ok = false;
try {
ByteBuffer byteBuffer = pooled.getResource();
byteBuffer.put(Protocol.CHANNEL_SHUTDOWN_WRITE);
byteBuffer.putInt(channelId);
byteBuffer.flip();
ok = true;
connection.send(pooled);
} finally {
if (! ok) pooled.free();
}
log.tracef("Closed channel reads on %s", this);
}
if ((oldState & (INBOUND_MESSAGES_MASK | OUTBOUND_MESSAGES_MASK)) == 0) {
// there were no channels open
log.tracef("Closed channel reads and writes on %s (unregistering)", this);
unregister();
} else {
log.tracef("Closed channel reads and writes on %s", this);
}
notifyEnd();
return true;
}
private void notifyEnd() {
synchronized (connection.getLock()) {
if (nextReceiver != null) {
final Receiver receiver = nextReceiver;
nextReceiver = null;
try {
getExecutor().execute(new Runnable() {
public void run() {
receiver.handleEnd(RemoteConnectionChannel.this);
}
});
} catch (Throwable t) {
connection.handleException(new IOException("Fatal connection error", t));
return;
}
}
}
}
private void unregister() {
log.tracef("Unregistering %s", this);
closeAsync();
connectionHandler.handleChannelClosed(this);
}
public MessageOutputStream writeMessage() throws IOException {
int tries = 50;
IntIndexMap outboundMessages = this.outboundMessages;
openOutboundMessage();
boolean ok = false;
try {
final Random random = ProtocolUtils.randomHolder.get();
while (tries > 0) {
final int id = random.nextInt() & 0xfffe;
if (! outboundMessages.containsKey(id)) {
OutboundMessage message = new OutboundMessage((short) id, this, outboundWindow, maxOutboundMessageSize);
OutboundMessage existing = outboundMessages.putIfAbsent(message);
if (existing == null) {
ok = true;
return message;
}
}
tries --;
}
throw log.channelBusy();
} finally {
if (! ok) {
closeOutboundMessage();
}
}
}
void free(OutboundMessage outboundMessage) {
if (outboundMessages.remove(outboundMessage)) {
log.tracef("Removed %s", outboundMessage);
} else {
log.tracef("Got redundant free for %s", outboundMessage);
}
}
public void writeShutdown() throws IOException {
if (closeWrites()) {
Pooled pooled = connection.allocate();
boolean ok = false;
try {
ByteBuffer byteBuffer = pooled.getResource();
byteBuffer.put(Protocol.CHANNEL_SHUTDOWN_WRITE);
byteBuffer.putInt(channelId);
byteBuffer.flip();
connection.send(pooled);
ok = true;
} finally {
if (! ok) pooled.free();
}
}
}
void handleRemoteClose() {
closeReadsAndWrites();
}
void handleIncomingWriteShutdown() {
closeReads();
}
public void receiveMessage(final Receiver handler) {
synchronized (connection.getLock()) {
if (inboundMessageQueue.isEmpty()) {
if ((channelState & READ_CLOSED) != 0) {
getExecutor().execute(new Runnable() {
public void run() {
handler.handleEnd(RemoteConnectionChannel.this);
}
});
} else if (nextReceiver != null) {
throw new IllegalStateException("Message handler already queued");
} else {
nextReceiver = handler;
}
} else {
final InboundMessage message = inboundMessageQueue.remove();
try {
getExecutor().execute(new Runnable() {
public void run() {
handler.handleMessage(RemoteConnectionChannel.this, message.messageInputStream);
}
});
} catch (Throwable t) {
connection.handleException(new IOException("Fatal connection error", t));
return;
}
}
connection.getLock().notify();
}
}
private static Set
© 2015 - 2025 Weber Informatics LLC | Privacy Policy