/* * TeleStax, Open Source Cloud Communications * Copyright 2011-2017, Telestax Inc and individual contributors * by the @authors tag. * * This program is free software: you can redistribute it and/or modify * under the terms of the GNU Affero General Public License as * published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/> */ package org.mobicents.tools.heartbeat.impl; import static javax.ws.rs.core.MediaType.APPLICATION_JSON; import java.net.InetSocketAddress; import java.nio.charset.Charset; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.log4j.Logger; import org.jboss.netty.bootstrap.ServerBootstrap; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelFuture; import org.jboss.netty.channel.ChannelFutureListener; import org.jboss.netty.channel.MessageEvent; import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; 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.Node; import org.mobicents.tools.heartbeat.api.Packet; import org.mobicents.tools.heartbeat.api.Protocol; import org.mobicents.tools.heartbeat.interfaces.IClientListener; import org.mobicents.tools.heartbeat.packets.StopResponsePacket; import org.mobicents.tools.heartbeat.server.ServerPipelineFactory; import com.google.gson.Gson; import com.google.gson.JsonObject; /** * @author Konstantin Nosach (kostyantyn.nosach@telestax.com) */ public class HeartbeatService implements IClientListener{ private static final Logger logger = Logger.getLogger(Client.class.getCanonicalName()); private Set<ClientController> clientControllers = null; private String heartBeatIp = null; private int heartBeatPort = 2222; private int heartBeatInterval = 5000; private int startInterval = 5000; private int maxHeartbeatErrors = 3; private ExecutorService executor = Executors.newCachedThreadPool(); private ServerBootstrap serverBootstrap; private Channel serverChannel; private Gson gson = new Gson(); private AtomicBoolean started=new AtomicBoolean(false); public HeartbeatService(String heartBeatIp, int heartBeatPort, int heartBeatInterval, int startInterval, int maxHeartbeatErrors) { clientControllers = new CopyOnWriteArraySet<ClientController>(); this.heartBeatIp = heartBeatIp; this.heartBeatPort = heartBeatPort; this.heartBeatInterval = heartBeatInterval; this.startInterval = startInterval; this.maxHeartbeatErrors = maxHeartbeatErrors; } public void start () { serverBootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(executor, executor)); serverBootstrap.setPipelineFactory(new ServerPipelineFactory(this)); serverChannel = serverBootstrap.bind(new InetSocketAddress(heartBeatIp, heartBeatPort)); this.started.set(true); logger.info("Heartbeat service listen on " + heartBeatIp + ":"+ heartBeatPort+" (Node's side)"); } public void stop(boolean isGracefullShutdown) { for(ClientController clientController : clientControllers) clientController.stopClient(isGracefullShutdown); serverChannel.unbind(); serverChannel.close(); serverChannel.getCloseFuture().awaitUninterruptibly(); } public Boolean started() { return started.get(); } public void restartClientController(String lbAddress, int lbHeartbeatPort, Node node) { for(ClientController cc:clientControllers) { if(cc.getLbAddress().equalsIgnoreCase(lbAddress) && cc.getLbPort()==lbHeartbeatPort) { cc.updateNode(node); break; } } } public void startClientController(String lbAddress, int lbHeartbeatPort, Node node) { ClientController currController = new ClientController(this, lbAddress, lbHeartbeatPort, node, startInterval , heartBeatInterval, maxHeartbeatErrors, executor); currController.startClient(); clientControllers.add(currController); } public void stopClientController(String lbAddress, int lbHeartbeatPort) { for(ClientController cc:clientControllers) { if(cc.getLbAddress().equalsIgnoreCase(lbAddress) && cc.getLbPort()==lbHeartbeatPort) { cc.stopClient(false); break; } } } public void switchover(String lbAddress, int lbPort , String fromJvmRoute, String toJvmRoute) { for(ClientController cc : clientControllers) { if(cc.getLbAddress().equals(lbAddress)&&cc.getLbPort()==lbPort) { cc.switchover(fromJvmRoute, toJvmRoute); logger.info("client controller connected to LB " +cc.getLbAddress() + ":"+ cc.getLbPort() + " send switching over from " + fromJvmRoute + " to " + toJvmRoute); } } } @Override public void responseReceived(JsonObject json) { } @Override public void stopRequestReceived(MessageEvent e, JsonObject json) { logger.info("stop request received from LB : " + json); for(ClientController cc : clientControllers) { if(cc.getLbAddress().equals(json.get("ipAddress").toString().replace("\"",""))&&cc.getLbPort()==Integer.parseInt(json.get("port").toString())) { cc.restartClient(); logger.info("client controller connected to LB " +cc.getLbAddress() + ":"+ cc.getLbPort() + " have changed state to initial"); } } 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.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); } public boolean isStarted(String lbAddress, int lbPort) { boolean isStarted = false; for(ClientController cc : clientControllers) { if(cc.getLbAddress().equals(lbAddress)&&cc.getLbPort()==lbPort) { isStarted = true; } } return isStarted; } }