URingPaxos-master/src/java/ch/usi/da/paxos/ring/MultiLearnerRole.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.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import org.apache.log4j.Logger;

import ch.usi.da.paxos.api.ConfigKey;
import ch.usi.da.paxos.api.Learner;
import ch.usi.da.paxos.api.PaxosRole;
import ch.usi.da.paxos.storage.Decision;

/**
 * Name: MultiLearnerRole<br>
 * Description: <br>
 * 
 * Deprecated: use ElasticLearnerRole
 * Just here to make the code downward compatible!
 * 
 * Creation date: Mar 04, 2013<br>
 * $Id$
 * 
 * @author Samuel Benz benz@geoid.ch
 */
@Deprecated
public class MultiLearnerRole extends Role implements Learner {

	private final static Logger logger = Logger.getLogger(MultiLearnerRole.class);
	
	private final Map<Integer,RingDescription> ringmap = new HashMap<Integer,RingDescription>();
	
	private final List<Integer> ring = new ArrayList<Integer>();
	
	private final int maxRing = 20;
	
	private final BlockingQueue<Decision> values = new LinkedBlockingQueue<Decision>(); 
	
	private final LearnerRole[] learner = new LearnerRole[maxRing];
	
	private int M = 1;
		
	private int deliverRing;
	
	private final long[] skip_count = new long[maxRing];
	
	private boolean deliver_skip_messages = false;

	/**
	 * @param rings a list of rings
	 */
	@Deprecated
	public MultiLearnerRole(List<RingDescription> rings) {
		int minRing = maxRing+1;
		for(RingDescription ring : rings){
			if(ring.getRingID() < minRing){
				minRing = ring.getRingID();
			}
			this.ring.add(ring.getRingID());
			this.ringmap.put(ring.getRingID(),ring);
		}
		Collections.sort(ring);
		RingManager firstRing = rings.get(0).getRingManager();
		deliverRing = minRing;
		logger.debug("MultiRingLearner initial deliverRing=" + deliverRing);
		if(firstRing.getConfiguration().containsKey(ConfigKey.deliver_skip_messages)){
			if(firstRing.getConfiguration().get(ConfigKey.deliver_skip_messages).contains("1")){
				deliver_skip_messages = true;
			}
			logger.info("MultiRingLearner deliver_skip_messages: " + (deliver_skip_messages ? "enabled" : "disabled"));
		}
	}

	@Override
	public void run() {
		for(Entry<Integer,RingDescription> e : ringmap.entrySet()){
			// create learners
			RingManager ring = e.getValue().getRingManager();
			Role r = new LearnerRole(ring);
			learner[e.getKey()] = (LearnerRole) r;
			logger.debug("MultiRingLeaner register role: " + PaxosRole.Learner + " at node " + ring.getNodeID() + " in ring " + ring.getRingID());
			ring.registerRole(PaxosRole.Learner);		
			Thread t = new Thread(r);
			t.setName(PaxosRole.Learner + "-" + e.getKey());
			t.start();
			skip_count[e.getKey()] = 0;
		}
		int count = 0;
		while(true){
			try{
				if(skip_count[deliverRing] > 0){
					count++;
					skip_count[deliverRing]--;
					//logger.debug("MultiRingLearner " + ringmap.get(deliverRing).getNodeID() + " ring " + deliverRing + " skiped a value (" + skip_count[deliverRing] + " skips left)");
				}else{
					Decision d = learner[deliverRing].getDecisions().take();
					if(d.getValue() != null && d.getValue().isSkip()){
						// skip message
						try {
							long skip = Long.parseLong(new String(d.getValue().getValue()));
							skip_count[deliverRing] = skip_count[deliverRing] + skip;
						}catch (NumberFormatException e) {
							logger.error("MultiRingLearner received incomplete SKIP message! -> " + d,e);
						}
						if(deliver_skip_messages){
							values.add(d);
						}
					}else{
						count++;
						// learning an actual proposed value
						values.add(d);
					}
				}
				if(count >= M){
					count = 0;
					deliverRing = getRingSuccessor(deliverRing);
				}
			} catch (InterruptedException e) {
				Thread.currentThread().interrupt();
				break;				
			}
		}
	}

	private int getRingSuccessor(int id){
		int pos = ring.indexOf(new Integer(id));
		if(pos+1 >= ring.size()){
			return ring.get(0);
		}else{
			return ring.get(pos+1);
		}
	}

	@Override
	public BlockingQueue<Decision> getDecisions() {
		return values;
	}

	public void setSafeInstance(Integer ring, Long instance) {
		learner[ring].setSafeInstance(ring,instance);
	}

}
