/** * */ package system; import java.io.IOException; import java.net.InetAddress; import java.net.MalformedURLException; import java.net.UnknownHostException; import java.rmi.Naming; import java.rmi.NotBoundException; import java.rmi.RemoteException; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.Random; import java.util.Vector; import java.util.logging.Logger; import exceptions.DataNotFoundException; import system.Worker.WorkerState; import utility.JPregelLogger; import utility.Pair; /** * This class aggregates messages which are meant to be other machines at the * end of a superstep. This reduces the number of RMI class destined to any * machine in the cluster. * * @author Manasa Chandrasekhar * @author Kowshik Prakasam * */ public class Communicator implements Runnable { // A map of ID to MessageSpooler // Or rather, a map of ID to a remote worker manager private Map<String, Pair<MessageSpooler, List<Message>>> idSpoolerMap; private List<Worker> registeredWorkers; private Queue<Message> msgQueue; private WorkerManagerImpl wkrMgr; private DataLocator aDataLocator; /** * * @return DataLocator DataLocator internally stored in this class */ public DataLocator getDataLocator() { return aDataLocator; } /** * Sets the DataLocator internally stored in this class * * @param aDataLocator */ public void setDataLocator(DataLocator aDataLocator) { this.aDataLocator = aDataLocator; } private Thread t; public static enum CommunicatorState { EXECUTE, STOP }; private CommunicatorState state; private static final String LOG_FILE_PREFIX = JPregelConstants.LOG_DIR + "communicator_"; private static final String LOG_FILE_SUFFIX = ".log"; private Logger logger; private String id; private void setId(String id) { this.id = id; } /** * Gets the class ID * * @return */ public String getId() { return id; } /** * Queues an incoming message from a Worker thread * * @param msg */ public synchronized void queueMessage(Message msg) { this.msgQueue.add(msg); } /** * Returns the state of the Communicator * * @return */ public synchronized CommunicatorState getState() { return state; } /** * Sets the state of the Communicator * * @param state */ public synchronized void setState(CommunicatorState state) { this.state = state; } /** * * @param wkrMgr * WorkerManager that owns this Communicator * @param ID * of the owner * @throws IOException */ public Communicator(WorkerManagerImpl wkrMgr, String id) throws IOException { this.setId(id + "_" + this.getRandomChars()); this.initLogger(); this.wkrMgr = wkrMgr; this.registeredWorkers = new Vector<Worker>(); this.msgQueue = new LinkedList<Message>(); this.idSpoolerMap = new HashMap<String, Pair<MessageSpooler, List<Message>>>(); t = new Thread(this, "Communicator"); t.start(); } // The communicator loops forever polling workers to check if the have // transitioned states @Override public void run() { while (true) { if (this.getState() == CommunicatorState.EXECUTE) { boolean allDone = true; for (int index = 0; index < registeredWorkers.size(); index++) { Worker aWkr = registeredWorkers.get(index); if (aWkr.getState() != Worker.WorkerState.DONE) { allDone = false; } } if (allDone) { try { logger.info("Commencing communications"); communicate(); } catch (UnknownHostException e) { logger.severe("UnknownHostException occured in communicate()"); e.printStackTrace(); } catch (RemoteException e) { logger.severe("RemoteException occured in communicate()"); logger.severe(e.toString()); e.printStackTrace(); } catch (DataNotFoundException e) { logger.severe("DataNotFoundException occured in communicate()"); e.printStackTrace(); } catch (IOException e) { logger.severe("IOException occured in communicate()"); e.printStackTrace(); } catch (NotBoundException e) { logger.severe("NotBoundException occured in communicate()"); e.printStackTrace(); } finally { logger.info("Setting communicator state to STOP"); this.setState(CommunicatorState.STOP); } } } } } /** * All the communication happens here * @throws DataNotFoundException * @throws IOException * @throws NotBoundException * */ private void communicate() throws IOException, DataNotFoundException, NotBoundException, RemoteException, UnknownHostException { clearSpoolerQueues(); populateSpoolerQueues(); sendMessages(); stopWorkers(); logger.info("Ending superstep"); this.wkrMgr.endSuperStep(); this.setState(CommunicatorState.STOP); } /** * * @throws RemoteException * */ private void sendMessages() throws RemoteException { for (Map.Entry<String, Pair<MessageSpooler, List<Message>>> e : this.idSpoolerMap .entrySet()) { Pair<MessageSpooler, List<Message>> spoolerInfo = e.getValue(); MessageSpooler msgSpooler = spoolerInfo.getFirst(); List<Message> msgsToBeSent = spoolerInfo.getSecond(); for (Message msg : msgsToBeSent) { logger.info("Queueing msg to spooler : " + e.getKey() + " -> " + msg); msgSpooler.queueMessage(msg); } } } //Populates message spooler queues for all machines private void populateSpoolerQueues() throws IOException, DataNotFoundException, NotBoundException, MalformedURLException, RemoteException, UnknownHostException { Message msg; Map<Integer, Pair<String, String>> partitionMap = this.aDataLocator .readPartitionMap(); while (!msgQueue.isEmpty()) { msg = msgQueue.remove(); int targetVertexID = msg.getDestVertexID(); int targetPartition = this.aDataLocator .getPartitionNumber(targetVertexID); Pair<String, String> targetWkrMgrInfo = partitionMap .get(targetPartition); String targetWkrMgrServiceName = targetWkrMgrInfo.getFirst(); String targetWkrMgrHostName = targetWkrMgrInfo.getSecond().split( ":")[0]; String targetHostPort = targetWkrMgrInfo.getSecond().split(":")[1]; MessageSpooler targetMsgSpooler = null; List<Message> msgList = null; if (!this.idSpoolerMap.containsKey(targetWkrMgrServiceName)) { msgList = new Vector<Message>(); if (isSelfLookup(targetWkrMgrServiceName)) { logger.info("Avoiding self lookup for service : " + targetWkrMgrServiceName); targetMsgSpooler = wkrMgr; } else if (isSameHost(targetWkrMgrHostName)) { logger.info("In current host : " + targetWkrMgrHostName + " looking up spooler service : " + "localhost:" + targetHostPort + "/" + targetWkrMgrServiceName); targetMsgSpooler = (MessageSpooler) Naming.lookup("//" + "localhost:" + targetHostPort + "/" + targetWkrMgrServiceName); } else { logger.info("Looking up spooler service : " + targetWkrMgrHostName + ":" + targetHostPort + "/" + targetWkrMgrServiceName); targetMsgSpooler = (MessageSpooler) Naming.lookup("//" + targetWkrMgrHostName + ":" + targetHostPort + "/" + targetWkrMgrServiceName); } Pair<MessageSpooler, List<Message>> spoolerInfo = new Pair<MessageSpooler, List<Message>>( targetMsgSpooler, msgList); this.idSpoolerMap.put(targetWkrMgrServiceName, spoolerInfo); logger.info("Added spooler service and queue to map : " + spoolerInfo); } else { msgList = this.idSpoolerMap.get(targetWkrMgrServiceName) .getSecond(); } msgList.add(msg); logger.info("Added message to spooler queue : " + targetWkrMgrServiceName + " : " + msg); } } /** * * @param targetWkrMgrHostName * @return * @throws */ private boolean isSameHost(String targetWkrMgrHostName) throws UnknownHostException { String hostName = InetAddress.getLocalHost().getHostName(); return targetWkrMgrHostName.equals(hostName); } /** * @return */ private boolean isSelfLookup(String targetServiceName) { logger.info("Checking if " + wkrMgr.getId() + " == " + targetServiceName); if (wkrMgr.getId().equals(targetServiceName)) { return true; } return false; } /** * */ private void clearSpoolerQueues() { for (Map.Entry<String, Pair<MessageSpooler, List<Message>>> e : this.idSpoolerMap .entrySet()) { Pair<MessageSpooler, List<Message>> aPair = e.getValue(); List<Message> thisMsgQueue = aPair.getSecond(); thisMsgQueue.clear(); } } private void stopWorkers() { for (int index = 0; index < registeredWorkers.size(); index++) { Worker aWorker = registeredWorkers.get(index); aWorker.setState(WorkerState.STOP); logger.info("Stopped worker : " + aWorker.getId()); } } /** * * @param aWorker A Worker thread that should be polled for state changes */ public void registerWorker(Worker aWorker) { this.registeredWorkers.add(aWorker); } private void initLogger() throws IOException { this.logger = JPregelLogger.getLogger(this.getId(), LOG_FILE_PREFIX + this.getId() + LOG_FILE_SUFFIX); } /** * * @return A random name made up of exactly three alphabets */ private static String getRandomChars() { char first = (char) ((new Random().nextInt(26)) + 65); char second = (char) ((new Random().nextInt(26)) + 65); char third = (char) ((new Random().nextInt(26)) + 65); return "" + first + second + third; } }