URingPaxos-master/src/java/ch/usi/da/paxos/ring/RingManager.java
package ch.usi.da.paxos.ring;
/* 
 * Copyright (c) 2013 Università della Svizzera italiana (USI)
 * 
 * This file is part of URingPaxos.
 *
 * URingPaxos is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * URingPaxos 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with URingPaxos.  If not, see <http://www.gnu.org/licenses/>.
 */

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Collections;
import java.util.List;

import org.apache.log4j.Logger;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.KeeperException.NoNodeException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.ZooKeeper;

import ch.usi.da.paxos.TopologyManager;
import ch.usi.da.paxos.api.PaxosNode;


/**
 * Name: RingManager<br>
 * Description: <br>
 * 
 * Creation date: Aug 12, 2012<br>
 * $Id$
 * 
 * @author Samuel Benz benz@geoid.ch
 */
public class RingManager extends TopologyManager {
	
	private final static Logger logger = Logger.getLogger(RingManager.class);

	private volatile int last_acceptor = 0;

	/**
	 * @param node
	 * @param ringID
	 * @param addr
	 * @param zoo
	 */
	public RingManager(PaxosNode node,int ringID,InetSocketAddress addr,ZooKeeper zoo) {
		super(node,ringID,addr,zoo,"/ringpaxos");
	}

	/**
	 * @param node
	 * @param ringID
	 * @param addr
	 * @param zoo
	 * @param prefix zookeeper prefix
	 */
	public RingManager(PaxosNode node, int ringID,InetSocketAddress addr,ZooKeeper zoo,String prefix) {
		super(node,ringID,addr,zoo,prefix);
	}

	/**
	 * @param nodeID
	 * @param ringID
	 * @param addr
	 * @param zoo
	 * @param prefix zookeeper prefix
	 */
	public RingManager(int nodeID, int ringID,InetSocketAddress addr,ZooKeeper zoo,String prefix) {
		super(nodeID,ringID,addr,zoo,prefix);
	}

	/**
	 * Init the ring manger
	 * 
	 * (we need this init() because of the "this" references) 
	 * 
	 * @throws IOException
	 * @throws KeeperException
	 * @throws InterruptedException
	 */
	public void init() throws IOException, KeeperException, InterruptedException {
		network = new NetworkManager(this);
		zoo.register(this);
		getConfig();
		network.startServer();
		//Thread.sleep(1000); // give server time to start-up
		registerNode();		
	}
	
	/**
	 * @throws InterruptedException 
	 * @throws KeeperException 
	 */
	protected void getConfig() throws KeeperException, InterruptedException {
		super.getConfig();
		
		// get last_acceptor
		try {
			List<String> l = zoo.getChildren(path + "/" + acceptor_path, true);
			int max = 0;
			for(String s : l){
				int i = Integer.valueOf(s);
				if(i > max){
					max = i; 
				}
			}
			last_acceptor = max;
		} catch (NoNodeException e){
		}
	}

	/**
	 * @throws InterruptedException 
	 * @throws KeeperException 
	 */
	protected void registerNode() throws KeeperException, InterruptedException {
		super.registerNode();
	}
	private void notifyRingChanged(){
		InetSocketAddress saddr = getNodeAddress(getRingSuccessor(nodeID));
		logger.info("RingManager ring " + topologyID + " changed: " + nodes + " (succsessor: " + getRingSuccessor(nodeID) + " at " + saddr + ")");
		if(saddr != null && currentConnection == null || !currentConnection.equals(saddr)){
			network.disconnectClient();
			network.connectClient(saddr);
			currentConnection = saddr;
		}
	}
	
	private void notifyNewCoordinator(){
		logger.info("RingManger this node is the new coordinator for ring " + topologyID + "!");
		Thread c = new Thread(new CoordinatorRole(this));
		c.setName("Coordinator");
		c.start();
	}
		
	/**
	 * @return the ring
	 */
	public List<Integer> getRing(){
		return Collections.unmodifiableList(nodes);
	}
	
	/**
	 * @return the ring id
	 */
	public int getRingID(){
		return topologyID;
	}
	
	/**
	 * @param id 
	 * @return the node id of the ring successor
	 */
	public int getRingSuccessor(int id){
		int pos = nodes.indexOf(new Integer(id));
		if(pos+1 >= nodes.size()){
			return nodes.get(0);
		}else{
			return nodes.get(pos+1);
		}
	}
	
	/**
	 * @param id 
	 * @return the node id of the ring predecessor
	 */
	public int getRingPredecessor(int id){
		int pos = nodes.indexOf(new Integer(id));
		if(pos-1 < 0){
			return nodes.get(nodes.size()-1);
		}else{
			return nodes.get(pos-1);
		}
	}
	
	/**
	 * @return the id of the last acceptor in the ring
	 */
	public int getLastAcceptor(){
		return last_acceptor;
	}
	
	@Override
	public void process(WatchedEvent event) {
		int old_coordinator = coordinator;
		super.process(event);
		
		// ls nodes to form the ring
		try {
			if(event.getType() == EventType.NodeChildrenChanged){
				if(event.getPath().startsWith(path + "/" + id_path)){
					nodes.clear();
					List<String> l = zoo.getChildren(path + "/" + id_path, true);
					for(String s : l){
						nodes.add(Integer.valueOf(s));
					}
					Collections.sort(nodes);
					notifyRingChanged();
				}else if(event.getPath().startsWith(path + "/" + acceptor_path)){
					List<String> l = zoo.getChildren(path + "/" + acceptor_path, true);
					int min = nodeID+1;
					int max = 0;
					for(String s : l){
						int i = Integer.valueOf(s);
						if(i < min){
							min = i;
						}
						if(i > max){
							max = i; 
						}
					}
					last_acceptor = max;
					coordinator = min;
					if(nodeID == min && old_coordinator != coordinator){
						notifyNewCoordinator();
					}
				}
			}
		} catch (KeeperException e) {
			logger.error(e);
		} catch (InterruptedException e) {
		}		
	}

}
