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

io.atleon.core.AloOps Maven / Gradle / Ivy

package io.atleon.core;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.SynchronousSink;
import reactor.util.context.ContextView;

import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * Common utility methods associated with operations on Alo and related components
 */
final class AloOps {

    private static final Logger LOGGER = LoggerFactory.getLogger(AloOps.class);

    private AloOps() {

    }

    public static  BiConsumer, SynchronousSink>>
    filteringHandler(Predicate predicate, Consumer> negativeConsumer) {
        return (alo, sink) -> {
            Boolean result = null;
            try {
                result = alo.supplyInContext(() -> predicate.test(alo.get()));
            } catch (Throwable error) {
                processFailureOrNacknowledge(sink, alo, error);
            }

            if (result != null) {
                if (result) {
                    sink.next(alo);
                } else {
                    handleDiscard(sink.contextView(), alo, negativeConsumer);
                }
            }
        };
    }

    public static  BiConsumer, SynchronousSink>>
    typeFilteringHandler(Class clazz, Consumer> negativeConsumer) {
        return (alo, sink) -> {
            if (clazz.isAssignableFrom(alo.get().getClass())) {
                sink.next((Alo) alo);
            } else {
                handleDiscard(sink.contextView(), alo, negativeConsumer);
            }
        };
    }

    public static  BiConsumer, SynchronousSink>>
    mappingHandler(Function mapper) {
        return (alo, sink) -> {
            Alo result = null;
            try {
                // Note: mapping is not invoked with *InContext since we're delegating to Alo anyway
                result = Objects.requireNonNull(alo.map(mapper), "Alo implementation returned null mapping");
            } catch (Throwable error) {
                processFailureOrNacknowledge(sink, alo, error);
            }

            if (result != null) {
                sink.next(result);
            }
        };
    }

    public static  BiConsumer, SynchronousSink>>
    mappingPresentHandler(Function> mapper, Consumer> absentConsumer) {
        return (alo, sink) -> {
            Alo> result = null;
            try {
                // Note: mapping is not invoked with *InContext since we're delegating to Alo anyway
                result = Objects.requireNonNull(alo.map(mapper), "Alo implementation returned null mapping");
            } catch (Throwable error) {
                processFailureOrNacknowledge(sink, alo, error);
            }

            if (result != null) {
                if (result.get().isPresent()) {
                    sink.next(PresentAlo.wrap(result));
                } else {
                    absentConsumer.accept(alo);
                }
            }
        };
    }

    public static  BiConsumer, SynchronousSink>>
    consumingHandler(Consumer consumer, Consumer> afterSuccessConsumer) {
        return (alo, sink) -> {
            boolean consumed = false;
            try {
                alo.runInContext(() -> consumer.accept(alo.get()));
                consumed = true;
            } catch (Throwable error) {
                processFailureOrNacknowledge(sink, alo, error);
            }

            if (consumed) {
                afterSuccessConsumer.accept(alo);
            }
        };
    }

    public static  BiConsumer, SynchronousSink>>
    failureProcessingHandler(Predicate isFailure, Function errorExtractor) {
        return (alo, sink) -> {
            T t = alo.get();
            if (isFailure.test(t)) {
                processFailure(sink, alo, errorExtractor.apply(t), () -> sink.next(alo));
            } else {
                sink.next(alo);
            }
        };
    }

    public static  Alo> fanIn(List> alos) {
        Alo firstAlo = alos.get(0);
        if (alos.size() == 1) {
            return firstAlo.map(Collections::singletonList);
        } else {
            return firstAlo.fanInPropagator(alos).create(
                alos.stream().map(Alo::get).collect(Collectors.toList()),
                combineAcknowledgers(alos.stream().map(Alo::getAcknowledger).collect(Collectors.toList())),
                combineNacknowledgers(alos.stream().map(Alo::getNacknowledger).collect(Collectors.toList()))
            );
        }
    }

    private static void processFailureOrNacknowledge(SynchronousSink sink, Alo alo, Throwable error) {
        processFailure(sink, alo, error, () -> Alo.nacknowledge(alo, error));
    }

    private static void processFailure(SynchronousSink sink, Alo alo, Throwable error, Runnable unprocessedFallback) {
        if (!AloFailureStrategy.choose(sink).process(alo, error, sink::error)) {
            unprocessedFallback.run();
        }
    }

    private static  void handleDiscard(ContextView contextView, Alo alo, Consumer> afterHandle) {
        try {
            alo.runInContext(() -> DiscardHook.choose(contextView).accept(alo.get()));
        } catch (Throwable error) {
            LOGGER.warn("Error in discard hook", error);
        } finally {
            afterHandle.accept(alo);
        }
    }

    private static Runnable combineAcknowledgers(Iterable acknowledgers) {
        return () -> acknowledgers.forEach(Runnable::run);
    }

    private static Consumer
    combineNacknowledgers(Iterable> nacknowledgers) {
        return error -> nacknowledgers.forEach(nacknowledger -> nacknowledger.accept(error));
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy