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

io.servicefabric.transport.ConnectorHandshakeChannelHandler Maven / Gradle / Ivy

package io.servicefabric.transport;

import static com.google.common.base.Throwables.propagate;
import static io.servicefabric.transport.utils.ChannelFutureUtils.wrap;
import static java.lang.Thread.interrupted;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.servicefabric.transport.protocol.Message;

import io.netty.channel.*;
import io.netty.util.concurrent.ScheduledFuture;

/**
 * Duplex handler.
 * On inbound recognizes only handshake message {@link TransportData#Q_TRANSPORT_HANDSHAKE_SYNC_ACK}
 * (rest inbound messages unsupported and results in {@link TransportBrokenException}).
 * On outbound may enqueue messages (see {@link TransportChannel#shouldEnqueueSend()}).
 * On 'channel active' starting handshake process.
 * 

* NOTE: this handler is not shareable (see {@link #sendMailbox}, {@link #handshakeTimer}); * and should always run in different executor than io-thread. */ final class ConnectorHandshakeChannelHandler extends ChannelDuplexHandler { static final Logger LOGGER = LoggerFactory.getLogger(ConnectorHandshakeChannelHandler.class); final ITransportSpi transportSpi; BlockingQueue sendMailbox; ScheduledFuture handshakeTimer; /** Tuple class. Contains message and promise. */ static class WriteAndFlush { final Object msg; final ChannelPromise promise; WriteAndFlush(Object msg, ChannelPromise promise) { this.msg = msg; this.promise = promise; } } ConnectorHandshakeChannelHandler(ITransportSpi transportSpi) { this.transportSpi = transportSpi; this.sendMailbox = new ArrayBlockingQueue<>(transportSpi.getSendHwm(), true/*fair*/); } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { final TransportChannel transport = transportSpi.getTransportChannel(ctx.channel()); transport.flip(TransportChannel.Status.CONNECT_IN_PROGRESS, TransportChannel.Status.CONNECTED); final TransportData handshake = TransportData.NEW(transport.transportSpi.getLocalMetadata()).build(); int handshakeTimeout = transport.transportSpi.getHandshakeTimeout(); handshakeTimer = transport.channel.eventLoop().schedule(new Runnable() { @Override public void run() { LOGGER.debug("HANDSHAKE_SYNC({}) timeout, connector: {}", handshake, transport); transport.flip(TransportChannel.Status.HANDSHAKE_IN_PROGRESS, TransportChannel.Status.HANDSHAKE_FAILED); transport.close(new TransportHandshakeException(transport, handshake, new TimeoutException())); } }, handshakeTimeout, TimeUnit.MILLISECONDS); transport.flip(TransportChannel.Status.CONNECTED, TransportChannel.Status.HANDSHAKE_IN_PROGRESS); ChannelFuture channelFuture = ctx.writeAndFlush(new Message(TransportData.Q_TRANSPORT_HANDSHAKE_SYNC, handshake)); channelFuture.addListener(wrap(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) { if (!future.isSuccess()) { LOGGER.debug("HANDSHAKE_SYNC({}) not sent, connector: {}", handshake, transport); cancelHandshakeTimer(); transport.flip(TransportChannel.Status.HANDSHAKE_IN_PROGRESS, TransportChannel.Status.HANDSHAKE_FAILED); transport.close(new TransportHandshakeException(transport, handshake, future.cause())); } } })); super.channelActive(ctx); } @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { TransportChannel transport = transportSpi.getTransportChannel(ctx.channel()); if (transport.shouldEnqueueSend()) { try { sendMailbox.put(new WriteAndFlush(msg, promise)); } catch (InterruptedException e) { interrupted(); if (promise != null) promise.setFailure(e); propagate(e); } } else { ctx.writeAndFlush(msg, promise); } } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { Message message = (Message) msg; if (!TransportData.Q_TRANSPORT_HANDSHAKE_SYNC_ACK.equals(message.qualifier())) throw new TransportBrokenException("Received unsupported " + msg + " (though expecting only Q_TRANSPORT_HANDSHAKE_SYNC_ACK)"); TransportData handshake = (TransportData) message.data(); final TransportChannel transport = transportSpi.getTransportChannel(ctx.channel()); if (handshake.isResolvedOk()) { cancelHandshakeTimer(); transport.setRemoteHandshake(handshake); transport.transportSpi.resetDueHandshake(transport.channel); transport.flip(TransportChannel.Status.HANDSHAKE_IN_PROGRESS, TransportChannel.Status.HANDSHAKE_PASSED); LOGGER.debug("HANDSHAKE passed on connector: {}", transport); drainSendMailbox(ctx); transport.flip(TransportChannel.Status.HANDSHAKE_PASSED, TransportChannel.Status.READY); LOGGER.debug("Set READY on connector: {}", transport); } else { LOGGER.debug("HANDSHAKE({}) not passed, connector: {}", handshake, transport); cancelHandshakeTimer(); transport.flip(TransportChannel.Status.HANDSHAKE_IN_PROGRESS, TransportChannel.Status.HANDSHAKE_FAILED); transport.close(new TransportHandshakeException(transport, handshake)); } } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { cancelHandshakeTimer(); cleanupSendMailbox(ctx); super.channelInactive(ctx); } @Override public void close(ChannelHandlerContext ctx, ChannelPromise future) throws Exception { cancelHandshakeTimer(); cleanupSendMailbox(ctx); super.close(ctx, future); } private void drainSendMailbox(ChannelHandlerContext ctx) { while (!sendMailbox.isEmpty()) { WriteAndFlush waf = sendMailbox.poll(); ctx.writeAndFlush(waf.msg, waf.promise); } } private void cleanupSendMailbox(ChannelHandlerContext ctx) { TransportChannel transport = transportSpi.getTransportChannel(ctx.channel()); Throwable transportCause = transport.getCause(); Throwable cause = transportCause != null ? transportCause : new TransportClosedException(transport); while (!sendMailbox.isEmpty()) { WriteAndFlush waf = sendMailbox.poll(); if (waf.promise != null) { waf.promise.setFailure(cause); } } } private void cancelHandshakeTimer() { if (handshakeTimer != null) { handshakeTimer.cancel(true); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy