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

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

The newest version!
package io.servicefabric.transport;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.Iterables.transform;
import static io.servicefabric.transport.TransportChannel.Status.CLOSED;
import static io.servicefabric.transport.TransportChannel.Status.CONNECTED;
import static io.servicefabric.transport.TransportChannel.Status.CONNECT_IN_PROGRESS;
import static io.servicefabric.transport.utils.ChannelFutureUtils.setPromise;

import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;

import io.netty.channel.Channel;
import io.netty.util.AttributeKey;

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

import rx.functions.Func1;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicReference;

import javax.annotation.CheckForNull;
import javax.annotation.Nullable;

final class TransportChannel implements ITransportChannel {

  private static final Logger LOGGER = LoggerFactory.getLogger(TransportChannel.class);

  private static final AttributeKey ATTR_TRANSPORT_CHANNEL = AttributeKey.valueOf("transport");

  public enum Status {
    CONNECT_IN_PROGRESS,
    CONNECTED,
    HANDSHAKE_IN_PROGRESS,
    HANDSHAKE_PASSED,
    READY,
    CLOSED
  }

  private final Channel channel;
  private final AtomicReference status;
  private final Func1 closeCallback;
  private final AtomicReference cause = new AtomicReference<>();
  private final SettableFuture handshakeFuture = SettableFuture.create();

  private TransportChannel(Channel channel, Status initialStatus, Func1 closeCallback) {
    checkArgument(channel != null);
    checkArgument(initialStatus != null);
    checkArgument(closeCallback != null);
    this.channel = channel;
    this.status = new AtomicReference<>(initialStatus);
    this.closeCallback = closeCallback;
  }

  public static TransportChannel newConnectorChannel(Channel channel, Func1 closeCallback) {
    TransportChannel target = new TransportChannel(channel, CONNECT_IN_PROGRESS, closeCallback);
    channel.attr(TransportChannel.ATTR_TRANSPORT_CHANNEL).set(target);
    return target;
  }

  public static TransportChannel newAcceptorChannel(Channel channel, Func1 closeCallback) {
    TransportChannel target = new TransportChannel(channel, CONNECTED, closeCallback);
    channel.attr(TransportChannel.ATTR_TRANSPORT_CHANNEL).set(target);
    return target;
  }

  /**
   * Resolve TransportChannel by given Netty channel. It doesn't create new instance of TransportChannel.
   * 
   * @throws TransportBrokenException if given Netty channel not associated with any transport channel.
   */
  public static TransportChannel from(Channel channel) {
    TransportChannel transport = channel.attr(ATTR_TRANSPORT_CHANNEL).get();
    if (transport == null) {
      throw new TransportBrokenException("Transport not set for the given channel: " + channel);
    }
    return transport;
  }

  /**
   * Setter for {@link #handshakeFuture}. Called when handshake passed successfully (RESOLVED_OK) on both sides.
   *
   * @param handshakeData remote handshake (non null)
   */
  void setHandshakeData(TransportHandshakeData handshakeData) {
    checkArgument(handshakeData != null);
    handshakeFuture.set(handshakeData);
  }

  Channel channel() {
    return channel;
  }

  ListenableFuture handshakeFuture() {
    return handshakeFuture;
  }

  /**
   * Origin/Destination of this transport.
   *
   * @return TransportEndpoint object this transport is referencing to; or {@code null} if this transport isn't READY
   *         yet
   */
  @Nullable
  public TransportEndpoint remoteEndpoint() {
    if (handshakeFuture.isDone()) {
      try {
        return handshakeFuture.get().endpoint();
      } catch (InterruptedException | ExecutionException ex) {
        LOGGER.error("Failed to get remote endpoint, ex");
      }
    }
    return null;
  }

  @Override
  public void send(@CheckForNull Message message) {
    send(message, null);
  }

  @Override
  public void send(@CheckForNull Message message, @Nullable SettableFuture promise) {
    checkArgument(message != null);
    if (promise != null && getCause() != null) {
      promise.setException(getCause());
    } else {
      setPromise(channel.writeAndFlush(message), promise);
    }
  }

  @Override
  public void close(@Nullable SettableFuture promise) {
    close(null/* cause */, promise);
  }

  void close(Throwable cause) {
    close(cause, null/* promise */);
  }

  void close() {
    close(null/* cause */, null/* promise */);
  }

  void close(Throwable cause, SettableFuture promise) {
    this.cause.compareAndSet(null, cause != null ? cause : new TransportClosedException());
    status.set(CLOSED);
    closeCallback.call(this);
    setPromise(channel.close(), promise);
    LOGGER.info("Closed {}", this);
  }

  /**
   * Flips the {@link #status}.
   *
   * @throws TransportBrokenException in case {@code expect} not actual
   */
  void flip(Status expect, Status update) throws TransportBrokenException {
    if (!status.compareAndSet(expect, update)) {
      String err = "Can't set status " + update + " (expect=" + expect + ", actual=" + status + ") on channel: " + this;
      throw new TransportBrokenException(err);
    }
  }

  Throwable getCause() {
    return cause.get();
  }

  @Override
  public String toString() {
    if (getCause() == null) {
      return "TransportChannel{" + "status=" + status + ", channel=" + channel + '}';
    }
    Class clazz = getCause().getClass();
    String packageName = clazz.getPackage().getName();
    String dottedPackageName =
        Joiner.on('.').join(transform(Splitter.on('.').split(packageName), new Function() {
          @Override
          public Character apply(String input) {
            return input.charAt(0);
          }
        }));
    return "TransportChannel{"
        + "status=" + status
        + ", cause=[" + dottedPackageName + "." + clazz.getSimpleName() + "]"
        + ", channel=" + channel + '}';
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy