view src/main/java/net/borgac/clusterrpc/client/ClientChannel.java @ 5:117cb812e28a

Implement several lower-level classes (networking/framing)
author Lewin Bormann <lbo@spheniscida.de>
date Fri, 23 Sep 2016 22:01:17 +0200
parents
children 0e608c466a58
line wrap: on
line source

package net.borgac.clusterrpc.client;

import java.io.Closeable;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

/**
 * ClientChannel implements the transport of RPC calls to the server.
 *
 * It's mainly responsible for peer management. Framing and error handling are
 * mostly handled in SocketWrapper.
 *
 * @author lbo
 */
public final class ClientChannel implements Closeable {

    // Simple seed is good enough here.
    private static final Random CHANNEL_ID_GENERATOR = new Random(
            Instant.now().getEpochSecond());

    private SocketWrapper sock;
    private int peerCounter;

    private Map<PeerHandle, PeerAddress> peers;
    private final long channelId;

    /**
     * Initialize a ClientChannel with no connections.
     */
    public ClientChannel() {
        this.sock = new SocketWrapper();
        this.peerCounter = 0;
        this.peers = new HashMap<>();
        this.channelId = CHANNEL_ID_GENERATOR.nextLong();
    }

    /**
     * Initialize a ClientChannel, and connect to `address`.
     *
     * @param address Address of the first peer to connect to.
     */
    public ClientChannel(PeerAddress address) {
        this();
        connect(address);
    }

    /**
     * Connect to an RPC server.
     *
     * This method can be called multiple times to connect to multiple peers. If
     * a channel is connected to multiple peers, requests are sent in a
     * round-robin manner. While possible, it is not recommended to use this as
     * debugging may become difficult in case one or more of the peers
     * disappear.
     *
     * @param address
     * @return A PeerHandle that can be used to later disconnect from that peer.
     */
    public PeerHandle connect(PeerAddress address) {
        PeerHandle handle = new PeerHandle(channelId, peerCounter);
        this.peerCounter++;
        peers.put(handle, address);

        sock.connect(address);

        return handle;
    }

    /**
     * Disconnect the channel from a peer.
     *
     * @param handle A handle returned by `connect()`.
     * @return True if the peer could be disconnected, otherwise false.
     */
    public boolean disconnect(PeerHandle handle) {
        if (handle.associatedChannel == channelId) {
            PeerAddress address = peers.get(handle);
            if (address != null) {
                sock.disconnect(address);
                peers.remove(handle);

                return true;
            }
        }
        return false;
    }

    @Override
    public void close() {
        sock.close();
    }
}