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