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

org.jboss.remoting3.remote.RemoteConnection Maven / Gradle / Ivy

There is a newer version: 5.0.29.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.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 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;
    }

    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 (queue) {
                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 (queue) {
                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) {
            synchronized (queue) {
                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 - 2024 Weber Informatics LLC | Privacy Policy