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

com.vmware.xenon.common.DeferredResult Maven / Gradle / Ivy

There is a newer version: 1.6.18
Show newest version
/*
 * Copyright (c) 2014-2016 VMware, Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License.  You may obtain a copy of
 * the License at http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed
 * under the License is distributed on an "AS IS" BASIS, without warranties or
 * conditions of any kind, EITHER EXPRESS OR IMPLIED.  See the License for the
 * specific language governing permissions and limitations under the License.
 */

package com.vmware.xenon.common;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Executor;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * This is an implementation of a condensed version of {@link CompletionStage},
 * excluding the methods that implicitly use a global executor. This class
 * encapsulates {@link CompletableFuture}
 * @param 
 */
public final class DeferredResult {

    private final CompletableFuture completableFuture;

    /**
     * Constructs an already realized {@link DeferredResult} with the provided
     * value.
     * @param value
     * @return
     */
    public static  DeferredResult completed(U value) {
        DeferredResult deferred = new DeferredResult<>();
        deferred.complete(value);
        return deferred;
    }

    /**
     * Constructs a failed {@link DeferredResult} with the provided exception.
     * @param ex
     * @return
     */
    public static  DeferredResult failed(Throwable ex) {
        DeferredResult deferred = new DeferredResult<>();
        deferred.fail(ex);
        return deferred;
    }

    /**
     * @see CompletableFuture#allOf(CompletableFuture...)
     * @param deferredResults
     * @return
     */
    public static DeferredResult allOf(DeferredResult... deferredResults) {
        CompletableFuture[] cfs = Arrays.stream(deferredResults)
                .map(d -> d.completableFuture)
                .collect(Collectors.toList())
                .toArray(new CompletableFuture[deferredResults.length]);
        CompletableFuture cf = CompletableFuture.allOf(cfs);
        return new DeferredResult<>(cf);
    }

    /**
     * Similar to CompletableFuture#allOf(CompletableFuture...) operating on
     * deferred results with the same value type. The result is the deferred
     * list of the values of the individual instances.
     * @param deferredResults
     * @return
     */
    public static  DeferredResult> allOf(List> deferredResults) {
        List> futures = deferredResults
                .stream()
                .map(d -> d.completableFuture)
                .collect(Collectors.toList());
        CompletableFuture> cf = CompletableFuture
                .allOf(futures.toArray(new CompletableFuture[deferredResults.size()]))
                .thenApply(ignore -> {
                    // Here all the futures are completed *successfully* which
                    // means that based on the CompletableFuture contract:
                    // 1. getNow() will return the actual value!
                    // 2. there are no exceptions
                    List results = futures
                            .stream()
                            .map(f -> f.getNow(null))
                            .collect(Collectors.toList());
                    return results;
                });
        return new DeferredResult<>(cf);
    }

    /**
     * @see CompletableFuture#anyOf(CompletableFuture...)
     * @param deferredResults
     * @return
     */
    public static DeferredResult anyOf(DeferredResult... deferredResults) {
        CompletableFuture[] cfs = Arrays.stream(deferredResults)
                .map(d -> d.completableFuture)
                .collect(Collectors.toList())
                .toArray(new CompletableFuture[deferredResults.length]);
        CompletableFuture cf = CompletableFuture.anyOf(cfs);
        return new DeferredResult<>(cf);
    }

    /**
     * Similar to CompletableFuture#anyOf(CompletableFuture...) operating on
     * deferred results with the value type.
     * @param deferredResults
     * @return
     */
    public static  DeferredResult anyOf(List> deferredResults) {
        List> futures = deferredResults
                .stream()
                .map(d -> d.completableFuture)
                .collect(Collectors.toList());
        @SuppressWarnings("unchecked")
        CompletableFuture cf = CompletableFuture
                .anyOf(futures.toArray(new CompletableFuture[deferredResults.size()]))
                .thenApply(obj -> (U) obj);
        return new DeferredResult<>(cf);
    }

    /**
     * Creates a new incomplete {@link DeferredResult}
     */
    public DeferredResult() {
        this(new CompletableFuture<>());
    }

    /**
     * Creates a new {@link DeferredResult} by wrapping the provided {@link CompletableFuture}
     * @param completionStage
     */
    public DeferredResult(CompletableFuture completableFuture) {
        this.completableFuture = completableFuture;
    }

    /**
     * Wraps the provided {@link CompletableFuture} into {@link DeferredResult}
     * @param completionStage
     * @return
     */
    protected  DeferredResult wrap(CompletableFuture completableFuture) {
        return new DeferredResult<>(completableFuture);
    }

    /**
     * @see CompletionStage#thenApply(Function)
     */
    public  DeferredResult thenApply(Function fn) {
        return wrap(this.completableFuture.thenApply(fn));
    }

    /**
     * @see CompletionStage#thenApplyAsync(Function, Executor)
     */
    public  DeferredResult thenApplyAsync(Function fn,
            Executor executor) {
        return wrap(this.completableFuture.thenApplyAsync(fn, executor));
    }

    /**
     * @see CompletionStage#thenAccept(Consumer)
     */
    public DeferredResult thenAccept(Consumer action) {
        return wrap(this.completableFuture.thenAccept(action));
    }

    /**
     * @see CompletionStage#thenAcceptAsync(Consumer, Executor)
     */
    public DeferredResult thenAcceptAsync(Consumer action, Executor executor) {
        return wrap(this.completableFuture.thenAcceptAsync(action, executor));
    }

    /**
     * @see CompletionStage#thenRun(Runnable)
     */
    public DeferredResult thenRun(Runnable action) {
        return wrap(this.completableFuture.thenRun(action));
    }

    /**
     * @see CompletionStage#thenRunAsync(Runnable, Executor)
     */
    public DeferredResult thenRunAsync(Runnable action, Executor executor) {
        return wrap(this.completableFuture.thenRunAsync(action, executor));
    }

    /**
     * @see CompletionStage#thenCombine(CompletionStage, BiFunction)
     */
    public  DeferredResult thenCombine(DeferredResult other,
            BiFunction fn) {
        return wrap(this.completableFuture.thenCombine(other.toCompletionStage(), fn));
    }

    /**
     * @see CompletionStage#thenCombineAsync(CompletionStage, BiFunction, Executor)
     */
    public  DeferredResult thenCombineAsync(DeferredResult other,
            BiFunction fn, Executor executor) {
        return wrap(this.completableFuture.thenCombineAsync(other.toCompletionStage(), fn,
                executor));
    }

    /**
     * @see CompletionStage#thenAcceptBoth(CompletionStage, BiConsumer)
     */
    public  DeferredResult thenAcceptBoth(DeferredResult other,
            BiConsumer action) {
        return wrap(this.completableFuture.thenAcceptBoth(other.toCompletionStage(), action));
    }

    /**
     * @see CompletionStage#thenAcceptBothAsync(CompletionStage, BiConsumer, Executor)
     */
    public  DeferredResult thenAcceptBothAsync(DeferredResult other,
            BiConsumer action, Executor executor) {
        return wrap(this.completableFuture.thenAcceptBothAsync(other.toCompletionStage(), action,
                executor));
    }

    /**
     * @see CompletionStage#runAfterBoth(CompletionStage, Runnable)
     */
    public DeferredResult runAfterBoth(DeferredResult other, Runnable action) {
        return wrap(this.completableFuture.runAfterBoth(other.toCompletionStage(), action));
    }

    /**
     * @see CompletionStage#runAfterBothAsync(CompletionStage, Runnable, Executor)
     */
    public DeferredResult runAfterBothAsync(DeferredResult other, Runnable action,
            Executor executor) {
        return wrap(this.completableFuture.runAfterBothAsync(other.toCompletionStage(), action,
                executor));
    }

    /**
     * @see CompletionStage#applyToEither(CompletionStage, Function)
     */
    public  DeferredResult applyToEither(DeferredResult other,
            Function fn) {
        return wrap(this.completableFuture.applyToEither(other.toCompletionStage(), fn));
    }

    /**
     * @see CompletionStage#applyToEitherAsync(CompletionStage, Function, Executor)
     */
    public  DeferredResult applyToEitherAsync(DeferredResult other,
            Function fn,
            Executor executor) {
        return wrap(this.completableFuture.applyToEitherAsync(other.toCompletionStage(), fn,
                executor));
    }

    /**
     * @see CompletionStage#acceptEither(CompletionStage, Consumer)
     */
    public DeferredResult acceptEither(DeferredResult other,
            Consumer action) {
        return wrap(this.completableFuture.acceptEither(other.toCompletionStage(), action));
    }

    /**
     * @see CompletionStage#acceptEitherAsync(CompletionStage, Consumer, Executor)
     */
    public DeferredResult acceptEitherAsync(DeferredResult other,
            Consumer action,
            Executor executor) {
        return wrap(this.completableFuture.acceptEitherAsync(other.toCompletionStage(), action,
                executor));
    }

    /**
     * @see CompletionStage#runAfterEither(CompletionStage, Runnable)
     */
    public DeferredResult runAfterEither(DeferredResult other, Runnable action) {
        return wrap(this.completableFuture.runAfterEither(other.toCompletionStage(), action));
    }

    /**
     * @see CompletionStage#runAfterEitherAsync(CompletionStage, Runnable, Executor)
     */
    public DeferredResult runAfterEitherAsync(DeferredResult other, Runnable action,
            Executor executor) {
        return wrap(this.completableFuture.runAfterEitherAsync(other.toCompletionStage(), action,
                executor));
    }

    /**
     * @see CompletionStage#thenCompose(Function)
     */
    public  DeferredResult thenCompose(Function> fn) {
        return wrap(this.completableFuture.thenCompose(fn.andThen(p -> p.toCompletionStage())));
    }

    /**
     * @see CompletionStage#thenComposeAsync(Function, Executor)
     */
    public  DeferredResult thenComposeAsync(
            Function> fn,
            Executor executor) {
        return wrap(this.completableFuture.thenComposeAsync(fn.andThen(p -> p.toCompletionStage()),
                executor));
    }

    /**
     * @see CompletionStage#exceptionally(Function)
     */
    public DeferredResult exceptionally(Function fn) {
        return wrap(this.completableFuture.exceptionally(fn));
    }

    /**
     * @see CompletionStage#whenComplete(BiConsumer)
     */
    public DeferredResult whenComplete(BiConsumer action) {
        return wrap(this.completableFuture.whenComplete(action));
    }

    /**
     * @see CompletionStage#whenCompleteAsync(BiConsumer, Executor)
     */
    public DeferredResult whenCompleteAsync(BiConsumer action,
            Executor executor) {
        return wrap(this.completableFuture.whenCompleteAsync(action, executor));
    }

    /**
     * @see CompletionStage#handle(BiFunction)
     */
    public  DeferredResult handle(BiFunction fn) {
        return wrap(this.completableFuture.handle(fn));
    }

    /**
     * @see CompletionStage#handleAsync(BiFunction, Executor)
     */
    public  DeferredResult handleAsync(BiFunction fn,
            Executor executor) {
        return wrap(this.completableFuture.handleAsync(fn, executor));
    }

    /**
     * @see CompletableFuture#getNow(Object)
     * @param valueIfAbsent
     * @return
     */
    public T getNow(T valueIfAbsent) {
        return this.completableFuture.getNow(valueIfAbsent);
    }

    /**
     * @throws CompletionException if the execution encountered exception.
     * @return The value of this DeferredResult if it is already completed else
     * uses the provided valueSupplierIfAbsent to generate the returned value.
     */
    public T getNow(Supplier valueSupplierIfAbsent) {
        if (this.completableFuture.isDone()) {
            return this.completableFuture.join();
        }
        return valueSupplierIfAbsent.get();
    }

    /**
     * @see CompletableFuture#isDone()
     * @return
     */
    public boolean isDone() {
        return this.completableFuture.isDone();
    }

    /**
     * Converts this {@link DeferredResult} to {@link CompletionStage}
     * @return
     */
    public CompletionStage toCompletionStage() {
        return this.completableFuture;
    }

    /**
     * Explicitly signals the deferred realization is complete with the supplied value.
     * @param value
     * @return
     */
    public boolean complete(T value) {
        return this.completableFuture.complete(value);
    }

    /**
     * Signals that the deferred realization has failed with the exception.
     * @param ex
     * @return
     */
    public boolean fail(Throwable ex) {
        return this.completableFuture.completeExceptionally(ex);
    }

    /**
     * Has the same semantics as {@link #whenComplete(BiConsumer)} but notifies
     * the provided operation that the stage is completed.
     * @param operation
     * @return
     */
    public DeferredResult whenCompleteNotify(Operation operation) {
        return wrap(this.completableFuture.whenComplete((ignore, ex) -> {
            if (ex != null) {
                if (ex instanceof CompletionException) {
                    ex = ex.getCause();
                }
                operation.fail(ex);
            } else {
                operation.complete();
            }
        }));
    }
}