package com.yahoo.dtf; import java.util.ArrayList; import java.util.Hashtable; import com.yahoo.dtf.NodeInfo; import com.yahoo.dtf.NodeState; import com.yahoo.dtf.actions.protocol.Connect; import com.yahoo.dtf.actions.protocol.Lock; import com.yahoo.dtf.comm.CommClient; import com.yahoo.dtf.exception.ActionException; import com.yahoo.dtf.exception.DTFException; import com.yahoo.dtf.exception.LockException; import com.yahoo.dtf.logger.DTFLogger; public class NodeState { private static DTFLogger _logger = DTFLogger.getLogger(NodeState.class); private static NodeState instance = null; private final static Object _lockchanges = new Object(); public synchronized static NodeState getInstance() { if (instance == null) { instance = new NodeState(); } return instance; } private ArrayList<NodeInfo> connectedNodes = null; private Hashtable<String,ArrayList<NodeInfo>> lockedNodes = null; private NodeState() { connectedNodes = new ArrayList<NodeInfo>(); lockedNodes = new Hashtable<String, ArrayList<NodeInfo>>(); } public ArrayList<NodeInfo> getRegisteredNodes() { return connectedNodes; } public synchronized NodeInfo addNode(Connect connect, CommClient client) throws DTFException{ try { NodeInfo ni = new NodeInfo(connect, client); _logger.info("Registering " + ni); if (ni.findAttrib(DTFProperties.DTF_NODE_TYPE).equalsIgnoreCase("dtfx")) { // dtfx are not to be locked or unlocked ni.lock(null); } if (connectedNodes.contains(ni)) throw new DTFException("Node already registered with id: " + ni.getId()); connectedNodes.add(ni); return ni; } finally { synchronized( _lockchanges ) { _lockchanges.notifyAll(); } } } public synchronized boolean isNodeRegistered(String id) { for (int i = 0; i < connectedNodes.size(); i++) { NodeInfo ni = (NodeInfo)connectedNodes.get(i); if (ni.getId().equals(id)) return true; } return false; } public synchronized NodeInfo getNodeInfo(String id) throws DTFException { for (int i = 0; i < connectedNodes.size(); i++) { NodeInfo ni = (NodeInfo)connectedNodes.get(i); if (ni.getId().equals(id)) return ni; } throw new DTFException("Unable to find node: " + id); } public synchronized void removeNode(Connect conn) throws DTFException{ NodeInfo ni = new NodeInfo(conn,null); _logger.info("Unregistering " + conn); if (!connectedNodes.contains(ni)) throw new DTFException("Node not registered with id: " + conn.getId()); connectedNodes.remove(ni); // unlock all components held by this agent ArrayList<NodeInfo> locks = lockedNodes.get(conn.getId()); if (locks != null && locks.size() != 0) { _logger.info("Unlocking all compoennts for " + conn.getId()); String ids = ""; for(int i = 0; i < locks.size(); i++) { NodeInfo locked = locks.get(i); ids += locked.getId() + ", "; try { locked.unlock(); } catch (DTFException e) { _logger.error("Unable to unlock component [" + locked + "] will remove this agent.",e); removeNode(locked); throw e; } } _logger.info("Unlocked components {" + ids + "}"); } else { _logger.info("No locked components."); } lockedNodes.remove(conn.getId()); } public synchronized void checkForOrphans() { for (int i = 0; i < connectedNodes.size(); i++) { NodeInfo ni = connectedNodes.get(i); if (ni.isLocked() && ni.getOwner() != null) { if (!isNodeRegistered(ni.getOwner())) { _logger.warn("Found orpahend agent [" + ni.getId() + "], releasing now."); try { ni.unlock(); } catch (DTFException e) { _logger.error("Failure to unlock " + ni, e); } } } } } private void addNode(String id, NodeInfo node) { ArrayList<NodeInfo> locks = lockedNodes.get(id); if (locks == null) { locks = new ArrayList<NodeInfo>(); lockedNodes.put(id, locks); } locks.add(node); } private void removeNode(String id, NodeInfo node) throws ActionException { ArrayList<NodeInfo> locks = lockedNodes.get(id); if (locks == null) throw new ActionException("Agent never locked by this agent."); locks.remove(node); } /** * Will return when there are lock changes on this NodeState object. This * includes if locks were returned or taken from the pool of available * nodes. */ public void waitForLockChanges() { synchronized(_lockchanges) { try { _lockchanges.wait(); } catch (InterruptedException e) { } } } /** * This method will attempt to all or none of the locks specified by the * array of Locks passed as an argument. If we were unable to lock all of * the * * @param locks * @return * @throws DTFException */ public synchronized NodeInfo[] lockNodes(Lock[] locks) throws DTFException { try { NodeInfo[] niLocks = new NodeInfo[locks.length]; for (int i = 0; i < niLocks.length; i++) niLocks[i] = new NodeInfo(locks[i]); int n = 0; boolean matched = false; for (n = 0; n < niLocks.length; n++) { matched = false; for(int i = 0; i < connectedNodes.size(); i++) { NodeInfo ni = (NodeInfo) connectedNodes.get(i); if ( _logger.isDebugEnabled() ) _logger.debug("Trying to match " + ni + " with " + niLocks[n]); if (!ni.isLocked() && niLocks[n].matches(ni)) { if (_logger.isDebugEnabled()) _logger.debug("Locked " + ni + " for " + locks[n].getOwner()); ni.lock(locks[n].getOwner()); niLocks[n] = ni; matched = true; } else if (_logger.isDebugEnabled()) _logger.debug("Didn't find match " + ni); } if ( !matched ) break; } if ( matched ) { for (n = 0; n < niLocks.length; n++) { addNode(locks[n].getOwner(),niLocks[n]); // move this node to the end of the list so we'll give all nodes // a chance when locking connectedNodes.add(connectedNodes.remove(n)); } return niLocks; } for (n = 0; n < niLocks.length; n++) { if ( niLocks[n] != null ) { if ( niLocks[n].isLocked() ) niLocks[n].unlockWithoutRelease(); } } StringBuffer buff = new StringBuffer("["); for (NodeInfo ni : niLocks) if ( ni != null ) buff.append(ni + ", "); buff.replace(buff.length()-1, buff.length(), "]"); throw new LockException("No agent found to match " + buff); } finally { synchronized(_lockchanges) { _lockchanges.notifyAll(); } } } public synchronized NodeInfo unlockNode(Lock lock) throws DTFException { try { NodeInfo niLock = new NodeInfo(lock); for(int i = 0; i < connectedNodes.size(); i++) { NodeInfo ni = (NodeInfo) connectedNodes.get(i); if (_logger.isDebugEnabled()) _logger.debug("Trying to match " + ni + " with " + niLock); /* * We only care that the id matches the list of ids that this * component owns. */ if (ni.isLocked() && niLock.getId().equals(ni.getId())) { removeNode(lock.getOwner(), ni); ni.unlock(); if (_logger.isDebugEnabled()) _logger.debug("Unlocked " + ni + " for " + lock.getOwner()); return ni; } } throw new ActionException("Agent: " + niLock + " never locked."); } finally { synchronized( _lockchanges ) { _lockchanges.notifyAll(); } } } public synchronized NodeInfo unlockNodeWithoutRelease(Lock lock) throws DTFException { try { NodeInfo niLock = new NodeInfo(lock); for(int i = 0; i < connectedNodes.size(); i++) { NodeInfo ni = (NodeInfo) connectedNodes.get(i); if (_logger.isDebugEnabled()) _logger.debug("Trying to match " + ni + " with " + niLock); /* * We only care that the id matches the list of ids that this * component owns. */ if (ni.isLocked() && niLock.getId().equals(ni.getId())) { removeNode(lock.getOwner(), ni); if (_logger.isDebugEnabled()) _logger.debug("Unlocked " + ni + " for " + lock.getOwner()); return ni; } } throw new ActionException("Agent: " + niLock + " never locked."); } finally { synchronized( _lockchanges ) { _lockchanges.notifyAll(); } } } }