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);
}
}
}