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

io.smallrye.reactive.messaging.providers.locals.ContextOperator Maven / Gradle / Ivy

package io.smallrye.reactive.messaging.providers.locals;

import java.util.Optional;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

import org.eclipse.microprofile.reactive.messaging.Message;

import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.operators.MultiOperator;
import io.smallrye.mutiny.operators.multi.MultiOperatorProcessor;
import io.smallrye.mutiny.subscription.MultiSubscriber;
import io.smallrye.reactive.messaging.providers.helpers.VertxContext;
import io.vertx.core.Context;

/**
 * Decorator to dispatch messages on the Vert.x context attached to the message via {@link LocalContextMetadata}.
 * Low priority to be called before other decorators.
 */
public class ContextOperator {

    public static > Multi apply(Multi publisher) {
        return new ContextMulti<>(publisher);
    }

    static class ContextMulti> extends MultiOperator {

        public ContextMulti(Multi upstream) {
            super(upstream);
        }

        @Override
        public void subscribe(MultiSubscriber subscriber) {
            MultiOperatorProcessor operator = new ContextProcessor<>(subscriber);
            upstream().subscribe().withSubscriber(operator);
        }

        static class ContextProcessor> extends MultiOperatorProcessor {

            private volatile Context rootContext;

            private static final AtomicReferenceFieldUpdater ROOT_CONTEXT_UPDATER = AtomicReferenceFieldUpdater
                    .newUpdater(ContextProcessor.class, Context.class, "rootContext");

            public ContextProcessor(MultiSubscriber downstream) {
                super(downstream);
            }

            @Override
            public void onFailure(Throwable throwable) {
                // Release context on terminal events
                Context root = ROOT_CONTEXT_UPDATER.getAndSet(this, null);
                // Do NOT check for the current context and trampoline, as we may short-cut the completion event
                // before the processing of the items.
                if (root == null) {
                    super.onFailure(throwable);
                } else {
                    root.runOnContext(ignored -> super.onFailure(throwable));
                }
            }

            @Override
            public void onItem(T item) {
                Optional metadata = item.getMetadata().get(LocalContextMetadata.class);
                if (metadata.isPresent()) {
                    Context context = metadata.get().context();
                    // This make the assumption that ALL the receives message belongs to the same event loop
                    // It's not the case when using multiple Kafka partitions, however this root context is only
                    // used for completion and failure event. As these are terminal events it should not matter.
                    ROOT_CONTEXT_UPDATER.compareAndSet(this, null, VertxContext.getRootContext(context));

                    VertxContext.runOnContext(context, () -> super.onItem(item));
                } else {
                    // No stored context, immediate call
                    super.onItem(item);
                }
            }

            @Override
            public void request(long numberOfItems) {
                Context root = ROOT_CONTEXT_UPDATER.get(this);
                if (root != null) {
                    root.runOnContext(x -> super.request(numberOfItems));
                } else {
                    super.request(numberOfItems);
                }
            }

            @Override
            public void onCompletion() {
                // Release context on terminal events
                Context root = ROOT_CONTEXT_UPDATER.getAndSet(this, null);
                // Do NOT check for the current context and trampoline, as we may short-cut the completion event
                // before the processing of the items.
                if (root == null) {
                    super.onCompletion();
                } else {
                    root.runOnContext(ignored -> super.onCompletion());
                }
            }
        }

    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy