/*
* JBoss, Home of Professional Open Source
* Copyright 2011, Red Hat, Inc. and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.mobicents.tools.sip.balancer;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import java.net.InetAddress;
import java.nio.charset.Charset;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import org.apache.commons.validator.routines.InetAddressValidator;
import org.apache.log4j.Logger;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
import org.jboss.netty.handler.codec.http.HttpHeaders;
import org.jboss.netty.handler.codec.http.HttpResponse;
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
import org.jboss.netty.handler.codec.http.HttpVersion;
import org.mobicents.tools.heartbeat.api.HeartbeatConfig;
import org.mobicents.tools.heartbeat.api.IListener;
import org.mobicents.tools.heartbeat.api.IServerHeartbeatService;
import org.mobicents.tools.heartbeat.api.IServerListener;
import org.mobicents.tools.heartbeat.api.Node;
import org.mobicents.tools.heartbeat.api.Packet;
import org.mobicents.tools.heartbeat.api.Protocol;
import org.mobicents.tools.heartbeat.packets.HeartbeatResponsePacket;
import org.mobicents.tools.heartbeat.packets.ShutdownResponsePacket;
import org.mobicents.tools.heartbeat.packets.StartResponsePacket;
import org.mobicents.tools.heartbeat.packets.StopResponsePacket;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
/**
* <p>
* This is the placeholder for maintening information about alive nodes and
* the relation between a Call-Id and its attributed node.
* </p>
*
* @author M. Ranganathan
* @author baranowb
* @author <A HREF="mailto:jean.deruelle@gmail.com">Jean Deruelle</A>
*
*/
public class NodeRegisterImpl implements NodeRegister, IServerListener {
private static Logger logger = Logger.getLogger(NodeRegisterImpl.class.getCanonicalName());
public static final int POINTER_START = 0;
private long nodeInfoExpirationTaskInterval = 5000;
private long nodeExpiration = 5100;
private Timer taskTimer = new Timer();
private TimerTask nodeExpirationTask = null;
private InetAddress serverAddress = null;
private String latestVersion = Integer.MIN_VALUE + "";
BalancerRunner balancerRunner;
private IServerHeartbeatService heartbeatService;
private Gson gson = new Gson();
public NodeRegisterImpl(InetAddress serverAddress) {
super();
this.serverAddress = serverAddress;
}
/**
* {@inheritDoc}
*/
public boolean startRegistry(HeartbeatConfig heartbeatConfig) {
if(logger.isInfoEnabled()) {
logger.info("Node registry starting...");
}
try {
try {
Class<?> clazz = Class.forName(balancerRunner.balancerContext.nodeCommunicationProtocolClassName);
heartbeatService = (IServerHeartbeatService) clazz.newInstance();
} catch (Exception e) {
throw new RuntimeException("Error loading the node communication protocol: " + balancerRunner.balancerContext.nodeCommunicationProtocolClassName, e);
}
balancerRunner.balancerContext.aliveNodes = new CopyOnWriteArrayList<Node>();
balancerRunner.balancerContext.jvmRouteToSipNode = new ConcurrentHashMap<String, Node>();
heartbeatService.init(this,serverAddress, heartbeatConfig);
heartbeatService.startServer();
this.nodeExpirationTask = new NodeExpirationTimerTask();
this.taskTimer.scheduleAtFixedRate(this.nodeExpirationTask, this.nodeInfoExpirationTaskInterval, this.nodeInfoExpirationTaskInterval);
if(logger.isInfoEnabled()) {
logger.info("Node expiration task created");
logger.info("Node registry started");
}
} catch (Exception e) {
logger.error("Unexpected exception while starting the registry", e);
return false;
}
return true;
}
/**
* {@inheritDoc}
*/
public boolean stopRegistry() {
if(logger.isInfoEnabled()) {
logger.info("Stopping node registry...");
}
for(Entry<String, InvocationContext> ctxEntry : balancerRunner.contexts.entrySet())
{
for(Entry<KeySip, Node> entry : ctxEntry.getValue().sipNodeMap(true).entrySet())
{
if(entry.getValue().getProperties().get(Protocol.HEARTBEAT_PORT)!=null)
heartbeatService.sendPacket(entry.getValue().getIp(),Integer.parseInt(entry.getValue().getProperties().get(Protocol.HEARTBEAT_PORT)));
}
for(Entry<KeySip, Node> entry : ctxEntry.getValue().sipNodeMap(false).entrySet())
{
if(entry.getValue().getProperties().get(Protocol.HEARTBEAT_PORT)!=null)
heartbeatService.sendPacket(entry.getValue().getIp(),Integer.parseInt(entry.getValue().getProperties().get(Protocol.HEARTBEAT_PORT)));
}
}
heartbeatService.stopServer();
boolean isDeregistered = true;
boolean taskCancelled = nodeExpirationTask.cancel();
if(logger.isInfoEnabled()) {
logger.info("Node Expiration Task cancelled " + taskCancelled);
}
if(balancerRunner.balancerContext.allNodesEver!=null)
balancerRunner.balancerContext.allNodesEver.clear();
if(logger.isInfoEnabled()) {
logger.info("Node registry stopped.");
}
return isDeregistered;
}
// ********* CLASS TO BE EXPOSED VIA RMI
private class RegisterRMIStub extends UnicastRemoteObject implements NodeRegisterRMIStub {
/**
*
*/
private static final long serialVersionUID = 1L;
protected RegisterRMIStub(int port) throws RemoteException {
super(port);
}
/*
* (non-Javadoc)
* @see org.mobicents.tools.sip.balancer.NodeRegisterRMIStub#handlePing(java.util.ArrayList)
*/
public void handlePing(ArrayList<Node> ping) throws RemoteException {
handlePingInRegister(ping);
}
/*
* (non-Javadoc)
* @see org.mobicents.tools.sip.balancer.NodeRegisterRMIStub#forceRemoval(java.util.ArrayList)
*/
public void forceRemoval(ArrayList<Node> ping)
throws RemoteException {
forceRemovalInRegister(ping);
}
public void switchover(String fromJvmRoute, String toJvmRoute) throws RemoteException {
jvmRouteSwitchover(fromJvmRoute, toJvmRoute);
}
}
// ***** NODE MGMT METHODS
/**
* {@inheritDoc}
*/
public void unStickSessionFromNode(String callID) {
if(logger.isDebugEnabled()) {
logger.debug("unsticked CallId " + callID + " from node " + null);
}
}
/**
* {@inheritDoc}
*/
public Node stickSessionToNode(String callID, Node sipNode) {
if(logger.isDebugEnabled()) {
logger.debug("sticking CallId " + callID + " to node " + null);
}
return null;
}
/**
* {@inheritDoc}
*/
public Node getGluedNode(String callID) {
if(logger.isDebugEnabled()) {
logger.debug("glueued node " + null + " for CallId " + callID);
}
return null;
}
/**
* {@inheritDoc}
*/
public boolean isNodePresent(String host, int port, String transport, String version) {
if(getNode(host, port, transport, version) != null) {
return true;
}
return false;
}
/**
* {@inheritDoc}
*/
public Node getNode(String host, int port, String transport, String version) {
for (Node node : balancerRunner.balancerContext.aliveNodes) {
if(logger.isDebugEnabled()) {
logger.debug("node to check against " + node);
}
// https://telestax.atlassian.net/browse/LB-9 Prevent Routing of Requests to Nodes that exposed null IP address
if(node != null && node.getIp() != null && node.getIp().equals(host)) {
Integer nodePort = Integer.parseInt(node.getProperties().get(transport.toLowerCase() + "Port"));
if(nodePort != null) {
if(nodePort == port) {
if(version == null) {
return node;
} else {
String nodeVersion = node.getProperties().get("version");
if(nodeVersion == null) nodeVersion = "0";
if(version.equals(nodeVersion)) {
return node;
}
}
}
}
}
}
if(logger.isDebugEnabled()) {
logger.debug("checking if the node is still alive for " + host + ":" + port + "/" + transport + " : false");
}
return null;
}
class NodeExpirationTimerTask extends TimerTask {
public void run() {
if(logger.isTraceEnabled()) {
logger.trace("NodeExpirationTimerTask Running");
}
for (Node node : balancerRunner.balancerContext.aliveNodes) {
long expirationTime = node.getTimeStamp() + nodeExpiration;
String nodeHostname = node.getHostName();
if (expirationTime < System.currentTimeMillis() && !nodeHostname.contains("ExtraServerNode")) {
InvocationContext ctx = balancerRunner.getInvocationContext(node.getProperties().get("version"));
balancerRunner.balancerContext.aliveNodes.remove(node);
String instanceId = node.getProperties().get("Restcomm-Instance-Id");
if(instanceId!=null)
ctx.httpNodeMap.remove(instanceId);
if(node.getProperties().get("smppPort")!=null)
{
ctx.smppNodeMap.remove(new KeySmpp(node));
}
Boolean isIpV6=LbUtils.isValidInet6Address(node.getIp());
ctx.sipNodeMap(isIpV6).remove(new KeySip(node,isIpV6));
ctx.sessionNodeMap(isIpV6).remove(new KeySession(node.getProperties().get(Protocol.SESSION_ID)));
ctx.balancerAlgorithm.nodeRemoved(node);
logger.warn("NodeExpirationTimerTask Run NSync["
+ node + "] removed. Last timestamp: " + node.getTimeStamp() +
", current: " + System.currentTimeMillis()
+ " diff=" + ((double)System.currentTimeMillis()-node.getTimeStamp() ) +
"ms and tolerance=" + nodeExpiration + " ms");
} else {
if(logger.isTraceEnabled()) {
logger.trace("node time stamp : " + expirationTime + " , current time : "
+ System.currentTimeMillis());
}
}
}
if(logger.isTraceEnabled()) {
logger.trace("NodeExpirationTimerTask Done");
}
}
}
/**
* {@inheritDoc}
*/
public synchronized void handlePingInRegister(ArrayList<Node> ping) {
for (Node pingNode : ping) {
if(pingNode.getIp() == null) {
// https://telestax.atlassian.net/browse/LB-9 Prevent Routing of Requests to Nodes that exposed null IP address
logger.warn("[" + pingNode + "] not added as its IP is null, the node is sending bad information");
} else {
Boolean isIpV6=LbUtils.isValidInet6Address(pingNode.getIp());
Boolean isIpV4=InetAddressValidator.getInstance().isValidInet4Address(pingNode.getIp());
if(!isIpV4 && !isIpV6)
logger.warn("[" + pingNode + "] not added as its IP is null, the node is sending bad information");
else
{
String version = pingNode.getProperties().get("version");
if(version == null) version = "0";
InvocationContext ctx = balancerRunner.getInvocationContext(version);
//if bad node changed sessioId it means that the node was restarted so we remove it from map of bad nodes
KeySip keySip = new KeySip(pingNode,isIpV6);
if(ctx.sipNodeMap(isIpV6).get(keySip)!=null&&ctx.sipNodeMap(isIpV6).get(keySip).isBad())
{
if(ctx.sipNodeMap(isIpV6).get(keySip).getProperties().get("sessionId").equals(pingNode.getProperties().get("sessionId")))
continue;
else
ctx.sipNodeMap(isIpV6).get(keySip).setBad(false);
}
pingNode.updateTimerStamp();
//logger.info("Pingnode updated " + pingNode);
if(pingNode.getProperties().get("jvmRoute") != null) {
// Let it leak, we will have 10-100 nodes, not a big deal if it leaks.
// We need info about inactive nodes to do the failover
balancerRunner.balancerContext.jvmRouteToSipNode.put(
pingNode.getProperties().get("jvmRoute"), pingNode);
}
Node nodePresent = ctx.sipNodeMap(isIpV6).get(keySip);
// adding done afterwards to avoid ConcurrentModificationException when adding the node while going through the iterator
if(nodePresent != null)
{
nodePresent.updateTimerStamp();
if(logger.isTraceEnabled()) {
logger.trace("Ping " + nodePresent.getTimeStamp());
}
if(pingNode.getProperties().get("GRACEFUL_SHUTDOWN")!=null&&
pingNode.getProperties().get("GRACEFUL_SHUTDOWN").equals("true"))
{
logger.info("LB will exclude node " + nodePresent + " for new calls because of GRACEFUL_SHUTDOWN");
ctx.sipNodeMap(isIpV6).get(keySip).setGracefulShutdown(true);
}
}
else if(pingNode.getProperties().get("GRACEFUL_SHUTDOWN")!=null&&
pingNode.getProperties().get("GRACEFUL_SHUTDOWN").equals("true"))
{
if(logger.isDebugEnabled())
logger.debug("Ping from node which LB exclude because of GRACEFUL_SHUTDOWN : " + pingNode);
}
else
{
Integer current = Integer.parseInt(version);
Integer latest = Integer.parseInt(latestVersion);
latestVersion = Math.max(current, latest) + "";
balancerRunner.balancerContext.aliveNodes.add(pingNode);
ctx.sipNodeMap(isIpV6).put(keySip, pingNode);
String instanceId = pingNode.getProperties().get("Restcomm-Instance-Id");
if(instanceId!=null)
ctx.httpNodeMap.put(new KeyHttp(instanceId), pingNode);
Integer smppPort = null;
if(pingNode.getProperties().get("smppPort")!=null)
{
smppPort = Integer.parseInt(pingNode.getProperties().get("smppPort"));
ctx.smppNodeMap.put(new KeySmpp(pingNode), pingNode);
}
ctx.balancerAlgorithm.nodeAdded(pingNode);
balancerRunner.balancerContext.allNodesEver.add(pingNode);
pingNode.updateTimerStamp();
if(logger.isInfoEnabled()) {
logger.info("NodeExpirationTimerTask Run NSync["
+ pingNode + "] added");
}
}
}
}
}
}
public String getLatestVersion() {
return latestVersion;
}
/**
* {@inheritDoc}
*/
public void forceRemovalInRegister(ArrayList<Node> ping) {
for (Node pingNode : ping) {
InvocationContext ctx = balancerRunner.getInvocationContext(
pingNode.getProperties().get("version"));
//ctx.nodes.remove(pingNode);
String instanceId = pingNode.getProperties().get("Restcomm-Instance-Id");
if(instanceId!=null)
ctx.httpNodeMap.remove(instanceId);
Integer smppPort = null;
if(pingNode.getProperties().get("smppPort") != null)
smppPort = Integer.parseInt(pingNode.getProperties().get("smppPort"));
if(smppPort!=null)
ctx.smppNodeMap.remove(new KeySmpp(pingNode));
Boolean isIpV6=LbUtils.isValidInet6Address(pingNode.getIp());
ctx.sipNodeMap(isIpV6).remove(new KeySip(pingNode,isIpV6));
boolean nodePresent = false;
Iterator<Node> nodesIterator = balancerRunner.balancerContext.aliveNodes.iterator();
while (nodesIterator.hasNext() && !nodePresent) {
Node node = (Node) nodesIterator.next();
if (node.equals(pingNode)) {
nodePresent = true;
}
}
// removal done afterwards to avoid ConcurrentModificationException when removing the node while goign through the iterator
if(nodePresent) {
balancerRunner.balancerContext.aliveNodes.remove(pingNode);
ctx.balancerAlgorithm.nodeRemoved(pingNode);
if(logger.isInfoEnabled()) {
logger.info("NodeExpirationTimerTask Run NSync["
+ pingNode + "] forcibly removed due to a clean shutdown of a node. Numbers of nodes present in the balancer : "
+ balancerRunner.balancerContext.aliveNodes.size());
}
}
}
}
/**
* {@inheritDoc}
*/
public InetAddress getAddress() {
return this.serverAddress;
}
/**
* {@inheritDoc}
*/
public long getNodeExpiration() {
return this.nodeExpiration;
}
/**
* {@inheritDoc}
*/
public long getNodeExpirationTaskInterval() {
return this.nodeInfoExpirationTaskInterval;
}
/**
* {@inheritDoc}
*/
public void setNodeExpiration(long value) throws IllegalArgumentException {
if (value < 15)
throw new IllegalArgumentException("Value cant be less than 15");
this.nodeExpiration = value;
}
/**
* {@inheritDoc}
*/
public void setNodeExpirationTaskInterval(long value) {
if (value < 15)
throw new IllegalArgumentException("Value cant be less than 15");
this.nodeInfoExpirationTaskInterval = value;
}
public Node[] getAllNodes() {
return balancerRunner.balancerContext.aliveNodes.toArray(new Node[]{});
}
public Node getNextNode() throws IndexOutOfBoundsException {
// TODO Auto-generated method stub
return null;
}
public void jvmRouteSwitchover(String fromJvmRoute, String toJvmRoute) {
for(InvocationContext ctx : balancerRunner.contexts.values()) {
ctx.balancerAlgorithm.jvmRouteSwitchover(fromJvmRoute, toJvmRoute);
}
}
@Override
public void responseReceived(JsonObject json) {
// TODO Auto-generated method stub
}
@Override
public synchronized void startRequestReceived(MessageEvent e, JsonObject json)
{
Node node = new Node(json);
if(node.getIp() == null)
{
logger.warn("[" + node + "] not added as it's IP is null, the node is sending bad information");
}
else
{
Boolean isIpV6=LbUtils.isValidInet6Address(node.getIp());
Boolean isIpV4=InetAddressValidator.getInstance().isValidInet4Address(node.getIp());
if(!isIpV4 && !isIpV6)
{
logger.warn("[" + node + "] not added as it's IP is incorrect, the node is sending bad information");
}
else
{
String version = node.getProperties().get("version");
if(version == null) version = "0";
InvocationContext ctx = balancerRunner.getInvocationContext(version);
KeySip keySip = new KeySip(node,isIpV6);
KeySession keySession = new KeySession(node.getProperties().get(Protocol.SESSION_ID));
node.updateTimerStamp();
if(node.getProperties().get("jvmRoute") != null)
balancerRunner.balancerContext.jvmRouteToSipNode.put(node.getProperties().get("jvmRoute"), node);
//Node nodePresent = ctx.sipNodeMap(isIpV6).get(keySip);
Node nodePresent = ctx.sessionNodeMap(isIpV6).get(keySession);
if(nodePresent != null&&!nodePresent.isBad())
{
logger.warn("LB got start request from existed node " + nodePresent);
}else if(nodePresent != null&&nodePresent.isBad())
{
logger.info("LB got start request from restarted node " + nodePresent);
nodePresent.setBad(false);
nodePresent.setFailCounter(0);
}
else
{
logger.debug("LB got start request from node " + node);
Integer current = Integer.parseInt(version);
Integer latest = Integer.parseInt(latestVersion);
latestVersion = Math.max(current, latest) + "";
balancerRunner.balancerContext.aliveNodes.add(node);
ctx.sessionNodeMap(isIpV6).put(keySession, node);
ctx.sipNodeMap(isIpV6).put(keySip, node);
String instanceId = node.getProperties().get(Protocol.RESTCOMM_INSTANCE_ID);
if(instanceId!=null)
ctx.httpNodeMap.put(new KeyHttp(instanceId), node);
if(node.getProperties().get("smppPort")!=null)
ctx.smppNodeMap.put(new KeySmpp(node), node);
ctx.balancerAlgorithm.nodeAdded(node);
balancerRunner.balancerContext.allNodesEver.add(node);
node.updateTimerStamp();
if(logger.isInfoEnabled())
logger.info("New node added to map of nodes [" + node + "] ");
}
}
}
if(e!=null)
writeResponse(e, HttpResponseStatus.OK, Protocol.START, Protocol.OK);
}
@Override
public synchronized void heartbeatRequestReceived(MessageEvent e, JsonObject json)
{
logger.trace("LB got heartbeat from Node : " + json );
KeySession keySession = new KeySession(json.get(Protocol.SESSION_ID).toString());
boolean was = false;
for(Entry<String, InvocationContext> ctxEntry : balancerRunner.contexts.entrySet())
{
InvocationContext ctx = ctxEntry.getValue();
Node nodePresentIPv4 = ctx.sessionNodeMap(false).get(keySession);
Node nodePresentIPv6 = null;
if(nodePresentIPv4!=null)
{
nodePresentIPv4.updateTimerStamp();
was = true;
}
else if((nodePresentIPv6 = ctx.sessionNodeMap(true).get(keySession))!=null)
{
nodePresentIPv6.updateTimerStamp();
was = true;
}
}
if(!was)
{
logger.error("LB got heartbeat ( " + json + " ) from node which not pesent in maps");
}
if(e!=null)
writeResponse(e, HttpResponseStatus.OK, Protocol.HEARTBEAT, Protocol.OK);
}
@Override
public synchronized void shutdownRequestReceived(MessageEvent e, JsonObject json)
{
boolean was = false;
logger.info("LB got graceful shutdown from Node : " + json);
KeySession keySession = new KeySession(json.get(Protocol.SESSION_ID).toString());
for(Entry<String, InvocationContext> ctxEntry : balancerRunner.contexts.entrySet())
{
InvocationContext ctx = ctxEntry.getValue();
Node nodePresentIPv4 = ctx.sessionNodeMap(false).get(keySession);
Node nodePresentIPv6 = null;
if(nodePresentIPv4!=null)
{
logger.info("LB will exclude node "+ nodePresentIPv4 +"for new calls because of shutdown request");
nodePresentIPv4.setGracefulShutdown(true);
was = true;
}
else if((nodePresentIPv6 = balancerRunner.getLatestInvocationContext().sessionNodeMap(true).get(keySession))!=null)
{
logger.info("LB will exclude node "+ nodePresentIPv6 +"for new calls because of shutdown request");
nodePresentIPv6.setGracefulShutdown(true);
was = true;
}
}
if(!was)
{
logger.error("LB got shutdown request ( " + json + " ) from node which not pesent in maps");
}
if(e!=null)
writeResponse(e, HttpResponseStatus.OK, Protocol.SHUTDOWN, Protocol.OK);
}
@Override
public synchronized void stopRequestReceived(MessageEvent e, JsonObject json)
{
boolean isIpV6 = false;
boolean was = false;
logger.info("LB got stop request from Node : " + json);
KeySession keySession = new KeySession(json.get(Protocol.SESSION_ID).toString());
for(Entry<String, InvocationContext> ctxEntry : balancerRunner.contexts.entrySet())
{
InvocationContext ctx = ctxEntry.getValue();
Node nodePresent = null;
Node nodePresentIpv4 = ctx.sessionNodeMap(true).get(keySession);
Node nodePresentIpv6 = ctx.sessionNodeMap(false).get(keySession);
if(nodePresentIpv4!=null)
{
isIpV6 = true;
nodePresent = nodePresentIpv4;
}
else if(nodePresentIpv6!=null)
{
nodePresent = nodePresentIpv6;
}
if(nodePresent!=null)
{
was = true;
Node removedNode = ctx.sessionNodeMap(isIpV6).remove(keySession);
KeySip keySip = new KeySip(removedNode,isIpV6);
ctx.sipNodeMap(isIpV6).remove(keySip);
String instanceId = nodePresent.getProperties().get(Protocol.RESTCOMM_INSTANCE_ID);
if(instanceId!=null)
ctx.httpNodeMap.remove(instanceId);
String smppPort = nodePresent.getProperties().get(Protocol.SMPP_PORT);
if(smppPort!=null)
ctx.smppNodeMap.remove(new KeySmpp(nodePresent));
balancerRunner.balancerContext.aliveNodes.remove(nodePresent);
ctx.balancerAlgorithm.nodeRemoved(nodePresent);
if(logger.isInfoEnabled())
logger.info(" LB got STOP request from node : " + nodePresent + ". So it will be rmoved : " + balancerRunner.balancerContext.aliveNodes.size());
}
}
if(!was)
{
logger.error("LB got shutdown request ( " + json + " ) from node which not pesent in maps : " +
balancerRunner.getLatestInvocationContext().sipNodeMap(isIpV6));
}
if(e!=null)
writeResponse(e, HttpResponseStatus.OK, Protocol.STOP, Protocol.OK);
}
private synchronized void writeResponse(MessageEvent e, HttpResponseStatus status, String command, String responceString)
{
Packet packet = null;
switch(command)
{
case Protocol.HEARTBEAT:
packet = new HeartbeatResponsePacket(responceString);
break;
case Protocol.START:
packet = new StartResponsePacket(responceString);
break;
case Protocol.SHUTDOWN:
packet = new ShutdownResponsePacket(responceString);
break;
case Protocol.STOP:
packet = new StopResponsePacket(responceString);
break;
}
ChannelBuffer buf = ChannelBuffers.copiedBuffer(gson.toJson(packet), Charset.forName("UTF-8"));
HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, status);
response.setHeader(HttpHeaders.Names.CONTENT_TYPE, APPLICATION_JSON);
response.setHeader(HttpHeaders.Names.CONTENT_LENGTH, buf.readableBytes());
response.setContent(buf);
ChannelFuture future = e.getChannel().write(response);
future.addListener(ChannelFutureListener.CLOSE);
}
@Override
public void switchoverRequestReceived(MessageEvent e, JsonObject json) {
jvmRouteSwitchover(json.get("fromJvmRoute").toString(), json.get("toJvmRoute").toString());
writeResponse(e, HttpResponseStatus.OK, Protocol.SWITCHOVER, Protocol.OK);
}
}