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

org.reactfx.Await Maven / Gradle / Ivy

There is a newer version: 1.11
Show newest version
package org.reactfx;

import static javafx.concurrent.WorkerStateEvent.*;

import java.util.concurrent.CompletionStage;
import java.util.concurrent.Executor;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

import javafx.beans.binding.BooleanBinding;
import javafx.beans.value.ObservableBooleanValue;
import javafx.concurrent.Task;

import org.reactfx.util.TriConsumer;
import org.reactfx.util.Try;

class Await extends EventStreamBase> implements AwaitingEventStream> {

    public static  AwaitingEventStream> awaitCompletionStage(
            EventStream> source,
            Executor clientThreadExecutor) {
        return new Await<>(
                source,
                (future, handler) -> addCompletionHandler(future, handler, clientThreadExecutor));
    }

    public static  AwaitingEventStream> awaitTask(
            EventStream> source) {
        return new Await<>(source, Await::addCompletionHandler);
    }

    static  void addCompletionHandler(
            CompletionStage future,
            TriConsumer handler,
            Executor executor) {
        future.whenCompleteAsync((result, error) -> {
            handler.accept(result, error, false);
        }, executor);
    }

    static  void addCompletionHandler(
            Task t,
            TriConsumer handler) {
        t.addEventHandler(WORKER_STATE_SUCCEEDED, e -> handler.accept(t.getValue(), null, false));
        t.addEventHandler(WORKER_STATE_FAILED, e -> handler.accept(null, t.getException(), false));
        t.addEventHandler(WORKER_STATE_CANCELLED, e -> handler.accept(null, null, true));
    }

    private final EventStream source;
    private final SuspendableNo pending = new SuspendableNo();
    private final BiConsumer> addCompletionHandler;

    private Await(
            EventStream source,
            BiConsumer> addCompletionHandler) {
        this.source = source;
        this.addCompletionHandler = addCompletionHandler;
    }

    @Override
    public final ObservableBooleanValue pendingProperty() {
        return pending;
    }

    @Override
    public final boolean isPending() {
        return pending.get();
    }

    @Override
    protected final Subscription observeInputs() {
        return source.subscribe(future -> {
            Guard g = pending.suspend();
            addCompletionHandler.accept(future, (result, error, cancelled) -> {
                if(!cancelled) {
                    emit(error == null ? Try.success(result) : Try.failure(error));
                }
                g.close();
            });
        });
    }
}


class AwaitLatest extends EventStreamBase> implements AwaitingEventStream> {

    public static  AwaitingEventStream> awaitCompletionStage(
            EventStream> source,
            Executor clientThreadExecutor) {
        return new AwaitLatest<>(
                source,
                EventStreams.never(), // no cancel impulse
                future -> {}, // cannot cancel a CompletionStage
                (future, handler) -> Await.addCompletionHandler(future, handler, clientThreadExecutor));
    }

    public static  AwaitingEventStream> awaitTask(
            EventStream> source) {
        return new AwaitLatest<>(
                source,
                EventStreams.never(), // no cancel impulse
                Task::cancel,
                Await::addCompletionHandler);
    }

    public static  AwaitingEventStream> awaitCompletionStage(
            EventStream> source,
            EventStream cancelImpulse,
            Executor clientThreadExecutor) {
        return new AwaitLatest<>(
                source,
                cancelImpulse,
                future -> {}, // cannot cancel a CompletionStage
                (future, handler) -> Await.addCompletionHandler(future, handler, clientThreadExecutor));
    }

    public static  AwaitingEventStream> awaitTask(
            EventStream> source,
            EventStream cancelImpulse) {
        return new AwaitLatest<>(
                source,
                cancelImpulse,
                Task::cancel,
                Await::addCompletionHandler);
    }

    private final EventStream source;
    private final EventStream cancelImpulse;
    private final Consumer canceller;
    private final BiConsumer> addCompletionHandler;

    private long revision = 0;
    private F expectedFuture = null;

    private BooleanBinding pending = null;

    private AwaitLatest(
            EventStream source,
            EventStream cancelImpulse,
            Consumer canceller,
            BiConsumer> addCompletionHandler) {
        this.source = source;
        this.cancelImpulse = cancelImpulse;
        this.canceller = canceller;
        this.addCompletionHandler = addCompletionHandler;
    }

    @Override
    public ObservableBooleanValue pendingProperty() {
        if(pending == null) {
            pending = new BooleanBinding() {
                @Override
                protected boolean computeValue() {
                    return expectedFuture != null;
                }
            };
        }
        return pending;
    }

    @Override
    public boolean isPending() {
        return pending != null ? pending.get() : expectedFuture != null;
    }

    @Override
    protected Subscription observeInputs() {
        Subscription s1 = source.subscribe(future -> {
            long rev = replaceExpected(future);
            addCompletionHandler.accept(future, (result, error, cancelled) -> {
                if(rev == revision) {
                    if(!cancelled) {
                        // emit before setting pending to false
                        emit(error == null ? Try.success(result) : Try.failure(error));
                    }
                    setExpected(null);
                }
            });
        });

        Subscription s2 = cancelImpulse.subscribe(x -> replaceExpected(null));

        return s1.and(s2);
    }

    private final long replaceExpected(F newExpected) {
        ++revision; // increment before cancelling, so that the cancellation handler is not executed
        if(expectedFuture != null) {
            canceller.accept(expectedFuture);
        }
        setExpected(newExpected);
        return revision;
    }

    private void setExpected(F newExpected) {
        expectedFuture = newExpected;
        if(pending != null) {
            pending.invalidate();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy