package com.basrikahveci.p2p.peer.service; import com.basrikahveci.p2p.peer.Config; import com.basrikahveci.p2p.peer.Peer; import com.basrikahveci.p2p.peer.network.Connection; import com.basrikahveci.p2p.peer.network.PeerChannelHandler; import com.basrikahveci.p2p.peer.network.PeerChannelInitializer; import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.serialization.ObjectEncoder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; /** * Maintains TCP connections between this peer and its neighbours */ public class ConnectionService { private static final Logger LOGGER = LoggerFactory.getLogger(ConnectionService.class); private final Config config; private final EventLoopGroup networkEventLoopGroup; private final EventLoopGroup peerEventLoopGroup; private final ObjectEncoder encoder; // server name -> connection private final Map<String, Connection> connections = new HashMap<String, Connection>(); public ConnectionService(Config config, EventLoopGroup networkEventLoopGroup, EventLoopGroup peerEventLoopGroup, ObjectEncoder encoder) { this.config = config; this.networkEventLoopGroup = networkEventLoopGroup; this.peerEventLoopGroup = peerEventLoopGroup; this.encoder = encoder; } public void addConnection(final Connection connection) { final String peerName = connection.getPeerName(); final Connection previousConnection = connections.put(peerName, connection); LOGGER.info("Connection to " + peerName + " is added."); if (previousConnection != null) { previousConnection.close(); LOGGER.warn("Already existing connection to " + peerName + " is closed."); } } public boolean removeConnection(final Connection connection) { final boolean removed = connections.remove(connection.getPeerName()) != null; if (removed) { LOGGER.info(connection + " is removed from connections!"); } else { LOGGER.warn("Connection to " + connection.getPeerName() + " is not removed since not found in connections!"); } return removed; } public int getNumberOfConnections() { return connections.size(); } public boolean isConnectedTo(final String peerName) { return connections.containsKey(peerName); } public Connection getConnection(final String peerName) { return connections.get(peerName); } public Collection<Connection> getConnections() { return Collections.unmodifiableCollection(connections.values()); } public void connectTo(final Peer peer, final String host, final int port, final CompletableFuture<Void> futureToNotify) { final PeerChannelHandler handler = new PeerChannelHandler(config, peer); final PeerChannelInitializer initializer = new PeerChannelInitializer(config, encoder, peerEventLoopGroup, handler); final Bootstrap clientBootstrap = new Bootstrap(); clientBootstrap.group(networkEventLoopGroup).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true) .handler(initializer); final ChannelFuture connectFuture = clientBootstrap.connect(host, port); if (futureToNotify != null) { connectFuture.addListener(new ChannelFutureListener() { public void operationComplete(ChannelFuture future) throws Exception { if (future.isSuccess()) { futureToNotify.complete(null); LOGGER.info("Successfully connect to {}:{}", host, port); } else { futureToNotify.completeExceptionally(future.cause()); LOGGER.error("Could not connect to " + host + ":" + port, future.cause()); } } }); } } }