/**
* 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.gmp;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.logging.Level;
import java.util.logging.Logger;
import framework.Constants;
import framework.GroupCommEventArgs;
import framework.GroupCommException;
import framework.GroupCommMessage;
import framework.PID;
import framework.libraries.Trigger;
import framework.libraries.serialization.TArrayList;
import framework.libraries.serialization.TBoolean;
import framework.libraries.serialization.THashSet;
import framework.libraries.serialization.TInteger;
import framework.libraries.serialization.TList;
import framework.libraries.serialization.TSet;
/**
* Common code for the Group Membership Layer.
* <br />
* Events handled :
* <ul>
* <li>Init</li>
* <li>Join</li>
* <li>Remove</li>
* <li>Pt2PtDeliver</li>
* <li>GDeliver (or ADeliver)</li>
* </ul>
* Events provided :
* <ul>
* <li>Agcast (or Abcast)</li>
* <li>JoinRemoveList</li>
* <li>Pt2PtSend</li>
* <li>NewView</li>
* <li>GmpDeliver</li>
* </ul>
* @author Olivier FAURAX
* @see groupcomm.appia.gmp
* @see groupcomm.cactus.gmp
*/
public class GroupMembershipImpl {
private PID myself;
// initialized ?
private boolean init = false;
// do we have some processes when init ?
private boolean initData = false;
// Clause When of GDeliver
private LinkedList whenADeliver = new LinkedList();
// Logging
private static final Logger logger =
Logger.getLogger(GroupMembershipImpl.class.getName());
// item for trigerring at the end of methods
private static class TriggerItem {
public int type;
public GroupCommEventArgs args;
public TriggerItem(int type, GroupCommEventArgs args) {
this.type = type;
this.args = args;
}
}
/** List of known processes */
protected TArrayList processes;
/** Current view_id */
protected int view_id = 0;
/** The trigger used to launch events */
protected Trigger gmp = null;
/**
* Constructor
*
* @param trig the trigger used to launch events
*/
public GroupMembershipImpl(Trigger trig, PID myself) {
logger.entering("GroupMembershipImpl", "<constr>");
this.myself = myself;
gmp = trig;
logger.exiting("GroupMembershipImpl", "<constr>");
}
/**
* Return the current view
*
* @return current view
*/
public TList getView(){
return processes;
}
/**
* To handle init events
*
* @param ev the initial ensemble of processes.
* Must contains a {@link LinkedList} as first element.
* @throws InitException
*/
public void handleInit(GroupCommEventArgs ev) throws GroupCommException {
logger.entering("GroupMembershipImpl", "handleInit");
// assert initialized != true
if (init)
throw new GroupCommException("GroupMembershipImpl already initialized.");
init = true;
view_id = 0;
TList arg1 = (TList) ev.removeFirst();
if (!arg1.isEmpty()) {
initData = true;
processes = new TArrayList(arg1);
//Look for duplicate processes in the group
for (int i = 0; i < processes.size(); i++)
for (int j = i + 1; j < processes.size(); j++)
if (processes.get(i).equals(processes.get(j)))
throw new GroupCommException(
"Process"
+ processes.get(i)
+ " appears more than once in the group.");
// trigger NEW_VIEW
GroupCommEventArgs argNV = new GroupCommEventArgs();
argNV.addLast(new TInteger(view_id));
argNV.addLast((TList) processes.clone());
logger.log(
Level.FINE,
"Sending NewView with view_id {0} containing {1}",
new Object[] { new Integer(view_id), processes });
// join-remove
GroupCommEventArgs jrl = new GroupCommEventArgs();
jrl.addLast (new THashSet(processes)); // join
jrl.addLast (new THashSet()); // remove
gmp.trigger (Constants.JOINREMOVELIST, jrl);
gmp.trigger(Constants.NEW_VIEW, argNV);
}
logger.exiting("GroupMembershipImpl", "handleInit");
}
/**
* To handle join events
*
* @param ev the process that join. Must contain a {@link PID} as first element.
*/
public void handleJoin(GroupCommEventArgs ev) {
logger.entering("GroupMembershipImpl", "handleJoin");
PID p = (PID) ev.removeFirst();
GroupCommMessage msg = new GroupCommMessage();
GroupCommEventArgs arg = new GroupCommEventArgs();
arg.addLast(new TInteger(Constants.ADD));
arg.addLast(msg);
arg.addLast(p);
logger.log(Level.FINE, "Sending Join Broadcast for PID: {0}", p);
// gmp.trigger(Constants.AGCAST, arg );
gmp.trigger(Constants.ABCAST, arg);
logger.exiting("GroupMembershipImpl", "handleJoin");
}
/**
* To handle remove events
*
* @param P the process that is removed. Must contain a {@link PID} as first element.
*/
public void handleRemove(GroupCommEventArgs ev) {
logger.entering("GroupMembershipImpl", "handleRemove");
PID P = (PID) ev.removeFirst();
GroupCommMessage msg = new GroupCommMessage();
GroupCommEventArgs arg = new GroupCommEventArgs();
arg.addLast(new TInteger(Constants.REM));
arg.addLast(msg);
arg.addLast(P);
logger.log(Level.FINE, "Sending Remove Broadcast for PID: {0}", P);
// gmp.trigger(Constants.AGCAST, arg );
gmp.trigger(Constants.ABCAST, arg);
logger.exiting("GroupMembershipImpl", "handleRemove");
}
/**
* To handle Pt2PtDeliver events
*
* @param ev the message. Must contains a {@link GroupCommMessage} of the form :
* {@link Integer}::{@link LinkedList}(processes)::{@link HashSet}(newProc)
* @throws GroupCommException if there are items of newProc that are not in processes
*/
public void handlePt2PtDeliver(GroupCommEventArgs ev)
throws GroupCommException {
logger.entering("GroupMembershipImpl", "handlePt2PtDeliver");
//HashSet newProc = null;
LinkedList toTrigger = new LinkedList();
if (!initData) {
GroupCommMessage m = (GroupCommMessage) ev.removeFirst();
// m = view_id::processes::newProc
GroupCommMessage m2 = m.cloneGroupCommMessage();
// m2 = m = view_id::processes::newProc
view_id = ((TInteger) m2.tunpack()).intValue();
// m2 = processes::newProc
processes = (TArrayList) m2.tunpack();
//Look for duplicate processes in the group
for (int i = 0; i < processes.size(); i++)
for (int j = i + 1; j < processes.size(); j++)
if (processes.get(i).equals(processes.get(j)))
throw new GroupCommException(
"Process"
+ processes.get(i)
+ " appears more than once in the group.");
// m2 = newProc
TSet newProc = (TSet) m2.tunpack();
// m2 is empty
// assert( newProc in processes )
if (!processes.containsAll(newProc))
throw new GroupCommException("GMP: No new process in NewProc list");
initData = true;
// trigger JOIN_REMOVE_LIST
GroupCommEventArgs argJRL = new GroupCommEventArgs();
argJRL.addLast(new THashSet(processes));
argJRL.addLast(new THashSet());
logger.log(
Level.FINE,
"Sending JoinRemoveList. Joining: ({0}), Leaving: (none)",
processes);
toTrigger.addLast(
new TriggerItem(Constants.JOINREMOVELIST, argJRL));
//I remove myself from the new processes
newProc.remove(myself);
// forall p in newProc
GroupCommEventArgs arg;
PID p;
Iterator it = newProc.iterator();
while (it.hasNext()) {
p = (PID) it.next();
arg = new GroupCommEventArgs();
arg.addLast(m.cloneGroupCommMessage());
arg.addLast(p);
arg.addLast(new TBoolean(true)); // promisc
Object[] temp = new Object[2];
temp[0] = m;
temp[1] = p;
logger.log(
Level.FINE,
"Sending Pt2PtSend with message {0} to {1} in mode promisc",
temp);
// trigger PT2PTSEND
toTrigger.addLast(new TriggerItem(Constants.PT2PTSEND, arg));
}
// trigger NEW_VIEW
GroupCommEventArgs argNV = new GroupCommEventArgs();
argNV.addLast(new TInteger(view_id));
argNV.addLast((TList) processes.clone());
Object[] temp = new Object[2];
temp[0] = new Integer(view_id);
temp[1] = processes;
logger.log(
Level.FINE,
"Sending NewView with view_id {0} containing {1}",
temp);
toTrigger.addLast(new TriggerItem(Constants.NEW_VIEW, argNV));
//Now we trigger all events scheduled
while (!toTrigger.isEmpty()) {
TriggerItem item = (TriggerItem) toTrigger.removeFirst();
gmp.trigger(item.type, item.args);
}
// ADeliver WhenClause (as initData is true) (FLUSH !)
while (!whenADeliver.isEmpty()) {
handleADeliver((GroupCommEventArgs) whenADeliver.removeFirst());
}
}
logger.exiting("GroupMembershipImpl", "handlePt2PtDeliver");
}
/**
* To handle adeliver (and adeliver) events
*
* @param ev the message delivered. Must contain an {@link Integer},
* the {@link GroupCommMessage} delivered, a {@link PID}
*/
public void handleADeliver(GroupCommEventArgs ev) throws GroupCommException {
logger.entering("GroupMembershipImpl", "handleADeliver");
TList oldProcesses = null;
THashSet newProc = new THashSet();
THashSet argtemp = null;
GroupCommEventArgs arg = null;
Object[] temp = new Object[2];
LinkedList toTrigger = new LinkedList();
GroupCommEventArgs argNV = new GroupCommEventArgs();
// When clause: if we can't treat the event now, we store it for later.
if (!initData) {
whenADeliver.addLast(ev);
} else {
int type = ((TInteger) ev.get(0)).intValue();
//GroupCommMessage evmsg = (GroupCommMessage)ev.get(1);
PID m = (PID) ev.get(2);
switch (type) {
case Constants.ADD :
if (!processes.contains(m)) {
oldProcesses = (TList) processes.clone();
processes.add(m);
view_id++;
// newProc = processes - oldProcesses;
TList newProcTemp = (TList) processes.clone();
newProcTemp.removeAll(oldProcesses);
newProc = new THashSet(newProcTemp);
// argtemp = {m}
argtemp = new THashSet();
argtemp.add(m);
arg = new GroupCommEventArgs();
arg.addLast(argtemp);
arg.addLast(new THashSet());
logger.log(
Level.FINE,
"Sending JoinRemoveList. Joining: ({0}), Leaving: (none)",
argtemp);
toTrigger.addLast(
new TriggerItem(Constants.JOINREMOVELIST, arg));
// msg = view_id::processes::newProc
GroupCommMessage msg = new GroupCommMessage();
msg.tpack((TSet) newProc.clone());
msg.tpack((TArrayList) processes.clone());
msg.tpack(new TInteger(view_id));
// forall p in newProc
PID p;
Iterator it = newProc.iterator();
while (it.hasNext()) {
p = (PID) it.next();
arg = new GroupCommEventArgs();
arg.addLast(msg.cloneGroupCommMessage());
arg.addLast(p);
arg.addLast(new TBoolean(true)); // promisc
temp[0] = msg;
temp[1] = p;
logger.log(
Level.FINE,
"Sending Pt2PtSend with message {0} to {1} in mode promisc",
temp);
// trigger PT2PTSEND
toTrigger.addLast(
new TriggerItem(Constants.PT2PTSEND, arg));
}
// trigger NEW_VIEW
argNV = new GroupCommEventArgs();
argNV.addLast(new TInteger(view_id));
argNV.addLast((TList) processes.clone());
temp[0] = new Integer(view_id);
temp[1] = processes;
logger.log(
Level.FINE,
"Sending NewView with view_id {0} containing {1}",
temp);
toTrigger.addLast(
new TriggerItem(Constants.NEW_VIEW, argNV));
} else {
System.err.println(
"Warning: trying to join a process twice. "
+ "Second <join> will be ignored.");
}
break;
case Constants.REM :
if (processes.contains(m)) {
processes.remove(m);
view_id++;
// argtemp = {m}
argtemp = new THashSet();
argtemp.add(m);
// trigger join_remove_list
arg = new GroupCommEventArgs();
arg.addLast(new THashSet());
arg.addLast(argtemp);
toTrigger.addLast(
new TriggerItem(Constants.JOINREMOVELIST, arg));
// trigger NEW_VIEW
argNV = new GroupCommEventArgs();
argNV.addLast(new TInteger(view_id));
argNV.addLast((TList) processes.clone());
temp = new Object[2];
temp[0] = new Integer(view_id);
temp[1] = processes;
logger.log(
Level.FINE,
"Sending NewView with view_id {0} containing {1}",
temp);
toTrigger.addLast(
new TriggerItem(Constants.NEW_VIEW, argNV));
}
break;
default :
// arg = new GroupCommEventArgs();
// arg.addLast(m);
// logger.log(Level.FINE,
// "Sending GMPDeliver. Message: {0}",
// m);
// toTrigger.addLast(new TriggerItem(Constants.GMPDELIVER, arg));
throw new GroupCommException(
"GroupMembershipImpl: Handle adeliver: Unknown message type");
} // switch
//Now we trigger all events scheduled
while (!toTrigger.isEmpty()) {
TriggerItem item = (TriggerItem) toTrigger.removeFirst();
gmp.trigger(item.type, item.args);
}
} // if else (whenClause)
logger.exiting("GroupMembershipImpl", "handleADeliver");
}
/**
* Print the current state of the layer.
*
* @param out The output stream used for showing infos
*/
public void dump(OutputStream out) {
PrintStream err = new PrintStream(out);
err.println("======== GroupMembershipImpl debug infos ============");
err.println(" Initialized: " + String.valueOf(init));
err.println(" Initdata: " + String.valueOf(initData));
if (initData) {
err.println(" View id: " + view_id);
Iterator it = processes.iterator();
PID pid;
while (it.hasNext()) {
pid = (PID) it.next();
err.println("\t" + pid.toString());
}
}
err.println("=====================================================");
}
}