URingPaxos-master/src/java/ch/usi/da/paxos/TTYNode.java
package ch.usi.da.paxos;
/* 
 * 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.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;

import org.apache.log4j.Logger;
import org.apache.zookeeper.KeeperException;

import ch.usi.da.paxos.api.PaxosNode;
import ch.usi.da.paxos.lab.Experiment1;
import ch.usi.da.paxos.message.Control;
import ch.usi.da.paxos.message.ControlType;
import ch.usi.da.paxos.message.Value;
import ch.usi.da.paxos.ring.Node;
import ch.usi.da.paxos.ring.ProposerRole;
import ch.usi.da.paxos.ring.RingDescription;
import ch.usi.da.paxos.storage.Decision;

/**
 * Name: TTYNode<br>
 * Description: <br>
 * 
 * Creation date: Jun 21, 2013<br>
 * $Id$
 * 
 * @author leandro.pacheco.de.sousa@usi.ch
 */
public class TTYNode {
	static {
		// get hostname and pid for log file name
		String host = "localhost";
		try {
			Process proc = Runtime.getRuntime().exec("hostname");
			BufferedInputStream in = new BufferedInputStream(proc.getInputStream());
			byte [] b = new byte[in.available()];
			in.read(b);
			in.close();
			host = new String(b).replace("\n","");
		} catch (IOException e) {
		}
		int pid = 0;
		try {
			pid = Integer.parseInt((new File("/proc/self")).getCanonicalFile().getName());
		} catch (NumberFormatException | IOException e) {
		}
		System.setProperty("logfilename", "L" + host + "-" + pid + ".log");
		System.setProperty("valuesfilename", "L" + host + "-" + pid + ".values");
		System.setProperty("proposalfilename", "L" + host + "-" + pid + ".proposal");		
	}

	private final static Logger logger = Logger.getLogger(TTYNode.class);

	private final static Logger valuelogger = Logger.getLogger(Value.class);

	/**
	 * Thread that reads values from the standard input and proposes them to all
	 * the rings. 
	 */
	private static class StdinProposer implements Runnable {
		PaxosNode paxos;

		public StdinProposer(PaxosNode paxos) {
			this.paxos = paxos;
		}

		@Override
		public void run() {
			BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
			String s;
			try {
				while ((s = in.readLine()) != null && s.length() != 0) {
					if (s.startsWith("exp")) {
						Thread t = new Thread(new Experiment1(paxos));
						t.setName("Experiment");
						t.run();
						continue;
					}
					// propose value read from stdin to ALL the rings
					for (RingDescription ring : paxos.getRings()) {
						if (s.contains("start")) {
							try {
								ProposerRole p = (ProposerRole) paxos.getProposer(ring.getRingID());
								p.setTestMode();
								for(int i=0;i<p.getConcurrentValues();i++){
									p.propose(p.getTestValue());
								}
							} catch (ClassCastException e){
								logger.error("Proposer did not support TestMode");
							}
						} else if (s.startsWith("!")) {
							if(paxos.getProposer(ring.getRingID()) != null){
								paxos.getProposer(ring.getRingID()).control(parseControl(s.replace("!","")));
							}else{
								logger.info("Node isn't a proposer for ring " + ring.getRingID());
							}
						} else {
							if(paxos.getProposer(ring.getRingID()) != null){
								paxos.getProposer(ring.getRingID()).propose(s.getBytes());
							}else{
								logger.info("Node isn't a proposer for ring " + ring.getRingID());
							}
						}
					}
				}
				in.close();
				System.exit(0); // used to stop properly in eclipse
			} catch (IOException e) {
				logger.error(e);
			}
		}
	}

	/**
	 * Thread that prints learned values to stdout.
	 */
	private static class StdoutLearner implements Runnable {
		PaxosNode paxos;

		public StdoutLearner(PaxosNode paxos) {
			this.paxos = paxos;
		}

		@Override
		public void run() {
			if (paxos.getLearner() == null) {
				return; // not a learner
			}
			while(true) {
				try {
					Decision d = paxos.getLearner().getDecisions().take();
					if(valuelogger.isDebugEnabled()){
						valuelogger.debug(d);
					}else if(valuelogger.isInfoEnabled() && !d.getValue().isSkip() && d.getValue().getValue().length > 0){
						valuelogger.info(d.getValue().asString());
					}
				} catch (InterruptedException e) {
					logger.error(e);
				}
			}
		}
	}

	protected static Control parseControl(String s){
		String[] token = s.split(",");
		if(token.length > 2){
			ControlType type = ControlType.Prepare;
			if(token[0].startsWith("s")){
				type = ControlType.Subscribe;
			}else if(token[0].startsWith("u")){
				type = ControlType.Unsubscribe;
			}
			return new Control(1,type,Integer.parseInt(token[1]),Integer.parseInt(token[2]));
		}else{
			return null;
		}
	}

	public static void main(String[] args) {
		String zoo_host = "127.0.0.1:2181";
		if (args.length > 2) {
			zoo_host = args[2];
		}
		int groupID = -1;
		if (args.length > 3) {
			groupID = Integer.parseInt(args[3]);
			logger.info("Node is in group " + groupID);
		}
		if (args.length < 2) {
			System.err.println("Plese use \"Node\" \"node ID\" \"ring ID:roles[;ring ID:roles]\" [zookeeper] [groupID] (eg. 1 1:PAL)");
		} else {
			int nodeID = Integer.parseInt(args[0]);
			List<RingDescription> rings = Util.parseRingsArgument(args[1]);
			// start paxos node
			final Node node = new Node(nodeID,groupID,zoo_host,rings);
			try {
				node.start();
				Runtime.getRuntime().addShutdownHook(new Thread(){
					@Override
					public void run(){
						try {
							node.stop();
						} catch (InterruptedException e) {
						}
					}
				});
			} catch (IOException | KeeperException | InterruptedException e) {
				e.printStackTrace();
				System.exit(1);
			}

			// start stdin learner
			Thread lt = new Thread(new StdoutLearner(node));
			lt.setName("StdoutLearner");
			lt.start();

			// start stdout proposer
			Thread pt = new Thread(new StdinProposer(node));
			pt.setName("StdinProposer");
			pt.start();
		}
	}
}
