URingPaxos-master/src/java/ch/usi/da/paxos/ring/NetworkManager.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.net.StandardSocketOptions;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.TransferQueue;

import org.apache.log4j.Logger;

import ch.usi.da.paxos.api.ConfigKey;
import ch.usi.da.paxos.api.PaxosRole;
import ch.usi.da.paxos.message.Message;
import ch.usi.da.paxos.message.MessageType;

/**
 * Name: NetworkManager<br>
 * Description: <br>
 * 
 * Creation date: Aug 14, 2012<br>
 * $Id$
 * 
 * @author Samuel Benz benz@geoid.ch
 */
public class NetworkManager {

	public final static int MAGIC_NUMBER = 0x756d7270; // ASCII for "umrp"
	
	private final static Logger logger = Logger.getLogger(NetworkManager.class);

	private final static Logger stats = Logger.getLogger("ch.usi.da.paxos.Stats");

	private final RingManager ring;
	
	private ServerSocketChannel server;
	
	private Selector selector;
	
	private SocketChannel client;
	
	private final TransferQueue<Message> send_queue = new LinkedTransferQueue<Message>();
	
	private Role acceptor = null;

	private Role leader = null;

	private Role learner = null;

	private Role proposer = null;

	private boolean tcp_nodelay = false;
	
	public boolean crc_32 = false;
	
	public int buf_size = 131071;

	public long recv_count = 0;

	public long recv_bytes = 0;

	public long send_count = 0;

	public long send_bytes = 0;

	public final long[] messages_distribution = new long[MessageType.values().length];

	public final long[] messages_size = new long[MessageType.values().length];

	//private final Random random = new Random();
	
	/**
	 * @param ring the ring manager
	 * @throws IOException
	 */
	public NetworkManager(RingManager ring) throws IOException {
		this.ring = ring;
		if(stats.isDebugEnabled()){
			for(MessageType t : MessageType.values()){
				messages_distribution[t.getId()] = 0;
				messages_size[t.getId()] = 0;
			}
		}
	}
	
	/**
	 * Start the TCP listener
	 * 
	 * @throws IOException
	 */
	public void startServer() throws IOException {
		if(ring.getConfiguration().containsKey(ConfigKey.tcp_nodelay)){
			if(Integer.parseInt(ring.getConfiguration().get(ConfigKey.tcp_nodelay)) == 1){
				tcp_nodelay = true;
			}
			logger.info("NetworkManager tcp_nodelay: " + tcp_nodelay);
		}
		if(ring.getConfiguration().containsKey(ConfigKey.tcp_crc)){
			if(Integer.parseInt(ring.getConfiguration().get(ConfigKey.tcp_crc)) == 1){
				crc_32 = true;
			}
			logger.info("NetworkManager tcp_crc: " + crc_32);
		}
		if(ring.getConfiguration().containsKey(ConfigKey.buffer_size)){
			buf_size = Integer.parseInt(ring.getConfiguration().get(ConfigKey.buffer_size));
			logger.info("NetworkManager buf_size: " + buf_size);
		}
		selector = Selector.open();
		server = ServerSocketChannel.open();
		server.setOption(StandardSocketOptions.SO_RCVBUF,buf_size);
		server.configureBlocking(false);
		server.socket().bind(ring.getNodeAddress());
		server.register(selector, SelectionKey.OP_ACCEPT);
		
		Thread t = new Thread(new TCPListener(this,server,selector));
		t.setName("TCPListener");
		t.start();
		logger.debug("NetworkManager listener started " + server.socket().getLocalSocketAddress() + " (buffer size: " + server.socket().getReceiveBufferSize() + ")");
		
		Thread t2 = new Thread(new NetworkStatsWriter(ring));
		t2.setName("NetworkStatsWriter");
		t2.start();
	}
	
	/**
	 * Called from the server listener when a packet arrives
	 * 
	 * @param m the received message
	 */
	public synchronized void receive(Message m){
		/*if(logger.isDebugEnabled()){
			logger.debug("receive network message (ring:" + ring.getRingID() + ") : " + m);
		}*/

		/*if(random.nextInt(100) == 1){
			logger.debug("!! drop: " + m);
			return;
		}*/
		
		if(stats.isDebugEnabled()){
			messages_distribution[m.getType().getId()]++;
			messages_size[m.getType().getId()] = messages_size[m.getType().getId()] + Message.length(m);
		}
		
		// network forwarding
		if(m.getType() == MessageType.Relearn){
			if(leader == null){
				send(m);
			}
		}else if(m.getType() == MessageType.Value){
			if(ring.getRingSuccessor(ring.getNodeID()) != m.getSender()){ // D,v -> until predecessor(P0)
				send(m);
			}
		}else if(m.getType() == MessageType.Phase2){
			if(acceptor == null && ring.getNodeID() != ring.getLastAcceptor()){ // network -> until last_accept
				send(m);
			}	
		}else if(m.getType() == MessageType.Decision){
			// network -> predecessor(deciding acceptor)
			if(ring.getRingSuccessor(ring.getNodeID()) != m.getSender()){
				send(m);
			}
		}else if(m.getType() == MessageType.Phase1 || m.getType() == MessageType.Phase1Range){
			if(m.getReceiver() == PaxosRole.Leader){
				if(leader == null){
					send(m);
				}
			}else if(m.getReceiver() == PaxosRole.Acceptor){
				if(acceptor == null){
					send(m);
				}
			}
		}else if(m.getType() == MessageType.Safe){
			if(learner == null && ring.getNodeID() != ring.getCoordinatorID()){ // network -> until coordinator
				send(m);
			}	
		}else if(m.getType() == MessageType.Trim){
			if(acceptor == null && ring.getNodeID() != ring.getCoordinatorID()){ // network -> until coordinator
				send(m);
			}				
		}

		// local delivery
		if(m.getType() == MessageType.Relearn){
			if(leader != null){
				leader.deliver(ring,m);
			}
		}else if(m.getType() == MessageType.Value){
			if(learner != null){
				learner.deliver(ring,m);
			}
			if(acceptor != null){
				acceptor.deliver(ring,m);
			}
			if(leader != null){
				leader.deliver(ring,m);
			}
		}else if(m.getType() == MessageType.Phase2){
			if(learner != null){
				learner.deliver(ring,m);
			}			
			if(acceptor != null){
				acceptor.deliver(ring,m);
			}	
		}else if(m.getType() == MessageType.Decision){
			if(leader != null){
				leader.deliver(ring,m);
			}
			if(acceptor != null){
				acceptor.deliver(ring,m);
			}
			if(learner != null){
				learner.deliver(ring,m);
			}			
			if(proposer != null){
				proposer.deliver(ring,m);
			}
		}else if(m.getType() == MessageType.Phase1 || m.getType() == MessageType.Phase1Range){
			if(m.getReceiver() == PaxosRole.Leader){
				if(leader != null){
					leader.deliver(ring,m);
				}
			}else if(m.getReceiver() == PaxosRole.Acceptor){
				if(acceptor != null){
					acceptor.deliver(ring,m);
				}
			}
		}else if(m.getType() == MessageType.Safe){
			if(leader != null){
				leader.deliver(ring,m);
			}else if(learner != null){
				learner.deliver(ring,m);
			}
		}else if(m.getType() == MessageType.Trim){
			if(learner != null){
				learner.deliver(ring,m);
			}
			if(leader != null){
				leader.deliver(ring,m);
			}else if(acceptor != null){
				acceptor.deliver(ring,m);
			}
		}
	}
	
	/**
	 * close the server listener
	 */
	public void closeServer(){
		try {
			selector.close();
			server.close();
		} catch (IOException e) {
			logger.error("NetworkManager server close error",e);
		}
	}
		
	/**
	 * connect to the ring successor
	 * 
	 * @param addr
	 */
	public void connectClient(InetSocketAddress addr){
		try {
			client = SocketChannel.open();
			client.setOption(StandardSocketOptions.SO_SNDBUF,buf_size);
			client.setOption(StandardSocketOptions.SO_RCVBUF,buf_size);			
			client.socket().setSendBufferSize(buf_size);
			client.configureBlocking(true); // Client runs in Blocking Mode !!!
			client.connect(addr);
			client.setOption(StandardSocketOptions.TCP_NODELAY,tcp_nodelay);
			Thread t = new Thread(new TCPSender(this,client,send_queue));
			t.setName("TCPSender");
			t.start();
			logger.debug("NetworkManager create connection " + addr + " (" + client.getLocalAddress() + ")");
		} catch (IOException e) {
			logger.error("NetworkManager client connect error",e);
		}
	}

	/**
	 * disconnect client (ring successor)
	 */
	public void disconnectClient(){
		try {
			if(client != null){
				client.close();
				logger.debug("NetworkManager close connection");
			}
		} catch (IOException e) {
			logger.error("NetworkManager client close error",e);
		}
	}	
	
	/**
	 * @param m the message to send
	 */
	public void send(Message m){
		try {
			send_queue.put(m); // transfer(m) (blocking call)
		} catch (InterruptedException e) {
		}
	}
	
	/**
	 * @return the acceptor
	 */
	public Role getAcceptor() {
		return acceptor;
	}

	/**
	 * @param acceptor the acceptor to set
	 */
	public void setAcceptor(Role acceptor) {
		this.acceptor = acceptor;
	}

	/**
	 * @return the leader
	 */
	public Role getLeader() {
		return leader;
	}

	/**
	 * @param leader the leader to set
	 */
	public void setLeader(Role leader) {
		this.leader = leader;
	}

	/**
	 * @return the learner
	 */
	public Role getLearner() {
		return learner;
	}

	/**
	 * @param learner the learner to set
	 */
	public void setLearner(Role learner) {
		this.learner = learner;
	}

	/**
	 * @return the proposer
	 */
	public Role getProposer() {
		return proposer;
	}

	/**
	 * @param proposer the proposer to set
	 */
	public void setProposer(Role proposer) {
		this.proposer = proposer;
	}

	/**
	 * @param role
	 */
	public synchronized void registerCallback(Role role){
		if(role instanceof AcceptorRole){
			this.acceptor = role;			
		}else if(role instanceof CoordinatorRole){
			this.leader = role;
		}else if(role instanceof LearnerRole){
			this.learner = role;
		}else if(role instanceof ProposerRole){
			this.proposer = role;
		}
	}

}
