org.jboss.remoting3.remote.RemoteConnectionChannel Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote Jakarta Enterprise Beans and Jakarta Messaging, including
all dependencies. It is intended for use by those not using maven, maven users should just import the Jakarta Enterprise Beans and
Jakarta Messaging 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).
/*
* 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 static org.xnio.IoUtils.safeClose;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
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) {
boolean immediateEnd = false;
synchronized (connection.getLock()) {
if (inboundMessageQueue.isEmpty()) {
if ((channelState & READ_CLOSED) != 0) try {
getExecutor().execute(new Runnable() {
public void run() {
handler.handleEnd(RemoteConnectionChannel.this);
}
});
} catch (RejectedExecutionException ignored) {
// oops, endpoint shut down out from under us; call directly and hope for the best
immediateEnd = true;
} 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 (RejectedExecutionException ignored) {
safeClose(message.messageInputStream);
// oops, endpoint shut down out from under us; call handleEnd directly and hope for the best
immediateEnd = true;
}
}
connection.getLock().notify();
}
if (immediateEnd) {
handler.handleEnd(this);
}
}
private static Set
© 2015 - 2025 Weber Informatics LLC | Privacy Policy