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

net.openhft.chronicle.queue.channel.SubscribeHandler Maven / Gradle / Ivy

There is a newer version: 5.27ea0
Show newest version
package net.openhft.chronicle.queue.channel;


import net.openhft.affinity.AffinityLock;
import net.openhft.chronicle.bytes.SyncMode;
import net.openhft.chronicle.core.io.Closeable;
import net.openhft.chronicle.core.io.SimpleCloseable;
import net.openhft.chronicle.queue.ChronicleQueue;
import net.openhft.chronicle.queue.ExcerptTailer;
import net.openhft.chronicle.queue.channel.impl.SubscribeQueueChannel;
import net.openhft.chronicle.threads.Pauser;
import net.openhft.chronicle.wire.DocumentContext;
import net.openhft.chronicle.wire.SelfDescribingMarshallable;
import net.openhft.chronicle.wire.Wire;
import net.openhft.chronicle.wire.channel.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.function.Consumer;
import java.util.function.Predicate;

import static net.openhft.chronicle.queue.channel.PipeHandler.newQueue;

public class SubscribeHandler extends AbstractHandler {

    private static class NoOp extends SelfDescribingMarshallable implements Consumer {
        @Override
        public void accept(Object o) {
            return;
        }
    }

    public final static Consumer NO_OP = new NoOp();

    private String subscribe;
    private transient boolean closeWhenRunEnds = true;

    private SyncMode syncMode;

    private Predicate filter;
    private int sourceId;

    private Consumer subscriptionIndexController = NO_OP;


    static void queueTailer(@NotNull Pauser pauser,
                            @NotNull ChronicleChannel channel,
                            @NotNull ChronicleQueue subscribeQueue,
                            @Nullable Predicate filter,
                            @NotNull Consumer subscriptionIndexController) {
        try (ChronicleQueue subscribeQ = subscribeQueue; // leave here so it gets closed
             ExcerptTailer tailer = subscribeQ.createTailer()) {

            tailer.singleThreadedCheckDisabled(true);  // assume we are thread safe
            subscriptionIndexController.accept(tailer);

            while (!channel.isClosing()) {
                if (copyOneMessage(channel, tailer, filter))
                    pauser.reset();
                else
                    pauser.pause();
            }
        } catch (Exception e) {
            Thread.yield();
            if (channel.isClosing() || subscribeQueue.isClosing())
                return;
            throw e;
        }
    }

    static boolean copyOneMessage(ChronicleChannel channel, ExcerptTailer tailer, Predicate filter) {
        try (DocumentContext dc = tailer.readingDocument()) {
            if (!dc.isPresent()) {
                return false;
            }
            if (dc.isMetaData()) {
                return true;
            }


            Wire wire1 = dc.wire();
            if (filter != null) {
                long pos = wire1.bytes().readPosition();
                if (!filter.test(wire1)) {
                    wire1.bytes().readPosition(wire1.bytes().readLimit());
                    return true;
                }
                wire1.bytes().readPosition(pos);
            }

            try (DocumentContext dc2 = channel.writingDocument()) {
                Wire wire2 = dc2.wire();
                wire1.copyTo(wire2);

                final long dataBuffered = wire2.bytes().writePosition();
                // wait for it to drain
                return dataBuffered < 32 << 10;
            }
        }
    }

    public String subscribe() {
        return subscribe;
    }

    public SubscribeHandler subscribe(String subscribe) {
        this.subscribe = subscribe;
        return this;
    }

    public SyncMode syncMode() {
        return syncMode;
    }

    public SubscribeHandler syncMode(SyncMode syncMode) {
        this.syncMode = syncMode;
        return this;
    }

    public Predicate filter() {
        return filter;
    }

    public SubscribeHandler filter(Predicate filter) {
        this.filter = filter;
        return this;
    }

    @Override
    public void run(ChronicleContext context, ChronicleChannel channel) {
        Pauser pauser = Pauser.balanced();

        final ExcerptTailer tailer;

        try (ChronicleQueue subscribeQ = newQueue(context, subscribe, syncMode, sourceId)) {
            InternalChronicleChannel icc = (InternalChronicleChannel) channel;
            if (icc.supportsEventPoller()) {
                tailer = subscribeQ.createTailer();
                icc.eventPoller(new SHEventHandler(tailer, filter));
                closeWhenRunEnds = false;
            } else {
                try (AffinityLock lock = context.affinityLock()) {
                    queueTailer(pauser, channel, newQueue(context, subscribe, syncMode, sourceId), filter, subscriptionIndexController);
                }
                closeWhenRunEnds = true;
            }
        }
    }

    @Override
    public boolean closeWhenRunEnds() {
        return closeWhenRunEnds;
    }

    @Override
    public ChronicleChannel asInternalChannel(ChronicleContext context, ChronicleChannelCfg channelCfg) {
        return new SubscribeQueueChannel(channelCfg, this, newQueue(context, subscribe, syncMode, sourceId));
    }

    static class SHEventHandler extends SimpleCloseable implements EventPoller {
        private final ExcerptTailer tailer;
        private final Predicate filter;

        SHEventHandler(ExcerptTailer tailer, Predicate filter) {
            this.tailer = tailer;
            this.filter = filter;
        }

        @Override
        public boolean onPoll(ChronicleChannel channel) {
            boolean wrote = false;
            while (copyOneMessage(channel, tailer, filter))
                wrote = true;
            return wrote;
        }

        @Override
        protected void performClose() {
            super.performClose();
            Closeable.closeQuietly(tailer);
        }
    }

    /**
     * @param sourceId the sourceId of the subscribe queue
     * @return this
     */
    public SubscribeHandler subscribeSourceId(int sourceId) {
        this.sourceId = sourceId;
        return this;
    }


    /**
     * @param subscriptionIndexController controls where the subscriptions will start to read from, by allowing the caller to
     *                                    {@link net.openhft.chronicle.queue.ExcerptTailer#moveToIndex(long) to control the first read location
     */
    public SubscribeHandler subscriptionIndexController(Consumer subscriptionIndexController) {
        this.subscriptionIndexController = subscriptionIndexController;
        return this;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy