All Downloads are FREE. Search and download functionalities are using the official Maven repository.

bboss.org.jgroups.protocols.VERIFY_SUSPECT Maven / Gradle / Ivy

The newest version!

package bboss.org.jgroups.protocols;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import bboss.org.jgroups.Address;
import bboss.org.jgroups.Event;
import bboss.org.jgroups.Global;
import bboss.org.jgroups.Header;
import bboss.org.jgroups.Message;
import bboss.org.jgroups.annotations.Property;
import bboss.org.jgroups.conf.PropertyConverters;
import bboss.org.jgroups.stack.IpAddress;
import bboss.org.jgroups.stack.Protocol;
import bboss.org.jgroups.util.Util;


/**
 * Catches SUSPECT events traveling up the stack. Verifies that the suspected member is really dead. If yes,
 * passes SUSPECT event up the stack, otherwise discards it. Has to be placed somewhere above the FD layer and
 * below the GMS layer (receiver of the SUSPECT event). Note that SUSPECT events may be reordered by this protocol.
 * @author Bela Ban
 * @version $Id: VERIFY_SUSPECT.java,v 1.46 2010/06/15 06:44:35 belaban Exp $
 */
public class VERIFY_SUSPECT extends Protocol implements Runnable {

    /* ------------------------------------------ Properties  ------------------------------------------ */
    
    @Property(description="Number of millisecs to wait for a response from a suspected member")
    private long timeout=2000; 
    
    @Property(description="Number of verify heartbeats sent to a suspected member")
    private int num_msgs=1; 
    
    @Property(description="Use InetAddress.isReachable() to verify suspected member instead of regular messages")
    private boolean use_icmp=false; 
    
    @Property(description="Interface for ICMP pings. Used if use_icmp is true " +
            "The following special values are also recognized: GLOBAL, SITE_LOCAL, LINK_LOCAL and NON_LOOPBACK",
              systemProperty={Global.BIND_ADDR, Global.BIND_ADDR_OLD},
              defaultValueIPv4=Global.NON_LOOPBACK_ADDRESS, defaultValueIPv6=Global.NON_LOOPBACK_ADDRESS)
    private InetAddress bind_addr; // interface for ICMP pings
    
    @Property(name="bind_interface", converter=PropertyConverters.BindInterface.class, 
    		description="The interface (NIC) which should be used by this transport", dependsUpon="bind_addr")
    protected String bind_interface_str=null;
     
    /* --------------------------------------------- Fields ------------------------------------------------ */   
    
    
    /** network interface to be used to send the ICMP packets */
    private NetworkInterface intf=null;
    
    private Address local_addr=null;
    
    /**keys=Addresses, vals=time in mcses since added **/
    private final Hashtable suspects=new Hashtable();
    
    private Thread timer=null;
    
    
    
    public VERIFY_SUSPECT() {       
    }

    public Object down(Event evt) {
        if(evt.getType() == Event.SET_LOCAL_ADDRESS) {
            local_addr=(Address)evt.getArg();
        }
        return down_prot.down(evt);
    }

    public Object up(Event evt) {
        switch(evt.getType()) {

            case Event.SUSPECT:  // it all starts here ...
                Address suspected_mbr=(Address)evt.getArg();
                if(suspected_mbr == null) {
                    if(log.isErrorEnabled()) log.error("suspected member is null");
                    return null;
                }

                if(local_addr != null && local_addr.equals(suspected_mbr)) {
                    if(log.isTraceEnabled())
                        log.trace("I was suspected; ignoring SUSPECT message");
                    return null;
                }

                if(!use_icmp)
                    verifySuspect(suspected_mbr);
                else
                    verifySuspectWithICMP(suspected_mbr);
                return null;  // don't pass up; we will decide later (after verification) whether to pass it up


            case Event.MSG:
                Message msg=(Message)evt.getArg();
                VerifyHeader hdr=(VerifyHeader)msg.getHeader(this.id);
                if(hdr == null)
                    break;
                switch(hdr.type) {
                    case VerifyHeader.ARE_YOU_DEAD:
                        if(hdr.from == null) {
                            if(log.isErrorEnabled()) log.error("ARE_YOU_DEAD: hdr.from is null");
                        }
                        else {
                            Message rsp;
                            for(int i=0; i < num_msgs; i++) {
                                rsp=new Message(hdr.from, null, null);
                                rsp.setFlag(Message.OOB);
                                rsp.putHeader(this.id, new VerifyHeader(VerifyHeader.I_AM_NOT_DEAD, local_addr));
                                down_prot.down(new Event(Event.MSG, rsp));
                            }
                        }
                        return null;
                    case VerifyHeader.I_AM_NOT_DEAD:
                        if(hdr.from == null) {
                            if(log.isErrorEnabled()) log.error("I_AM_NOT_DEAD: hdr.from is null");
                            return null;
                        }
                        unsuspect(hdr.from);
                        return null;
                }
                return null;


            case Event.CONFIG:
                if(bind_addr == null) {
                    Map config=(Map)evt.getArg();
                    bind_addr=(InetAddress)config.get("bind_addr");
                }
        }
        return up_prot.up(evt);
    }


    /**
     * Will be started when a suspect is added to the suspects hashtable. Continually iterates over the
     * entries and removes entries whose time have elapsed. For each removed entry, a SUSPECT event is passed
     * up the stack (because elapsed time means verification of member's liveness failed). Computes the shortest
     * time to wait (min of all timeouts) and waits(time) msecs. Will be woken up when entry is removed (in case
     * of successful verification of that member's liveness). Terminates when no entry remains in the hashtable.
     */
    public void run() {       
        long val, diff;

        while(!suspects.isEmpty()) {
            diff=0;

            List
confirmed_suspects=new LinkedList
(); synchronized(suspects) { for(Enumeration
e=suspects.keys(); e.hasMoreElements();) { Address mbr=e.nextElement(); val=suspects.get(mbr).longValue(); diff=System.currentTimeMillis() - val; if(diff >= timeout) { // haven't been unsuspected, pass up SUSPECT if(log.isTraceEnabled()) log.trace("diff=" + diff + ", mbr " + mbr + " is dead (passing up SUSPECT event)"); confirmed_suspects.add(mbr); suspects.remove(mbr); continue; } diff=Math.max(diff, timeout - diff); } } for(Address suspect:confirmed_suspects) up_prot.up(new Event(Event.SUSPECT,suspect)); if(diff > 0) Util.sleep(diff); } } /* --------------------------------- Private Methods ----------------------------------- */ /** * Sends ARE_YOU_DEAD message to suspected_mbr, wait for return or timeout */ void verifySuspect(Address mbr) { Message msg; if(mbr == null) return; synchronized(suspects) { if(suspects.containsKey(mbr)) return; suspects.put(mbr, new Long(System.currentTimeMillis())); } //start timer before we send out are you dead messages startTimer(); // moved out of synchronized statement (bela): http://jira.jboss.com/jira/browse/JGRP-302 if(log.isTraceEnabled()) log.trace("verifying that " + mbr + " is dead"); for(int i=0; i < num_msgs; i++) { msg=new Message(mbr, null, null); msg.setFlag(Message.OOB); msg.putHeader(this.id, new VerifyHeader(VerifyHeader.ARE_YOU_DEAD, local_addr)); down_prot.down(new Event(Event.MSG, msg)); } } void verifySuspectWithICMP(Address suspected_mbr) { InetAddress host=suspected_mbr instanceof IpAddress? ((IpAddress)suspected_mbr).getIpAddress() : null; if(host == null) throw new IllegalArgumentException("suspected_mbr is not of type IpAddress - FD_ICMP only works with these"); try { if(log.isTraceEnabled()) log.trace("pinging host " + suspected_mbr + " using interface " + intf); long start=System.currentTimeMillis(), stop; boolean rc=host.isReachable(intf, 0, (int)timeout); stop=System.currentTimeMillis(); if(rc) { // success if(log.isTraceEnabled()) log.trace("successfully received response from " + host + " (after " + (stop-start) + "ms)"); } else { // failure if(log.isTraceEnabled()) log.debug("could not ping " + suspected_mbr + " after " + (stop-start) + "ms; " + "passing up SUSPECT event"); suspects.remove(suspected_mbr); up_prot.up(new Event(Event.SUSPECT, suspected_mbr)); } } catch(Exception ex) { if(log.isErrorEnabled()) log.error("failed pinging " + suspected_mbr, ex); } } void unsuspect(Address mbr) { if(mbr == null) return; boolean removed=false; synchronized(suspects) { if(suspects.containsKey(mbr)) { if(log.isTraceEnabled()) log.trace("member " + mbr + " is not dead !"); suspects.remove(mbr); removed=true; } } if(removed) { down_prot.down(new Event(Event.UNSUSPECT, mbr)); up_prot.up(new Event(Event.UNSUSPECT, mbr)); } } private synchronized void startTimer() { if(timer == null || !timer.isAlive()) { timer=getThreadFactory().newThread(this,"VERIFY_SUSPECT.TimerThread"); timer.setDaemon(true); timer.start(); } } public void init() throws Exception { super.init(); if(bind_addr != null) intf=NetworkInterface.getByInetAddress(bind_addr); } public synchronized void stop() { Thread tmp; if(timer != null && timer.isAlive()) { tmp=timer; timer=null; tmp.interrupt(); tmp=null; } timer=null; } /* ----------------------------- End of Private Methods -------------------------------- */ public static class VerifyHeader extends Header { static final short ARE_YOU_DEAD=1; // 'from' is sender of verify msg static final short I_AM_NOT_DEAD=2; // 'from' is suspected member short type=ARE_YOU_DEAD; Address from=null; // member who wants to verify that suspected_mbr is dead public VerifyHeader() { } // used for externalization VerifyHeader(short type) { this.type=type; } VerifyHeader(short type, Address from) { this(type); this.from=from; } public String toString() { switch(type) { case ARE_YOU_DEAD: return "[VERIFY_SUSPECT: ARE_YOU_DEAD]"; case I_AM_NOT_DEAD: return "[VERIFY_SUSPECT: I_AM_NOT_DEAD]"; default: return "[VERIFY_SUSPECT: unknown type (" + type + ")]"; } } public void writeTo(DataOutputStream out) throws IOException { out.writeShort(type); Util.writeAddress(from, out); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { type=in.readShort(); from=Util.readAddress(in); } public int size() { return Global.SHORT_SIZE + Util.size(from); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy