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

com.gemstone.org.jgroups.protocols.FRAG2 Maven / Gradle / Ivy

There is a newer version: 2.0-BETA
Show newest version
/** Notice of modification as required by the LGPL
 *  This file was modified by Gemstone Systems Inc. on
 *  $Date$
 **/
// $Id: FRAG2.java,v 1.20 2005/08/11 12:43:47 belaban Exp $

package com.gemstone.org.jgroups.protocols;


import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Vector;

import com.gemstone.org.jgroups.Address;
import com.gemstone.org.jgroups.Event;
import com.gemstone.org.jgroups.Message;
import com.gemstone.org.jgroups.View;
import com.gemstone.org.jgroups.ViewId;
import com.gemstone.org.jgroups.stack.Protocol;
import com.gemstone.org.jgroups.util.ExternalStrings;
import com.gemstone.org.jgroups.util.Range;
import com.gemstone.org.jgroups.util.Util;


/**
 * Fragmentation layer. Fragments messages larger than frag_size into smaller packets.
 * Reassembles fragmented packets into bigger ones. The fragmentation number is prepended
 * to the messages as a header (and removed at the receiving side).

* Each fragment is identified by (a) the sender (part of the message to which the header is appended), * (b) the fragmentation ID (which is unique per FRAG2 layer (monotonically increasing) and (c) the * fragement ID which ranges from 0 to number_of_fragments-1.

* Requirement: lossless delivery (e.g. NAK, ACK). No requirement on ordering. Works for both unicast and * multicast messages.
* Compared to FRAG, this protocol does not need to serialize the message in order to break it into * smaller fragments: it looks only at the message's buffer, which is a byte[] array anyway. We assume that the * size addition for headers and src and dest address is minimal when the transport finally has to serialize the * message, so we add a constant (1000 bytes). * @author Bela Ban * @version $Id: FRAG2.java,v 1.20 2005/08/11 12:43:47 belaban Exp $ */ public class FRAG2 extends Protocol { public static boolean DEBUG_FRAG2 = Boolean.getBoolean("gemfire.debug-frag2"); /** The max number of bytes in a message. If a message's buffer is bigger, it will be fragmented */ int frag_size=1500; /** Number of bytes that we think the headers plus src and dest will take up when message is serialized by transport. This will be subtracted from frag_size */ int overhead=50; /*the fragmentation list contains a fragmentation table per sender *this way it becomes easier to clean up if a sender (member) leaves or crashes */ private final FragmentationList fragment_list=new FragmentationList(); private int curr_id=1; private final Vector members=new Vector(11); static String name="FRAG2"; // GemStone - increased visibility for DirAck long num_sent_msgs=0; long num_sent_frags=0; long num_received_msgs=0; long num_received_frags=0; // DirAck dirAckProtocol; /** GemStoneAddition - birth view id */ private long initialViewId; /** GemStoneAddition - current view id */ private long currentViewId; /** GemStoneAddition - messages that should be queued before getting a view id */ private List preJoinMessages = new LinkedList(); @Override // GemStoneAddition public String getName() { return name; } // start GemStoneAddition @Override // GemStoneAddition public int getProtocolEnum() { return com.gemstone.org.jgroups.stack.Protocol.enumFRAG2; } // end GemStone addition public int getFragSize() {return frag_size;} public void setFragSize(int s) {frag_size=s;} public int getOverhead() {return overhead;} public void setOverhead(int o) {overhead=o;} public long getNumberOfSentMessages() {return num_sent_msgs;} public long getNumberOfSentFragments() {return num_sent_frags;} public long getNumberOfReceivedMessages() {return num_received_msgs;} public long getNumberOfReceivedFragments() {return num_received_frags;} synchronized int getNextId() { return curr_id++; } /** Setup the Protocol instance acording to the configuration string */ @Override // GemStoneAddition public boolean setProperties(Properties props) { String str; super.setProperties(props); str=props.getProperty("frag_size"); if(str != null) { frag_size=Integer.parseInt(str); props.remove("frag_size"); } str=props.getProperty("overhead"); if(str != null) { overhead=Integer.parseInt(str); props.remove("overhead"); } int old_frag_size=frag_size; frag_size-=overhead; if(frag_size <=0) { log.error("frag_size=" + old_frag_size + ", overhead=" + overhead + ", new frag_size=" + frag_size + ": new frag_size is invalid"); return false; } if(log.isInfoEnabled()) log.info(ExternalStrings.FRAG2_FRAG_SIZE_0__OVERHEAD_1__NEW_FRAG_SIZE_2, new Object[] {old_frag_size, overhead, frag_size}); if(props.size() > 0) { log.error(ExternalStrings.FRAG2_FRAG2SETPROPERTIES_THE_FOLLOWING_PROPERTIES_ARE_NOT_RECOGNIZED__0, props); return false; } return true; } @Override // GemStoneAddition public void start() throws Exception { // dirAckProtocol = (DirAck)stack.findProtocol(DirAck.name); // GemStoneAddition } @Override // GemStoneAddition public void resetStats() { super.resetStats(); num_sent_msgs=num_sent_frags=num_received_msgs=num_received_frags=0; } /** * Fragment a packet if larger than frag_size (add a header). Otherwise just pass down. Only * add a header if framentation is needed ! */ @Override // GemStoneAddition public void down(Event evt) { switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); long size=msg.getLength(); synchronized(this) { num_sent_msgs++; } if(size > frag_size) { if(trace) { StringBuffer sb=new StringBuffer("message's buffer size is "); sb.append(size).append(", will fragment ").append("(frag_size="); sb.append(frag_size).append(')'); log.trace(sb.toString()); } gf_fragment(msg); // GemStoneAddition //fragment(msg); // Fragment and pass down recordSize(); return; } break; case Event.VIEW_CHANGE: long initId = this.initialViewId; viewChanged(evt); synchronized(this.preJoinMessages) { // GemStoneAddition - preJoinMessages if (initId == 0 && this.initialViewId != 0) { for (Iterator it=this.preJoinMessages.iterator(); it.hasNext(); /**/) { Message m = (Message)it.next(); unfragment(m); recordSize(); } this.preJoinMessages.clear(); } } break; case Event.CONFIG: passDown(evt); if(log.isDebugEnabled()) log.debug("received CONFIG event: " + evt.getArg()); handleConfigEvent((HashMap)evt.getArg()); return; } passDown(evt); // Pass on to the layer below us } /** * If event is a message, if it is fragmented, re-assemble fragments into big message and pass up * the stack. */ @Override // GemStoneAddition public void up(Event evt) { switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); Object obj=msg.getHeader(getName()); if(obj != null && obj instanceof FragHeader) { // needs to be defragmented unfragment(msg); // Unfragment and possibly pass up recordSize(); return; } else { num_received_msgs++; } break; case Event.CONFIG: passUp(evt); if(log.isInfoEnabled()) log.info(ExternalStrings.FRAG2_RECEIVED_CONFIG_EVENT__0, (Object)evt.getArg()); handleConfigEvent((HashMap)evt.getArg()); return; // GemStoneAddition - track the birth and current view ids case Event.VIEW_CHANGE: // members anymore ! long initId = this.initialViewId; viewChanged(evt); synchronized(this.preJoinMessages) { if (initId == 0 && this.initialViewId != 0) { for (Iterator it=this.preJoinMessages.iterator(); it.hasNext(); /**/) { Message m = (Message)it.next(); unfragment(m); recordSize(); } this.preJoinMessages.clear(); } } break; } passUp(evt); // Pass up to the layer above us by default } private void viewChanged(Event evt) { // GemStoneAddition - track the initial view id and the current // view id. Some fragments of early messages may be thrown away // or queued by DirAck, and FRAG2 may hold corresponding fragments, // so it needs to do the same thing. ViewId vid = ((View)evt.getArg()).getVid(); if (this.initialViewId == 0) { this.initialViewId = vid.getId(); } this.currentViewId = vid.getId(); /* gemstoneaddition - moved this code out of down() so it could be used * in up(), where view changes are actually seen */ //don't do anything if this dude is sending out the view change //we are receiving a view change, //in here we check for the View view=(View)evt.getArg(); Vector new_mbrs=view.getMembers(), left_mbrs; Address mbr; left_mbrs=Util.determineLeftMembers(members, new_mbrs); synchronized(members) { // GemStoneAddition - synchronize all access to members collection members.clear(); members.addAll(new_mbrs); } for(int i=0; i < left_mbrs.size(); i++) { mbr=(Address)left_mbrs.elementAt(i); //the new view doesn't contain the sender, he must have left, //hence we will clear all his fragmentation tables fragment_list.remove(mbr); if(trace) log.trace("removed " + mbr + " from fragmentation table"); } recordSize(); } private void recordSize() { // GemstoneAddition - memory debugging // if (DEBUG_FRAG2) { // stack.gemfireStats.setJg3(this.fragment_list.size()); // } } /** * GemStoneAddition - [obsolete] check for dirAck multiple destinations. If found, * copy the message for each dest. An assertion in DirAck will check to * make sure this has happened */ void gf_fragment(Message msg) { fragment(msg); } /** Send all fragments as separate messages (with same ID !). Example:

     Given the generated ID is 2344, number of fragments=3, message {dst,src,buf}
     would be fragmented into:

     [2344,3,0]{dst,src,buf1},
     [2344,3,1]{dst,src,buf2} and
     [2344,3,2]{dst,src,buf3}
     
*/ void fragment(Message msg) { // long start; // GemStoneAddition byte[] buffer; List fragments; Event evt; FragHeader hdr; Message frag_msg; Address dest=msg.getDest(); long id=0; // GemStoneAddition getNextId(); // used as seqnos int num_frags; StringBuffer sb; Range r; try { hdr = (FragHeader)msg.getHeader(getName()); id = getNextId(); buffer=msg.getBuffer(); fragments=Util.computeFragOffsets(buffer, frag_size); num_frags=fragments.size(); if (stack != null) { stack.gfPeerFunctions.incJgFragmentsCreated(num_frags); stack.gfPeerFunctions.incJgFragmentationsPerformed(); } //synchronized(this) { // num_sent_frags+=num_frags; //} if(trace) { sb=new StringBuffer("fragmenting packet to "); sb.append((dest != null ? dest.toString() : "")).append(" (size=").append(buffer.length); sb.append(") into ").append(num_frags).append(" fragment(s) [frag_size=").append(frag_size).append(']'); log.trace(sb.toString()); } int fsize = fragments.size(); // GemStoneAddition for(int i=0; i < fsize; i++) { r=(Range)fragments.get(i); // Copy the original msg (needed because we need to copy the headers too) frag_msg=msg.copy(false); // don't copy the buffer, only src, dest and headers frag_msg.bundleable = false; // GemStoneAddition frag_msg.setBuffer(buffer, (int)r.low, (int)r.high); // GemStoneAddition: // send the view id with the message hdr=new FragHeader(id, i, num_frags, this.currentViewId); frag_msg.putHeader(getName(), hdr); // GemStoneAddition - only the last fragment is processed in DirAck evt=new Event(Event.MSG, frag_msg); passDown(evt); } msg.putHeader(getName(), hdr); // GemStoneAddition - save the frag header for rexmits } catch(Exception e) { if(log.isErrorEnabled()) log.error(ExternalStrings.FRAG2_EXCEPTION_IS__0, e); } } /** 1. Get all the fragment buffers 2. When all are received -> Assemble them into one big buffer 3. Read headers and byte buffer from big buffer 4. Set headers and buffer in msg 5. Pass msg up the stack */ void unfragment(Message msg) { FragmentationTable frag_table; Address sender=msg.getSrc(); Message assembled_msg; FragHeader hdr=(FragHeader)msg.removeHeader(getName()); synchronized(preJoinMessages) { if (this.initialViewId == 0) { preJoinMessages.add(msg); return; } } frag_table=fragment_list.get(sender); if(frag_table == null) { frag_table=new FragmentationTable(sender); try { fragment_list.add(sender, frag_table); } catch(IllegalArgumentException x) { // the entry has already been added, probably in parallel from another thread frag_table=fragment_list.get(sender); } } num_received_frags++; assembled_msg=frag_table.add(hdr.id, hdr.frag_id, hdr.num_frags, msg); if(assembled_msg != null) { try { if(trace) log.trace("assembled_msg is " + assembled_msg); assembled_msg.setSrc(sender); // needed ? YES, because fragments have a null src !! num_received_msgs++; //this.log.getLogWriter().info("fg size = " + this.fragment_list.size()); passUp(new Event(Event.MSG, assembled_msg)); } catch(Exception e) { if(log.isErrorEnabled()) log.error(ExternalStrings.FRAG2_EXCEPTION_IS__0, e); } } } void handleConfigEvent(HashMap map) { if(map == null) return; if(map.containsKey("frag_size")) { frag_size=((Integer)map.get("frag_size")).intValue(); if(log.isDebugEnabled()) log.debug("setting frag_size=" + frag_size); } } /** * A fragmentation list keeps a list of fragmentation tables * sorted by an Address ( the sender ). * This way, if the sender disappears or leaves the group half way * sending the content, we can simply remove this members fragmentation * table and clean up the memory of the receiver. * We do not have to do the same for the sender, since the sender doesn't keep a fragmentation table */ static class FragmentationList { /* * HashMap key=Address, value=FragmentationTable, initialize the hashtable to hold all the fragmentation * tables (11 is the best growth capacity to start with) */ private final HashMap frag_tables=new HashMap(11); /** * Adds a fragmentation table for this particular sender * If this sender already has a fragmentation table, an IllegalArgumentException * will be thrown. * @param sender - the address of the sender, cannot be null * @param table - the fragmentation table of this sender, cannot be null * @exception IllegalArgumentException if an entry for this sender already exist */ public void add(Address sender, FragmentationTable table) throws IllegalArgumentException { FragmentationTable healthCheck; synchronized(frag_tables) { healthCheck=(FragmentationTable)frag_tables.get(sender); if(healthCheck == null) { frag_tables.put(sender, table); } else { throw new IllegalArgumentException("Sender <" + sender + "> already exists in the fragementation list."); } } } /** * returns a fragmentation table for this sender * returns null if the sender doesn't have a fragmentation table * @return the fragmentation table for this sender, or null if no table exist */ public FragmentationTable get(Address sender) { synchronized(frag_tables) { return (FragmentationTable)frag_tables.get(sender); } } /** * returns true if this sender already holds a * fragmentation for this sender, false otherwise * @param sender - the sender, cannot be null * @return true if this sender already has a fragmentation table */ public boolean containsSender(Address sender) { synchronized(frag_tables) { return frag_tables.containsKey(sender); } } /** * removes the fragmentation table from the list. * after this operation, the fragementation list will no longer * hold a reference to this sender's fragmentation table * @param sender - the sender who's fragmentation table you wish to remove, cannot be null * @return true if the table was removed, false if the sender doesn't have an entry */ public boolean remove(Address sender) { synchronized(frag_tables) { boolean result=containsSender(sender); frag_tables.remove(sender); return result; } } /** * returns a list of all the senders that have fragmentation tables * opened. * @return an array of all the senders in the fragmentation list */ public Address[] getSenders() { Address[] result; int index=0; synchronized(frag_tables) { result=new Address[frag_tables.size()]; for(Iterator it=frag_tables.keySet().iterator(); it.hasNext();) { result[index++]=(Address)it.next(); } } return result; } @Override // GemStoneAddition public String toString() { Map.Entry entry; StringBuffer buf=new StringBuffer("Fragmentation list contains "); synchronized(frag_tables) { buf.append(frag_tables.size()).append(" tables\n"); for(Iterator it=frag_tables.entrySet().iterator(); it.hasNext();) { entry=(Map.Entry)it.next(); buf.append(entry.getKey()).append(": " ).append(entry.getValue()).append("\n"); } } return buf.toString(); } /** GemStoneAddition - sanity check */ public int size() { int result= 0; synchronized(frag_tables) { for (Iterator it=frag_tables.values().iterator(); it.hasNext(); ) { FragmentationTable f = (FragmentationTable)it.next(); result += f.size(); } } return result; } } /** * Keeps track of the fragments that are received. * Reassembles fragements into entire messages when all fragments have been received. * The fragmentation holds a an array of byte arrays for a unique sender * The first dimension of the array is the order of the fragmentation, in case the arrive out of order */ static class FragmentationTable { private final Address sender; /* the hashtable that holds the fragmentation entries for this sender*/ private final Hashtable h=new Hashtable(11); // keys: frag_ids, vals: Entrys FragmentationTable(Address sender) { this.sender=sender; } /** * inner class represents an entry for a message * each entry holds an array of byte arrays sorted * once all the byte buffer entries have been filled * the fragmentation is considered complete. */ static class Entry { //the total number of fragment in this message int tot_frags=0; // each fragment is a byte buffer Message fragments[]=null; //the number of fragments we have received int number_of_frags_recvd=0; // the message ID long msg_id=-1; /** * Creates a new entry * @param tot_frags the number of fragments to expect for this message */ Entry(long msg_id, int tot_frags) { this.msg_id=msg_id; this.tot_frags=tot_frags; fragments=new Message[tot_frags]; for(int i=0; i < tot_frags; i++) fragments[i]=null; } /** * adds on fragmentation buffer to the message * @param frag_id the number of the fragment being added 0..(tot_num_of_frags - 1) * @param frag the byte buffer containing the data for this fragmentation, should not be null */ public void set(int frag_id, Message frag) { // don't count an already received fragment (should not happen though because the // reliable transmission protocol(s) below should weed out duplicates if(fragments[frag_id] == null) { fragments[frag_id]=frag; number_of_frags_recvd++; } } /** returns true if this fragmentation is complete * ie, all fragmentations have been received for this buffer * */ public boolean isComplete() { /*first make the simple check*/ if(number_of_frags_recvd < tot_frags) { return false; } /*then double check just in case*/ for(int i=0; i < fragments.length; i++) { if(fragments[i] == null) return false; } /*all fragmentations have been received*/ return true; } /** * Assembles all the fragments into one buffer. Takes all Messages, and combines their buffers into one * buffer. * This method does not check if the fragmentation is complete (use {@link #isComplete()} to verify * before calling this method) * @return the complete message in one buffer * */ public Message assembleMessage() { Message retval; byte[] combined_buffer, tmp; int combined_length=0, length, offset; Message fragment; int index=0; for(int i=0; i < fragments.length; i++) { fragment=fragments[i]; combined_length+=fragment.getLength(); } combined_buffer=new byte[combined_length]; for(int i=0; i < fragments.length; i++) { fragment=fragments[i]; tmp=fragment.getRawBuffer(); length=fragment.getLength(); offset=fragment.getOffset(); System.arraycopy(tmp, offset, combined_buffer, index, length); index+=length; } retval=fragments[fragments.length-1].copy(false); // GemStoneAddition - for DirAck support we copy the final fragment retval.setBuffer(combined_buffer); return retval; } /** * debug only */ @Override // GemStoneAddition public String toString() { StringBuffer ret=new StringBuffer(); ret.append("[tot_frags=" + tot_frags + ", number_of_frags_recvd=" + number_of_frags_recvd + ']'); return ret.toString(); } @Override // GemStoneAddition public int hashCode() { return super.hashCode(); } /** GemStoneAddition - debugging memory */ public int size() { int result = 0; for (int i=0; i




© 2015 - 2024 Weber Informatics LLC | Privacy Policy