Java tutorial
/* * copyright 2014, gash * * Gash licenses this file to you 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. */ package poke.server.comm.monitor; import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioSocketChannel; import java.util.ArrayList; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import poke.comm.Image.Request; import poke.core.Mgmt.Management; import poke.core.Mgmt.MgmtHeader; import poke.core.Mgmt.Network; import poke.core.Mgmt.Network.NetworkAction; import poke.server.managers.ConnectionManager; /** * The monitor is a client-side component that can exist as as its own client or * as an internal component to a server. Its purpose is to process responses * from server management messages/responses - heartbeats (HB). * * It is conceivable to create a separate application/process that listens to * the network. However, one must consider the HB (management port) is more for * localized communication through the overlay network and not as a tool for * overall health of the network. For an external monitoring device, a UDP-based * communication is more appropriate. * * @author gash * */ public class CommMonitor { protected static Logger logger = LoggerFactory.getLogger("mgmt"); protected boolean messageWasSent = false; protected ChannelFuture channel; // do not use directly, call connect()! private EventLoopGroup group; private static int N = 0; // unique identifier private String whoami; private int iamNode; private int toNodeId; private String host; private int port; // this list is only used if the connection cannot be established - it holds // the listeners to be added. private List<CommMonitorListener> listeners = new ArrayList<CommMonitorListener>(); private CommMonitorHandler handler; /** * Create a heartbeat message processor. * * @param host * the hostname * @param port * This is the management port */ public CommMonitor(int iamNode, String host, int port, int toNodeId) { this.iamNode = iamNode; this.toNodeId = toNodeId; this.whoami = "mgmt-" + iamNode; this.host = host; this.port = port; this.group = new NioEventLoopGroup(); logger.info("Creating heartbeat monitor for " + host + "(" + port + ")"); } public boolean getmessageWasSent() { return messageWasSent; } public void setmessageWasSent(boolean value) { messageWasSent = value; } public CommMonitorHandler getHandler() { return handler; } /** * abstraction of notification in the communication * * @param listener */ public void addListener(CommMonitorListener listener) { if (handler == null && !listeners.contains(listener)) { listeners.add(listener); return; } try { handler.addListener(listener); } catch (Exception e) { logger.error("failed to add listener", e); } } public void release() { logger.warn("HeartMonitor: releasing resources"); ConnectionManager.removeConnection(toNodeId, false); for (Integer id : handler.listeners.keySet()) { CommMonitorListener ml = handler.listeners.get(id); ml.connectionClosed(); // hold back listeners to re-apply if the connection is // re-established. listeners.add(ml); } // TODO should wait a fixed time and use a listener to reset values;s channel = null; handler = null; } /** * create connection to remote server * * @return */ public Channel connect() { // Start the connection attempt. if (channel == null) { try { handler = new CommMonitorHandler(); CommMonitorInitializer mi = new CommMonitorInitializer(handler, false); Bootstrap b = new Bootstrap(); // @TODO newFixedThreadPool(2); b.group(group).channel(NioSocketChannel.class).handler(mi); b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000); b.option(ChannelOption.TCP_NODELAY, true); b.option(ChannelOption.SO_KEEPALIVE, true); // Make the connection attempt. System.out.println("Connecting to Host " + host + " Port " + port); channel = b.connect(host, port).syncUninterruptibly(); channel.awaitUninterruptibly(5000l); channel.channel().closeFuture().addListener(new MonitorClosedListener(this)); if (N == Integer.MAX_VALUE) N = 1; else N++; // add listeners waiting to be added if (listeners.size() > 0) { for (CommMonitorListener ml : listeners) handler.addListener(ml); listeners.clear(); } } catch (Exception ex) { if (logger.isDebugEnabled()) logger.debug("HeartMonitor: failed to initialize the heartbeat connection", ex); // logger.error("failed to initialize the heartbeat connection", // ex); } } if (channel != null && channel.isDone() && channel.isSuccess()) return channel.channel(); else { System.out.println("Unable to establish connection"); return null; } //throw new RuntimeException("Not able to establish connection to server"); } public boolean isConnected() { if (channel == null) return false; else return channel.channel().isOpen(); } public String getNodeInfo() { if (host != null) return host + ":" + port; else return "Unknown"; } /** * request the node to send heartbeats. * * @return did a connect and message succeed */ public boolean sendMessage(Request req) { // the join will initiate the other node's heartbeatMgr to reply to // this node's (caller) listeners. boolean rtn = false; try { Channel ch = connect(); if (!ch.isWritable()) { logger.error("Channel to node " + toNodeId + " not writable!"); } logger.info("HeartMonitor sending join message to " + toNodeId); // Network.Builder n = Network.newBuilder(); // // // 'N' allows us to track the connection restarts and to provide // // uniqueness // n.setFromNodeId(iamNode); // n.setToNodeId(toNodeId); // n.setAction(NetworkAction.NODEJOIN); // // MgmtHeader.Builder mhb = MgmtHeader.newBuilder(); // mhb.setOriginator(iamNode); // mhb.setTime(System.currentTimeMillis()); // // // TODO the security code is an authentication token to joint the // // cluster, all nodes in the cluster should share the same token or // // know how to authenticate a node. // mhb.setSecurityCode(-999); // // Management.Builder m = Management.newBuilder(); // m.setHeader(mhb.build()); // m.setGraph(n.build()); ch.writeAndFlush(req); rtn = true; } catch (Exception e) { // normal to get this exception as a node may not be reachable logger.debug("could not send connect to node " + toNodeId); } return rtn; } public String getHost() { return host; } public int getPort() { return port; } /** * for demo application only - this will enter a loop waiting for * heartbeatMgr messages. * * Notes: * <ol> * <li>this method is not used by the servers * <li>blocks if connection is created otherwise, it returns if the node is * not available. * </ol> */ // public void waitForever() { // try { // boolean connected = sendMessage(); // while (connected) { // Thread.sleep(2000); // } // // logger.info("---> trying to connect heartbeat"); // } catch (Exception e) { // // e.printStackTrace(); // } // } /** * Called when the channel tied to the monitor closes. Usage: * */ public static class MonitorClosedListener implements ChannelFutureListener { private CommMonitor monitor; public MonitorClosedListener(CommMonitor monitor) { this.monitor = monitor; } @Override public void operationComplete(ChannelFuture future) throws Exception { System.out.println("Operation completed, closing channel " + monitor.getNodeInfo()); monitor.release(); } } }