/**
* SAMOA - PROTOCOL FRAMEWORK
* Copyright (C) 2005 Olivier Rütti (EPFL) (olivier.rutti@a3.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 seqSamoa.protocols.abcast;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import seqSamoa.Message;
import seqSamoa.ProtocolModule;
import seqSamoa.ProtocolStack;
import seqSamoa.ServiceCallOrResponse;
import seqSamoa.AtomicTask;
import seqSamoa.exceptions.AlreadyExistingProtocolModuleException;
import seqSamoa.exceptions.NotScheduledTaskException;
import seqSamoa.services.abcast.Abcast;
import seqSamoa.services.abcast.AbcastResponseParameters;
import seqSamoa.services.consensus.Consensus;
import seqSamoa.services.consensus.ConsensusCallParameters;
import seqSamoa.services.consensus.ConsensusResponseParameters;
import seqSamoa.services.rpt2pt.RPT2PT;
import seqSamoa.services.rpt2pt.RPT2PTCallParameters;
import seqSamoa.services.rpt2pt.RPT2PTResponseParameters;
import uka.transport.Transportable;
import framework.Compressable;
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.TBoolean;
import framework.libraries.serialization.TList;
import framework.libraries.serialization.TLong;
import framework.libraries.serialization.TSet;
import groupcomm.common.abcast.AbcastImpl;
/**
* This class implement the ABcast with dynamic set of processes (view
* synchrony). It manage many consensus instances in parallel in contrary to
* classical Chandra-Toueg ABcast algorithms.
*
* This Protocol need a Protocol that implements RPT2PT and Consensus.
*
* The service implemented is dynAbcast (described in util/Services.java)
*/
public class ProtocolAbcast extends ProtocolModule implements Trigger, Timer {
final private static int MAX_PROCESSES = 7;
final private static int MAX_MESSAGES = 45;
public static int nbDynAbcast = 0;
@SuppressWarnings("serial")
public static class ConsensusID implements Transportable, Compressable {
int protocolValue;
TLong value;
public ConsensusID(long value, int pValue) {
this.value = new TLong(value);
this.protocolValue = pValue;
}
public ConsensusID(TLong value, int pValue) {
this.value = value;
this.protocolValue = pValue;
}
public boolean equals(Object o) {
if (!(o instanceof ConsensusID))
return false;
ConsensusID cID = (ConsensusID) o;
return ((cID.protocolValue == this.protocolValue) && (cID.value
.longValue() == this.value.longValue()));
}
public int compareToCompressable(Object o) {
if (o instanceof ConsensusID) {
ConsensusID id = (ConsensusID) o;
if (this.protocolValue != id.protocolValue)
return Compressable.NOT_COMPARABLE;
else
return this.value.compareToCompressable(id.value);
} else {
return Compressable.NOT_COMPARABLE;
}
}
public String toString() {
return new String("protocolValue: "+protocolValue+" -> value: "+value);
}
public int hashCode(){
return value.hashCode() + protocolValue;
}
protected static final int _SIZE = uka.transport.BasicIO.SIZEOF_int;
/** Used by uka.transport.UnmarshalStream to unmarshal the object */
public ConsensusID(uka.transport.UnmarshalStream _stream)
throws java.io.IOException, ClassNotFoundException {
this(_stream, _SIZE);
_stream.accept(_SIZE);
}
protected ConsensusID(uka.transport.UnmarshalStream _stream, int _size)
throws java.io.IOException, ClassNotFoundException {
byte[] _buffer = _stream.getBuffer();
int _pos = _stream.getPosition();
protocolValue = uka.transport.BasicIO.extractInt(_buffer, _pos);
_pos += uka.transport.BasicIO.SIZEOF_int;
}
/**
* Method of interface Transportable, it must be declared public. It is
* called from within UnmarshalStream after creating the object and
* assigning a stream reference to it.
*/
public void unmarshalReferences(uka.transport.UnmarshalStream _stream)
throws java.io.IOException, ClassNotFoundException {
this.value = (TLong) _stream.readObject();
}
/** Called directly by uka.transport.MarshalStream */
public void marshal(uka.transport.MarshalStream _stream)
throws java.io.IOException {
_stream.reserve(_SIZE);
byte[] _buffer = _stream.getBuffer();
int _pos = _stream.getPosition();
marshalPrimitives(_buffer, _pos);
_stream.deliver(_SIZE);
marshalReferences(_stream);
}
protected void marshalPrimitives(byte[] _buffer, int _pos)
throws java.io.IOException {
_pos = uka.transport.BasicIO.insert(_buffer, _pos, protocolValue);
}
protected void marshalReferences(uka.transport.MarshalStream _stream)
throws java.io.IOException {
_stream.writeObject(this.value);
}
public final Object deepClone(uka.transport.DeepClone _helper)
throws CloneNotSupportedException
{
Object _copy = clone();
_helper.add(this, _copy);
((ConsensusID) _copy).deepCloneReferences(_helper);
return _copy;
}
/** Clone all references to other objects. Use the
DeepClone to resolve cycles */
protected void deepCloneReferences(uka.transport.DeepClone _helper)
throws CloneNotSupportedException
{
this.value = (TLong) _helper.doDeepClone(this.value);
}
}
// Service provided
private Abcast abcast;
// Service required
private RPT2PT rpt2pt;
private Consensus consensus;
// Common Code
protected AbcastImpl handlers;
// Timers scheduled
private Map<Transportable, AtomicTask> timers = null;
// The Executer
// It ABcasts a message
protected Abcast.Executer abcastExecuter;
// The Listeners
// It listen for rpt2pt messages
protected RPT2PT.Listener rpt2ptListener;
// It listen for process supsicion
protected Consensus.Listener consensusListener;
// Identifier of the protocol
protected int pValue;
// The COR for call to abcast
private ServiceCallOrResponse abcastCallCOR;
/**
* Constructor. <br>
*
* @param name
* Name of the layer
* @param stack
* The stack in which the module will be
*/
public ProtocolAbcast(String name, ProtocolStack stack, Abcast abcast, Consensus consensus, RPT2PT rpt2pt) throws AlreadyExistingProtocolModuleException {
super(name, stack);
handlers = new AbcastImpl(this, stack.getFlowControl() , this, stack.getPID());
this.pValue = nbDynAbcast;
nbDynAbcast++;
this.timers = new HashMap<Transportable, AtomicTask>();
this.abcast = abcast;
this.consensus = consensus;
this.rpt2pt = rpt2pt;
this.abcastCallCOR = ServiceCallOrResponse.createServiceCallOrResponse(this.abcast, true);
LinkedList<ServiceCallOrResponse> initiatedAbcast = new LinkedList<ServiceCallOrResponse>();
for (int i=0; i<MAX_PROCESSES; i++)
initiatedAbcast.add(ServiceCallOrResponse.createServiceCallOrResponse(rpt2pt, true));
initiatedAbcast.add(ServiceCallOrResponse.createServiceCallOrResponse(consensus, true));
abcastExecuter = abcast.new Executer(this, initiatedAbcast) {
public void evaluate(Object params,
Message dmessage) {
synchronized (this.parent) {
GroupCommEventArgs ga = new GroupCommEventArgs();
ga.addLast(dmessage.toGroupCommMessage());
try{
handlers.handleAbcast(ga);
} catch (Exception ex) {
ex.printStackTrace();
throw new RuntimeException("ProtocolFastAbcast: "
+ "abcastExecuter: " + ex.getMessage());
}
}
}
};
// Init listeners
LinkedList<ServiceCallOrResponse> initiatedRpt2pt = new LinkedList<ServiceCallOrResponse>();
for (int i=0; i<MAX_PROCESSES; i++)
initiatedRpt2pt.add(ServiceCallOrResponse.createServiceCallOrResponse(rpt2pt, true));
initiatedRpt2pt.add(ServiceCallOrResponse.createServiceCallOrResponse(consensus, true));
rpt2ptListener = rpt2pt.new Listener(this, initiatedRpt2pt) {
public void evaluate(RPT2PTResponseParameters infos,
Transportable message) {
synchronized (this.parent) {
GroupCommEventArgs ga = new GroupCommEventArgs();
ga.addLast(message);
ga.addLast(infos.pid);
try {
handlers.handlePt2PtDeliver(ga);
} catch (Exception ex) {
throw new RuntimeException("ProtocolFastAbcast: "
+ "rpt2ptListener: " + ex.getMessage());
}
}
}
};
LinkedList<ServiceCallOrResponse> initiatedConsensus = new LinkedList<ServiceCallOrResponse>();
for (int i=0; i<MAX_PROCESSES; i++)
initiatedConsensus.add(ServiceCallOrResponse.createServiceCallOrResponse(rpt2pt, true));
initiatedConsensus.add(ServiceCallOrResponse.createServiceCallOrResponse(consensus, true));
for (int i=0; i<MAX_MESSAGES; i++)
initiatedConsensus.add(ServiceCallOrResponse.createServiceCallOrResponse(abcast, false));
consensusListener = consensus.new Listener(this, initiatedConsensus) {
public void evaluate(
ConsensusResponseParameters infos, Transportable message) {
synchronized (this.parent) {
GroupCommEventArgs ga = new GroupCommEventArgs();
ga.addLast(message);
ga.addLast(((ConsensusID) infos.id).value);
try {
handlers.handleDecide(ga);
} catch (GroupCommException ex) {
throw new RuntimeException("ProtocolDynAbcast: "
+ "handleDecide: " + ex.getMessage());
}
}
}
};
}
synchronized public void init() {
try {
GroupCommEventArgs initev = new GroupCommEventArgs();
initev.addLast(stack.getGroup());
handlers.handleInit(initev);
} catch (Exception ex) {
throw new RuntimeException("ProtocolFastAbcast: handleInit: "
+ ex.getMessage());
}
super.init();
}
synchronized public void dump(OutputStream stream) {
handlers.dump(stream);
}
/**
* Manage the triggering of the events
*/
@SuppressWarnings("unchecked")
public void trigger(int type, GroupCommEventArgs l) {
switch (type) {
case Constants.JOINREMOVELIST:
TSet start = (TSet) l.remove(0);
TSet stop = (TSet) l.remove(0);
Iterator itStart = start.iterator();
while (itStart.hasNext()) {
RPT2PTCallParameters jparams = new RPT2PTCallParameters(
(PID) itStart.next(), new TBoolean(true), new TBoolean(
false));
rpt2pt.call(jparams, null);
}
Iterator itStop = stop.iterator();
while (itStop.hasNext()) {
RPT2PTCallParameters jparams = new RPT2PTCallParameters(
(PID) itStop.next(), new TBoolean(false), new TBoolean(
false));
rpt2pt.call(jparams, null);
}
break;
case Constants.PT2PTSEND:
Transportable message = l.remove(0);
PID ppid = (PID) l.remove(0);
TBoolean promisc = (TBoolean) l.remove(0);
RPT2PTCallParameters rparams = new RPT2PTCallParameters(ppid,
promisc, new TBoolean(true));
rpt2pt.call(rparams, new Message(message, rpt2ptListener));
break;
case Constants.ADELIVER:
GroupCommMessage gm = (GroupCommMessage) l.remove(0);
Message dmessage = new Message(gm);
PID apid = (PID) l.remove(0);
AbcastResponseParameters infos = new AbcastResponseParameters(apid);
abcast.response(infos, dmessage);
break;
case Constants.PROPOSE:
TList group = (TList) l.remove(0);
Transportable cmessage = l.remove(0);
TLong id = (TLong) l.remove(0);
ConsensusCallParameters cparams = new ConsensusCallParameters(
group, new ConsensusID(id, this.pValue));
consensus.call(cparams, new Message(cmessage, consensusListener));
break;
default:
throw new RuntimeException("Unknow event triggered : " + type);
}
}
// Interface for the timers
synchronized public void schedule(final Transportable key, boolean periodic,
int time) {
if (periodic)
throw new RuntimeException("ProtocolHB: schedule: Periodic "
+ "timers not supported. Discarding it.");
if (!timers.containsKey(key)) {
// There is no entry in the map
// Create the entry and start the timer
AtomicTask trigger = new AtomicTask() {
public void execute() {
timeout(key);
}
public ServiceCallOrResponse getCOR(){
return abcastCallCOR;
}
};
timers.put(key, trigger);
stack.getScheduler().schedule(trigger, periodic, time);
} else {
throw new RuntimeException("ProtocolHB:schedule: Suspect "
+ "Task already scheduled!");
}
}
synchronized public void cancel(Transportable key) {
try{
stack.getScheduler().cancel(timers.remove(key));
} catch (NotScheduledTaskException ex){
throw new RuntimeException("ProtocolPing: cancel: The task is not"
+ " currently scheduled");
}
}
synchronized public void reset(Transportable key) {
try{
stack.getScheduler().reset(timers.get(key));
} catch (NotScheduledTaskException ex){
throw new RuntimeException("ProtocolPing: reset: The task is not"
+ " currently scheduled");
}
}
synchronized private void timeout(Object o) {
GroupCommEventArgs ga = new GroupCommEventArgs();
final Transportable key = (Transportable) o;
if (!timers.containsKey(key))
// Timer already canceled
return;
ga.add(key);
handlers.handleTimeout(ga);
}
}