package net.tomp2p.connection; import io.netty.channel.ChannelFuture; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GenericFutureListener; import lombok.Getter; import lombok.experimental.Accessors; import net.tomp2p.futures.FutureDone; import net.tomp2p.futures.Futures; import net.tomp2p.peers.PeerAddress; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * * @author Thomas Bocek */ @Accessors(chain = true, fluent = true) public class PeerConnection { final private static Logger LOG = LoggerFactory.getLogger(PeerConnection.class); final public static int HEART_BEAT_MILLIS = 2000; @Getter final private PeerAddress remotePeer; @Getter final private boolean initiator; @Getter final private FutureDone<Void> closeFuture = new FutureDone<Void>(); @Getter final private int idleMillis; //-1 no heartbeat, 0 heartbeat but not done by use, > 0 heartbeat done by us @Getter final private int heartBeatMillis; @Getter final private ConnectionBean.Protocol protocol; @Getter final private ChannelCreator channelCreator; @Getter private ChannelFuture channelFuture = null; @Getter private ChannelCreator.ChannelCloseListener closeListener = null; private boolean closeRequested = false; public PeerConnection() { this.remotePeer = null; this.initiator = false; this.idleMillis = -1; this.heartBeatMillis = -1; this.protocol = null; this.channelCreator = null; } private PeerConnection(final PeerAddress remotePeer, final int idleMillis, final ConnectionBean.Protocol protocol, final int heartBeatMillis, final ChannelFuture channelFuture, ChannelCreator.ChannelCloseListener closeListener, final boolean initiator) { this.remotePeer = remotePeer; this.initiator = initiator; this.idleMillis = idleMillis; this.heartBeatMillis = heartBeatMillis; this.protocol = protocol; channelFuture(channelFuture, closeListener); this.channelCreator = null; } private PeerConnection(final PeerAddress remotePeer, final int idleMillis, final ConnectionBean.Protocol protocol, final int heartBeatMillis, final ChannelCreator channelCreator, final boolean initiator) { this.remotePeer = remotePeer; this.initiator = initiator; this.idleMillis = idleMillis; this.heartBeatMillis = heartBeatMillis; this.protocol = protocol; this.channelCreator = channelCreator; } public static PeerConnection newPeerConnectionTCP(final ChannelCreator channelCreator, final PeerAddress remotePeer, final int idleMillis) { return new PeerConnection(remotePeer, idleMillis, ConnectionBean.Protocol.TCP, -1, channelCreator, true); } public static PeerConnection existingPeerConnectionTCP(final PeerAddress remotePeer, final int idleMillis, final ChannelFuture channelFuture, ChannelCreator.ChannelCloseListener closeListener) { return new PeerConnection(remotePeer, idleMillis, ConnectionBean.Protocol.TCP, -1, channelFuture, closeListener, false); } public static PeerConnection newPermanentPeerConnectionTCP(final ChannelCreator channelCreator, final PeerAddress remotePeer, final int idleMillis, final int heartBeatMillis) { return new PeerConnection(remotePeer, idleMillis, ConnectionBean.Protocol.TCP, heartBeatMillis, channelCreator, true); } public static PeerConnection existingPermanentPeerConnectionTCP(final PeerAddress remotePeer, final int idleMillis, final ChannelFuture channelFuture, ChannelCreator.ChannelCloseListener closeListener) { return new PeerConnection(remotePeer, idleMillis, ConnectionBean.Protocol.TCP, 0, channelFuture, closeListener, false); } public static PeerConnection newPeerConnectionUDT(final ChannelCreator channelCreator, final PeerAddress remotePeer, final int idleMillis) { return new PeerConnection(remotePeer, idleMillis, ConnectionBean.Protocol.UDT, -1, channelCreator, true); } public static PeerConnection existingPeerConnectionUDT(final PeerAddress remotePeer, final int idleMillis, final ChannelFuture channelFuture, ChannelCreator.ChannelCloseListener closeListener) { return new PeerConnection(remotePeer, idleMillis, ConnectionBean.Protocol.UDT, -1, channelFuture, closeListener, false); } public static PeerConnection newPermanentPeerConnectionUDT(final ChannelCreator channelCreator, final PeerAddress remotePeer, final int idleMillis, final int heartBeatMillis) { return new PeerConnection(remotePeer, idleMillis, ConnectionBean.Protocol.UDT, heartBeatMillis, channelCreator, true); } public static PeerConnection existingPermanentPeerConnectionUDT(final PeerAddress remotePeer, final int idleMillis, final ChannelFuture channelFuture, ChannelCreator.ChannelCloseListener closeListener) { return new PeerConnection(remotePeer, idleMillis, ConnectionBean.Protocol.UDT, 0, channelFuture, closeListener, false); } public static PeerConnection newPeerConnectionUDP(final ChannelCreator channelCreator, final PeerAddress remotePeer, final int idleMillis) { return new PeerConnection(remotePeer, idleMillis, ConnectionBean.Protocol.UDP, -1, channelCreator, true); } public static PeerConnection existingPeerConnectionUDP(final PeerAddress remotePeer, final int idleMillis, final ChannelFuture channelFuture, ChannelCreator.ChannelCloseListener closeListener) { return new PeerConnection(remotePeer, idleMillis, ConnectionBean.Protocol.UDP, -1, channelFuture, closeListener, false); } public PeerConnection channelFuture(final ChannelFuture channelFuture, ChannelCreator.ChannelCloseListener closeListener) { synchronized (this) { if (this.channelFuture != null && isOpen()) { throw new IllegalArgumentException("cannot set twice the channel future"); } if (this.closeListener != null && isOpen()) { throw new IllegalArgumentException("cannot set twice the close listener"); } this.closeListener = closeListener; this.channelFuture = channelFuture; if (closeRequested) { channelFuture.channel().close(); } } closeListener.doneAfterSemaphoreRelease(closeFuture); return this; } public boolean isOpen() { synchronized (this) { if (channelFuture == null) { return false; } return channelFuture.channel().isOpen(); } } public boolean isKeepAlive() { return heartBeatMillis >= 0; } public boolean isExisting() { return channelCreator == null; } public FutureDone shutdown() { FutureDone<Void> done1 = new FutureDone<>(); if(channelFuture != null) { //TODO: this runs in the background channelFuture.channel().close().addListener(new GenericFutureListener<Future<? super Void>>() { @Override public void operationComplete(Future<? super Void> future) throws Exception { done1.done(); } }); } else { done1.done(); } FutureDone<Void> done2; if(channelCreator != null) { done2 = channelCreator.shutdown(); } else { done2 = FutureDone.SUCCESS; } return Futures.whenAll(done1, done2); } public FutureDone<Void> close() { synchronized (this) { if (channelFuture == null) { closeRequested = true; } else { LOG.debug("Close connection {}. Initiator {}.", initiator, channelFuture); channelFuture.channel().close(); } } return closeFuture; } @Override public String toString() { StringBuilder sb = new StringBuilder("pconn: "); sb.append(remotePeer); return sb.toString(); } }