text:
/** Notice of modification as required by the LGPL
 *  This file was modified by Gemstone Systems Inc. on
 *  $Date$
 **/
// $Id: NAKACK.java,v 1.61 2005/12/16 16:10:26 belaban Exp $

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

import com.gemstone.org.jgroups.*;
import com.gemstone.org.jgroups.protocols.pbcast.GMS.GmsHeader;
import com.gemstone.org.jgroups.stack.IpAddress;
import com.gemstone.org.jgroups.stack.NakReceiverWindow;
import com.gemstone.org.jgroups.stack.Protocol;
import com.gemstone.org.jgroups.stack.Retransmitter;
import com.gemstone.org.jgroups.util.*;
import com.gemstone.org.jgroups.util.TimeScheduler.Task;

import java.util.*;
import java.util.List;
import java.io.*;

/**
 * Negative AcKnowledgement layer (NAKs). Messages are assigned a monotonically increasing sequence number (seqno).
 * Receivers deliver messages ordered according to seqno and request retransmission of missing messages. Retransmitted
 * messages are bundled into bigger ones, e.g. when getting an xmit request for messages 1-10, instead of sending 10
 * unicast messages, we bundle all 10 messages into 1 and send it. However, since this protocol typically sits below
 * FRAG, we cannot count on FRAG to fragement/defragment the (possibly) large message into smaller ones. Therefore we
 * only bundle messages up to max_xmit_size bytes to prevent too large messages. For example, if the bundled message
 * size was a total of 34000 bytes, and max_xmit_size=16000, we'd send 3 messages: 2 16K and a 2K message. Note that
 * max_xmit_size should be the same value as FRAG.frag_size (or smaller).
 Retransmit requests are always sent
 * to the sender. If the sender dies, and not everyone has received its messages, they will be lost. In the future, this
 * may be changed to have receivers store all messages, so that retransmit requests can be answered by any member.
 * Trivial to implement, but not done yet. For most apps, the default retransmit properties are sufficient, if not use
 * vsync.
 *
 * @author Bela Ban
 */
public class NAKACK extends Protocol implements Retransmitter.RetransmitCommand, NakReceiverWindow.Listener {
    private long[]  retransmit_timeout={600, 1200, 2400, 4800}; // time(s) to wait before requesting retransmission
    private volatile/*GemStoneAddition*/ boolean is_server=false;
    private Address local_addr=null;
    private final Vector  members=new Vector(11);
    private ViewId viewId; // GemStoneAddition
    private long    seqno=0;                                   // current message sequence number (starts with 0)
    private long    max_xmit_size=8192;                        // max size of a retransmit message (otherwise send multiple)
    private int     gc_lag=20;                                 // number of msgs garbage collection lags behind

    private long max_xmit_burst=0; // GemStoneAddition
    /**
     * Retransmit messages using multicast rather than unicast. This has the advantage that, if many receivers lost a
     * message, the sender only retransmits once.
     */
    private boolean use_mcast_xmit=false;

    /**
     * Ask a random member for retransmission of a missing message. If set to true, discard_delivered_msgs will be
     * set to false
     */
    private boolean xmit_from_random_member=false;

    /**
     * Messages that have been received in order are sent up the stack (= delivered to the application). Delivered
     * messages are removed from NakReceiverWindow.received_msgs and moved to NakReceiverWindow.delivered_msgs, where
     * they are later garbage collected (by STABLE). Since we do retransmits only from sent messages, never
     * received or delivered messages, we can turn the moving to delivered_msgs off, so we don't keep the message
     * around, and don't need to wait for garbage collection to remove them.
     */
    private boolean discard_delivered_msgs=false;

    /** If value is > 0, the retransmit buffer is bounded: only the max_xmit_buf_size latest messages are kept,
     * older ones are discarded when the buffer size is exceeded. A value <= 0 means unbounded buffers
     */
    private int max_xmit_buf_size=0;

    /**
     * Hashtable key is Address, value is NakReceiverWindow. Stores received messages (keyed by sender). Note that this is no long term
     * storage; messages are just stored until they can be delivered (ie., until the correct FIFO order is established)
     */
    private final HashMap received_msgs=new HashMap(11);

    private long received_msgs_size = 0; // statistic

   /** TreeMap key is Long, value is Message. Map of messages sent by me (keyed and sorted on sequence number) */
    private final TreeMap sent_msgs=new TreeMap();

    private boolean leaving=false;
    private TimeScheduler timer=null;
    private static final String name="NAKACK";

    private long xmit_reqs_received;
    private long xmit_reqs_sent;
    private long xmit_rsps_received;
    private long xmit_rsps_sent;
    private long missing_msgs_received;

    /** Captures stats on XMIT_REQS, XMIT_RSPS per sender */
    private HashMap sent=new HashMap();

    /** Captures stats on XMIT_REQS, XMIT_RSPS per receiver */
    private HashMap received=new HashMap();

    private int stats_list_size=20;

    /** BoundedList values are XmitRequest. Keeps track of the last stats_list_size XMIT requests */
    private BoundedList receive_history;

    /** BoundedList values are MissingMessage. Keeps track of the last stats_list_size missing messages received */
    private BoundedList send_history;

    // GemStoneAddition
    long max_sent_msgs_size = 100000; // maximum number of sent messages

    public NAKACK() {
    }

    @Override // GemStoneAddition
    public String getName() {
        return name;
    }

    // start GemStoneAddition
    @Override // GemStoneAddition
    public int getProtocolEnum() {
      return com.gemstone.org.jgroups.stack.Protocol.enumNAKACK;
    }
    // end GemStone addition

    public long getXmitRequestsReceived() {return xmit_reqs_received;}
    public long getXmitRequestsSent() {return xmit_reqs_sent;}
    public long getXmitResponsesReceived() {return xmit_rsps_received;}
    public long getXmitResponsesSent() {return xmit_rsps_sent;}
    public long getMissingMessagesReceived() {return missing_msgs_received;}

    public int getPendingRetransmissionRequests() {
        // TODO just return received_msgs_size?
        int num=0;
        NakReceiverWindow win;
        synchronized(received_msgs) {
            for(Iterator it=received_msgs.values().iterator(); it.hasNext();) {
                win=(NakReceiverWindow)it.next();
                num+=win.size();
            }
       }
         return num;
    }

    public int getSentTableSize() {
        return sent_msgs.size();
    }

    public int getReceivedTableSize() {
        // TODO: just return received_msgs_size?
        // TODO: why is this sum not calculated in a synchronized block?
        int ret=0;
        NakReceiverWindow win;
        Set s=new LinkedHashSet(received_msgs.values());
        for(Iterator it=s.iterator(); it.hasNext();) {
            win=(NakReceiverWindow)it.next();
            ret+=win.size();
        }
        return ret;
    }

    @Override // GemStoneAddition
    public void resetStats() {
        xmit_reqs_received=xmit_reqs_sent=xmit_rsps_received=xmit_rsps_sent=missing_msgs_received=0;
        sent.clear();
        received.clear();
        if(receive_history !=null)
            receive_history.removeAll();
        if(send_history != null)
            send_history.removeAll();
    }

    @Override // GemStoneAddition
    public void init() throws Exception {
        if(stats) {
            send_history=new BoundedList(stats_list_size);
            receive_history=new BoundedList(stats_list_size);
        }
    }

    public int getGcLag() {
        return gc_lag;
    }

    public void setGcLag(int gc_lag) {
        this.gc_lag=gc_lag;
    }

    public boolean isUseMcastXmit() {
        return use_mcast_xmit;
    }

    public void setUseMcastXmit(boolean use_mcast_xmit) {
        this.use_mcast_xmit=use_mcast_xmit;
    }

    public boolean isXmitFromRandomMember() {
        return xmit_from_random_member;
    }

    public void setXmitFromRandomMember(boolean xmit_from_random_member) {
        this.xmit_from_random_member=xmit_from_random_member;
    }

    public boolean isDiscardDeliveredMsgs() {
        return discard_delivered_msgs;
    }

    public void setDiscardDeliveredMsgs(boolean discard_delivered_msgs) {
        this.discard_delivered_msgs=discard_delivered_msgs;
    }

    public int getMaxXmitBufSize() {
        return max_xmit_buf_size;
    }

    public void setMaxXmitBufSize(int max_xmit_buf_size) {
        this.max_xmit_buf_size=max_xmit_buf_size;
    }

    public long getMaxXmitSize() {
        return max_xmit_size;
    }

    public void setMaxXmitSize(long max_xmit_size) {
        this.max_xmit_size=max_xmit_size;
    }

    @Override // GemStoneAddition
    public boolean setProperties(Properties props) {
        String str;
        long[] tmp;

        super.setProperties(props);
        str=props.getProperty("retransmit_timeout");
        if(str != null) {
            tmp=Util.parseCommaDelimitedLongs(str);
            props.remove("retransmit_timeout");
            if(tmp != null && tmp.length > 0) {
                retransmit_timeout=tmp;
            }
        }

        str=props.getProperty("gc_lag");
        if(str != null) {
            gc_lag=Integer.parseInt(str);
            if(gc_lag < 0) {
                log.error(ExternalStrings.NAKACK_NAKACKSETPROPERTIES_GC_LAG_CANNOT_BE_NEGATIVE_SETTING_IT_TO_0);
            }
            props.remove("gc_lag");
        }

        str=props.getProperty("max_xmit_size");
        if(str != null) {
            max_xmit_size=Long.parseLong(str);
            props.remove("max_xmit_size");
        }

        // GemStoneAddition - limit size of retransmission bursts
        str=props.getProperty("max_xmit_burst");
        if (str != null) {
          max_xmit_burst = Long.parseLong(str);
          props.remove("max_xmit_burst");
        }

        str=props.getProperty("use_mcast_xmit");
        if(str != null) {
            use_mcast_xmit=Boolean.valueOf(str).booleanValue();
            props.remove("use_mcast_xmit");
        }

        str=props.getProperty("discard_delivered_msgs");
        if(str != null) {
            discard_delivered_msgs=Boolean.valueOf(str).booleanValue();
            props.remove("discard_delivered_msgs");
        }

        str=props.getProperty("xmit_from_random_member");
        if(str != null) {
            xmit_from_random_member=Boolean.valueOf(str).booleanValue();
            props.remove("xmit_from_random_member");
        }

        str=props.getProperty("max_xmit_buf_size");
        if(str != null) {
            max_xmit_buf_size=Integer.parseInt(str);
            props.remove("max_xmit_buf_size");
        }

        str=props.getProperty("stats_list_size");
        if(str != null) {
            stats_list_size=Integer.parseInt(str);
            props.remove("stats_list_size");
        }

        //GemStoneAddition
        str = props.getProperty("max_sent_msgs_size");
        if (str != null) {
          max_sent_msgs_size = Long.parseLong(str);
          props.remove("max_sent_msgs_size");
        }

        if(xmit_from_random_member) {
            if(discard_delivered_msgs) {
                discard_delivered_msgs=false;
                log.warn("xmit_from_random_member set to true: changed discard_delivered_msgs to false");
            }
        }

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

            return false;
        }
        return true;
    }

    @Override // GemStoneAddition
    public Map dumpStats() {
        Map retval=super.dumpStats();
        if(retval == null)
            retval=new HashMap();

        retval.put("xmit_reqs_received", Long.valueOf(xmit_reqs_received));
        retval.put("xmit_reqs_sent", Long.valueOf(xmit_reqs_sent));
        retval.put("xmit_rsps_received", Long.valueOf(xmit_rsps_received));
        retval.put("xmit_rsps_sent", Long.valueOf(xmit_rsps_sent));
        retval.put("missing_msgs_received", Long.valueOf(missing_msgs_received));

        retval.put("sent_msgs", printSentMsgs());

        StringBuffer sb=new StringBuffer();
        Map.Entry entry;
        Address addr;
        Object w;
        synchronized(received_msgs) {
            for(Iterator it=received_msgs.entrySet().iterator(); it.hasNext();) {
                entry=(Map.Entry)it.next();
                addr=(Address)entry.getKey();
                w=entry.getValue();
                sb.append(addr).append(": ").append(w.toString()).append('\n');
            }
        }

        retval.put("received_msgs", sb.toString());
        return retval;
    }

    @Override // GemStoneAddition
    public String printStats() {
        Map.Entry entry;
        Object key, val;
        StringBuffer sb=new StringBuffer();
        sb.append("sent:\n");
        for(Iterator it=sent.entrySet().iterator(); it.hasNext();) {
            entry=(Map.Entry)it.next();
            key=entry.getKey();
            if(key == null) key="<mcast dest>";
            val=entry.getValue();
            sb.append(key).append(": ").append(val).append("\n");
        }
        sb.append("\nreceived:\n");
        for(Iterator it=received.entrySet().iterator(); it.hasNext();) {
            entry=(Map.Entry)it.next();
            key=entry.getKey();
            val=entry.getValue();
            sb.append(key).append(": ").append(val).append("\n");
        }

        sb.append("\nXMIT_REQS sent:\n");
        XmitRequest tmp;
        for(Enumeration en=send_history.elements(); en.hasMoreElements();) {
            tmp=(XmitRequest)en.nextElement();
            sb.append(tmp).append("\n");
        }

        sb.append("\nMissing messages received\n");
        MissingMessage missing;
        for(Enumeration en=receive_history.elements(); en.hasMoreElements();) {
            missing=(MissingMessage)en.nextElement();
            sb.append(missing).append("\n");
        }

        return sb.toString();
    }

    @Override // GemStoneAddition
    public Vector providedUpServices() {
        Vector retval=new Vector(5);
        retval.addElement(Integer.valueOf(Event.GET_DIGEST));
        retval.addElement(Integer.valueOf(Event.GET_DIGEST_STABLE));
        retval.addElement(Integer.valueOf(Event.GET_DIGEST_STATE));
        retval.addElement(Integer.valueOf(Event.SET_DIGEST));
        retval.addElement(Integer.valueOf(Event.MERGE_DIGEST));
        return retval;
    }

    @Override // GemStoneAddition
    public Vector providedDownServices() {
        Vector retval=new Vector(2);
        retval.addElement(Integer.valueOf(Event.GET_DIGEST));
        retval.addElement(Integer.valueOf(Event.GET_DIGEST_STABLE));
        return retval;
    }

    @Override // GemStoneAddition
    public void start() throws Exception {
        timer=stack != null ? stack.timer : null;
        if(timer == null) {
            throw new Exception("NAKACK.up(): timer is null");
        }
    }

    @Override // GemStoneAddition
    public void stop() {
        removeAll();  // clears sent_msgs and destroys all NakReceiverWindows
    }

    /**
     * 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 !
     */
    @Override // GemStoneAddition
    public void down(Event evt) {
//      long start; // GemStoneAddition
       Digest  digest;
        Vector  mbrs;

        switch(evt.getType()) {

        case Event.MSG:
            Message msg=(Message)evt.getArg();
            Address dest=msg.getDest();
            if(dest != null && !dest.isMulticastAddress()) {
                break; // unicast address: not null and not mcast, pass down unchanged
            }
            send(evt, msg);
            return;    // don't pass down the stack

        case Event.STABLE:  // generated by STABLE layer. Delete stable messages passed in arg
            stable((Digest)evt.getArg());
            return;  // do not pass down further (Bela Aug 7 2001)

        case Event.GET_DIGEST:
            digest=getDigest();
            passUp(new Event(Event.GET_DIGEST_OK, digest != null ? digest.copy() : null));
            return;

        case Event.GET_DIGEST_STABLE:
            digest=getDigestHighestDeliveredMsgs();
            passUp(new Event(Event.GET_DIGEST_STABLE_OK, digest != null ? digest.copy() : null));
            return;

        case Event.GET_DIGEST_STATE:
            digest=getDigest();
            passUp(new Event(Event.GET_DIGEST_STATE_OK, digest != null ? digest.copy() : null));
            return;

        case Event.SET_DIGEST:
            setDigest((Digest)evt.getArg());
            return;

        case Event.MERGE_DIGEST:
            mergeDigest((Digest)evt.getArg());
            return;

        case Event.CONFIG:
            passDown(evt);
            if(log.isDebugEnabled()) {
                log.debug("received CONFIG event: " + evt.getArg());
            }
            handleConfigEvent((HashMap)evt.getArg());
            return;

        case Event.TMP_VIEW:
            mbrs=((View)evt.getArg()).getMembers();
            members.clear();
            members.addAll(mbrs);
            adjustReceivers();
            break;

        case Event.VIEW_CHANGE:
            mbrs=((View)evt.getArg()).getMembers();
            members.clear();
            members.addAll(mbrs);
            this.viewId = ((View)evt.getArg()).getVid();
            adjustReceivers();
            is_server=true;  // check vids from now on

            Set tmp=new LinkedHashSet(members);
            tmp.add(null); // for null destination (= mcast)
            sent.keySet().retainAll(tmp);
            received.keySet().retainAll(tmp);
            break;

        case Event.BECOME_SERVER:
            is_server=true;
            break;

        case Event.DISCONNECT:
            leaving=true;
            removeAll();
            seqno=0;
            break;
        }

        passDown(evt);
    }

    /**
     * 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 !
     */
    @Override // GemStoneAddition
    public void up(Event evt) {
//        NakAckHeader hdr;
//        Message msg;
        Digest digest;

//       long start; // GemStoneAddition
       switch(evt.getType()) {

        case Event.MSG:
            final Message msg=(Message)evt.getArg(); // GemStoneAddition - final
            final NakAckHeader hdr=(NakAckHeader)msg.getHeader(name);
            if(hdr == null)
                break;  // pass up (e.g. unicast msg)

            // discard messages while not yet server (i.e., until JOIN has returned)
            boolean isServer = is_server;
            if(!isServer || !isMember(msg.getSrc())) {
              GmsHeader ghdr = (GmsHeader)msg.getHeader("GMS");
              // GemstoneAddition:
              // see if this is a view that was sent out with NAKACK.  If so, we
              // really need to respond to it to keep from stalling the view handler
              if (ghdr != null && ghdr.type == GmsHeader.VIEW) {
                // bug 42152 - bypass stack and hand directly to GMS
                this.stack.findProtocol("GMS").up(evt);
              } else {
                if(trace)
                    log.trace("message was discarded (not yet server)");
              }
              if (!isServer) {
                return;
              }
            }

            // Changed by bela Jan 29 2003: we must not remove the header, otherwise
            // further xmit requests will fail !
            //hdr=(NakAckHeader)msg.removeHeader(getName());

            switch(hdr.type) {

            case NakAckHeader.MSG:
                handleMessage(msg, hdr);
                return;        // transmitter passes message up for us !

            case NakAckHeader.XMIT_REQ:
                if(hdr.range == null) {
                    if(log.isErrorEnabled()) {
                        log.error(ExternalStrings.NAKACK_XMIT_REQ_RANGE_OF_XMIT_MSG_IS_NULL_DISCARDING_REQUEST_FROM__0, msg.getSrc());
                    }
                    return;
                }
                handleXmitReq(msg.getSrc(), hdr.range.low, hdr.range.high, hdr.sender);
                return;

            case NakAckHeader.XMIT_RSP:
                if(trace)
                    log.trace("received missing messages " + hdr.range);
                handleXmitRsp(msg);
                 return;

            default:
                if(log.isErrorEnabled()) {
                    log.error(ExternalStrings.NAKACK_NAKACK_HEADER_TYPE__0__NOT_KNOWN_, hdr.type);
                }
                return;
            }

        case Event.STABLE:  // generated by STABLE layer. Delete stable messages passed in arg
            stable((Digest)evt.getArg());
            return;  // do not pass up further (Bela Aug 7 2001)

        case Event.GET_DIGEST:
            digest=getDigestHighestDeliveredMsgs();
            passDown(new Event(Event.GET_DIGEST_OK, digest));
            return;

        case Event.GET_DIGEST_STABLE:
            digest=getDigestHighestDeliveredMsgs();
            passDown(new Event(Event.GET_DIGEST_STABLE_OK, digest));
            return;

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

        case Event.CONFIG:
            passUp(evt);
            if(log.isDebugEnabled()) {
                log.debug("received CONFIG event: " + evt.getArg());
            }
            handleConfigEvent((HashMap)evt.getArg());
            return;
        }
        passUp(evt);
    }

  // GemStoneAddition - check to see if a sender is a member or not
  private boolean isMember(Address addr) {
    if (!this.members.contains(addr) && this.viewId != null) {
      // we may not have seen the view announcing this member
      return addr.getBirthViewId() > viewId.getId();
    }
    return true;
  }

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

    private synchronized long getNextSeqno() {
        return seqno++;
    }

    /**
     * GemStoneAddition - return the current sequence number
     * @return the current sequence number
     */
    public long getCurrentSeqno() {
      return seqno-1;
    }

//    private void recalculate_send_msgs() {
//      long old_size = sent_msgs_size;
//      long new_size = 0;
//
//      synchronized (sent_msgs) {
//        Iterator it = sent_msgs.keySet().iterator();
//        while (it.hasNext()) {
//          Long k = (Long)it.next();
//          Message m = (Message)sent_msgs.get(k);
//          new_size += m.size();
//        }
//      if (stack.gemfireStats != null)
//        stack.gemfireStats.incJgSentMessagesSize(new_size - old_size);
//      }
//    }

   /**
     * Adds the message to the sent_msgs table and then passes it down the stack. Change Bela Ban May 26 2002: we don't
     * store a copy of the message, but a reference ! This saves us a lot of memory. However, this also means that a
     * message should not be changed after storing it in the sent-table ! See protocols/DESIGN for details.
     */
    private void send(Event evt, Message msg) {
      synchronized(this) { // GemStone - fix for bug #34551
        long msg_id=getNextSeqno();
        if(trace)
            log.trace(local_addr.toString() + ": sending msg #" + msg_id);

        msg.putHeader(name, new NakAckHeader(NakAckHeader.MSG, msg_id));
        synchronized(sent_msgs) {
            if(Global.copy) {
                sent_msgs.put(Long.valueOf(msg_id), msg.copy());
            }
            else {
                sent_msgs.put(Long.valueOf(msg_id), msg);
            }
            // GemStoneAddition
            if (max_sent_msgs_size > 0) { // feature enabled
              if (sent_msgs.size() > max_sent_msgs_size) { // full
                try {
                  sent_msgs.wait(1);
                }
                catch (InterruptedException e) {
                  Thread.currentThread().interrupt();
                }
                stack.gfPeerFunctions.incJgNAKACKwaits(1);
              } // full
            } // fetaure enabled
            stack.gfPeerFunctions.setJgSTABLEsentMessagesSize(sent_msgs.size());
        }
      } // synchronized(this)
      passDown(evt);
    }

    /**
     * GemStoneAddition - sync delivery to prevent race between mcast receiver thread
     * and ucast receiver thread delivering messages out of order
     */
    private final Object deliverySync = new Object();

    /**
     * Finds the corresponding NakReceiverWindow and adds the message to it (according to seqno). Then removes as many
     * messages as possible from the NRW and passes them up the stack. Discards messages from non-members.
     */
    private void handleMessage(Message msg, NakAckHeader hdr) {
//        long start; // GemStoneAddition
        NakReceiverWindow win;
        Message msg_to_deliver;
        Address sender=msg.getSrc();

        if(sender == null) {
            if(log.isErrorEnabled()) { // GemStoneAddition - verbose msg for debugging 36611 (bruce)
                log.error("sender of message is null: " + msg.toString(),
                    new Exception("Stack trace - please send to GemStone " +
                                "customer support and reference bug #36611"));
            }
            return;
        }

        if(trace) {
            StringBuffer sb=new StringBuffer('[');
            sb.append(local_addr).append("] received ").append(sender).append('#').append(hdr.seqno);
            log.trace(sb.toString());
        }

        // msg is potentially re-sent later as result of XMIT_REQ reception; that's why hdr is added !

        // Changed by bela Jan 29 2003: we currently don't resend from received msgs, just from sent_msgs !
        // msg.putHeader(getName(), hdr);

        synchronized(received_msgs) {
            win=(NakReceiverWindow)received_msgs.get(sender);
        }
        if(win == null) {  // discard message if there is no entry for sender
            if(leaving)
                return;
            if(warn) {
                StringBuffer sb=new StringBuffer('[');
                sb.append(local_addr).append("] discarded message from non-member ").append(sender);
                if(warn)
                    log.warn(sb.toString());
            }
            return;
        }
        synchronized(deliverySync) {
          win.add(hdr.seqno, msg);  // add in order, then remove and pass up as many msgs as possible

          while((msg_to_deliver=win.remove()) != null) {
              // Changed by bela Jan 29 2003: not needed (see above)
              //msg_to_deliver.removeHeader(getName());
            if (trace) {
              log.trace(local_addr.toString() + ": NAKACK dispatching " + msg_to_deliver);
            }
              passUp(new Event(Event.MSG, msg_to_deliver));
          }
        }
    }

    // GemStoneAddition - test of failure to transmit a large message.
    // It should throw a RetransmissionTooLargeException
    public boolean testRetransmitLargeMessage(byte[] bytesToSend) {
      IpAddress myAddress = (IpAddress)local_addr;
      int myPort = myAddress.getPort();
      Message msg = new Message(new IpAddress(myAddress.getIpAddress(), myPort-1), this.local_addr, bytesToSend, 0, bytesToSend.length);
      msg.putHeader(name, new NakAckHeader(NakAckHeader.XMIT_RSP, 0, 0));
      try {
        passDown(new Event(Event.MSG, msg));
      } catch (RetransmissionTooLargeException e) {
        return true;
      }
      return false;
    }

    /**
     * Retransmit from sent-table, called when XMIT_REQ is received. Bundles all messages to be xmitted into one large
     * message and sends them back with an XMIT_RSP header. Note that since we cannot count on a fragmentation layer
     * below us, we have to make sure the message doesn't exceed max_xmit_size bytes. If this is the case, we split the
     * message into multiple, smaller-chunked messages. But in most cases this still yields fewer messages than if each
     * requested message was retransmitted separately.
     *
     * @param xmit_requester        The sender of the XMIT_REQ, we have to send the requested copy of the message to this address
     * @param first_seqno The first sequence number to be retransmitted (<= last_seqno)
     * @param last_seqno  The last sequence number to be retransmitted (>= first_seqno)
     * @param original_sender The member who originally sent the messsage. Guaranteed to be non-null
     */
    void handleXmitReq(Address xmit_requester, long first_seqno, long last_seqno, Address original_sender) {
        Message m, tmp;
        LinkedList list;
        long size=0, marker=first_seqno, len;
        long burstSize=0; // GemStoneAddition - limit bursts of retransmissions
        NakReceiverWindow win=null;
        boolean      amISender; // am I the original sender ?

        // GemStoneAddition - ignore requests from those not in membership
        synchronized(this.members) {
          if (!isMember(xmit_requester)) {
            // request from someone who's not in membership - ignore it
            if (trace) {
              log.trace("Ignoring xmit request from " + xmit_requester + " who is not in membership");
            }
            return;
          }
        }

        if(trace) {
            StringBuffer sb=new StringBuffer();
            sb.append(local_addr).append(": received xmit request from ").append(xmit_requester).append(" for ");
            sb.append(original_sender).append(" [").append(first_seqno).append(" - ").append(last_seqno).append("]");
            log.trace(sb.toString());
        }

        if(first_seqno > last_seqno) {
            if(log.isErrorEnabled())
                log.error(ExternalStrings.NAKACK_FIRST_SEQNO__0___LAST_SEQNO__1__NOT_ABLE_TO_RETRANSMIT, new Object[] {Long.valueOf(first_seqno), Long.valueOf(last_seqno)});
            return;
        }

        if(stats) {
            xmit_reqs_received+=last_seqno - first_seqno +1;
            updateStats(received, xmit_requester, 1, 0, 0);
        }

        amISender=local_addr.equals(original_sender);
        if(!amISender)
            win=(NakReceiverWindow)received_msgs.get(original_sender);

        list=new LinkedList();
        for(long i=first_seqno; i <= last_seqno && (max_xmit_burst <= 0 || burstSize <= max_xmit_burst); i++) {  // GemStoneAddition - burst control
            if(amISender) {
                m=(Message)sent_msgs.get(Long.valueOf(i)); // no need to synchronize
            }
            else {
                m=win != null? win.get(i) : null;
            }
            if(m == null) {
                  // GemStone: don't log this.  It can happen when the other member leaves
//                if(log.isErrorEnabled() && !stack.getChannel().closing()) {
//                    StringBuffer sb=new StringBuffer();
//                    sb.append("(requester=").append(xmit_requester).append(", local_addr=").append(this.local_addr);
//                    sb.append(") message ").append(original_sender).append("::").append(i);
//                    sb.append(" not found in ").append((amISender? "sent" : "received")).append(" msgs. ");
//                    if(win != null) {
//                        sb.append("Received messages from ").append(original_sender).append(": ").append(win.toString());
//                    }
//                    else {
//                        sb.append("\nSent messages: ").append(printSentMsgs());
//                    }
//                    log.error(sb.toString());
//                }
               // GemStoneAddition - if the message isn't here anymore we need to send something to stop
               // further retransmission requests
                if (stack.gfPeerFunctions.isDisconnecting()) {
                  return;
            }
              // requested message is not present
              if (log.getLogWriter().fineEnabled()) {
                log.getLogWriter().fine("Retransmission request received from " + xmit_requester + " for a message that has already been discarded");
              }
                m = new Message();
                m.setDest(xmit_requester);
                m.setSrc(local_addr);
                NakAckHeader hdr = new NakAckHeader(NakAckHeader.MSG, i);
                m.putHeader(name, hdr);
//                continue;
            }
            len=m.size();
            size+=len;
            burstSize += len;
            if(size > max_xmit_size && list.size() > 0) { // changed from >= to > (yaron-r, bug #943709)
                // yaronr: added &&listSize()>0 since protocols between FRAG and NAKACK add headers, and message exceeds size.

                // size has reached max_xmit_size. go ahead and send message (excluding the current message)
                if(trace)
                    log.trace("xmitting msgs [" + marker + '-' + (i - 1) + "] to " + xmit_requester);
                sendXmitRsp(xmit_requester, (LinkedList)list.clone(), marker, i - 1);
                marker=i;
                list.clear();
                // fixed Dec 15 2003 (bela, patch from Joel Dice (dicej)), see explanantion under
                // bug report #854887
                size=len;
                burstSize=len;
            }
            if(Global.copy) {
                tmp=m.copy();
            }
            else {
                tmp=m;
            }
            // tmp.setDest(xmit_requester);
            // tmp.setSrc(local_addr);
            if(tmp.getSrc() == null)
                tmp.setSrc(local_addr);
//            if (trace) {
//              log.trace("xmitting message " + m); // GemStoneAddition - debugging size problem
//            }
            list.add(tmp);
        }

        if(list.size() > 0) {
            if(trace)
                log.trace("xmitting msgs [" + marker + '-' + last_seqno + "] to " + xmit_requester);
            sendXmitRsp(xmit_requester, (LinkedList)list.clone(), marker, last_seqno);
            list.clear();
        }
    }

    private static void updateStats(HashMap map, Address key, int req, int rsp, int missing) {
        Entry entry=(Entry)map.get(key);
        if(entry == null) {
            entry=new Entry();
            map.put(key, entry);
        }
        entry.xmit_reqs+=req;
        entry.xmit_rsps+=rsp;
        entry.missing_msgs_rcvd+=missing;
    }

    private void sendXmitRsp(Address dest, LinkedList xmit_list, long first_seqno, long last_seqno) {
        Buffer buf;
        if(xmit_list == null || xmit_list.size() == 0) {
            if(log.isErrorEnabled())
                log.error(ExternalStrings.NAKACK_XMIT_LIST_IS_EMPTY);
            return;
        }
        if(use_mcast_xmit)
            dest=null;

        if(stats) {
            xmit_rsps_sent+=xmit_list.size();
            updateStats(sent, dest, 0, 1, 0);
        }

        try {
            buf=Util.msgListToByteBuffer(xmit_list, dest);
            Message msg=new Message(dest, null, buf.getBuf(), buf.getOffset(), buf.getLength());
            msg.putHeader(name, new NakAckHeader(NakAckHeader.XMIT_RSP, first_seqno, last_seqno));
            passDown(new Event(Event.MSG, msg));
            stack.gfPeerFunctions.incMcastRetransmits(); // GemStoneAddition
        }
        // GemStoneAddition - if the buffer is too large, retry by sending each
        // message individually
        catch (RetransmissionTooLargeException e) {
          for (Message xmitMsg:  (List<Message>)xmit_list) {
            NakAckHeader xmitHdr = (NakAckHeader)xmitMsg.getHeader(name);
            long seqno = xmitHdr.seqno;
            Message msg;
            try {
              buf=Util.msgListToByteBuffer(Collections.singletonList(xmitMsg), null);
              msg=new Message(dest, null, buf.getBuf(), buf.getOffset(), buf.getLength());
              msg.putHeader(name, new NakAckHeader(NakAckHeader.XMIT_RSP, seqno, seqno));
              passDown(new Event(Event.MSG, msg));
              stack.gfPeerFunctions.incMcastRetransmits(); // GemStoneAddition
            } catch (IOException ex) {
              log.error(ExternalStrings.NAKACK_FAILED_MARSHALLING_XMIT_LIST, ex);
              continue;
            }
          }
        }
        catch(IOException ex) {
            log.error(ExternalStrings.NAKACK_FAILED_MARSHALLING_XMIT_LIST, ex);
        }
    }

    // GemStoneAddition - handle messages that are too large
    public static boolean isRetransmission(Message msg) {
      NakAckHeader hdr = (NakAckHeader)msg.getHeader(name);
      if (hdr != null && hdr.type == NakAckHeader.XMIT_RSP) {
        return true;
      }
      return false;
    }

    // GemStoneAddition - handle messages that are too large
    public static class RetransmissionTooLargeException extends RuntimeException {
      public RetransmissionTooLargeException(String message) {
        super(message);
      }
    }

    private void handleXmitRsp(Message msg) {
        LinkedList list;
        Message m;

        if(msg == null) {
            if(warn)
                log.warn("message is null");
            return;
        }
        try {
            list=Util.byteBufferToMessageList(msg.getRawBuffer(), msg.getOffset(), msg.getLength());
            if(list != null) {
                if(stats) {
                    xmit_rsps_received+=list.size();
                    updateStats(received, msg.getSrc(), 0, 1, 0);
                }
                for(Iterator it=list.iterator(); it.hasNext();) {
                    m=(Message)it.next();
                    up(new Event(Event.MSG, m));
                }
                list.clear();
            }
        }
        catch(Exception ex) {
            if(log.isErrorEnabled() && !ex.getClass().getSimpleName().equals("CancelException")) {
              // GemStoneAddition - debugging for 36612
              log.error("Exception caught while processing a message from " + msg.getSrc(), ex);
            }
        }
    }

    /**
     * Remove old members from NakReceiverWindows and add new members (starting seqno=0). Essentially removes all
     * entries from received_msgs that are not in members
     */
    private void adjustReceivers() {
        Address sender;
        NakReceiverWindow win;
        synchronized(received_msgs) {
          received_msgs_size = 0; // we will recalculate it

            // 1. Remove all senders in received_msgs that are not members anymore
            for(Iterator it=received_msgs.keySet().iterator(); it.hasNext();) {
                sender=(Address)it.next();
                if(!isMember(sender)) {
                    win=(NakReceiverWindow)received_msgs.get(sender);
                    win.reset();
                    if(log.isDebugEnabled()) {
                        log.debug("removing " + sender + " from received_msgs (not member anymore)");
                    }
                    it.remove();
                }
                else {
                  // statistics gather
                  win = (NakReceiverWindow)received_msgs.get(sender);
                  received_msgs_size += win.unsafeGetSize();
                  stack.gfPeerFunctions.setJgSTABLEreceivedMessagesSize(received_msgs_size);
                }
            }

            // 2. Add newly joined members to received_msgs (starting seqno=0)
            for(int i=0; i < members.size(); i++) {
                sender=(Address)members.elementAt(i);
                if(!received_msgs.containsKey(sender)) {
                    win=createNakReceiverWindow(sender, 0);
                    received_msgs.put(sender, win);
                    // It's zero length, no need to update received_msgs_size
                }
            }

            // 3. update statistic
            stack.gfPeerFunctions.setJgSTABLEreceivedMessagesSize(received_msgs_size);
        }
    }

    /**
     * Returns a message digest: for each member P the highest seqno received from P is added to the digest.
     */
    private Digest getDigest() {
        Digest digest;
        Address sender;
        Range range;

        digest=new Digest(members.size());
        for(int i=0; i < members.size(); i++) {
            sender=(Address)members.elementAt(i);
            range=getLowestAndHighestSeqno(sender, false);  // get the highest received seqno
            if(range == null) {
                if(log.isErrorEnabled() && !stack.getChannel().closing()) { // GemStoneAddition - don't log this if the channel is closing
                    log.error(ExternalStrings.NAKACK_RANGE_IS_NULL);
                }
                continue;
            }
            digest.add(sender, range.low, range.high);  // add another entry to the digest
        }
        return digest;
    }

    /**
     * Returns a message digest: for each member P the highest seqno received from P without a gap is added to
     * the digest. E.g. if the seqnos received from P are [+3 +4 +5 -6 +7 +8], then 5 will be returned. Also, the
     * highest seqno seen is added. The max of all highest seqnos seen will be used (in STABLE) to determine
     * whether the last seqno from a sender was received (see "Last Message Dropped" topic in DESIGN).
     */
    private Digest getDigestHighestDeliveredMsgs() {
        Digest digest;
        Address sender;
        Range range;
        long high_seqno_seen;

        digest=new Digest(members.size());
        for(int i=0; i < members.size(); i++) {
            sender=(Address)members.elementAt(i);
            range=getLowestAndHighestSeqno(sender, true);  // get the highest deliverable seqno
            if(range == null) {
                if(log.isErrorEnabled() && !stack.getChannel().closing()) { // GemStoneAddition - don't log this if the channel is closing
                    log.error(ExternalStrings.NAKACK_RANGE_IS_NULL_2); // GemStoneAddition to distinguish from other place this error is generated
                }
                continue;
            }
            high_seqno_seen=getHighSeqnoSeen(sender);
            digest.add(sender, range.low, range.high, high_seqno_seen);  // add another entry to the digest
        }
        return digest;
    }

    /**
     * Creates a NakReceiverWindow for each sender in the digest according to the sender's seqno. If NRW already exists,
     * reset it.
     */
    private void setDigest(Digest d) {
        if(d == null || d.senders == null) {
            if(log.isErrorEnabled()) {
                log.error(ExternalStrings.NAKACK_DIGEST_OR_DIGESTSENDERS_IS_NULL);
            }
            return;
        }
        if (trace) log.trace("installing NAKACK digest " + d);

        clear();

        Map.Entry entry;
        Address sender;
        com.gemstone.org.jgroups.protocols.pbcast.Digest.Entry val;
        long initial_seqno;
        NakReceiverWindow win;

        for(Iterator it=d.senders.entrySet().iterator(); it.hasNext();) {
            entry=(Map.Entry)it.next();
            sender=(Address)entry.getKey();
            val=(com.gemstone.org.jgroups.protocols.pbcast.Digest.Entry)entry.getValue();

            if(sender == null || val == null) {
                if(warn) {
                    log.warn("sender or value is null");
                }
                continue;
            }
            initial_seqno=val.high_seqno;
            win=createNakReceiverWindow(sender, initial_seqno);
            synchronized(received_msgs) {
                received_msgs.put(sender, win);
                // all new entries are of size 0, no need to increment received_msgs_size
            }
        }
    }

    /**
     * For all members of the digest, adjust the NakReceiverWindows in the received_msgs hashtable. If the member
     * already exists, sets its seqno to be the max of the seqno and the seqno of the member in the digest. If no entry
     * exists, create one with the initial seqno set to the seqno of the member in the digest.
     */
    private void mergeDigest(Digest d) {
        if(d == null || d.senders == null) {
            if(log.isErrorEnabled()) {
                log.error(ExternalStrings.NAKACK_DIGEST_OR_DIGESTSENDERS_IS_NULL);
            }
            return;
        }

        Map.Entry entry;
        Address sender;
        com.gemstone.org.jgroups.protocols.pbcast.Digest.Entry val;
        NakReceiverWindow win;
        long initial_seqno;

        for(Iterator it=d.senders.entrySet().iterator(); it.hasNext();) {
            entry=(Map.Entry)it.next();
            sender=(Address)entry.getKey();
            val=(com.gemstone.org.jgroups.protocols.pbcast.Digest.Entry)entry.getValue();

            if(sender == null || val == null) {
                if(warn) {
                    log.warn("sender or value is null");
                }
                continue;
            }
            initial_seqno=val.high_seqno;
            synchronized(received_msgs) {
                win=(NakReceiverWindow)received_msgs.get(sender);
                if(win == null) {
                    win=createNakReceiverWindow(sender, initial_seqno);
                    // it's empty, no need to increment received_msgs_size
                }
                else {
                    if(win.getHighestReceived() < initial_seqno) {
                        received_msgs_size -= win.unsafeGetSize();
                        stack.gfPeerFunctions.setJgSTABLEreceivedMessagesSize(received_msgs_size);
                        win.reset();
                        received_msgs.remove(sender);
                        win=createNakReceiverWindow(sender, initial_seqno);
                        received_msgs.put(sender, win);
                    }
                }
                stack.gfPeerFunctions.setJgSTABLEreceivedMessagesSize(received_msgs_size);

            }
        }
    }

    private NakReceiverWindow createNakReceiverWindow(Address sender, long initial_seqno) {
        NakReceiverWindow win=new NakReceiverWindow(sender, this, initial_seqno, timer);
        win.setRetransmitTimeouts(retransmit_timeout);
        win.setDiscardDeliveredMessages(discard_delivered_msgs);
        win.setMaxXmitBufSize(this.max_xmit_buf_size);
        if(stats)
            win.setListener(this);
        return win;
    }

    /**
     * Returns the lowest seqno still in cache (so it can be retransmitted) and the highest seqno received so far.
     *
     * @param sender       The address for which the highest and lowest seqnos are to be retrieved
     * @param stop_at_gaps If true, the highest seqno *deliverable* will be returned. If false, the highest seqno
     *                     *received* will be returned. E.g. for [+3 +4 +5 -6 +7 +8], the highest_seqno_received is 8,
     *                     whereas the higheset_seqno_seen (deliverable) is 5.
     */
    private Range getLowestAndHighestSeqno(Address sender, boolean stop_at_gaps) {
        Range r=null;
        NakReceiverWindow win;

        if(sender == null) {
            if(log.isErrorEnabled()) {
                log.error(ExternalStrings.NAKACK_SENDER_IS_NULL);
            }
            return r;
        }
        synchronized(received_msgs) {
            win=(NakReceiverWindow)received_msgs.get(sender);
        }
        if(win == null) {
            if(log.isErrorEnabled() && !stack.getChannel().closing()) {  // GemStoneAddition - don't log if we're shutting down
                log.error(ExternalStrings.NAKACK_SENDER__0__NOT_FOUND_IN_RECEIVED_MSGS, sender);
            }
            return r;
        }
        if(stop_at_gaps) {
            r=new Range(win.getLowestSeen(), win.getHighestSeen());       // deliverable messages (no gaps)
        }
        else {
            r=new Range(win.getLowestSeen(), win.getHighestReceived() + 1); // received messages
        }
        return r;
    }

    /**
     * Returns the highest seqno seen from sender. E.g. if we received 1, 2, 4, 5 from P, then 5 will be returned
     * (doesn't take gaps into account). If we are the sender, we will return the highest seqno sent rather
     * then received
     */
    private long getHighSeqnoSeen(Address sender) {
        NakReceiverWindow win;
        long ret=0;

        if(sender == null) {
            if(log.isErrorEnabled()) {
                log.error(ExternalStrings.NAKACK_SENDER_IS_NULL);
            }
            return ret;
        }
        if(sender.equals(local_addr)) {
            return seqno - 1;
        }

        synchronized(received_msgs) {
            win=(NakReceiverWindow)received_msgs.get(sender);
        }
        if(win == null) {
          // GemStoneAddition - this can happen in the normal course of events if we haven't
          // received a digest that has the sender and haven't received any messages from
          // the sender that made it to NAKACK
//            if(log.isErrorEnabled()&& !stack.getChannel().closing()) {  // GemStoneAddition - don't log if we're shutting down
//                log.error("sender " + sender + " not found in received_msgs (2)"); // GemStoneAddition - to distinguish from other message
//            }
            return ret;
        }
        ret=win.getHighestReceived();
        return ret;
    }

    /**
     * GemStoneAddition

     * Returns the highest seqno dispatched for the given sender. E.g. if we received 1, 2, 4, 5 from P, then 2 will be returned
     * (takes gaps into account). If we are the sender, we will return the highest seqno sent rather
     * then received
     */
    public long getHighSeqnoDispatched(Address sender) {
        NakReceiverWindow win;
        long ret=0;

        if(sender == null) {
            if(log.isErrorEnabled()) {
                log.error(ExternalStrings.NAKACK_SENDER_IS_NULL);
            }
            return ret;
        }
        if(sender.equals(local_addr)) {
            return seqno - 1;
        }

        synchronized(received_msgs) {
            win=(NakReceiverWindow)received_msgs.get(sender);
        }
        if(win == null) {
            if(log.isErrorEnabled()&& !stack.getChannel().closing()) {  // GemStoneAddition - don't log if we're shutting down
                log.error(ExternalStrings.NAKACK_SENDER__0__NOT_FOUND_IN_RECEIVED_MSGS_2, sender); // GemStoneAddition - to distinguish from other message
            }
            return ret;
        }
        ret=win.getHighestSeen();
        return ret;
    }

    /**
     * Garbage collect messages that have been seen by all members. Update sent_msgs: for the sender P in the digest
     * which is equal to the local address, garbage collect all messages <= seqno at digest[P]. Update received_msgs:
     * for each sender P in the digest and its highest seqno seen SEQ, garbage collect all delivered_msgs in the
     * NakReceiverWindow corresponding to P which are <= seqno at digest[P].
     */
    private void stable(Digest d) {
        NakReceiverWindow recv_win;
        long my_highest_rcvd;        // highest seqno received in my digest for a sender P
        long stability_highest_rcvd; // highest seqno received in the stability vector for a sender P

        if(members == null || local_addr == null || d == null) {
            if(warn)
                log.warn("members, local_addr or digest are null !");
            return;
        }

        if(trace) {
            log.trace("received stable digest " + d);
        }

        Map.Entry entry;
        Address sender;
        com.gemstone.org.jgroups.protocols.pbcast.Digest.Entry val;
        long high_seqno_delivered, high_seqno_received;

        for(Iterator it=d.senders.entrySet().iterator(); it.hasNext();) {
            entry=(Map.Entry)it.next();
            sender=(Address)entry.getKey();
            if(sender == null)
                continue;
            val=(com.gemstone.org.jgroups.protocols.pbcast.Digest.Entry)entry.getValue();
            high_seqno_delivered=val.high_seqno;
            high_seqno_received=val.high_seqno_seen;

            // check whether the last seqno received for a sender P in the stability vector is > last seqno
            // received for P in my digest. if yes, request retransmission (see "Last Message Dropped" topic
            // in DESIGN)
            synchronized(received_msgs) {
                recv_win=(NakReceiverWindow)received_msgs.get(sender);
            }
            if(recv_win != null) {
                my_highest_rcvd=recv_win.getHighestReceived();
                stability_highest_rcvd=high_seqno_received;

                if(stability_highest_rcvd >= 0 && stability_highest_rcvd > my_highest_rcvd) {
                    if(trace) {
                        log.trace("my_highest_rcvd (" + my_highest_rcvd + ") < stability_highest_rcvd (" +
                                stability_highest_rcvd + "): requesting retransmission of " +
                                sender + '#' + stability_highest_rcvd);
                    }
                    retransmit(stability_highest_rcvd, stability_highest_rcvd, sender);
                }
            }

            high_seqno_delivered-=gc_lag;
            if(high_seqno_delivered < 0) {
                continue;
            }

            if(trace)
                log.trace("deleting msgs <= " + high_seqno_delivered + " from " + sender);

            // garbage collect from sent_msgs if sender was myself
            if(sender.equals(local_addr)) {
                synchronized(sent_msgs) {
                    // gets us a subset from [lowest seqno - seqno]
                    SortedMap stable_keys=sent_msgs.headMap(Long.valueOf(high_seqno_delivered));
                    if(stable_keys != null) {
//                      if (stack.gemfireStats != null) {
//                        Iterator i = stable_keys.keySet().iterator();
//                        long sz = 0;
//                        while (i.hasNext()) {
//                          Long k = (Long)i.next();
//                          Message m = (Message)sent_msgs.get(k);
//                          sz += m.size();
//                        }
//                        stack.gemfireStats.incJgSentMessagesSize(-sz);
//                      }
                      stable_keys.clear(); // this will modify sent_msgs directly
                      sent_msgs.notifyAll(); // GemStoneAddition
                      stack.gfPeerFunctions.setJgSTABLEsentMessagesSize(sent_msgs.size());
                    }
                }
            }

            // delete *delivered* msgs that are stable
            // recv_win=(NakReceiverWindow)received_msgs.get(sender);
            if(recv_win != null) {
                recv_win.stable(high_seqno_delivered);  // delete all messages with seqnos <= seqno
            }
        }
    }

    /* ---------------------- Interface Retransmitter.RetransmitCommand ---------------------- */

    public Address getDest() {
      return null;
    }

    /**
     * Implementation of Retransmitter.RetransmitCommand. Called by retransmission thread when gap is detected.
     */
    public void retransmit(long first_seqno, long last_seqno, Address sender) {
        NakAckHeader hdr;
        Message retransmit_msg;
        Address dest=sender; // to whom do we send the XMIT request ?

        if(xmit_from_random_member && !local_addr.equals(sender)) {
            Address random_member=(Address)Util.pickRandomElement(members);
            if(random_member != null && !local_addr.equals(random_member)) {
                dest=random_member;
                if(trace)
                    log.trace("picked random member " + dest + " to send XMIT request to");
            }
        }

        hdr=new NakAckHeader(NakAckHeader.XMIT_REQ, first_seqno, last_seqno, sender);
        retransmit_msg=new Message(dest, null, null);
        if(trace)
            log.trace(local_addr + ": sending XMIT_REQ ([" + first_seqno + ", " + last_seqno + "]) to " + dest);
        retransmit_msg.putHeader(name, hdr);
        passDown(new Event(Event.MSG, retransmit_msg));
        stack.gfPeerFunctions.incMcastRetransmitRequests();
        if(stats) {
            xmit_reqs_sent+=last_seqno - first_seqno +1;
            updateStats(sent, dest, 1, 0, 0);
            for(long i=first_seqno; i <= last_seqno; i++) {
                XmitRequest req=new XmitRequest(sender, i, dest);
                send_history.add(req);
            }
        }
    }
    /* ------------------- End of Interface Retransmitter.RetransmitCommand -------------------- */

    /* ----------------------- Interface NakReceiverWindow.Listener ---------------------- */
    public void missingMessageReceived(long mseqno, Message msg) {
        if(stats) {
            missing_msgs_received++;
            updateStats(received, msg.getSrc(), 0, 0, 1);
            MissingMessage missing=new MissingMessage(msg.getSrc(), mseqno);
            receive_history.add(missing);
        }
    }
    /* ------------------- End of Interface NakReceiverWindow.Listener ------------------- */

    private void clear() {
        NakReceiverWindow win;

        // changed April 21 2004 (bela): SourceForge bug# 938584. We cannot delete our own messages sent between
        // a join() and a getState(). Otherwise retransmission requests from members who missed those msgs might
        // fail. Not to worry though: those msgs will be cleared by STABLE (message garbage collection)

        // sent_msgs.clear();

        synchronized(received_msgs) {
            for(Iterator it=received_msgs.values().iterator(); it.hasNext();) {
                win=(NakReceiverWindow)it.next();
                win.reset();
            }
             received_msgs.clear();
             received_msgs_size = 0;
             stack.gfPeerFunctions.setJgSTABLEreceivedMessagesSize(received_msgs_size);
        }
    }

    private void removeAll() {
        NakReceiverWindow win;

        synchronized(sent_msgs) {
          sent_msgs.clear();
            stack.gfPeerFunctions.setJgSTABLEsentMessagesSize(sent_msgs.size());
        }

        synchronized(received_msgs) {
            for(Iterator it=received_msgs.values().iterator(); it.hasNext();) {
                win=(NakReceiverWindow)it.next();
                win.destroy();
            }
            received_msgs.clear();
            received_msgs_size = 0;
            stack.gfPeerFunctions.setJgSTABLEreceivedMessagesSize(received_msgs_size);
        }
    }

   public String printMessages() {
        StringBuffer ret=new StringBuffer();
        Map.Entry entry;
        Address addr;
        Object w;

       ret.append("\nsent_msgs: ").append(printSentMsgs());
        ret.append("\nreceived_msgs:\n");
        synchronized(received_msgs) {
            for(Iterator it=received_msgs.entrySet().iterator(); it.hasNext();) {
                entry=(Map.Entry)it.next();
                addr=(Address)entry.getKey();
                w=entry.getValue();
                ret.append(addr).append(": ").append(w.toString()).append('\n');
            }
        }
        return ret.toString();
    }

    public String printSentMsgs() {
        StringBuffer sb=new StringBuffer();
        Long min_seqno, max_seqno;
        synchronized(sent_msgs) {
            min_seqno=sent_msgs.size() > 0 ? (Long)sent_msgs.firstKey() : Long.valueOf(0);
            max_seqno=sent_msgs.size() > 0 ? (Long)sent_msgs.lastKey() : Long.valueOf(0);
        }
        sb.append('[').append(min_seqno).append(" - ").append(max_seqno).append("] (" + sent_msgs.size() + ")");
        return sb.toString();
    }

    private void handleConfigEvent(HashMap map) {
        if(map == null) {
            return;
        }
        if(map.containsKey("frag_size")) {
            max_xmit_size=((Integer)map.get("frag_size")).intValue();
            if(log.isInfoEnabled()) {
                log.info(ExternalStrings.NAKACK_MAX_XMIT_SIZE_0, max_xmit_size);
            }
        }
    }

    static class Entry  {
        long xmit_reqs, xmit_rsps, missing_msgs_rcvd;

        @Override // GemStoneAddition
        public String toString() {
            StringBuffer sb=new StringBuffer();
            sb.append(xmit_reqs).append(" xmit_reqs").append(", ").append(xmit_rsps).append(" xmit_rsps");
            sb.append(", ").append(missing_msgs_rcvd).append(" missing msgs");
            return sb.toString();
        }
    }

    static class XmitRequest  {
        Address original_sender; // original sender of message
        long    seq, timestamp=System.currentTimeMillis();
        Address xmit_dest;       // destination to which XMIT_REQ is sent, usually the original sender

        XmitRequest(Address original_sender, long seqno, Address xmit_dest) {
            this.original_sender=original_sender;
            this.xmit_dest=xmit_dest;
            this.seq=seqno;
        }

        @Override // GemStoneAddition
        public String toString() {
            StringBuffer sb=new StringBuffer();
            sb.append(new Date(timestamp)).append(": ").append(original_sender).append(" #").append(seq);
            sb.append(" (XMIT_REQ sent to ").append(xmit_dest).append(")");
            return sb.toString();
        }
    }

    static class MissingMessage  {
        Address original_sender;
        long    seq, timestamp=System.currentTimeMillis();

        MissingMessage(Address original_sender, long seqno) {
            this.original_sender=original_sender;
            this.seq=seqno;
        }

        @Override // GemStoneAddition
        public String toString() {
            StringBuffer sb=new StringBuffer();
            sb.append(new Date(timestamp)).append(": ").append(original_sender).append(" #").append(seq);
            return sb.toString();
        }
    }

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

}


text:
package com.twosigma.beakerx.groovy.autocomplete;

import com.twosigma.beakerx.autocomplete.AutocompleteCandidate;
import com.twosigma.beakerx.autocomplete.AutocompleteRegistry;
import com.twosigma.beakerx.autocomplete.ClassUtils;
import com.twosigma.beakerx.groovy.autocomplete.GroovyParser.ClassNameExpressionContext;
import com.twosigma.beakerx.groovy.autocomplete.GroovyParser.CompilationUnitContext;
import com.twosigma.beakerx.groovy.autocomplete.GroovyParser.PathExpressionContext;
import org.antlr.v4.runtime.ParserRuleContext;
import com.twosigma.beakerx.groovy.autocomplete.GroovyParser.StatementContext;

import static com.twosigma.beakerx.autocomplete.AutocompleteCandidate.EMPTY_NODE;

public class GroovyNodeCompletion extends GroovyAbstractListener {
  private AutocompleteRegistry registry;
  private int cursor;
  @SuppressWarnings("unused")
  private String text;
  private ClassUtils classUtils;

  public GroovyNodeCompletion(String t, int c, AutocompleteRegistry r, ClassUtils cu) {
    cursor = c;
    text = t;
    registry = r;
    classUtils = cu;
  }

  @Override
  public void exitClassNameExpression(ClassNameExpressionContext ctx) {
    if (ctx.getStart().getStartIndex() < cursor && ctx.getStop().getStopIndex() + 1 >= cursor) {
      if (ctx.getText().contains(".")) {
        addQuery(classUtils.expandExpression(ctx.getText(), registry, classUtils.DO_STATIC), AutocompleteGroovyResult.getStartIndex(ctx));
        // complete with standard groovy extension functions
        AutocompleteCandidate c = new AutocompleteCandidate(GroovyCompletionTypes.STDFUNCS, ctx.getText().substring(ctx.getText().lastIndexOf(".") + 1));
        addQuery(c, AutocompleteGroovyResult.getStartIndex(ctx));
        completeClassFromPath(ctx);
      } else {
        AutocompleteCandidate c = new AutocompleteCandidate(GroovyCompletionTypes.NAME, ctx.getText());
        addQuery(c, AutocompleteGroovyResult.getStartIndex(ctx));
        c = new AutocompleteCandidate(GroovyCompletionTypes.CUSTOM_TYPE, ctx.getText());
        addQuery(c, AutocompleteGroovyResult.getStartIndex(ctx));
      }
    }
  }

  private void completeClassFromPath(ClassNameExpressionContext ctx) {
    String text = ctx.getText();
    text = text.substring(text.lastIndexOf(".") + 1, text.length());
    AutocompleteCandidate cName = new AutocompleteCandidate(GroovyCompletionTypes.FQ_TYPE, text);
    addQuery(cName, AutocompleteGroovyResult.getStartIndex(ctx));
  }

  @Override
  public void exitPathExpression(PathExpressionContext ctx) {
    if (ctx.getStart().getStartIndex() < cursor && ctx.getStop().getStopIndex() + 1 >= cursor) {
      // is this the start of a compilation unit?
      // it might be package
//      if (ctx.getParent().getParent() != null && ctx.getParent().getParent() instanceof StatementContext && ctx.getParent().getParent().getChildCount() == 1 &&
//              ctx.getParent().getParent().getParent() != null && ctx.getParent().getParent().getParent() instanceof CompilationUnitContext &&
//              ctx.getParent().getParent().getParent().getChild(0).equals(ctx.getParent().getParent())) {
//        AutocompleteCandidate c = new AutocompleteCandidate(GroovyCompletionTypes.INITIAL, ctx.getText());
//        addQuery(c, AutocompleteGroovyResult.getStartIndex(ctx));
//      }
//
      // is this leftmost part of an statement?
      // it might be import
      if (ctx.getParent().getChild(0).equals(ctx) &&
              ctx.getParent().getParent() != null && ctx.getParent().getParent() instanceof StatementContext &&
              ctx.getParent().getParent().getChild(0).equals(ctx.getParent())) {
        AutocompleteCandidate c = new AutocompleteCandidate(GroovyCompletionTypes.TOPLEVEL, ctx.getText());
        addQuery(c, AutocompleteGroovyResult.getStartIndex(ctx));
      }

      // check out for implements and/or extends
//      CommandExpressionStatementContext st = (CommandExpressionStatementContext) findParentNode(ctx, CommandExpressionStatementContext.class);
//      if (st != null && st.getParent() != null) {
//        ParseTree left = findLeftSibling(st);
//        if (left != null && left.getText().trim().equals("class")) {
//          AutocompleteCandidate c = new AutocompleteCandidate(GroovyCompletionTypes.CLASSLEVEL, ctx.getText());
//          addQuery(c, AutocompleteGroovyResult.getStartIndex(ctx));
//        }
//      }

//        // try to expand this as a path expression
      if (ctx.getText().contains(".")) {
        addQuery(classUtils.expandExpression(ctx.getText(), registry, classUtils.DO_NON_STATIC), AutocompleteGroovyResult.getStartIndex(ctx));
        // complete with standard groovy extension functions
        AutocompleteCandidate c = new AutocompleteCandidate(GroovyCompletionTypes.STDFUNCS, ctx.getText().substring(ctx.getText().lastIndexOf(".") + 1));
        addQuery(c, AutocompleteGroovyResult.getStartIndex(ctx));
        addQuery(classUtils.expandExpression(ctx.getText(), registry, classUtils.DO_ALL), AutocompleteGroovyResult.getStartIndex(ctx));
      } else {
        AutocompleteCandidate c = new AutocompleteCandidate(GroovyCompletionTypes.NAME, ctx.getText());
        addQuery(c, AutocompleteGroovyResult.getStartIndex(ctx));
        c = new AutocompleteCandidate(GroovyCompletionTypes.CUSTOM_TYPE, ctx.getText());
        addQuery(c, AutocompleteGroovyResult.getStartIndex(ctx));
      }
    }
  }

//  @Override
//  public void visitErrorNode(ErrorNode arg0) {
//    if (arg0.getSymbol().getStartIndex() < cursor && arg0.getSymbol().getStopIndex() + 1 >= cursor) {
//      //System.out.println("ERR: "+arg0.getSymbol().getStartIndex()+" "+arg0.getSymbol().getStopIndex()+" "+arg0.getSymbol().getText());
//      ParseTree cuc = arg0.getParent();
//      if (cuc.getChild(0).equals(arg0)) {
//        AutocompleteCandidate c = new AutocompleteCandidate(GroovyCompletionTypes.INITIAL, arg0.getText());
//        addQuery(c, 0);
//      } else {
//        AutocompleteCandidate c = new AutocompleteCandidate(GroovyCompletionTypes.TOPLEVEL, arg0.getText());
//        addQuery(c, 0);
//      }
//      if (cuc instanceof GroovyParser.StatementContext && ((GroovyParser.StatementContext) cuc).getStop().getStopIndex() + 1 == cursor) {
//        if (cuc.getText().contains(".")) {
//          addQuery(classUtils.expandExpression(cuc.getText(), registry, classUtils.DO_ALL), cursor);
//          // complete with standard groovy extension functions
//          AutocompleteCandidate c = new AutocompleteCandidate(GroovyCompletionTypes.STDFUNCS, cuc.getText().substring(cuc.getText().lastIndexOf(".") + 1));
//          addQuery(c, cursor);
//        } else {
//          AutocompleteCandidate c = new AutocompleteCandidate(GroovyCompletionTypes.NAME, cuc.getText());
//          addQuery(c, 0);
//          c = new AutocompleteCandidate(GroovyCompletionTypes.CUSTOM_TYPE, cuc.getText());
//          addQuery(c, 0);
//        }
//      }
//    }
//  }
//
//
//  private RuleContext findParentNode(RuleContext ctx, Class<?> classtype) {
//    RuleContext p = ctx.getParent();
//    while (p != null) {
//      if (p.getClass().equals(classtype)) {
//        return p;
//      }
//      p = p.getParent();
//    }
//    return null;
//  }
//
//  private ParseTree findLeftSibling(RuleContext ctx) {
//    RuleContext p = ctx.getParent();
//    if (p != null) {
//      for (int i = 0; i < p.getChildCount(); i++) {
//        if (p.getChild(i).equals(ctx)) {
//          if (i > 0)
//            return p.getChild(i - 1);
//          break;
//        }
//      }
//    }
//    return null;
//  }

  @Override
  public void exitCompilationUnit(CompilationUnitContext ctx) {
    if (ctx.getStop() != null && ctx.getStop().getStopIndex() + 1 == cursor) {
//      if (ctx.getChildCount() > 0) {
//        String t = ctx.getChild(ctx.getChildCount() - 2).getText();
//        if (t.endsWith("\n")) {
//          String txt = ctx.getChild(ctx.getChildCount() - 1).getText();
//          if (txt.contains(".")) {
//            addQuery(classUtils.expandExpression(txt, registry, classUtils.DO_ALL), AutocompleteGroovyResult.getStartIndex(ctx));
//          } else {
//            AutocompleteCandidate c = new AutocompleteCandidate(GroovyCompletionTypes.TOPLEVEL, txt);
//            addQuery(c, AutocompleteGroovyResult.getStartIndex(ctx));
//            c = new AutocompleteCandidate(GroovyCompletionTypes.NAME, txt);
//            addQuery(c, AutocompleteGroovyResult.getStartIndex(ctx));
//          }
//        } else {
//          for (int i = ctx.getChildCount() - 1; i >= 0; i--) {
//            if (!ctx.getChild(i).getText().isEmpty() && !ctx.getChild(i).getText().equals("<EOF>")) {
//              String txt = ctx.getChild(i).getText();
//              if (txt.contains(".")) {
//                addQuery(classUtils.expandExpression(txt, registry, classUtils.DO_ALL), AutocompleteGroovyResult.getStartIndex(ctx));
//              } else {
//                AutocompleteCandidate c = new AutocompleteCandidate(GroovyCompletionTypes.NAME, txt);
//                addQuery(c, AutocompleteGroovyResult.getStartIndex(ctx));
//              }
//              break;
//            }
//          }
//        }
//      }
      String t = removeEOF(ctx.getText());
      addQuery(classUtils.expandExpression(t, registry, classUtils.DO_ALL), calculateStartIndex(t, ctx));
      String[] txtv = splitByDot(t);
      AutocompleteCandidate c2 = new AutocompleteCandidate(GroovyCompletionTypes.FQ_TYPE, txtv);
      addQuery(c2, calculateStartIndex(t, ctx));
    }
  }

  private String removeEOF(String text) {
    return text.substring(0, text.length() - 5);
  }

  private String[] splitByDot(String t) {
    String[] txtv;
    if (t.endsWith(".")) {
      txtv = (t + "X").split("\\.");
      txtv[txtv.length - 1] = EMPTY_NODE;
    } else {
      txtv = (t).split("\\.");
    }
    return txtv;
  }

  private int calculateStartIndex(String t, ParserRuleContext ctx) {
    return AutocompleteGroovyResult.getStartIndex(ctx) + (t.endsWith(".") ? 1 : 0);
  }
}

redpajama_set_name: RedPajamaGithub


text:
visible_cards_in_hands = []
visible_cards_in_discard_deck = []
top_cards_on_board = []
player_hand_knowledge = []

# Start
# Figure out who's turn it is
q = 'MATCH (t:Token {name: "Player_Turn" }), (p:Player) WHERE (t)-[:IS]->(p) RETURN p.name'
results = db.query(q, returns=(str))
active_player_name = results[0][0]

################ LOOK AT ALL POSSIBLE ATTAINABLE INFORMATION ################
# He looks at all the player's cards
q = 'MATCH (p:Player {name: "' + active_player_name +'" }), (c:Card) WHERE (p)-[:CAN_SEE]->(c) RETURN c.value, c.position, c.color, p.name'
results = db.query(q, returns=(int, int, str, str))
for r in results:
    visible_cards_in_hands.extend([r[0],r[1],r[2],r[3]])

# He looks at what's been discarded
q = 'MATCH (d:Deck {name: "Discard_Deck" }), (c:Card) WHERE (d)-[:CONTAINS]->(c) RETURN c.value, c.color'
results = db.query(q, returns=(str))
for r in results:
    visible_cards_in_discard_deck.extend([r[0],r[1]])

# He looks at the game board
q = 'MATCH (c:Card {c.column_order: "Top") RETURN c.value, c.color'
results = db.query(q, returns=(str))
for r in results:
    top_cards_on_board.extend([r[0],r[1]])

# He thinks about the cards he has
q = 'MATCH (p:Player {name: "' + active_player_name +'" }), (ci:Card) WHERE (p)-[:KNOWS]->(ci) RETURN ci.value, ci.color, ci.name'
results = db.query(q, returns=(int, int, str, str))
for r in results:
    player_hand_knowledge.extend([r[0],r[1],r[2]])

# See how many Info Tokens are available
q = 'MATCH (t:Token {type: "Info", }) WHERE t.used == False RETURN t.name'
results = db.query(q, returns=(str))
active_player_name = results[0][0]

################ THE PLAYER THINKS !!!!! ################

# Actions: 1) Gives information, 2) plays card

weight_give_info = 0
weight_play_card = 0

################ ASSESS THE NEED OF ALL THE OTHER CARDS TO BE PLAYED !!!!! ################

if strategy_look_at_card_hand_order == 1:
    # Check the thing out ...
    #
    #
    #

# He crunches through a couple of agreed to philosophies
# {Expanse of effort}
# He decides to throw a card away, put a card down, or to give advice
# He organizes his/her own card order if he/she picked up a card

# Next player Go & Repeat

# Game is done when either 1) there is one more round after draw deck = 0, 2) all the columns are done, or 3) there are 3 failures to put down.

# Strategies:
# Only observe X players
# Information means playable or don't play
# Complete knowledge first, before playing
# A player never gives info for a dead color
# A player can give info for a dead color if he should not throw out the next couple of cards.


text:
package main

import (
	"flag"
	"koding/db/models"
	helper "koding/db/mongodb/modelhelper"
	"koding/helpers"
	"koding/tools/config"
	"koding/tools/logger"
	"time"

	"gopkg.in/mgo.v2/bson"
)

var log = logger.New("Obsolete deleter")

var (
	conf          *config.Config
	flagDebug     = flag.Bool("d", false, "Debug mode")
	flagProfile   = flag.String("c", "vagrant", "Configuration profile from file")
	flagDirection = flag.String("direction", "targetName", "direction name ")
	flagSkip      = flag.Int("s", 0, "Configuration profile from file")
	flagLimit     = flag.Int("l", 1000, "Configuration profile from file")
)

func initialize() {
	flag.Parse()
	log.SetLevel(logger.INFO)
	if *flagProfile == "" {
		log.Fatalf("Please specify profile via -c. Aborting.")
	}

	conf = config.MustConfig(*flagProfile)
	helper.Initialize(conf.Mongo)
}

func main() {
	// init the package
	initialize()
	log.Info("Obsolete Deleter worker started")

	iterOptions := helpers.NewIterOptions()
	iterOptions.CollectionName = "relationships"
	iterOptions.F = deleteRel
	iterOptions.Filter = createFilter(*flagDirection)
	iterOptions.Result = &models.Relationship{}
	iterOptions.Limit = *flagLimit
	iterOptions.Skip = *flagSkip
	iterOptions.Log = log

	log.SetLevel(logger.INFO)
	if *flagDebug {
		log.SetLevel(logger.DEBUG)
	}

	err := helpers.Iter(helper.Mongo, iterOptions)
	if err != nil {
		log.Fatalf("Error while iter: %v", err)
	}
	log.Info("Obsolete Deleter worker finished")

}

func createFilter(directionName string) helper.Selector {
	oneMonthAgo := time.Now().Add(-time.Minute * 60 * 24 * 30).UTC()
	return helper.Selector{
		directionName: helper.Selector{"$in": ToBeDeletedNames},
		"timestamp":   helper.Selector{"$lte": oneMonthAgo},
	}

}

func deleteRel(rel interface{}) error {
	result := rel.(*models.Relationship)

	var collectionName string
	var collectionId bson.ObjectId
	directionName := *flagDirection

	if directionName == "sourceName" {
		if result.SourceName == "" {
			log.Info("source name is not valid %v", result.SourceName)
			return nil
		}
		collectionName = helper.GetCollectionName(result.SourceName)
		collectionId = result.SourceId

	}
	if directionName == "targetName" {
		if result.TargetName == "" {
			log.Info("target name is not valid %v", result.TargetName)
			return nil
		}
		collectionName = helper.GetCollectionName(result.TargetName)
		collectionId = result.TargetId
	}

	if !collectionId.Valid() {
		log.Info("collectionId name is not valid %v", collectionId)
		return nil
	}
	log.Info("removing collectionId: %v from collectionName: %v ", collectionId.Hex(), collectionName)

	if err := helper.RemoveDocument(collectionName, collectionId); err != nil {
		log.Error("couldnt remove collectionId: %v from collectionName: %v  ", collectionId.Hex(), collectionName)
	}

	if !result.Id.Valid() {
		log.Info("relationship id is not valid %v", collectionId)
		return nil
	}

	if err := helper.DeleteRelationship(result.Id); err != nil {
		log.Error("couldnt remove collectionId: %v from relationships  ", result.Id.Hex())
	}
	return nil

}

var ToBeDeletedNames = []string{
	"JInvitation",
	"JPasswordRecovery",
}


text:
namespace Owin.Scim.Services
{
    using System;
    using System.Collections.Generic;
    using System.Net;
    using System.Threading.Tasks;

    using Configuration;

    using Model;

    public abstract class SchemaService : ServiceBase, ISchemaService
    {
        private readonly Lazy<IReadOnlyDictionary<string, ScimSchema>> _Schemas;

        /// <summary>
        /// Initializes a new instance of the <see cref="SchemaService" /> class.
        /// </summary>
        /// <param name="serverConfiguration">The configuration.</param>
        /// <param name="versionProvider">The version provider.</param>
        protected SchemaService(ScimServerConfiguration serverConfiguration, IResourceVersionProvider versionProvider)
            : base(serverConfiguration, versionProvider)
        {
            _Schemas = new Lazy<IReadOnlyDictionary<string, ScimSchema>>(CreateSchemas);
        }

        /// <summary>
        /// Gets the schemas.
        /// </summary>
        /// <value>The schemas.</value>
        protected virtual IReadOnlyDictionary<string, ScimSchema> Schemas
        {
            get { return _Schemas.Value; }
        }

        /// <summary>
        /// Gets the <see cref="ScimSchema" /> associated with the specified <paramref name="schemaId" />.
        /// </summary>
        /// <param name="schemaId">The schema identifier.</param>
        /// <returns>IScimResponse<ScimSchema>.</returns>
        public virtual Task<IScimResponse<ScimSchema>> GetSchema(string schemaId)
        {
            ScimSchema schema;
            if (!Schemas.TryGetValue(schemaId, out schema))
                return Task.FromResult<IScimResponse<ScimSchema>>(
                    new ScimErrorResponse<ScimSchema>(
                        new ScimError(HttpStatusCode.NotFound)));

            return Task.FromResult<IScimResponse<ScimSchema>>(
                new ScimDataResponse<ScimSchema>(schema));
        }

        /// <summary>
        /// Gets all defined <see cref="ScimSchema" />s.
        /// </summary>
        /// <returns>IScimResponse<IEnumerable<ScimSchema>>.</returns>
        public virtual Task<IScimResponse<IEnumerable<ScimSchema>>> GetSchemas()
        {
            return Task.FromResult<IScimResponse<IEnumerable<ScimSchema>>>(
                new ScimDataResponse<IEnumerable<ScimSchema>>(Schemas.Values));
        }

        /// <summary>
        /// Creates the schemas dictionary.
        /// </summary>
        /// <returns>IReadOnlyDictionary<System.String, ScimSchema>.</returns>
        protected abstract IReadOnlyDictionary<string, ScimSchema> CreateSchemas();
    }
}


text:
import { debounce } from 'lodash/function'
import $ from 'jquery'
import { ButtonBox } from '../html/ButtonBox'
import { Element } from './Element'

export class GroupElement extends Element {
  constructor (layerSelector, config, map) {
    super(layerSelector, config, map)
    this.children_ = []
    this.makeExclusive_ = debounce(child => {
      for (const other of this.children_) {
        if (other !== child) {
          other.setActive(false)
        }
      }
    })
    this.deactivatable_ = config.deactivatable !== false
    this.buildGroup()
  }

  detach () {
    super.detach()
    for (const child of this.children_) {
      child.detach()
    }
  }

  addChild (child) {
    this.children_.push(child)
    child.on('update', () => {
      this.debouncedUpdate()
      if (this.config.hasOwnProperty('items') && this.config.items === 'exclusive' && child.getActive()) {
        this.makeExclusive_(child)
      }
    })
  }

  buildGroup () {
    if (this.config.hasOwnProperty('groupButton') && this.config.groupButton === 'noButton') {
      this.$element_ = $()
      for (const childConfig of this.config.buttons) {
        childConfig.deactivatable = this.deactivatable_
        const child = this.layerSelector.getElement(childConfig)
        this.addChild(child)
        this.$element_ = this.$element_.add(child.get$Element())
      }
    } else {
      this.activateChildren_ = this.config.hasOwnProperty('groupButton') && this.config.groupButton === 'activate'
      // TODO: where is categoryLayer.get('collapsed') used (or 'change:collapsed')

      const localiser = this.map.get('localiser')

      this.menu_ = new ButtonBox({
        className: 'g4u-layerselector-menu',
        title: localiser.selectL10N(this.config.title),
        rtl: localiser.isRtl(),
        titleButton: this.activateChildren_,
        collapsed: this.config.collapsed !== false,
        collapsible: this.config.collapsible !== false,
        addClass: this.config.addClass
      })

      this.$element_ = this.menu_.get$Element()

      this.listenAt(this.menu_).on('change:collapsed', () => {
        this.layerSelector.dispatchEvent('change:size')
      })

      if (this.activateChildren_) {
        this.listenAt(this.menu_).on('title:click', () => {
          this.setActive(!this.getAllActive())
        })
      }

      // TODO: menu set disabled ?

      for (const childConfig of this.config.buttons) {
        const child = this.layerSelector.getElement(childConfig)
        this.addChild(child)
        this.menu_.get$Body().append(child.get$Element())
      }

      this.debouncedUpdate()
    }
  }

  update () {
    if (this.menu_) {
      this.menu_.setCollapseButtonActive(this.getActive())
      if (this.activateChildren_) {
        this.menu_.setTitleButtonActive(this.getAllActive())
      }
    }
    super.update()
  }

  updateDisabled (zoom) {
    super.updateDisabled(zoom)
    for (const child of this.children_) {
      child.updateDisabled(zoom)
    }
    if (!this.deactivatable_ && !this.getActive()) {
      for (const child of this.children_) {
        if (!child.getDisabled()) {
          child.setActive(true)
          return
        }
      }
    }
  }

  getActive () {
    return this.children_.some(c => c.getActive())
  }

  getAllActive () {
    return this.children_.every(c => c.getActive())
  }

  getLoading () {
    return this.children_.some(c => c.getLoading())
  }

  setActive (active) {
    if (this.activateChildren_) {
      for (const child of this.children_) {
        child.setActive(active)
      }
    }
  }

  count () {
    return this.children_.reduce((p, n) => p + n.count(), 0)
  }
}


text:
Polyline Encoder is a simple PHP class that will help you work with polyline encoding / decoding.
For more details about the polyline encoder algorithm, please refer to : https://developers.google.com/maps/documentation/utilities/polylinealgorithm

## License

This project is under MIT and GPLv2 Licenses.

## Usage

Just insert the following line at the top of your PHP file :

```php
#require_once 'PolylineEncoder.php';
```

### Encode a polyline

To encode a polyline just follow those steps :

```php
$polylineEncoder = new PolylineEncoder();

$polylineEncoder->addPoint(30.5,-120.2);
$polylineEncoder->addPoint(40.7,-120.95);
$polylineEncoder->addPoint(43.252,-126.453);

$encodedString = $polylineEncoder->encodedString();
```

### Decode a polyline

```php
$decodedPoints = PolylineEncoder::decodeValue("_p~iF~ps|U_ulLnnqC_mqNvxq`@");
```

### Note

Please note that the encoding levels are currently not supported.


text: few bash scripts for keeping my server running and up to date