com.gemstone.org.jgroups.protocols.NAKACK.MessageProtocol Maven / Gradle / Ivy
Show all versions of gemfire-jgroups Show documentation
package JavaGroups.JavaStack.Protocols;
import java.io.*;
import java.util.*;
import JavaGroups.*;
import JavaGroups.JavaStack.*;
class NakAckHeader implements Serializable {
public static final int NAK_MSG = 1; // asynchronous msg
public static final int ACK_MSG = 2; // synchronous msg
public static final int RETRANSMIT_MSG = 3; // retransmit msg
int type=0;
long seqno=-1;
ViewId vid=null;
NakAckHeader(int type, long seqno, ViewId vid) {
this.type=type;
this.seqno=seqno;
this.vid=vid;
}
String Type2Str(int t) {
switch(t) {
case NAK_MSG: return "NAK_MSG";
case ACK_MSG: return "ACK_MSG";
case RETRANSMIT_MSG: return "RETRANSMIT_MSG";
default: return "";
}
}
public String toString() {
return "[NAKACK: " + Type2Str(type) + ", seqno=" + seqno + ", vid=" + vid + "]";
}
}
/**
Combined NAK and ACK class. The layer contains a NAKer and an ACKer: depending on which
one is set, a message will be sent using a negative acknowldegment scheme (NAK) or regular
acknowledgement (ACK). Event SET_ACK_MODE switches between the 2 modes. The following
communication between 2 peers exists (left side is initiator, right side receiver):
send_nak
--------------> asynchronous
send_ack
------------->> synchronous
<--------------
ack
retransmit
<-------------- asynchronous
When using NAKs, a messages is just tagged with a sequence number (seqno) and
broadcast asynchronously. A receiver requests retransmissions (asynchronous) for gaps
between received messages. When ACKs are used, each message is broadcast to the current
membership synchronously and only when a response from each member has been received
(or a suspect notification) will the call return. If a response has not been received after
a timeout, the message will be retransmitted to the corresponding member. If the response
is not received after n retries, and no suspect notification has been received, the member
will be suspected.
When a message is sent, it will contain a header describing the type of the message, and containing
additional data, such as sequence number etc. When a message is received, it is fed into either
the ACKer or NAKer, depending on the header's type.
*/
public class NAKACK extends MessageProtocol {
LosslessTransmission naker=new NAKer(); //
LosslessTransmission acker=new ACKer(); //
LosslessTransmission transmitter=naker; // NAK is default
long seqno=0; // current message sequence number
Hashtable received_msgs=new Hashtable(); // ordered by sender -> NakReceiverWindow
Hashtable sent_msgs=new Hashtable(); // ordered by seqno (sent by me !)
ViewId vid=null;
View view=null;
//Object myaddr=null;
class LosslessTransmission implements NakReceiverWindow.RetransmitCommand {
void Reset() {
NakReceiverWindow win;
sent_msgs.clear();
for(Enumeration e=received_msgs.elements(); e.hasMoreElements();) {
win=(NakReceiverWindow)e.nextElement();
win.Reset();
}
received_msgs.clear();
}
void Send(long id, Message msg) {
}
void Receive(long id, Message msg) {
Object sender=msg.GetSrc();
NakReceiverWindow win=(NakReceiverWindow)received_msgs.get(sender);
Message msg_to_deliver;
if(win == null) {
win=new NakReceiverWindow(sender, this, 0);
received_msgs.put(sender, win);
}
System.out.println("RECV #" + id);
win.Add(id, msg);
while(true) {
msg_to_deliver=win.Remove();
if(msg_to_deliver != null)
PassUp(new Event(Event.MSG, msg_to_deliver));
else
break;
}
}
/** Called by retransmission thread when gap is detected. Sends retr. request
to originator of msg */
public void Retransmit(long seqno, Object sender, int num_tries) {
System.out.println("--> Retransmit(" + seqno + ") to " + sender);
NakAckHeader hdr=new NakAckHeader(NakAckHeader.RETRANSMIT_MSG, seqno, vid);
Message retransmit_msg=new Message(sender, null, null);
retransmit_msg.AddHeader(hdr);
try {
SendMessage(retransmit_msg, GroupRequest.GET_NONE, 0); // non-blocking request
}
catch(Exception e) {
System.err.println("NAKACK.NAKer.Retransmit(): " + e);
}
}
// Retransmit from sent-table
void Retransmit(Object dest, long id) {
Message m=(Message)sent_msgs.get(new Long(id)), retr_msg;
if(m == null) {
System.err.println("NAKACK.LosslessTransmission.Retransmit(): message with " +
"seqno=" + id + " not found !");
return;
}
retr_msg=m.Copy();
retr_msg.SetDest(dest);
try {
SendMessage(retr_msg, GroupRequest.GET_NONE, 0); // non-blocking
}
catch(Exception e) {
System.err.println("NAKACK.LosslessTransmission.Retransmit(): " +e);
}
}
void Stable(long id) {}
}
class NAKer extends LosslessTransmission {
void Send(long id, Message msg) {
Message copy;
if(vid == null) {
System.err.println("NAKACK.NAKer.Send(): vid is null !");
return;
}
msg.AddHeader(new NakAckHeader(NakAckHeader.NAK_MSG, id, vid));
copy=msg.Copy();
sent_msgs.put(new Long(id), copy);
System.out.println("SEND #" + id + ", msg is " + msg);
// if(id % 7 == 0) {
//transmitter.Retransmit(id, myaddr, 0);
//return;
//}
CastMessage(view.GetMembers(), msg, GroupRequest.GET_NONE, 0); // don't wait for rsps
}
}
class ACKer extends LosslessTransmission {
// contains sent_table
void Send(long id, Message msg) {
if(vid == null) {
System.err.println("NAKACK.ACKer.Send(): vid is null !");
return;
}
// +++ Send reliably (with ACKs)
// msg.AddHeader(new NakAckHeader(NakAckHeader.ACK_MSG, id, vid));
// sent_msgs.put(new Long(id), msg);
// System.out.println("NAKer: sending message #" + id + " --> " + msg);
// PassDown(new Event(Event.MSG, msg));
}
}
private long GetNextSeqno() {return seqno++;}
public String GetName() {return "NAKACK";}
/**
Callback. Called when a request for this protocol layer is received.
*/
public Object Handle(Message req) {
Object obj;
NakAckHeader hdr;
int rc;
obj=req.PeekHeader();
if(obj == null || !(obj instanceof NakAckHeader)) {
System.err.println("NAKACK.Handle(): message does not contain a NakAckHeader !");
return null;
}
hdr=(NakAckHeader)req.RemoveHeader();
if(vid != null) {
if(hdr.vid == null) {
System.err.println("NAKACK.Handle(): message's view is null ! " +
"Cannot check against our own view !");
}
else {
rc=hdr.vid.Compare(vid);
if(rc == 0) { // same vid -> OK
;
}
else if(rc < 0) { // message sent in prev. view -> discard !
System.err.println("NAKACK.Handle(): message's vid is smaller than " +
"current vid: message is discarded !");
return null;
}
else if(rc > 0) {
System.out.println("NAKACK.Handle(): message's vid is bigger than " +
"current vid: message is queued !");
// +++ Implement: queue message
}
else {
System.err.println("NAKACK.Handle(): comparison of message's vid with ours " +
"yielded " + rc);
}
}
}
else
System.err.println("NAKACK.Handle(): our vid is not available ! "+
"Cannot check against message's vid !");
switch(hdr.type) {
case NakAckHeader.NAK_MSG:
case NakAckHeader.ACK_MSG:
transmitter.Receive(hdr.seqno, req);
break;
case NakAckHeader.RETRANSMIT_MSG:
transmitter.Retransmit(req.GetSrc(), hdr.seqno);
break;
default:
System.err.println("NAKACK.HandleUpEvent(): NakAck header type " +
hdr.type + " not known !");
break;
}
return null;
}
/**
Callback. Called by superclass when event may be handled.
Do not use PassUp
in this method as the event is passed up
by default by the superclass after this method returns !
@return boolean Defaults to true. If false, event will not be passed up the stack.
*/
public boolean HandleUpEvent(Event evt) {
// switch(evt.GetType()) {
// case Event.SET_LOCAL_ADDRESS:
// myaddr=evt.GetArg();
// break;
// }
return true;
}
/**
Callback. Called by superclass when event may be handled.
Do not use PassDown
in this method as the event is passed down
by default by the superclass after this method returns !
@return boolean Defaults to true. If false, event will not be passed down the stack.
*/
public boolean HandleDownEvent(Event evt) {
Message msg;
switch(evt.GetType()) {
case Event.MSG:
msg=(Message)evt.GetArg();
if(msg.GetDest() != null && !((Address)msg.GetDest()).IsMulticastAddress())
return true; // unicast address: not null and not mcast, pass down unchanged
transmitter.Send(GetNextSeqno(), msg);
return false; // don't pass down the stack, transmitter does this for us !
case Event.VIEW_CHANGE:
view=(View)evt.GetArg();
vid=new ViewId((Address)view.GetCreator(), view.GetId());
seqno=0; // reset sequence number
transmitter.Reset();
// ++ Implement: if there are any queued messages that were previously
// sent for this vid, add those that match the new vid now !
break;
case Event.SWITCH_NAK:
transmitter=naker;
break;
case Event.SWITCH_ACK:
transmitter=acker;
break;
}
return true;
}
}