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

org.jboss.remoting3.remote.RemoteConnection 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).

There is a newer version: 35.0.0.Beta1
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.jboss.remoting3.remote;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import org.jboss.logging.Logger;
import org.jboss.remoting3.RemotingOptions;
import org.jboss.remoting3.spi.ConnectionHandlerFactory;
import org.xnio.Buffers;
import org.xnio.ChannelListener;
import org.xnio.IoUtils;
import org.xnio.OptionMap;
import org.xnio.Pool;
import org.xnio.Pooled;
import org.xnio.Result;
import org.xnio.XnioExecutor;
import org.xnio.channels.ConnectedMessageChannel;
import org.xnio.channels.ConnectedStreamChannel;
import org.xnio.channels.SslChannel;
import org.xnio.sasl.SaslWrapper;

/**
 * @author David M. Lloyd
 */
final class RemoteConnection {

    private static final String FQCN = RemoteConnection.class.getName();
    private final Pool messageBufferPool;
    private final ConnectedMessageChannel channel;
    private final ConnectedStreamChannel underlyingChannel;
    private final OptionMap optionMap;
    private final RemoteWriteListener writeListener = new RemoteWriteListener();
    private final Executor executor;
    private final int heartbeatInterval;
    private volatile Result result;
    private volatile SaslWrapper saslWrapper;
    private volatile boolean closing;
    private final RemoteConnectionProvider remoteConnectionProvider;

    RemoteConnection(final Pool messageBufferPool, final ConnectedStreamChannel underlyingChannel, final ConnectedMessageChannel channel, final OptionMap optionMap, final RemoteConnectionProvider remoteConnectionProvider) {
        this.messageBufferPool = messageBufferPool;
        this.underlyingChannel = underlyingChannel;
        this.channel = channel;
        this.optionMap = optionMap;
        heartbeatInterval = optionMap.get(RemotingOptions.HEARTBEAT_INTERVAL, RemotingOptions.DEFAULT_HEARTBEAT_INTERVAL);
        this.executor = remoteConnectionProvider.getExecutor();
        this.remoteConnectionProvider = remoteConnectionProvider;
    }

    Pooled allocate() {
        return messageBufferPool.allocate();
    }

    void setReadListener(ChannelListener listener, final boolean resume) {
        RemoteLogger.log.logf(RemoteConnection.class.getName(), Logger.Level.TRACE, null, "Setting read listener to %s", listener);
        synchronized (getLock()) {
            channel.getReadSetter().set(listener);
            if (listener != null && resume) {
                channel.resumeReads();
            }
        }
    }

    RemoteConnectionProvider getRemoteConnectionProvider() {
        return remoteConnectionProvider;
    }

    Result getResult() {
        return result;
    }

    void setResult(final Result result) {
        this.result = result;
    }

    void handleException(IOException e) {
        handleException(e, true);
    }

    void handleException(IOException e, boolean log) {
        RemoteLogger.conn.logf(RemoteConnection.class.getName(), Logger.Level.TRACE, e, "Connection error detail");
        if (log) {
            RemoteLogger.conn.connectionError(e);
        }
        final XnioExecutor.Key key = writeListener.heartKey;
        if (key != null) {
            key.remove();
        }
        IoUtils.safeClose(channel);
        final Result result = this.result;
        if (result != null) {
            result.setException(e);
            this.result = null;
        }
    }

    void send(final Pooled pooled) {
        writeListener.send(pooled, false);
    }

    void send(final Pooled pooled, boolean close) {
        writeListener.send(pooled, close);
    }

    void shutdownWrites() {
        writeListener.shutdownWrites();
    }

    OptionMap getOptionMap() {
        return optionMap;
    }

    ConnectedMessageChannel getChannel() {
        return channel;
    }

    ChannelListener getWriteListener() {
        return writeListener;
    }

    public Executor getExecutor() {
        return executor;
    }

    public SslChannel getSslChannel() {
        return underlyingChannel instanceof SslChannel ? (SslChannel) underlyingChannel : null;
    }

    SaslWrapper getSaslWrapper() {
        return saslWrapper;
    }

    void setSaslWrapper(final SaslWrapper saslWrapper) {
        this.saslWrapper = saslWrapper;
    }

    void handlePreAuthCloseRequest() {
        try {
            terminateHeartbeat();
            channel.close();
        } catch (IOException e) {
            RemoteLogger.conn.debug("Error closing remoting channel", e);
        }
    }

    void sendAlive() {
        final Pooled pooled = allocate();
        boolean ok = false;
        try {
            final ByteBuffer buffer = pooled.getResource();
            buffer.put(Protocol.CONNECTION_ALIVE);
            buffer.limit(80);
            Buffers.addRandom(buffer);
            buffer.flip();
            send(pooled);
            ok = true;
            channel.wakeupReads();
        } finally {
            if (! ok) pooled.free();
        }
    }

    void sendAliveResponse() {
        final Pooled pooled = allocate();
        boolean ok = false;
        try {
            final ByteBuffer buffer = pooled.getResource();
            buffer.put(Protocol.CONNECTION_ALIVE_ACK);
            buffer.limit(80);
            Buffers.addRandom(buffer);
            buffer.flip();
            send(pooled);
            ok = true;
        } finally {
            if (! ok) pooled.free();
        }
    }

    void terminateHeartbeat() {
        final XnioExecutor.Key key = writeListener.heartKey;
        if (key != null) {
            key.remove();
        }
    }

    Object getLock() {
        return writeListener.queue;
    }

    void closing() {
        this.closing = true;
    }

    final class RemoteWriteListener implements ChannelListener {

        private final Queue> queue = new ArrayDeque>();
        private XnioExecutor.Key heartKey;
        private boolean closed;

        RemoteWriteListener() {
        }

        public void handleEvent(final ConnectedMessageChannel channel) {
            synchronized (getLock()) {
                assert channel == getChannel();
                Pooled pooled;
                final Queue> queue = this.queue;
                try {
                    while ((pooled = queue.peek()) != null) {
                        final ByteBuffer buffer = pooled.getResource();
                        if (channel.send(buffer)) {
                            RemoteLogger.conn.logf(FQCN, Logger.Level.TRACE, null, "Sent message %s (via queue)", buffer);
                            queue.poll().free();
                        } else {
                            // try again later
                            return;
                        }
                    }
                    if (channel.flush()) {
                        RemoteLogger.conn.logf(FQCN, Logger.Level.TRACE, null, "Flushed channel");
                        if (closed) {
                            terminateHeartbeat();
                            // End of queue reached; shut down and try to flush the remainder
                            channel.shutdownWrites();
                            if (channel.flush()) {
                                RemoteLogger.conn.logf(FQCN, Logger.Level.TRACE, null, "Shut down writes on channel");
                                return;
                            }
                            // either this is successful and no more notifications will come, or not and it will be retried
                            // either way we're done here
                            return;
                        }
                        channel.suspendWrites();
                    }
                } catch (IOException e) {
                    handleException(e, false);
                    channel.wakeupReads();
                    while ((pooled = queue.poll()) != null) {
                        pooled.free();
                    }
                }
                // else try again later
            }
        }

        public void shutdownWrites() {
            synchronized (getLock()) {
                closed = true;
                terminateHeartbeat();
                final ConnectedMessageChannel channel = getChannel();
                try {
                    if (! queue.isEmpty()) {
                        channel.resumeWrites();
                        return;
                    }
                    channel.shutdownWrites();
                    if (! channel.flush()) {
                        channel.resumeWrites();
                        return;
                    }
                    RemoteLogger.conn.logf(FQCN, Logger.Level.TRACE, null, "Shut down writes on channel");
                } catch (IOException e) {
                    handleException(e, false);
                    channel.wakeupReads();
                    Pooled unqueued;
                    while ((unqueued = queue.poll()) != null) {
                        unqueued.free();
                    }
                }
            }
        }

        public void send(Pooled pooled, final boolean close) {
            if(!closing) {
                synchronized (getLock()) {
                    XnioExecutor.Key heartKey = this.heartKey;
                    if (heartKey != null) heartKey.remove();
                    if (closed) { pooled.free(); return; }
                    if (close) { closed = true; }
                    final ConnectedMessageChannel channel = getChannel();
                    boolean free = true;
                    try {
                        final SaslWrapper wrapper = saslWrapper;
                        if (wrapper != null) {
                            final ByteBuffer buffer = pooled.getResource();
                            final ByteBuffer source = buffer.duplicate();
                            buffer.clear();
                            wrapper.wrap(buffer, source);
                            buffer.flip();
                        }
                        if (queue.isEmpty()) {
                            final ByteBuffer buffer = pooled.getResource();
                            if (! channel.send(buffer)) {
                                RemoteLogger.conn.logf(FQCN, Logger.Level.TRACE, null, "Can't directly send message %s, enqueued", buffer);
                                queue.add(pooled);
                                free = false;
                                channel.resumeWrites();
                                return;
                            }
                            RemoteLogger.conn.logf(FQCN, Logger.Level.TRACE, null, "Sent message %s (direct)", buffer);
                            if (close) {
                                channel.shutdownWrites();
                                RemoteLogger.conn.logf(FQCN, Logger.Level.TRACE, null, "Shut down writes on channel (direct)");
                                // fall thru to flush
                            }
                            if (! channel.flush()) {
                                channel.resumeWrites();
                                return;
                            }
                            RemoteLogger.conn.logf(FQCN, Logger.Level.TRACE, null, "Flushed channel (direct)");
                            if (! close) {
                                this.heartKey = channel.getWriteThread().executeAfter(heartbeatCommand, heartbeatInterval, TimeUnit.MILLISECONDS);
                            }
                        } else {
                            queue.add(pooled);
                            free = false;
                        }
                    } catch (IOException e) {
                        handleException(e, false);
                        channel.wakeupReads();
                        Pooled unqueued;
                        while ((unqueued = queue.poll()) != null) {
                            unqueued.free();
                        }
                    } finally {
                        if (free) {
                            pooled.free();
                        }
                    }
                }
            }
        }
    }

    private final Runnable heartbeatCommand = new Runnable() {
        public void run() {
            sendAlive();
        }
    };

    public String toString() {
        return String.format("Remoting connection %08x to %s", Integer.valueOf(hashCode()), channel.getPeerAddress());
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy