com.kixeye.kixmpp.p2p.node.RemoteNode.java Source code

Java tutorial

Introduction

Here is the source code for com.kixeye.kixmpp.p2p.node.RemoteNode.java

Source

package com.kixeye.kixmpp.p2p.node;

/*
 * #%L
 * Hermes
 * %%
 * Copyright (C) 2014 Charles Barry
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

import com.kixeye.kixmpp.p2p.ClusterClient;
import com.kixeye.kixmpp.p2p.message.JoinRequest;
import com.kixeye.kixmpp.p2p.message.JoinResponse;
import com.kixeye.kixmpp.p2p.message.MessageRegistry;
import com.kixeye.kixmpp.p2p.message.MessageWrapper;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.EventLoopGroup;

public class RemoteNode extends Node {

    private Channel channel;
    private NodeClient client;
    private Node localNode;

    /**
     * Constructor used to create a RemoteNode that connects to the node's server.
     * @param address
     * @param workerGroup
     * @throws InterruptedException
     */
    public RemoteNode(ClusterClient repository, NodeAddress address, EventLoopGroup workerGroup,
            MessageRegistry messageRegistry, Node localNode) throws InterruptedException {
        super(repository, address);
        this.state = State.CONNECTING;
        this.localNode = localNode;
        this.channel = null;
        this.client = new NodeClient();
        client.initialize(address.getHost(), address.getPort(), workerGroup, messageRegistry,
                new NodeChannelHandler());
    }

    /**
     * Constructor used to create a RemoteNode from an incoming connection.
     * @param id
     * @param address
     * @param channel
     * @param oldHandler
     */
    public RemoteNode(ClusterClient cluster, NodeId id, NodeAddress address, Channel channel,
            ChannelInboundHandlerAdapter oldHandler) {
        super(cluster, id, address);
        this.state = State.CONNECTED;
        this.channel = channel;
        this.client = null;

        // remove initial server handler and replace with node handler
        channel.pipeline().remove(oldHandler);
        channel.pipeline().addLast(new NodeChannelHandler());
    }

    @Override
    public void sendMessage(MessageWrapper wrapper) {
        if (channel != null) {
            ByteBuf buf = wrapper.getSerialized(cluster.getMessageRegistry());
            channel.writeAndFlush(buf.retain());
        }
    }

    /**
     * Close network connections associated with the node
     */
    @Override
    public void close() {
        super.close();

        // close the socket connection
        if (channel != null) {
            channel.close();
            channel = null;
        }

        // shutdown client
        if (client != null) {
            client.shutdown();
            client = null;
        }
    }

    /**
     * Channel handler for the Node.
     */
    public class NodeChannelHandler extends ChannelInboundHandlerAdapter {
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            // on connection establishment, request to join the cluster
            ctx.writeAndFlush(new JoinRequest(localNode.getId(), localNode.getAddress()));
        }

        @Override
        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
            close();
        }

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            if (msg instanceof JoinResponse) {
                JoinResponse joinResponse = (JoinResponse) msg;
                switch (joinResponse.getResult()) {
                case OK:
                    setId(joinResponse.getResponderId());
                    state = State.CONNECTED;
                    channel = ctx.channel();
                    cluster.addNode(RemoteNode.this);
                    break;
                case REJECTED_EXISTING_CONNECTION:
                    ctx.close();
                    break;
                case REJECTED_ID_ALREADY_IN_USE:
                    logger.error("JoinResponse: Duplicate ID in peer network!");
                    ctx.close();
                    break;
                default:
                    logger.error("JoinResponse: Invalid result {}", joinResponse.getResult().toString());
                    ctx.close();
                    break;
                }
            } else {
                cluster.getClusterListener().onMessage(cluster, id, msg);
            }
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            logger.error("Exception processing a message from node {}", getId().toString(), cause);
        }
    }
}