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

com.gemstone.org.jgroups.protocols.pbcast.STATE_TRANSFER Maven / Gradle / Ivy

The newest version!
/** Notice of modification as required by the LGPL
 *  This file was modified by Gemstone Systems Inc. on
 *  $Date$
 **/
// $Id: STATE_TRANSFER.java,v 1.25 2005/12/16 16:21:07 belaban Exp $

package com.gemstone.org.jgroups.protocols.pbcast;


import com.gemstone.org.jgroups.*;
import com.gemstone.org.jgroups.stack.Protocol;
import com.gemstone.org.jgroups.stack.StateTransferInfo;
import com.gemstone.org.jgroups.util.ExternalStrings;
import com.gemstone.org.jgroups.util.List;
import com.gemstone.org.jgroups.util.Streamable;
import com.gemstone.org.jgroups.util.Util;

import java.io.*;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Properties;
import java.util.Vector;


/**
 * New STATE_TRANSFER protocol based on PBCAST. Compared to the one in ./protocols, it doesn't
 * need a QUEUE layer above it. A state request is sent to a chosen member (coordinator if
 * null). That member makes a copy D of its current digest and asks the application for a copy of
 * its current state S. Then the member returns both S and D to the requester. The requester
 * first sets its digest to D and then returns the state to the application.
 * @author Bela Ban
 */
public class STATE_TRANSFER extends Protocol  {
    Address        local_addr=null;
    final Vector   members=new Vector();
    long           state_id=1;  // used to differentiate between state transfers (not currently used)
    final List     state_requesters=new List(); // requesters of state (usually just 1, could be more)
    Digest         digest=null;
    final HashMap  map=new HashMap(); // to store configuration information
    long           start, stop; // to measure state transfer time
    int            num_state_reqs=0;
    long           num_bytes_sent=0;
    double         avg_state_size=0;
    final static   String name="STATE_TRANSFER";


    /** All protocol names have to be unique ! */
    @Override // GemStoneAddition
    public String getName() {
        return name;
    }

    public int getNumberOfStateRequests() {return num_state_reqs;}
    public long getNumberOfStateBytesSent() {return num_bytes_sent;}
    public double getAverageStateSize() {return avg_state_size;}

    @Override // GemStoneAddition
    public Vector requiredDownServices() {
        Vector retval=new Vector();
        retval.addElement(Integer.valueOf(Event.GET_DIGEST_STATE));
        retval.addElement(Integer.valueOf(Event.SET_DIGEST));
        return retval;
    }

    @Override // GemStoneAddition
    public void resetStats() {
        super.resetStats();
        num_state_reqs=0;
        num_bytes_sent=0;
        avg_state_size=0;
    }


    @Override // GemStoneAddition
    public boolean setProperties(Properties props) {
        super.setProperties(props);

        if(props.size() > 0) {
            log.error(ExternalStrings.STATE_TRANSFER_STATE_TRANSFERSETPROPERTIES_THE_FOLLOWING_PROPERTIES_ARE_NOT_RECOGNIZED__0, props);

            return false;
        }
        return true;
    }

    @Override // GemStoneAddition
    public void init() throws Exception {
        map.put("state_transfer", Boolean.TRUE);
        map.put("protocol_class", getClass().getName());
    }


    @Override // GemStoneAddition
    public void start() throws Exception {
        passUp(new Event(Event.CONFIG, map));
    }


    @Override // GemStoneAddition
    public void up(Event evt) {
        Message     msg;
        StateHeader hdr;

        switch(evt.getType()) {

        case Event.BECOME_SERVER:
            break;

        case Event.SET_LOCAL_ADDRESS:
            local_addr=(Address)evt.getArg();
            break;

        case Event.TMP_VIEW:
        case Event.VIEW_CHANGE:
            handleViewChange((View)evt.getArg());
            break;

        case Event.GET_DIGEST_STATE_OK:
            synchronized(state_requesters) {
                if(digest != null) {
                    if(warn)
                        log.warn("GET_DIGEST_STATE_OK: existing digest is not null, overwriting it !");
                }
                digest=(Digest)evt.getArg();
                if(log.isDebugEnabled())
                    log.debug("GET_DIGEST_STATE_OK: digest is " + digest + "\npassUp(GET_APPLSTATE)");
                passUp(new Event(Event.GET_APPLSTATE));
            }
            return;

        case Event.MSG:
            msg=(Message)evt.getArg();
            if(!(msg.getHeader(name) instanceof StateHeader))
                break;

            hdr=(StateHeader)msg.removeHeader(name);
            switch(hdr.type) {
            case StateHeader.STATE_REQ:
                handleStateReq(hdr.sender);
                break;
            case StateHeader.STATE_RSP:
                handleStateRsp(hdr.sender, hdr.my_digest, msg.getBuffer());
                break;
            default:
                if(log.isErrorEnabled()) log.error(ExternalStrings.STATE_TRANSFER_TYPE__0__NOT_KNOWN_IN_STATEHEADER, hdr.type);
                break;
            }
            return;
        }
        passUp(evt);
    }



    @Override // GemStoneAddition
    public void down(Event evt) {
        byte[] state;
        Address target, requester;
        StateTransferInfo info;
        StateHeader hdr;
        Message state_req, state_rsp;

        switch(evt.getType()) {

            case Event.TMP_VIEW:
            case Event.VIEW_CHANGE:
                handleViewChange((View)evt.getArg());
                break;

            // generated by JChannel.getState(). currently, getting the state from more than 1 mbr is not implemented
            case Event.GET_STATE:
                info=(StateTransferInfo)evt.getArg();
                if(info.type != StateTransferInfo.GET_FROM_SINGLE) {
                    if(warn) log.warn("[GET_STATE] (info=" + info + "): getting the state from " +
                            "all members is not currently supported by pbcast.STATE_TRANSFER, will use " +
                            "coordinator to fetch state instead");
                }
                if(info.target == null) {
                    target=determineCoordinator();
                }
                else {
                    target=info.target;
                    if(target.equals(local_addr)) {
                        if(log.isErrorEnabled()) log.error(ExternalStrings.STATE_TRANSFER_GET_STATE_CANNOT_FETCH_STATE_FROM_MYSELF_);
                        target=null;
                    }
                }
                if(target == null) {
                    if(log.isDebugEnabled()) log.debug("GET_STATE: first member (no state)");
                    passUp(new Event(Event.GET_STATE_OK, null));
                }
                else {
                    state_req=new Message(target, null, null);
                    state_req.putHeader(name, new StateHeader(StateHeader.STATE_REQ, local_addr, state_id++, null));
                    if(log.isDebugEnabled()) log.debug("GET_STATE: asking " + target + " for state");

                    // suspend sending and handling of mesage garbage collection gossip messages,
                    // fixes bugs #943480 and #938584). Wake up when state has been received
                    if(log.isDebugEnabled())
                        log.debug("passing down a SUSPEND_STABLE event");
                    passDown(new Event(Event.SUSPEND_STABLE, Long.valueOf(info.timeout)));

                    start=System.currentTimeMillis();
                    passDown(new Event(Event.MSG, state_req));
                }
                return;                 // don't pass down any further !

            case Event.GET_APPLSTATE_OK:
                state=(byte[])evt.getArg();
                synchronized(state_requesters) {
                    if(state_requesters.size() == 0) {
                        if(warn)
                            log.warn("GET_APPLSTATE_OK: received application state, but there are no requesters !");
                        return;
                    }
                    if(digest == null) { // GemStoneAddition: missing braces
                        if(warn) log.warn("GET_APPLSTATE_OK: received application state, " +
                                "but there is no digest !");
                    }
                    else
                        digest=digest.copy();
                    if(stats) {
                        num_state_reqs++;
                        if(state != null)
                            num_bytes_sent+=state.length;
                        avg_state_size=(double)/*GemStoneAddition*/num_bytes_sent / num_state_reqs;
                    }
                    for(Enumeration e=state_requesters.elements(); e.hasMoreElements();) {
                        requester=(Address)e.nextElement();
                        state_rsp=new Message(requester, null, state); // put the state into state_rsp.buffer
                        hdr=new StateHeader(StateHeader.STATE_RSP, local_addr, 0, digest);
                        state_rsp.putHeader(name, hdr);
                        passDown(new Event(Event.MSG, state_rsp));
                    }
                    digest=null;
                    state_requesters.removeAll();
                }
                return;                 // don't pass down any further !
        }

        passDown(evt);              // pass on to the layer below us
    }









    /* --------------------------- Private Methods -------------------------------- */


    /** Return the first element of members which is not me. Otherwise return null. */
    private Address determineCoordinator() {
        Address ret=null;
        synchronized(members) {
            if(/*members != null && GemStoneADdition (cannot be null) */ members.size() > 1) {
                for(int i=0; i < members.size(); i++)
                    if(!local_addr.equals(members.elementAt(i)))
                        return (Address)members.elementAt(i);
            }
        }
        return ret;
    }


    private void handleViewChange(View v) {
        Vector new_members=v.getMembers();
        synchronized(members) {
            members.clear();
            members.addAll(new_members);
        }
    }

    /**
     * If a state transfer is in progress, we don't need to send a GET_APPLSTATE event to the application, but
     * instead we just add the sender to the requester list so it will receive the same state when done. If not,
     * we add the sender to the requester list and send a GET_APPLSTATE event up.
     */
    private void handleStateReq(Object sender) {
        if(sender == null) {
            if(log.isErrorEnabled()) log.error(ExternalStrings.STATE_TRANSFER_SENDER_IS_NULL_);
            return;
        }

        synchronized(state_requesters) {
            if(state_requesters.size() > 0) {  // state transfer is in progress, digest was requested
                state_requesters.add(sender);
            }
            else {
                state_requesters.add(sender);
                digest=null;
                if(log.isDebugEnabled()) log.debug("passing down GET_DIGEST_STATE");
                passDown(new Event(Event.GET_DIGEST_STATE));
            }
        }
    }


    /** Set the digest and the send the state up to the application */
    void handleStateRsp(Object sender, Digest digest, byte[] state) {
        if(digest == null) {
            if(warn)
                log.warn("digest received from " + sender + " is null, skipping setting digest !");
        }
        else
            passDown(new Event(Event.SET_DIGEST, digest)); // set the digest (e.g. in NAKACK)
        stop=System.currentTimeMillis();

        // resume sending and handling of mesage garbage collection gossip messages,
        // fixes bugs #943480 and #938584). Wakes up a previously suspended message garbage
        // collection protocol (e.g. STABLE)
        if(log.isDebugEnabled())
            log.debug("passing down a RESUME_STABLE event");
        passDown(new Event(Event.RESUME_STABLE));

        if(state == null) {
            if(warn)
                log.warn("state received from " + sender + " is null, will return null state to application");
        }
        else
            log.debug("received state, size=" + state.length + " bytes. Time=" + (stop-start) + " milliseconds");
        passUp(new Event(Event.GET_STATE_OK, state));
    }


    /* ------------------------ End of Private Methods ------------------------------ */



    /**
     * Wraps data for a state request/response. Note that for a state response the actual state will not";
            }
        }


        public void writeExternal(ObjectOutput out) throws IOException {
            out.writeObject(sender);
            out.writeLong(id);
            out.writeByte(type);
            out.writeObject(my_digest);
        }


        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
            sender=(Address)in.readObject();
            id=in.readLong();
            type=in.readByte();
            my_digest=(Digest)in.readObject();
        }



        public void writeTo(DataOutputStream out) throws IOException {
            out.writeByte(type);
            out.writeLong(id);
            Util.writeAddress(sender, out);
            Util.writeStreamable(my_digest, out);
        }

        public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException {
            type=in.readByte();
            id=in.readLong();
            sender=Util.readAddress(in);
            my_digest=(Digest)Util.readStreamable(Digest.class, in);
        }

        @Override // GemStoneAddition
        public long size(short version) {
            long retval=Global.LONG_SIZE + Global.BYTE_SIZE; // id and type

            retval+=Util.size(sender,version);

            retval+=Global.BYTE_SIZE; // presence byte for my_digest
            if(my_digest != null)
                retval+=my_digest.serializedSize(version);

            return retval;
        }

    }


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy