/**
* 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
*/
package groupcomm.common.fd;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Iterator;
import java.util.logging.Logger;
import framework.Constants;
import framework.GroupCommEventArgs;
import framework.GroupCommException;
import framework.GroupCommMessage;
import framework.PID;
import framework.libraries.Timer;
import framework.libraries.Trigger;
import framework.libraries.serialization.THashMap;
import framework.libraries.serialization.THashSet;
import framework.libraries.serialization.TInteger;
import framework.libraries.serialization.TLinkedList;
import framework.libraries.serialization.TMap;
import framework.libraries.serialization.TSet;
public class HBHandler{
private PID myself;
private static final int DEFAULT_SEND_TIMEOUT = 1000; //milliseconds
private static final int DEFAULT_SUSPECT_TIMEOUT = 3; //retries
private static final int DEFAULT_SUSPECT_ASKING = 3;
// Constants for communication
private static final int ASK_LISTEN = 0;
private static final int LISTEN = 1;
private static final int HEARTBEAT = 2;
// Set of monitored processes
private TMap listenProcesses = null;
// Set of processes we want to monitor
private TMap askListenProcesses = null;
// Set of processes we send HeartBeat
private TLinkedList speakProcesses = null;
// Set of Suspected processes
private TSet suspects = null;
// Object that routes outgoing events
private Trigger trigger = null;
// Interface to framework's timer facilities
private Timer timer = null;
// Number of timeouts before suspecting a process
private int n = DEFAULT_SUSPECT_TIMEOUT;
// Number of demands before suspecting a process
private int nD = DEFAULT_SUSPECT_ASKING;
// Timeout value (milliseconds)
private int sendTimeOut = DEFAULT_SEND_TIMEOUT;
private static final Logger logger =
Logger.getLogger(HBHandler.class.getName());
public HBHandler (Trigger trigger,
Timer timer,
PID myself,
int sendTimeOut,
int suspectTimeOut,
int suspectAsking){
logger.entering("HBHandler","<constr> 4 parameters");
this.trigger = trigger;
this.timer = timer;
this.myself = myself;
speakProcesses = new TLinkedList();
listenProcesses = new THashMap();
askListenProcesses = new THashMap();
suspects = new THashSet(); //Rep�r� par A. Klaey
if (sendTimeOut > 0)
this.sendTimeOut = sendTimeOut;
if (suspectTimeOut > 0)
this.n = suspectTimeOut;
if (suspectAsking > 0)
this.nD = suspectAsking;
logger.exiting("HBHandler","<constr> 4 parameters");
}
public TLinkedList getState(){
TLinkedList state = new TLinkedList();
TSet tmpProcesses = new THashSet();
tmpProcesses.addAll(listenProcesses.keySet());
tmpProcesses.addAll(askListenProcesses.keySet());
tmpProcesses.addAll(speakProcesses);
state.addLast(tmpProcesses);
state.addLast(suspects);
return state;
}
public void setState(TLinkedList state){
askListenProcesses.clear();
listenProcesses.clear();
speakProcesses.clear();
TSet tmpProcesses = (TSet) state.removeFirst();
suspects = (TSet) state.removeFirst();
Iterator it = tmpProcesses.iterator();
while(it.hasNext()){
PID p = (PID) it.next();
if (!askListenProcesses.containsKey(p) &&
!p.equals(myself)) {
askListenProcesses.put(p, new TInteger(0));
TLinkedList l = new TLinkedList();
l.addLast(p);
l.addLast(new TInteger(ASK_LISTEN));
timer.schedule(l, true, sendTimeOut);
triggerAskListen(p);
}
if (!speakProcesses.contains(p) &&
!p.equals(myself)) {
speakProcesses.add(p);
TLinkedList l = new TLinkedList();
l.addLast(p);
l.addLast(new TInteger(HEARTBEAT));
timer.schedule(l, true, sendTimeOut);
triggerHeartBeat(p);
}
}
triggerSuspect();
}
/**
* Le handler de l'�v�nement <i>StartStopMonitor</i>. <br>
* Cet �v�nement permet de d�buter ou d'arreter le failure detector
* pour certains processus.
*
* @param e <dl>
* <dt> start : Set </dt> <dd> L'ensemble de processus que nous devons commencer � monitorer. </dd>
* <dt> stop : Set </dt> <dd> L'ensemble de processus que nous devons arr�ter de monitorer.</dd>
* </dl>
*/
public void handleStartStopMonitor (GroupCommEventArgs e)
throws GroupCommException{
logger.entering("HBHandler","handleStartStopMonitor");
TSet start = (TSet)e.removeFirst();
TSet stop = (TSet)e.removeFirst();
//Checks if there is some process in both sets
Iterator i = stop.iterator();
while (i.hasNext()){
PID p = (PID)i.next();
if (start.contains(p)){
throw new GroupCommException();
}
}
stop(stop);
start(start);
logger.exiting("HBHandler","handleStartStopMonitor");
}
/**
* Le "handler" d'un <i>TimeOut</i>. <br>
* Le FDPeriodicTimer appellera cette m�thode lorsque le timer
* du processus <i>p</i> expire.
*
* @param p : Le processus auquel nous devons envoyer un Alive.
*/
public void handleTimeOut(GroupCommEventArgs arg){
logger.entering("HBHandler","handleTimeOut");
TLinkedList l = (TLinkedList) arg.get(0);
PID pid = (PID) l.getFirst();
int role = ((TInteger) l.getLast()).intValue();
int timeOut;
switch(role){
case ASK_LISTEN:
timeOut = ((TInteger)askListenProcesses.get(pid)).intValue();
timeOut++;
if (timeOut < nD){
// Timeout not yet expired
askListenProcesses.put(pid, new TInteger(timeOut));
triggerAskListen(pid);
} else {
// Timeout expired
if(!suspects.contains(pid)){
suspects.add(pid);
triggerSuspect();
}
}
break;
case LISTEN:
timeOut = ((TInteger)listenProcesses.get(pid)).intValue();
timeOut++;
if (timeOut < n){
// Timeout not yet expired
listenProcesses.put(pid, new TInteger(timeOut));
} else {
// Timeout expired
if(!suspects.contains(pid)){
suspects.add(pid);
triggerSuspect();
}
}
break;
case HEARTBEAT:
triggerHeartBeat(pid);
break;
}
logger.exiting("HBHandler","handleTimeOut");
}
/**
* Le handler de l'�v�nement <i>Alive</i>. <br>
* Cet �v�nement indique au failure detector que le processus <i>src</i> est vivant.
*
* @param e <dl>
* <dt> src : PID </dt> <dd> Le processus ayant �mis les message. </dd>
* <dt> origin : boolean </dt> <dd> Vaut true si src est � l'origine du Alive, et faux si src r�pond � un de nos Alive's.</dd>
* </dl>
*/
public void handleUDPReceive(GroupCommEventArgs e){
//throws InvalidHostException{
logger.entering("HBHandler","handleAlive");
GroupCommMessage msg = (GroupCommMessage)e.removeFirst();
PID src = (PID) msg.tunpack();
int role = ((TInteger) msg.tunpack()).intValue();
PID dest = (PID) msg.tunpack();
if (!dest.equals(myself)){
//Maybe I'm another incarnation. Discard it
logger.exiting("HBHandler","handleAlive");
return;
}
if (role == HEARTBEAT){
if(askListenProcesses.containsKey(src)){
askListenProcesses.remove(src);
TLinkedList l = new TLinkedList();
l.addLast(src);
l.addLast(new TInteger(ASK_LISTEN));
timer.cancel(l);
TLinkedList l2 = new TLinkedList();
l2.addLast(src);
l2.addLast(new TInteger(LISTEN));
timer.schedule(l2, true, sendTimeOut);
listenProcesses.put(src, new TInteger(0));
}else if(listenProcesses.containsKey(src)){
//I monitor p
// I have to reset its timer
// and remove it from the suspects (if he was inside)
listenProcesses.put(src, new TInteger(0));
TLinkedList l = new TLinkedList();
l.addLast(src);
l.addLast(new TInteger(LISTEN));
timer.reset(l);
}
if(suspects.contains(src)){
suspects.remove(src);
if (listenProcesses.containsKey(src)){
TLinkedList l = new TLinkedList();
l.addLast(src);
l.addLast(new TInteger(LISTEN));
listenProcesses.remove(src);
timer.cancel(l);
TLinkedList l2 = new TLinkedList();
l2.addLast(src);
l2.addLast(new TInteger(ASK_LISTEN));
askListenProcesses.put(src, new TInteger(0));
timer.schedule(l2, true, sendTimeOut);
}else if (askListenProcesses.containsKey(src)){
TLinkedList l = new TLinkedList();
l.addLast(src);
l.addLast(new TInteger(ASK_LISTEN));
askListenProcesses.put(src, new TInteger(0));
timer.reset(l);
}
triggerSuspect();
}
}else if (role == ASK_LISTEN){
if (!speakProcesses.contains(src)){
speakProcesses.add(src);
TLinkedList l = new TLinkedList();
l.addLast(src);
l.addLast(new TInteger(HEARTBEAT));
timer.schedule(l, true, sendTimeOut);
}else{
TLinkedList l = new TLinkedList();
l.addLast(src);
l.addLast(new TInteger(HEARTBEAT));
timer.reset(l);
}
triggerHeartBeat(src);
if(suspects.contains(src)){
suspects.remove(src);
if (listenProcesses.containsKey(src)){
TLinkedList l = new TLinkedList();
l.addLast(src);
l.addLast(new TInteger(LISTEN));
listenProcesses.remove(src);
timer.cancel(l);
TLinkedList l2 = new TLinkedList();
l2.addLast(src);
l2.addLast(new TInteger(ASK_LISTEN));
askListenProcesses.put(src, new TInteger(0));
timer.schedule(l2, true, sendTimeOut);
}else if (askListenProcesses.containsKey(src)){
TLinkedList l = new TLinkedList();
l.addLast(src);
l.addLast(new TInteger(ASK_LISTEN));
askListenProcesses.put(src, new TInteger(0));
timer.reset(l);
}
triggerSuspect();
}
}
logger.exiting("HBHandler","handleAlive");
}
/**
* Commence � monitorer les processus dans <i>s</i>, si ils ne l'�taient pas d�j�.
*/
private void start (TSet s){
logger.entering("HBHandler","start");
boolean startSuspect = false;
TLinkedList toRemove = new TLinkedList();
Iterator k = suspects.iterator();
while(k.hasNext()){
PID p = (PID)k.next();
if(!listenProcesses.containsKey(p) &&
!askListenProcesses.containsKey(p)){
if(s.contains(p)){
startSuspect = true;
} else {
toRemove.add(p);
}
}
}
while(!toRemove.isEmpty())
suspects.remove(toRemove.removeFirst());
Iterator i = s.iterator();
while (i.hasNext()){
PID p = (PID)i.next();
//If the process is already being monitored, do nothing
//If the process is myself, do nothing, either
if (!listenProcesses.containsKey(p) &&
!askListenProcesses.containsKey(p) &&
!p.equals(myself)){
TLinkedList l = new TLinkedList();
l.addLast(p);
l.addLast(new TInteger(ASK_LISTEN));
timer.schedule(l, true, sendTimeOut);
askListenProcesses.put(p, new TInteger(0));
triggerAskListen(p);
}
}
if (startSuspect)
triggerSuspect();
logger.exiting("HBHandler","start");
}
/**
* Arr�te de monitorer les processus contenus dans <i>s</i>.
* Tous les processus dans <i>s</i> doivent �tre monitor�s.
*/
private void stop (TSet s){
logger.entering("HBHandler","stop");
Iterator i = s.iterator();
while (i.hasNext()){
PID p = (PID)i.next();
if(listenProcesses.containsKey(p)){
listenProcesses.remove(p);
TLinkedList l = new TLinkedList();
l.addLast(p);
l.addLast(new TInteger(LISTEN));
timer.cancel(l);
}else if(askListenProcesses.containsKey(p)){
askListenProcesses.remove(p);
TLinkedList l = new TLinkedList();
l.addLast(p);
l.addLast(new TInteger(ASK_LISTEN));
timer.cancel(l);
}
}
logger.exiting("HBHandler","stop");
}
private void triggerAskListen(PID dest){
logger.entering("HBHandler","triggerAskListen");
GroupCommMessage m = new GroupCommMessage();
m.tpack(dest);
m.tpack(new TInteger(ASK_LISTEN));
m.tpack(myself);
GroupCommEventArgs e = new GroupCommEventArgs();
e.addLast(m);
e.addLast(dest);
trigger.trigger(Constants.ALIVE, e);
logger.exiting("HBHandler","triggerAskListen");
}
private void triggerHeartBeat(PID dest){
logger.entering("HBHandler","triggerHeartBeat");
GroupCommMessage m = new GroupCommMessage();
m.tpack(dest);
m.tpack(new TInteger(HEARTBEAT));
m.tpack(myself);
GroupCommEventArgs e = new GroupCommEventArgs();
e.addLast(m);
e.addLast(dest);
trigger.trigger(Constants.ALIVE, e);
logger.exiting("HBHandler","triggerAlive");
}
private void triggerSuspect(){
logger.entering("HBHandler","triggerSuspect");
THashSet s = new THashSet(suspects);
GroupCommEventArgs e = new GroupCommEventArgs();
e.addFirst(s);
trigger.trigger(Constants.SUSPECT, e);
logger.exiting("HBHandler","triggerSuspect");
}
/**
* Used for debugging. </br>
* Undocumented.
*/
public void dump (OutputStream out) {
PrintStream err = new PrintStream(out);
err.println("========= HBHandler: dump =========");
err.println(" Monitored Processes: " + listenProcesses);
err.println(" Asking Processes: " + askListenProcesses);
err.println(" Speaking Processes: " + speakProcesses);
err.println(" Suspected Processes: " + suspects);
err.println(" Send timeout: " + sendTimeOut);
err.println(" Suspect timeout: " + n);
err.println("===================================");
}
}