/**
* Fortika - Robust Group Communication
* Copyright (C) 2002-2006 Sergio Mena de la Cruz (EPFL) (sergio.mena@epfl.ch)
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
// ###############################
// Projet de semestre I&C - LSR
// He Hui-Yang Informatique
// F�vrier 2005
// ###############################
package groupcomm.common.abcast;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import uka.transport.Transportable;
import framework.Constants;
import framework.GroupCommEventArgs;
import framework.GroupCommException;
import framework.GroupCommMessage;
import framework.PID;
import framework.libraries.FlowControl;
import framework.libraries.Trigger;
import framework.libraries.serialization.TArrayList;
import framework.libraries.serialization.TBoolean;
import framework.libraries.serialization.TCollection;
import framework.libraries.serialization.THashMap;
import framework.libraries.serialization.THashSet;
import framework.libraries.serialization.TInteger;
import framework.libraries.serialization.TList;
import framework.libraries.serialization.TMap;
/**
* <b> This class implements the common code for token-based algorithm abcast. </b>
* <hr>
* <b> Events:
* <dt> <i>Init</i> </dt> <dd> Initializes the abcast layer </dd>
* <dt> <i>Abcast</i> </dt> <dd> Send a Broadcast message, with the abcast algorithm </dd>
* <dt> <i>Pt2PtDeliver</i> </dt> <dd> Happend when a message is received by the lower layers </dd>
* <dt> <i>Suspect</i> </dt> <dd> Happend when the precedent is suspected</dd>
* </dl>
*/
public class TokenAbcast{
//initialized?
private boolean initialized=false;
//a list of all active processes, includes myself as well.
private TList processes;
//list of precedents
private TList procede;
//list of successors
private TList succede;
//Known processes to send the messages
private TArrayList known=null;
//Set of messages that have been abroadcast by pi or by another process,and not yet ordered.
private TList aBroadcast=new TArrayList();
//Sequence of messages adelivered by pi;
private TList aDeliver=new TArrayList();
//Abcast message current id;
private AbcastMessageID abcastId;
//initialize a trigger for myself
private Trigger trigger=null;
//indicate if "pre" is suspected;
private boolean suspected=false;
//a map of all token received ,pair( round,token),initialized empty
private TMap myReceivedToken=null;
//actual round of myself
private int myRound=0;
//identifier of myself
private int myID=0;
//number of precedent
private int f;
//votes
private int myVotes=0;
//direct precedent
private PID pre=null;
private FlowControl flow_control;
private int fc_key;
private int fc_threshold;
private int fc_used = 0;
private static final Logger logger =
Logger.getLogger(TokenAbcast.class.getName());
public static final int MAX_PROPOSE=50;
//constructor of TokenAbcast
public TokenAbcast(TList processes,int myID,int f,Trigger trigger,FlowControl fc)throws GroupCommException{
logger.entering("TokenAbcast", "<constr> 4 parameters");
//test of condition n>=f*(f+1)+1
if (processes.size()<=0 ||f<=0 || processes.size()<(f*(f+1)+1)){
throw new GroupCommException("construction Tokenabcast failed cause list size error");}
//test if processes contains myself
if (myID < 0 || myID>processes.size()){
throw new GroupCommException("construction Tokenabcast failed cause error myID");}
this.myID=myID;
this.f=f;
this.trigger=trigger;
this.flow_control=fc;
this.processes=new TArrayList();
this.processes=processes;
//list of f+1 precedents
int pred;
pred=(myID-f-1+processes.size())%processes.size();
this.procede=new TArrayList();
int nbrP=f+1;
while(nbrP>0){
this.procede.add((PID)(processes.get(pred)));
pred=(pred+1)%processes.size();
nbrP=nbrP-1;
}
//list of f+1 successors
int succ=(myID+1)%processes.size();
this.succede=new TArrayList();
int nbrS=f+1;
while(nbrS>0){
this.succede.add((PID)(processes.get(succ)));
succ=(succ+1)%processes.size();
nbrS=nbrS-1;
}
//direct precedent of myself
pre=(PID)(this.procede.get(this.procede.size()-1));
//construction of a new class AbcastMessageID to identify the messages sent by myself
abcastId= new AbcastMessageID((PID)(processes.get(myID)),0);
//Initialize the map of received tokens
myReceivedToken=new THashMap();
logger.exiting("TokenAbcast", "<constr> 6 parameters");
}
//public method of initialization
public void handleInit(GroupCommEventArgs arg)throws GroupCommException{
logger.entering("TokenAbcast", "handleInit");
TList p=(TList)arg.removeFirst();
if (initialized)
throw new GroupCommException("TokenAbcast already initialized.");
initialized = true;
fc_key=flow_control.getFreshKey();
known = new TArrayList(p);
//Look for duplicate processes in the group
for(int i = 0; i < known.size() ; i++)
for(int j = i+1; j < known.size(); j++)
if( known.get(i).equals(known.get(j)) )
throw new GroupCommException("Process" + known.get(i) +
" appears more than once in the group.");
// flow control
//flow_control.setThreshold(fc_key, MAX_PROPOSE / known.size() + 1);
fc_threshold = Math.max(MAX_PROPOSE / known.size(), 1);
// join-remove
GroupCommEventArgs jrl = new GroupCommEventArgs();
jrl.addLast (new THashSet(p)); // join
jrl.addLast (new THashSet()); // remove
trigger.trigger (Constants.JOINREMOVELIST, jrl);
//if it is the first processor, then it will send the tokens to the f+1 successors.
if (myID==0){
//construct a new token to send
Token tk=new Token(0, (PID)(processes.get(myID)),aBroadcast,0,new TArrayList(),aBroadcast );
//send tokens to f+1 successors
triggerSend(tk,succede);
myRound=1;
}
//if myId is between n-f and n-1, then it will send the tokens to P1 to P(myID+f+1)
else if ((myID>=(processes.size()-f))&&(myID<=(processes.size()-1))){
//construct a "dummy" token to send with round -1
Token tok=new Token(-1,(PID)(processes.get(myID)),new TArrayList(),0,new TArrayList(),new TArrayList());
//list contains p1 to p(myID+f+1)
TArrayList initialSuccede=new TArrayList();
int newPro=myID+f+1;
int first=1;
while(newPro>0){
initialSuccede.add((PID)(processes.get(first)));
first=(first+1)%processes.size();
newPro=newPro-1;
}
//send tokens to destinated processors
triggerSend(tok,initialSuccede);
}
logger.exiting("TokenAbcast","handleInit");
}
//public method dealing with atomic broadcast
public void handleAbcast(GroupCommEventArgs arg){
logger.entering("TokenAbcast", "handleAbcast");
//Flow control
//flow_control.alloc(fc_key, 1);
fc_used++; if(fc_used >= fc_threshold) flow_control.block(fc_key);
// msg
GroupCommMessage msg = (GroupCommMessage)arg.removeFirst();
//get a identifier for this new message
AbcastMessageID id=abcastId.nextId();
msg.tpack(id);
logger.log(Level.FINE,
"Abroadcast message id: {0} ",id);
//msg=id:m
aBroadcast.add(msg);
logger.exiting("TokenAbcast","handleAbcast");
}
//public method dealing with token received
public void handlePt2PtReceive(GroupCommEventArgs arg)throws GroupCommException{
logger.entering("TokenAbcast", "handlePt2PtReceive");
PID pro=(PID)arg.get(1);
System.out.println("source of the token is:" +pro.toString());
GroupCommMessage m=(GroupCommMessage)arg.get(0);
Token tok = (Token) m.tunpack();
//test if the sender of token is correct
if (!pro.equals(tok.sender))
throw new GroupCommException("source of token not correct");
/*if receive a token from a process with an index greater,
then the round of that token must be a previous round,
thus increase one*/
if (processes.indexOf(tok.sender)>myID)
tok.round=tok.round+1;
//test if round of token is greater or equals to myself.
if (tok.round<myRound){
tokenOfSmallRound(tok);
}
else{
//call private proedure to deal with this token
tokenReceived(tok);
}
logger.exiting("TokenAbcast", "handlePt2PtReceive");
}
//public method used when receive a token,but has already suspected "pre"
public void suspect(GroupCommEventArgs arg){
logger.entering("TokenAbcast", "suspect");
PID pi=(PID)arg.get(0);
//if process suspected is indeed his precedent
if ((pi==null)||(!pi.equals(pre))){
suspected=false;
System.out.println("now the precedent is not suspected");
}
else{
suspected=true;
System.out.println("precedent suspected");
//test if the token from one of his precedents exists,if it is the case,send it.
if (!myReceivedToken.containsKey(new TInteger(myRound)))
System.out.println("has not yet received a valid token");
else
processToken((Token)(myReceivedToken.get(new TInteger(myRound))));
}
logger.exiting("TokenAbcast", "suspect");
}
//when receiving a token of smaller round than myself
private void tokenOfSmallRound(Token tok){
logger.entering("TokenAbcast", "tokenOfSmallRound");
//print values of arributes of tokens
logger.log(Level.FINE,
"dealing with token of small round: {0} with sender {1}\nproposalSeq is {2}\nadeliv is {3}\n\tnextSet is {4}\nvotes {5}", new Object[]{new Integer(tok.round),tok.sender,tok.proposalSeq,tok.adeliv,tok.nextSet,new Integer(tok.votes)});
System.out.println("token of round: "+tok.round);
if ((tok.adeliv).size() > aDeliver.size())
triggerDelivery(tok.adeliv);
//update sequence aBroadcast, aBroadcast <- aBroadcast union tok.nextSet
int nextSetsize=0;
while ((nextSetsize<(tok.nextSet).size()) && (!(tok.nextSet).isEmpty())) {
GroupCommMessage gcmmm= (GroupCommMessage)((tok.nextSet).get(nextSetsize));
//get identifiant of messages
AbcastMessageID id1 = (AbcastMessageID) gcmmm.tunpack();
gcmmm.tpack(id1);
//myList1 is a sequence of identifiants of messages in aBroadcast
TArrayList myList1=new TArrayList();
int size=0;
while ((size<aBroadcast.size()) && (!aBroadcast.isEmpty())){
GroupCommMessage msgI=(GroupCommMessage)(aBroadcast.get(size));
AbcastMessageID id2 = (AbcastMessageID) msgI.tunpack();
msgI.tpack(id2);
myList1.add(id2);
size++;
}
//update aBroadcast by adding a new message from tok.nextSet
if (!myList1.contains(id1)){
aBroadcast.add(gcmmm);
System.out.println(" a new message is added to aBroadcast: "+ id1);
}
nextSetsize++;
}
logger.exiting("TokenAbcast", "tokenOfSmallRound");
};
//when receive a token from a certain process
private void tokenReceived(Token tok)throws GroupCommException{
logger.entering("TokenAbcast", "tokenReceived");
logger.log(Level.FINE,
"dealing with token of small round: {0} with sender {1}\nproposalSeq is {2}\nadeliv is {3}\n\tnextSet is {4}\nvotes {5}", new Object[]{new Integer(tok.round),tok.sender,tok.proposalSeq,tok.adeliv,tok.nextSet,new Integer(tok.votes)});
System.out.println("token is of round: "+tok.round);
PID p=tok.sender;
int round=tok.round;
//test if the token is from one of his precedents
if (!procede.contains(p))
throw new GroupCommException("token is not from a valid precedent");
//test if has already received a token with the same round as the new one
//if not the case
if (!myReceivedToken.containsKey(new TInteger(round))){
myReceivedToken.put(new TInteger(round),tok);
}
//otherwise
else{
PID q=((Token)(myReceivedToken.get(new TInteger(round)))).sender;
//if sender of token newly received is much nearer than sender of the old one
if ((myID-processes.indexOf(p))%processes.size() < (myID-processes.indexOf(q))%processes.size())
//update to new token
myReceivedToken.put(new TInteger(round), tok);
}
//test if token is from his direct precedent "pre",
//if it's the case, accept it even if it not has been suspected
if (( (p.equals(pre)) || suspected) && (round==myRound))
processToken(tok);
logger.exiting("TokenAbcast", "tokenReceived");
}
//deal with token
private void processToken(Token tok){
logger.entering("TokenAbcast", "processToken");
//aBroadcast<-(aBroadcast) union (token.proposalSeq) union (token.nextSet)
int proposalSize=0;
while ((proposalSize<(tok.proposalSeq).size())&&(!tok.proposalSeq.isEmpty())){
GroupCommMessage gcm= (GroupCommMessage)((tok.proposalSeq).get(proposalSize));
//get identifiant
AbcastMessageID id3 = (AbcastMessageID) gcm.tunpack();
gcm.tpack(id3);
//all ids of messages in aBroadcast
int size2=0;
TArrayList myList2=new TArrayList();
while ((size2<aBroadcast.size())&&(!aBroadcast.isEmpty())){
GroupCommMessage msgII=(GroupCommMessage)(aBroadcast.get(size2));
//get identifiant
AbcastMessageID id4 = (AbcastMessageID) msgII.tunpack();
msgII.tpack(id4);
myList2.add(id4);
size2++;
}
//update aBroadcast, aBroadcast <- aBroadcast union tok.proposalSeq
if (!myList2.contains(id3)){
aBroadcast.add(gcm);
System.out.println("new message added to aBroadcast: " + id3);}
proposalSize++;
}
int nextSize=0;
while ((nextSize<(tok.nextSet).size()) && (!(tok.nextSet).isEmpty())){
GroupCommMessage gcmm= (GroupCommMessage)((tok.nextSet).get(nextSize));
AbcastMessageID id5 = (AbcastMessageID) gcmm.tunpack();
gcmm.tpack(id5);
//ids of message in aBroadcast
int size3=0;
TArrayList myList3=new TArrayList();
while ((size3<aBroadcast.size())&&(!aBroadcast.isEmpty())){
GroupCommMessage msgIII=(GroupCommMessage)(aBroadcast.get(size3));
AbcastMessageID id6 = (AbcastMessageID) msgIII.tunpack();
msgIII.tpack(id6);
myList3.add(id6);
size3++;
}
if (!myList3.contains(id5)){
aBroadcast.add(gcmm);
System.out.println("new message added to aBroadcast: " + id5);
}
nextSize++;
}
//old token
if ((tok.adeliv).size() < aDeliver.size())
(tok.proposalSeq).clear();
//token with new information
else{
triggerDelivery(tok.adeliv);
if ((((PID)(tok.sender)).equals(pre)) && (!((tok.proposalSeq).isEmpty())))
myVotes=tok.votes+1;
else
myVotes=1;
System.out.println("now myVotes of pi is: "+myVotes);
if (myVotes>=(f+1)){
triggerDelivery(tok.proposalSeq);
(tok.proposalSeq).clear();
}
}
//new proposal
if ((tok.proposalSeq).isEmpty()){
tok.proposalSeq=aBroadcast;
myVotes=1;
}
//now thw sender of token is myself
tok.sender=(PID)processes.get(myID);
//re-construct the token
tok=new Token(myRound,tok.sender,tok.proposalSeq,myVotes,aDeliver,aBroadcast);
//send token to all his f+1 successors
triggerSend(tok,succede);
//update the round
myRound=myRound+1;
//remove the old token from Map myReceivedToken
myReceivedToken.remove(new TInteger(myRound-1));
logger.exiting("TokenAbcast", "processToken");
}
//trigger event adeliver
private void triggerDelivery(TList col){
logger.entering("TokenAbcast", "triggerDelivery");
//Adeliver messages in col not in aDeliver
int colSize=0;
while ((colSize<col.size()) && (!col.isEmpty())){
GroupCommMessage grc=(GroupCommMessage)(col.get(colSize));
//get identifier
AbcastMessageID id7 = (AbcastMessageID) grc.tunpack();
grc.tpack(id7);
//id of message in aBroadcast
int aSize=0;
TArrayList myList4=new TArrayList();
while ((aSize<aDeliver.size()) && (!aDeliver.isEmpty())){
GroupCommMessage msgIV=(GroupCommMessage)(aDeliver.get(aSize));
AbcastMessageID id8 = (AbcastMessageID) msgIV.tunpack();
msgIV.tpack(id8);
myList4.add(id8);
aSize++;
}
if (!myList4.contains(id7)){
GroupCommEventArgs ad=new GroupCommEventArgs();
ad.addFirst(grc);
//Flow control (added by Sergio: 17/2/2006)
if(id7.proc.equals(processes.get(myID))){
fc_used--; if(fc_used < fc_threshold) flow_control.release(fc_key);
}
logger.log(Level.FINE,"trigger Adeliver event {0}:",ad);
trigger.trigger(Constants.ADELIVER,ad);
//add this message to aDeliver
aDeliver.add(grc);
System.out.println("a new message added to aDeliver: "+id7);
}
colSize++;
}
//update aBroadcast ,aBroadcast <- aBroadcast\ aDeliver
int aDsize=0;
while ((aDsize<aDeliver.size()) &&(!aDeliver.isEmpty()) ){
GroupCommMessage grpc=(GroupCommMessage)(aDeliver.get(aDsize));
AbcastMessageID id9 = (AbcastMessageID) grpc.tunpack();
grpc.tpack(id9);
//id of message in aBroadcast
int size3=0;
TArrayList myList5=new TArrayList();
while ((size3<aBroadcast.size()) && (!aBroadcast.isEmpty())){
GroupCommMessage msgV=(GroupCommMessage)(aBroadcast.get(size3));
AbcastMessageID id10 = (AbcastMessageID) msgV.tunpack();
msgV.tpack(id10);
myList5.add(id10);
size3++;
}
if (myList5.contains(id9)){
aBroadcast.remove(grpc);
System.out.println("an old message removed from abraodcast: "+id9);
};
aDsize++;
}
logger.exiting("TokenAbcast", "triggerDelivery");
}
//trigger event send
private void triggerSend(Token token, TList succede){
logger.entering("TokenAbcast", "triggerSend");
Iterator it=succede.iterator();
while (it.hasNext()){
PID dest=(PID) it.next();
System.out.println("dest is:" +((PID)dest).toString());
//construction of message
GroupCommMessage m=new GroupCommMessage();
//m=token
m.tpack((Transportable)token.clone());
GroupCommEventArgs e=new GroupCommEventArgs();
e.addLast(m);
e.addLast(dest);
e.addLast(new TBoolean(false));
logger.log(Level.FINE,"trigger Pt2PtSend event {0}:",e);
trigger.trigger(Constants.PT2PTSEND,e);
}
logger.exiting("TokenAbcast", "triggerSend");
}
//print values to debug
public void dump(OutputStream out){
int i,j;
PrintStream err=new PrintStream(out);
err.println("=========TokenAbcast:dump==========");
err.println("index of myself is: "+myID);
err.println("myself is: "+((PID)(processes.get(myID))).toString());
err.println("the list of precedents is: ");
for(i=0;i<procede.size();i++)
err.println(((PID)procede.get(i)).toString());
err.println("Direct precedent is: "+pre.toString());
err.println("the list of of successors is: ");
for(j=0;j<succede.size();j++)
err.println(((PID)succede.get(j)).toString()+",");
err.println("actual round is: "+myRound);
err.println("Suspect the previous or not: "+suspected);
err.println("Received tokens are:");
TCollection col=myReceivedToken.values();
Iterator iter=col.iterator();
while (iter.hasNext()){
Token myToken=(Token) iter.next();
err.println("token from: " +((PID)myToken.sender).toString()+" is of round: "+myToken.round);
}
err.println("myVotes is: "+myVotes);
err.println("Set of Messages have been abroadcast is: ");
Iterator ite=aBroadcast.iterator();
while (ite.hasNext()){
GroupCommMessage myMessage=(GroupCommMessage) ite.next();
err.println("Message is: " +myMessage);
}
err.println("Sequence of Messages have been adelivered by myself is: ");
Iterator iterr=aDeliver.iterator();
while (iterr.hasNext()){
GroupCommMessage myMes=(GroupCommMessage) iterr.next();
err.println("Message is: " +myMes);
}
err.println("====================================");
}
}