/* * Copyright 2014-2016 CyberVision, Inc. * * 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. */ package org.kaaproject.kaa.server.thrift; import org.kaaproject.kaa.server.common.thrift.KaaThriftService; import org.kaaproject.kaa.server.common.zk.WorkerNodeTracker; import org.kaaproject.kaa.server.common.zk.gen.ConnectionInfo; import org.kaaproject.kaa.server.common.zk.gen.OperationsNodeInfo; import org.kaaproject.kaa.server.common.zk.operations.OperationsNodeListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * Neighbors Class. Collect all Operations Servers neighbors through listening * ZooKeeper OperationsNode changes. Use thriftHost:thriftPort as server Key and * hold Map of NeighborConnection. * * @author Andrey Panasenko * @author Andrew Shvayka */ public class Neighbors<T extends NeighborTemplate<V>, V> { private static final Logger LOG = LoggerFactory.getLogger(Neighbors.class); private final KaaThriftService serviceType; private final ConcurrentMap<String, NeighborConnection<T, V>> neigbors; private final int maxNumberNeighborConnections; private final T template; private volatile String zkId; /** * Default constructor. If zkNode is not set, postpone timer task to wait * until node is set. */ public Neighbors(KaaThriftService serviceType, T template, int maxNumberNeighborConnections) { this.serviceType = serviceType; this.template = template; this.maxNumberNeighborConnections = maxNumberNeighborConnections; this.neigbors = new ConcurrentHashMap<String, NeighborConnection<T, V>>(); } /** * Build server ID from ConnectionInfo object. * * @param info ConnectionInfo * @return server ID in format thriftHost:thriftPort */ public static String getServerId(ConnectionInfo info) { return getServerId(KaaThriftService.OPERATIONS_SERVICE, info); } /** * Build server ID from ConnectionInfo object. * * @param service KaaThriftService * @param info ConnectionInfo * @return server ID in format thriftHost:thriftPort */ public static String getServerId(KaaThriftService service, ConnectionInfo info) { StringBuffer sb = new StringBuffer(); sb.append(info.getThriftHost()); sb.append(":"); sb.append(info.getThriftPort()); sb.append(":"); sb.append(service.name()); return sb.toString(); } public void sendMessage(ConnectionInfo info, V msg) { sendMessages(info, Collections.singleton(msg)); } /** * Create connection and send messages. * * @param info is information about connection * @param msgs <code>Collection</code> of messages */ public void sendMessages(ConnectionInfo info, Collection<V> msgs) { NeighborConnection<T, V> neighbor = neigbors.get(getServerId(info)); if (neighbor != null) { try { neighbor.sendMessages(msgs); } catch (InterruptedException ex) { LOG.error("Failed to send message to {}", neighbor.getId()); throw new RuntimeException(ex); } } else { LOG.warn("Can't find server for id {}", getServerId(info)); } } public void brodcastMessage(V msg) { brodcastMessages(Collections.singleton(msg)); } /** * Get connections from field <code>neigbors</code> and send messages. * * @param msgs <code>Collection</code> of messages */ public void brodcastMessages(Collection<V> msgs) { if (LOG.isTraceEnabled()) { LOG.trace("Broadcasting {} msgs to {} neighbors", msgs.size(), neigbors.values().size()); } for (NeighborConnection<T, V> neighbor : neigbors.values()) { LOG.trace("Broadcasting to {} neighbor", neighbor); try { neighbor.sendMessages(msgs); } catch (InterruptedException ex) { LOG.warn("Failed to send message to {}", neighbor.getId()); } } } /** * Shutdown all neighbors connections and cancel timer task if exist. */ public void shutdown() { for (NeighborConnection<T, V> neigbor : neigbors.values()) { LOG.info("Shuting down neighbor connection {}", neigbor.getId()); neigbor.shutdown(); } neigbors.clear(); } /** * Return current list of Neighbors. * */ public List<NeighborConnection<T, V>> getNeighbors() { return new LinkedList<NeighborConnection<T, V>>(neigbors.values()); } /** * Return specific Neighbor connection by Id. * * @param serverId String in format thriftHost:thriftPort * @return NeighborConnection or null if such server not exist */ public NeighborConnection<T, V> getNeghborConnection(String serverId) { return neigbors.get(serverId); } public void setZkNode(KaaThriftService service, ConnectionInfo connectionInfo, WorkerNodeTracker zkNode) { setZkNode(getServerId(service, connectionInfo), zkNode); } /** * Zk Node setter. Notify ZK initialization. * * @param zkNode the zkNode to set */ private void setZkNode(String id, WorkerNodeTracker zkNode) { this.zkId = id; zkNode.addListener(new OperationsNodeListener() { @Override public void onNodeUpdated(OperationsNodeInfo nodeInfo) { addOpsServer(nodeInfo); } @Override public void onNodeAdded(OperationsNodeInfo nodeInfo) { addOpsServer(nodeInfo); } @Override public void onNodeRemoved(OperationsNodeInfo nodeInfo) { String opId = getServerId(nodeInfo.getConnectionInfo()); if (!zkId.equals(opId)) { NeighborConnection<T, V> connection = neigbors.remove(opId); if (connection != null) { connection.shutdown(); } LOG.info("Operations server {} removed to {} Neighbors list ({}). Now {} neighbors", opId, neigbors.size()); } } }); List<OperationsNodeInfo> nodes = zkNode.getCurrentOperationServerNodes(); for (OperationsNodeInfo opServer : nodes) { addOpsServer(opServer); } LOG.debug("Neighbor zk init complete: {} neighbors registered.", neigbors.size()); } private void addOpsServer(OperationsNodeInfo opServer) { LOG.trace("[{}] Building id for {}", zkId, opServer.getConnectionInfo()); String opId = getServerId(serviceType, opServer.getConnectionInfo()); if (!zkId.equals(opId)) { LOG.trace("Adding {} to {}", opId, neigbors); neigbors.putIfAbsent(opId, new NeighborConnection<T, V>(opServer.getConnectionInfo(), maxNumberNeighborConnections, template)); neigbors.get(opId).start(); LOG.info("Operations server {} added/updated to {} Neighbors list. Now {} neighbors", opId, zkId, neigbors.size()); } } }