org.jgroups.protocols.DISCARD Maven / Gradle / Ivy
package org.jgroups.protocols;
import org.jgroups.Address;
import org.jgroups.Event;
import org.jgroups.Message;
import org.jgroups.View;
import org.jgroups.annotations.*;
import org.jgroups.stack.Protocol;
import org.jgroups.util.MessageBatch;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.*;
import java.util.List;
/**
* Discards up or down messages based on a percentage; e.g., setting property 'up' to 0.1 causes 10%
* of all up messages to be discarded. Setting 'down' or 'up' to 0 causes no loss, whereas 1 discards
* all messages (not very useful).
*/
@MBean(description="Discards messages")
public class DISCARD extends Protocol {
@Property
protected double up=0.0; // probability of dropping up msgs
@Property
protected double down=0.0; // probability of dropping down msgs
@Property
protected boolean excludeItself=true; // if true don't discard messages sent/received in this stack
protected Address localAddress;
@ManagedAttribute(description="Number of dropped down messages",name="dropped_down_messages")
protected int num_down=0;
@ManagedAttribute(description="Number of dropped up messages",name="dropped_up_messages")
protected int num_up=0;
protected final Set ignoredMembers = Collections.synchronizedSet(new HashSet<>());
protected final Collection members = Collections.synchronizedList(new ArrayList<>());
@Property(description="drop all messages (up or down)",writable=true)
protected boolean discard_all=false;
@Property(description="Number of subsequent unicasts to drop in the down direction",writable=true)
protected int drop_down_unicasts=0;
@Property(description="Number of subsequent multicasts to drop in the down direction",writable=true)
protected int drop_down_multicasts=0;
protected DiscardDialog discard_dialog=null;
@Property(name="gui", description="use a GUI or not")
protected boolean use_gui=false;
public DISCARD localAddress(Address addr) {setLocalAddress(addr); return this;}
public Address localAddress() {
if(localAddress == null)
localAddress=(Address)up_prot.up(new Event(Event.GET_LOCAL_ADDRESS));
return localAddress;
}
public boolean isDiscardAll() {
return discard_all;
}
public DISCARD setDiscardAll(boolean discard_all) {
this.discard_all=discard_all; return this;
}
public boolean isExcludeItself() {
return excludeItself;
}
public DISCARD setLocalAddress(Address localAddress){
this.localAddress =localAddress;
if(discard_dialog != null)
discard_dialog.setTitle(localAddress != null? localAddress.toString() : "n/a");
return this;
}
public DISCARD setExcludeItself(boolean excludeItself) {
this.excludeItself=excludeItself; return this;
}
public double getUpDiscardRate() {
return up;
}
public DISCARD setUpDiscardRate(double up) {
this.up=up; return this;
}
public double getDownDiscardRate() {
return down;
}
public DISCARD setDownDiscardRate(double down) {
this.down=down; return this;
}
public int getDropDownUnicasts() {
return drop_down_unicasts;
}
/**
* Drop the next N unicasts down the stack
* @param drop_down_unicasts
*/
public DISCARD setDropDownUnicasts(int drop_down_unicasts) {
this.drop_down_unicasts=drop_down_unicasts; return this;
}
public int getDropDownMulticasts() {
return drop_down_multicasts;
}
public DISCARD setDropDownMulticasts(int drop_down_multicasts) {
this.drop_down_multicasts=drop_down_multicasts; return this;
}
/** Messages from this sender will get dropped */
public DISCARD addIgnoreMember(Address sender) {ignoredMembers.add(sender); return this;}
public DISCARD removeIgnoredMember(Address member) {ignoredMembers.remove(member); return this;}
public DISCARD resetIgnoredMembers() {ignoredMembers.clear(); return this;}
@ManagedOperation
public void startGui() {
if(discard_dialog == null) {
discard_dialog=new DiscardDialog();
discard_dialog.init();
discard_dialog.setTitle(localAddress() != null? localAddress().toString() : "n/a");
discard_dialog.handleView(members);
}
}
@ManagedOperation
public void stopGui() {
if(discard_dialog != null)
discard_dialog.dispose();
discard_dialog=null;
}
public void start() throws Exception {
super.start();
if(use_gui) {
discard_dialog=new DiscardDialog();
discard_dialog.init();
}
}
public void stop() {
super.stop();
if(discard_dialog != null)
discard_dialog.dispose();
}
public Object up(Event evt) {
if(evt.getType() == Event.SET_LOCAL_ADDRESS) {
localAddress=evt.getArg();
if(discard_dialog != null)
discard_dialog.setTitle("Discard dialog (" + localAddress + ")");
}
return up_prot.up(evt);
}
public Object up(Message msg) {
if(shouldDropUpMessage(msg, msg.getSrc()))
return null;
return up_prot.up(msg);
}
public void up(MessageBatch batch) {
for(Iterator it=batch.iterator(); it.hasNext();) {
Message msg=it.next();
if(msg != null && shouldDropUpMessage(msg, msg.getSrc()))
it.remove();
}
if(!batch.isEmpty())
up_prot.up(batch);
}
public Object down(Event evt) {
switch(evt.getType()) {
case Event.VIEW_CHANGE:
View view=evt.getArg();
List mbrs=view.getMembers();
members.clear();
members.addAll(mbrs);
// ignoredMembers.retainAll(mbrs); // remove all non members
if(discard_dialog != null)
discard_dialog.handleView(mbrs);
break;
case Event.SET_LOCAL_ADDRESS:
localAddress=evt.getArg();
if(discard_dialog != null)
discard_dialog.setTitle("Discard dialog (" + localAddress + ")");
break;
case Event.GET_PING_DATA:
if(discard_all)
return null;
break;
}
return down_prot.down(evt);
}
public Object down(Message msg) {
Address dest=msg.getDest();
boolean multicast=dest == null;
if(msg.getSrc() == null)
msg.setSrc(localAddress());
if(discard_all) {
if(dest == null || dest.equals(localAddress()))
loopback(msg);
return null;
}
if(!multicast && drop_down_unicasts > 0) {
drop_down_unicasts=Math.max(0, drop_down_unicasts -1);
return null;
}
if(multicast && drop_down_multicasts > 0) {
drop_down_multicasts=Math.max(0, drop_down_multicasts -1);
return null;
}
if(down > 0) {
double r=Math.random();
if(r < down) {
if(excludeItself && dest != null && dest.equals(localAddress())) {
if(log.isTraceEnabled()) log.trace("excluding itself");
}
else {
log.trace("dropping message");
num_down++;
return null;
}
}
}
return down_prot.down(msg);
}
/** Checks if a message should be passed up, or not */
protected boolean shouldDropUpMessage(@SuppressWarnings("UnusedParameters") Message msg, Address sender) {
if(discard_all && !sender.equals(localAddress()))
return true;
if(ignoredMembers.contains(sender)) {
if(log.isTraceEnabled())
log.trace(localAddress + ": dropping message from " + sender);
num_up++;
return true;
}
if(up > 0) {
double r=Math.random();
if(r < up) {
if(excludeItself && sender.equals(localAddress())) {
if(log.isTraceEnabled())
log.trace("excluding myself");
}
else {
if(log.isTraceEnabled())
log.trace(localAddress + ": dropping message from " + sender);
num_up++;
return true;
}
}
}
return false;
}
private void loopback(Message msg) {
final Message rsp=msg.copy(true);
if(rsp.getSrc() == null)
rsp.setSrc(localAddress());
// pretty inefficient: creates one thread per message, okay for testing only
Thread thread=new Thread(() -> {
up_prot.up(rsp);
});
thread.start();
}
public void resetStats() {
super.resetStats();
num_down=num_up=0;
}
protected class DiscardDialog extends JFrame implements ActionListener {
private final JButton start_discarding_button=new JButton("start discarding");
private final JButton stop_discarding_button=new JButton("stop discarding");
private final JPanel checkboxes=new JPanel();
protected DiscardDialog() {
}
void init() {
getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
checkboxes.setLayout(new BoxLayout(checkboxes, BoxLayout.Y_AXIS));
getContentPane().add(start_discarding_button);
getContentPane().add(stop_discarding_button);
start_discarding_button.addActionListener(this);
stop_discarding_button.addActionListener(this);
getContentPane().add(checkboxes);
pack();
setVisible(true);
setTitle(localAddress() != null? localAddress().toString() : "n/a");
}
public void actionPerformed(ActionEvent e) {
String command=e.getActionCommand();
if(command.startsWith("start")) {
discard_all=true;
}
else if(command.startsWith("stop")) {
discard_all=false;
Component[] comps=checkboxes.getComponents();
for(Component c: comps) {
if(c instanceof JCheckBox) {
((JCheckBox)c).setSelected(false);
}
}
ignoredMembers.clear();
}
}
void handleView(Collection mbrs) {
checkboxes.removeAll();
for(final Address addr: mbrs) {
final MyCheckBox box=new MyCheckBox("discard traffic from " + addr, addr);
box.addActionListener(e -> {
if(box.isSelected())
ignoredMembers.add(addr);
else
ignoredMembers.remove(addr);
});
checkboxes.add(box);
}
for(Component comp: checkboxes.getComponents()) {
MyCheckBox box=(MyCheckBox)comp;
if(ignoredMembers.contains(box.mbr))
box.setSelected(true);
}
pack();
}
}
private static class MyCheckBox extends JCheckBox {
final Address mbr;
public MyCheckBox(String name, Address member) {
super(name);
this.mbr=member;
}
public String toString() {
return super.toString() + " [mbr=" + mbr + "]";
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy