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

eu.lucaventuri.fibry.MessageBag Maven / Gradle / Ivy

There is a newer version: 3.0.1
Show newest version
package eu.lucaventuri.fibry;

import eu.lucaventuri.collections.ClassifiedMap;
import eu.lucaventuri.common.SystemUtils;

import java.util.AbstractQueue;
import java.util.Iterator;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Predicate;

/**
 * Provide the functionality to read messages in order and to retrieve them by class and filtering.
 * Thread safety is achieved using the blocking queue, while receive() uses the ClassifiedMap.
 * To improve performance, a lock-less blocking queue "with nodes" should be implemented to get both the retrieval behaviors from a single object
 */
public class MessageBag extends AbstractQueue implements MessageReceiver, MiniQueue {
    private final MiniQueue queue;
    private final ClassifiedMap map = new ClassifiedMap();
    private final Function converter;

    public MessageBag(MiniQueue queue, Function converter) {
        this.queue = queue;
        this.converter = converter;
    }

    public MessageBag(MiniQueue queue) {
        this.queue = queue;
        this.converter = null;
    }

    public T readMessage() {
        if (map.isEmpty())
            return retrieveFromQueue();

        return map.removeHead();
    }

    private T retrieveFromQueue() {
        while (true) {
            try {
                return queue.take();
            } catch (InterruptedException e) {
                SystemUtils.sleep(1);
            }
        }
    }

    /**
     * Used to receive specific messages; please notice that delivery order is not guaranteed.
     * This method can be slow, so it should be used with care, on actors that process a single request and that are not supposed to receive many messages.
     * Please consider using sendMessageReturn() if appropriate.
     */
    public  E receive(Class clz, Predicate filter) {
        if (map.isEmpty())
            return receiveFromQueue(clz, filter);

        E message = receiveFromMap(clz, filter);

        return message != null ? message : receiveFromQueue(clz, filter);
    }

    /**
     * Used to receive specific messages; please notice that delivery order is not guaranteed.
     * This method can be slow, so it should be used with care, on actors that process a single request and that are not supposed to receive many messages.
     * Please consider using sendMessageReturn() if appropriate.
     */
    public  CONV receiveAndConvert(Class clz, Predicate filter) {
        if (map.isEmpty())
            return receiveFromQueueAndConvert(clz, filter);

        CONV message = receiveFromMapAndConvert(clz, filter, converter);

        return message != null ? message : receiveFromQueueAndConvert(clz, filter);
    }

    private  K receiveFromMapAndConvert(Class clz, Predicate filter, Function converter) {
        return map.scanAndChooseAndConvert(clz, filter, converter);
    }

    private  E receiveFromMap(Class clz, Predicate filter) {
        return map.scanAndChoose(clz, filter);
    }

    // FIXME: adds timeout
    private  E receiveFromQueue(Class clz, Predicate filter) {
        while (true) {
            T message = retrieveFromQueue();

            if (clz.isInstance(message)) {
                if (filter.test((E) message)) {
                    return (E) message;
                }
            }

            if (converter != null)
                map.addToTailConverted(message, converter.apply(message).getClass());
            else
                map.addToTail(message);
        }
    }

    private  CONV receiveFromQueueAndConvert(Class clz, Predicate filter) {
        while (true) {
            T message = retrieveFromQueue();
            CONV messageConverted = converter.apply(message);

            if (clz.isInstance(messageConverted)) {
                if (filter.test((E) messageConverted)) {
                    return (E) messageConverted;
                }
            }

            if (converter != null)
                map.addToTailConverted(message, converter.apply(message).getClass());
            else
                map.addToTail(message);
        }
    }

    @Override
    public Iterator iterator() {
        throw new UnsupportedOperationException("Iterator not available in " + this.getClass().getName());
    }

    @Override
    public T take() throws InterruptedException {
        return readMessage();
    }

    @Override
    public T poll(long timeout, TimeUnit unit) throws InterruptedException {
        if (map.isEmpty())
            return queue.poll(timeout, unit);

        return map.removeHead();
    }

    @Override
    public int size() {
        return queue.size();
    }

    @Override
    public boolean offer(T message, long timeout, TimeUnit unit) throws InterruptedException {
        return queue.offer(message, timeout, unit);
    }

    @Override
    public boolean offer(T element) {
        return queue.offer(element);
    }

    @Override
    public T poll() {
        return retrieveFromQueue(); // We want it always blocking
    }

    @Override
    public T peek() {
        if (map.isEmpty())
            return queue.peek();

        return map.peekHead();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy