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

org.jgroups.util.MessageBatch Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

There is a newer version: 33.0.2.Final
Show newest version
package org.jgroups.util;

import org.jgroups.Address;
import org.jgroups.Message;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;

/**
 * Represents a message batch; multiple messages from the same sender to the same receiver(s). This class is unsynchronized.
 * @author Bela Ban
 * @since  3.3
 */
public class MessageBatch implements Iterable {

    /** The destination address. Null if this is a multicast message batch, non-null if the batch is sent to a specific member */
    protected Address          dest;

    /** The sender of the message batch */
    protected Address          sender;

    /** The name of the cluster in which the message batch is sent, this is equivalent to TpHeader.cluster_name */
    protected AsciiString      cluster_name;

    /** The storage of the messages; removed messages have a null element */
    protected Message[]        messages;

    /** Index of the next message to be inserted */
    protected int              index;

    /** Whether all messages have dest == null (multicast) or not */
    protected boolean          multicast;

    /** Whether this message batch contains only OOB messages, or only regular messages */
    protected Mode             mode=Mode.REG;

    protected static final int INCR=5; // number of elements to add when resizing


    public MessageBatch(int capacity) {
        this.messages=new Message[capacity];
    }

    public MessageBatch(Collection msgs) {
        messages=new Message[msgs.size()];
        for(Message msg: msgs)
            messages[index++]=msg;
        mode=determineMode();
    }

    public MessageBatch(Address dest, Address sender, AsciiString cluster_name, boolean multicast, Collection msgs) {
        this(dest, sender, cluster_name, multicast, msgs, null);
    }

    public MessageBatch(Address dest, Address sender, AsciiString cluster_name, boolean multicast,
                        Collection msgs, Filter filter) {
        messages=new Message[msgs.size()];
        for(Message msg: msgs) {
            if(filter != null && !filter.accept(msg))
                continue;
            messages[index++]=msg;
        }
        this.dest=dest;
        this.sender=sender;
        this.cluster_name=cluster_name;
        this.multicast=multicast;
        this.mode=determineMode();
    }

    public MessageBatch(Address dest, Address sender, AsciiString cluster_name, boolean multicast, Mode mode, int capacity) {
        this(capacity);
        this.dest=dest;
        this.sender=sender;
        this.cluster_name=cluster_name;
        this.multicast=multicast;
        this.mode=mode;
    }

    public Address      dest()                        {return dest;}
    public MessageBatch dest(Address dest)            {this.dest=dest; return this;}
    public Address      sender()                      {return sender;}
    public MessageBatch sender(Address sender)        {this.sender=sender; return this;}
    public AsciiString  clusterName()                 {return cluster_name;}
    public MessageBatch clusterName(AsciiString name) {this.cluster_name=name; return this;}
    public boolean      multicast()                   {return multicast;}
    public Mode         mode()                        {return mode;}
    public MessageBatch mode(Mode mode)               {this.mode=mode; return this;}
    public int          capacity()                    {return messages.length;}


    /** Returns the underlying message array. This is only intended for testing ! */
    public Message[]    array() {
        return messages;
    }

    public Message first() {
        for(int i=0; i < index; i++)
            if(messages[i] != null)
                return messages[i];
        return null;
    }

    public Message last() {
        for(int i=index -1; i >= 0; i--)
            if(messages[i] != null)
                return messages[i];
        return null;
    }

    public MessageBatch add(final Message msg) {
        if(msg == null) return this;
        if(index >= messages.length)
            resize();
        messages[index++]=msg;
        return this;
    }

    /**
     * Replaces a message in the batch with another one
     * @param existing_msg The message to be replaced. The message has to be non-null and is found by identity (==)
     *                     comparison
     * @param new_msg The message to replace the existing message with, can be null
     * @return
     */
    public MessageBatch replace(Message existing_msg, Message new_msg) {
        if(existing_msg == null)
            return this;
        for(int i=0; i < index; i++) {
            if(messages[i] != null && messages[i] == existing_msg) {
                messages[i]=new_msg;
                break;
            }
        }
        return this;
    }

    /**
     * Replaces all messages which match a given filter with a replacement message
     * @param filter the filter. If null, no changes take place. Note that filter needs to be able to handle null msgs
     * @param replacement the replacement message. Can be null, which essentially removes all messages matching filter
     * @param match_all whether to replace the first or all matches
     * @return the MessageBatch
     */
    public MessageBatch replace(Filter filter, Message replacement, boolean match_all) {
        if(filter == null)
            return this;
        for(int i=0; i < index; i++) {
            if(filter.accept(messages[i])) {
                messages[i]=replacement;
                if(!match_all)
                    break;
            }
        }
        return this;
    }

    /**
     * Removes the current message (found by indentity (==)) by nulling it in the message array
     * @param msg
     * @return
     */
    public MessageBatch remove(Message msg) {
        return replace(msg, null);
    }

    /**
     * Removes all messages which match filter
     * @param filter the filter. If null, no removal takes place
     * @return the MessageBatch
     */
    public MessageBatch remove(Filter filter) {
        return replace(filter, null, true);
    }

    public MessageBatch clear() {
        for(int i=0; i < index; i++)
            messages[i]=null;
        index=0;
        return this;
    }

    /** Removes and returns all messages which have a header with ID == id */
    public Collection getMatchingMessages(final short id, final boolean remove) {
        return map(new Visitor() {
            public Message visit(Message msg, MessageBatch batch) {
                if(msg != null && msg.getHeader(id) != null) {
                    if(remove)
                        batch.remove(msg);
                    return msg;
                }
                return null;
            }
        });
    }


    /** Applies a function to all messages and returns a list of the function results */
    public  Collection map(Visitor visitor) {
        Collection retval=null;
        for(int i=0; i < index; i++) {
            try {
                T result=visitor.visit(messages[i], this);
                if(result != null) {
                    if(retval == null)
                        retval=new ArrayList<>();
                    retval.add(result);
                }
            }
            catch(Throwable t) {
            }
        }
        return retval;
    }


    /** Returns the number of non-null messages */
    public int size() {
        int retval=0;
        for(int i=0; i < index; i++)
            if(messages[i] != null)
                retval++;
        return retval;
    }

    public boolean isEmpty() {
        for(int i=0; i < index; i++)
            if(messages[i] != null)
                return false;
        return true;
    }

    public Mode determineMode() {
        int num_oob=0, num_reg=0, num_internal=0;
        for(int i=0; i < index; i++) {
            if(messages[i] == null)
                continue;
            if(messages[i].isFlagSet(Message.Flag.OOB))
                num_oob++;
            else if(messages[i].isFlagSet(Message.Flag.INTERNAL))
                num_internal++;
            else
                num_reg++;
        }
        if(num_internal > 0 && num_oob == 0 && num_reg == 0)
            return Mode.INTERNAL;
        if(num_oob > 0 && num_internal == 0 && num_reg == 0)
            return Mode.OOB;
        if(num_reg > 0 && num_oob == 0 && num_internal == 0)
            return Mode.REG;
        return Mode.MIXED;
    }


    /** Returns the size of the message batch (by calling {@link org.jgroups.Message#size()} on all messages) */
    public long totalSize() {
        long retval=0;
        Visitor visitor=new Visitor() {
            public Long visit(Message msg, MessageBatch batch) {
                return msg != null? msg.size() : 0;
            }
        };
        for(int i=0; i < index; i++)
            retval+=visitor.visit(messages[i], this);
        return retval;
    }

    /** Returns the total number of bytes of the message batch (by calling {@link org.jgroups.Message#getLength()} on all messages) */
    public int length() {
        int retval=0;
        Visitor visitor=new Visitor() {
            public Integer visit(Message msg, MessageBatch batch) {
                return msg != null? msg.getLength() : 0;
            }
        };
        for(int i=0; i < index; i++)
            retval+=visitor.visit(messages[i], this);
        return retval;
    }


    /** Iterator which iterates only over non-null messages, skipping null messages */
    public Iterator iterator() {
        return new BatchIterator(index);
    }

    public String toString() {
        StringBuilder sb=new StringBuilder();
        sb.append("dest=" + dest);
        if(sender != null)
            sb.append(", sender=").append(sender);
        sb.append(", mode=" + mode);
        if(cluster_name != null)
            sb.append(", cluster=").append(cluster_name);
        if(sb.length() > 0)
            sb.append(", ");
        sb.append(size() + " messages [capacity=" + messages.length + "]");

        return sb.toString();
    }

    public String printHeaders() {
        StringBuilder sb=new StringBuilder().append("dest=" + dest);
        if(sender != null)
            sb.append(", sender=").append(sender);
        sb.append("\n").append(size()).append(":\n");
        int count=1;
        for(Message msg: this)
            sb.append("#").append(count++).append(": ").append(msg.printHeaders()).append("\n");
        return sb.toString();
    }

    protected void resize() {
        Message[] tmp=new Message[messages.length + INCR];
        System.arraycopy(messages,0,tmp,0,messages.length);
        messages=tmp;
    }


    /** Used for iteration over the messages */
    public interface Visitor {
        /**
         * Called when iterating over the message batch
         * @param msg The message, can be null
         * @param batch
         * @return
         */
        T visit(final Message msg, final MessageBatch batch);
    }

    public enum Mode {OOB, REG, INTERNAL, MIXED}


    /** Iterates over non-null elements of a batch, skipping null elements */
    protected class BatchIterator implements Iterator {
        protected int       current_index=-1;
        protected final int saved_index; // index at creation time of the iterator

        public BatchIterator(int saved_index) {
            this.saved_index=saved_index;
        }

        public boolean hasNext() {
            // skip null elements
            while(current_index +1 < saved_index && messages[current_index+1] == null)
                current_index++;
            return current_index +1 < saved_index;
        }

        public Message next() {
            if(current_index +1 >= messages.length)
                throw new NoSuchElementException();
            return messages[++current_index];
        }

        public void remove() {
            if(current_index >= 0)
                messages[current_index]=null;
        }
    }


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy