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

quickfix.mina.WatermarkTracker Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * Copyright (c) quickfixengine.org  All rights reserved.
 *
 * This file is part of the QuickFIX FIX Engine
 *
 * This file may be distributed under the terms of the quickfixengine.org
 * license as defined by quickfixengine.org and appearing in the file
 * LICENSE included in the packaging of this file.
 *
 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
 * THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE.
 *
 * See http://www.quickfixengine.org/LICENSE for licensing information.
 *
 * Contact [email protected] if any conditions of this licensing
 * are not clear to you.
 ******************************************************************************/

package quickfix.mina;

import java.util.AbstractCollection;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * A blocking queue wrapper implementing watermarks-based back pressure propagation
 * from the queue sink to one or more logical sources.
 *
 * @param  payload type
 * @param  logical source key type
 *
 * @author Vladimir Lysyy (mrbald@github)
 */
public class WatermarkTracker implements QueueTracker {
    private final BlockingQueue queue;
    private final long lowerWatermark;
    private final long upperWatermark;
    private final Consumer onLowerWatermarkCrossed;
    private final Consumer onUpperWatermarkCrossed;
    private final Function classifier;
    private final Function trackerSupplier;

    class StreamTracker {
        private final S key;
        long counter = 0;
        private boolean suspended = false;

        StreamTracker(S key) {
            this.key = key;
        }

        synchronized void incoming(int n) {
            if ((counter += n) >= upperWatermark && !suspended) {
                suspended = true;
                onUpperWatermarkCrossed.accept(key);
            }
        }

        synchronized void outgoing(int n) {
            if ((counter -= n) == lowerWatermark && suspended) {
                suspended = false;
                onLowerWatermarkCrossed.accept(key);
            }
        }

        synchronized boolean isSuspended() {
            return suspended;
        }
    }

    static  WatermarkTracker newMono(
            BlockingQueue queue,
            long lowerWatermark, long upperWatermark,
            Runnable onLowerWatermarkCrossed, Runnable onUpperWatermarkCrossed) {
        return new WatermarkTracker<>(queue, lowerWatermark, upperWatermark, onLowerWatermarkCrossed, onUpperWatermarkCrossed);
    }

    static  WatermarkTracker newMulti(
            BlockingQueue queue,
            long lowerWatermark, long upperWatermark,
            Function classifier,
            Consumer onLowerWatermarkCrossed, Consumer onUpperWatermarkCrossed) {
        return new WatermarkTracker<>(queue, lowerWatermark, upperWatermark, classifier, onLowerWatermarkCrossed, onUpperWatermarkCrossed);
    }

    private WatermarkTracker(
            BlockingQueue queue,
            long lowerWatermark, long upperWatermark,
            Function classifier,
            Consumer onLowerWatermarkCrossed, Consumer onUpperWatermarkCrossed) {
        this.queue = queue;
        this.lowerWatermark = lowerWatermark;
        this.upperWatermark = upperWatermark;
        this.classifier = classifier;
        this.onLowerWatermarkCrossed = onLowerWatermarkCrossed;
        this.onUpperWatermarkCrossed = onUpperWatermarkCrossed;

        final Map trackerMap = new ConcurrentHashMap<>();

        this.trackerSupplier = key -> trackerMap.computeIfAbsent(key, StreamTracker::new);
    }

    private WatermarkTracker(
            BlockingQueue queue,
            long lowerWatermark, long upperWatermark,
            Runnable onLowerWatermarkCrossed, Runnable onUpperWatermarkCrossed) {
        this.queue = queue;
        this.lowerWatermark = lowerWatermark;
        this.upperWatermark = upperWatermark;
        this.classifier = x -> null;
        this.onLowerWatermarkCrossed = x -> onLowerWatermarkCrossed.run();
        this.onUpperWatermarkCrossed = x -> onUpperWatermarkCrossed.run();

        final StreamTracker streamTracker = new StreamTracker(null);

        this.trackerSupplier = key -> streamTracker;
    }

    @Override
    public void put(E e) throws InterruptedException {
        queue.put(e);
        trackerForPayload(e).incoming(1);
    }

    @Override
    public E poll(long timeout, TimeUnit unit) throws InterruptedException {
        final E e = queue.poll(timeout, unit);

        if (e != null) {
            trackerForPayload(e).outgoing(1);
        }

        return e;
    }

    @Override
    public int drainTo(Collection collection) {
        return queue.drainTo(new AbstractCollection() {
            @Override public Iterator iterator() { throw new UnsupportedOperationException(); }
            @Override public int size() { throw new UnsupportedOperationException(); }

            public boolean add(E e) {
                final boolean added = collection.add(e);
                if (added) {
                    trackerForPayload(e).outgoing(1);
                }
                return added;
            }

        });
    }

    public boolean isSuspended(S key) {
        return trackerForStream(key).isSuspended();
    }

    public boolean isSuspended() {
        return isSuspended(null);
    }

    StreamTracker trackerForPayload(E e) {
        return trackerForStream(classifier.apply(e));
    }

    StreamTracker trackerForStream(S s) {
        return trackerSupplier.apply(s);
    }

}