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

com.yahoo.maha.parrequest2.future.ParRequest3Option Maven / Gradle / Ivy

There is a newer version: 6.158
Show newest version
// Copyright 2017, Yahoo Holdings Inc.
// Licensed under the terms of the Apache License 2.0. Please see LICENSE file in project root for terms.
package com.yahoo.maha.parrequest2.future;

import com.google.common.util.concurrent.ListenableFuture;
import com.yahoo.maha.parrequest2.EitherUtils;
import com.yahoo.maha.parrequest2.ParCallable;
import scala.util.Either;
import com.yahoo.maha.parrequest2.GeneralError;
import scala.Option;

import scala.Tuple3;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;

/**
 * This class represents three parallel requests which can be composed synchronously with resultMap, or composed
 * asynchronously with map and fold
 */
public class ParRequest3Option extends CombinableRequest, Option, Option>> {

    private final ParallelServiceExecutor executor;
    private final CombinedFuture3Option combinedFuture3;

    ParRequest3Option(String label, ParallelServiceExecutor executor,
                      ParCallable> firstRequest,
                      ParCallable> secondRequest,
                      ParCallable> thirdRequest,
                      boolean allMustSucceed) {
        checkNotNull(executor, "Executor is null");
        checkNotNull(firstRequest, "First request is null");
        checkNotNull(secondRequest, "Second request is null");
        checkNotNull(thirdRequest, "Third request is null");
        this.label = label;
        this.executor = executor;

        //fire requests
        final ListenableFuture> firstFuture = executor.submitParCallable(firstRequest);
        final ListenableFuture> secondFuture = executor.submitParCallable(secondRequest);
        final ListenableFuture> thirdFuture = executor.submitParCallable(thirdRequest);

        try {
            combinedFuture3 =
                CombinedFuture3Option.from(executor, firstFuture, secondFuture, thirdFuture, allMustSucceed);
        } catch (Exception e) {
            //failed to create combiner, cancel futures and re-throw exception
            firstFuture.cancel(false);
            secondFuture.cancel(false);
            thirdFuture.cancel(false);
            throw e;
        }
    }

    ParRequest3Option(String label, ParallelServiceExecutor executor,
                      CombinableRequest firstRequest,
                      CombinableRequest secondRequest,
                      CombinableRequest thirdRequest,
                      boolean allMustSucceed) {
        checkNotNull(executor, "Executor is null");
        checkNotNull(firstRequest, "First request is null");
        checkNotNull(secondRequest, "Second request is null");
        checkNotNull(thirdRequest, "Third request is null");
        this.label = label;
        this.executor = executor;
        combinedFuture3 = CombinedFuture3Option.from(
            executor, firstRequest.asFuture(), secondRequest.asFuture(), thirdRequest.asFuture(), allMustSucceed);
    }

    public  Either resultMap(ParFunction, Option, Option>, O> fn) {
        Either, Option, Option>>
            result =
            executor.getEitherSafely(label, combinedFuture3);
        return EitherUtils.map(fn, result);
    }

    public  NoopRequest fold(ParFunction errFn,
                                   ParFunction, Option, Option>, O> fn) {
        return new NoopRequest(executor, new FoldableFuture<>(executor, combinedFuture3, fn, errFn));
    }

    public  ParRequest map(String label,
                                 ParFunction, Option, Option>, Either> fn) {
        return new ParRequest<>(label, executor, new ComposableFuture<>(executor, combinedFuture3, fn));
    }

    public  ParRequest flatMap(String label,
                                     ParFunction, Option, Option>, CombinableRequest> fn) {
        return new ParRequest<>(label, executor, new ComposableFutureFuture<>(executor, combinedFuture3, fn));
    }

    public Either, Option, Option>> get() {
        return executor.getEitherSafely(label, combinedFuture3);
    }

    ListenableFuture, Option, Option>>> asFuture() {
        return combinedFuture3;
    }

    public static class Builder {

        private final ParallelServiceExecutor executor;
        private Option>> firstParCallable = Option.empty();
        private Option>> secondParCallable = Option.empty();
        private Option>> thirdParCallable = Option.empty();
        private boolean built = false;
        private boolean allMustSucceed = false;
        private String label = "changethis";

        public Builder setLabel(String label) {
            this.label = label;
            return this;
        }

        public Builder(ParallelServiceExecutor executor) {
            this.executor = executor;
        }

        public Builder setFirstParCallable(ParCallable> parCallable) {
            checkState(firstParCallable.isEmpty(), "Cannot set the first parCallable twice!");
            firstParCallable = Option.apply(parCallable);
            return this;
        }

        public Builder setSecondParCallable(ParCallable> parCallable) {
            checkState(secondParCallable.isEmpty(), "Cannot set the second parCallable twice!");
            secondParCallable = Option.apply(parCallable);
            return this;
        }

        public Builder setThirdParCallable(ParCallable> parCallable) {
            checkState(thirdParCallable.isEmpty(), "Cannot set the third parCallable twice!");
            thirdParCallable = Option.apply(parCallable);
            return this;
        }

        public Builder allMustSucceed(boolean allMustSucceed) {
            this.allMustSucceed = allMustSucceed;
            return this;
        }

        public ParRequest3Option build() {
            checkState(!built, "Cannot build a request twice!");
            checkState(firstParCallable.isDefined(), "First parCallable not defined!");
            checkState(secondParCallable.isDefined(), "Second parCallable not defined!");
            checkState(thirdParCallable.isDefined(), "Third parCallable not defined!");
            try {
                ParCallable> first = ParCallable.from(firstParCallable.get());
                ParCallable> second = ParCallable.from(secondParCallable.get());
                ParCallable> third = ParCallable.from(thirdParCallable.get());
                return new ParRequest3Option<>(label, executor, first, second, third, allMustSucceed);
            } finally {
                built = true;
            }
        }
    }
}