com.spotify.futures.FuturesExtra.java Source code

Java tutorial

Introduction

Here is the source code for com.spotify.futures.FuturesExtra.java

Source

/*
 * Copyright (c) 2013-2015 Spotify AB
 *
 * 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.spotify.futures;

import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.AsyncFunction;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import com.google.common.util.concurrent.Uninterruptibles;

import java.util.Arrays;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Static utility methods pertaining to the {@link ListenableFuture} interface.
 */
@SuppressWarnings("unchecked")
public class FuturesExtra {

    /**
     * Returns a future that fails with a {@link TimeoutException} if the parent future has not
     * finished before the timeout. The new returned future will always be executed on the provided
     * scheduledExecutorService, even when the parent future does not timeout.
     *
     * @param scheduledExecutorService executor that runs the timeout code. If the future times out,
     *                                 this is also the thread any callbacks will run on.
     * @param future                   the future to wrap as a timeout future.
     * @param timeout                  how long to wait before timing out a future
     * @param unit                     unit of the timeout
     * @return a future that may timeout before the parent future is done.
     */
    public static <T> ListenableFuture<T> makeTimeoutFuture(ScheduledExecutorService scheduledExecutorService,
            ListenableFuture<T> future, final long timeout, final TimeUnit unit) {
        final SettableFuture<T> promise = SettableFuture.create();

        scheduledExecutorService.schedule(new Runnable() {
            @Override
            public void run() {
                String message = "Future timed out after " + timeout + " " + unit.name();
                promise.setException(new TimeoutException(message));
            }
        }, timeout, unit);

        Futures.addCallback(future, new FutureCallback<T>() {
            @Override
            public void onSuccess(T result) {
                promise.set(result);
            }

            @Override
            public void onFailure(Throwable t) {
                promise.setException(t);
            }
        }, scheduledExecutorService);

        return promise;
    }

    /**
     * This takes two futures of type {@link A} and {@link B} and works like
     * a valve on {@link A}, with validation executed on {@link B}.
     *
     * Returns a future with the result of {@link A} that will wait for a
     * condition on {@link B} to be validated first. Both futures can run in
     * parallel. If the condition fails validation, the {@link A} future will
     * be cancelled by a call to {@link ListenableFuture#cancel(boolean)} with
     * {@code false}.
     *
     * This is useful for when you want to optimistically run a time consuming
     * path while validating if it should be computed or not by a parallel
     * async computation.
     *
     * @param conditionValue  The future computing the value for validation.
     * @param future          The actual value future.
     * @param validator       A validator for the condition.
     *
     * @return a new {@link ListenableFuture} eventually either containing
     * {@param future} or any exception thrown by {@param validator}.
     */
    public static <A, B> ListenableFuture<A> fastFail(final ListenableFuture<B> conditionValue,
            final ListenableFuture<A> future, final Validator<B> validator) {
        return Futures.transform(conditionValue, new AsyncFunction<B, A>() {
            @Override
            public ListenableFuture<A> apply(B value) throws Exception {
                try {
                    validator.validate(value);
                    return future;

                } catch (Exception e) {
                    future.cancel(false);
                    throw e;
                }
            }
        });
    }

    /**
     * Returns a new {@link ListenableFuture} with the result of the first of futures that
     * successfully completes. If all ListenableFutures in futures fails the returned feature
     * fails as well with the Exception of the last failed future. If futures is an empty
     * list the returned future fails immediately with {@link java.util.NoSuchElementException}
     *
     * @param futures a {@link List} of futures
     * @return a new future with the result of the first completing future.
     * @throws NullPointerException if the {@param futures} is null
     */
    public static <T> ListenableFuture<T> select(final List<? extends ListenableFuture<T>> futures) {
        Preconditions.checkNotNull(futures);
        if (futures.isEmpty()) {
            return Futures.immediateFailedFuture(new NoSuchElementException("List is empty"));
        }
        final int count = futures.size();
        final AtomicInteger failures = new AtomicInteger();

        final SettableFuture<T> promise = SettableFuture.create();
        final FutureCallback<T> cb = new FutureCallback<T>() {
            @Override
            public void onSuccess(final T result) {
                promise.set(result);
            }

            @Override
            public void onFailure(Throwable t) {
                if (failures.incrementAndGet() == count) {
                    promise.setException(t);
                }
            }
        };

        for (final ListenableFuture<T> future : futures) {
            Futures.addCallback(future, cb);
        }
        return promise;
    }

    /**
     * Represents an operation that accepts a single input argument and returns no result.
     */
    public interface Consumer<T> {

        /**
         * Performs this operation on the given argument.
         * @param t the input argument
         */
        void accept(T t);
    }

    /**
     * A lambda-friendly way to attach callbacks to a {@link ListenableFuture}. The success
     * callback will only be run if the future completes successfully, and failure will
     * only be run if the future fails.
     * @param future a ListenableFuture to attach the callbacks to.
     * @param success a consumer, to be called with the result of the successful future.
     * @param failure a consumer, to be called with the result of the failed future.
     * @throws NullPointerException if the {@param success} and {@param failure} are null
     */
    public static <T> void addCallback(final ListenableFuture<T> future, final Consumer<? super T> success,
            final Consumer<Throwable> failure) {
        if (success == null && failure == null) {
            throw new NullPointerException();
        }
        Futures.addCallback(future, new FutureCallback<T>() {
            @Override
            public void onSuccess(final T result) {
                if (success != null) {
                    success.accept(result);
                }
            }

            @Override
            public void onFailure(final Throwable throwable) {
                if (failure != null) {
                    failure.accept(throwable);
                }
            }
        });
    }

    /**
     * A lambda-friendly way to attach a callback to a {@link ListenableFuture}. The callback will
     * only be run if the future completes successfully.
     * @param future a ListenableFuture to attach the callback to.
     * @param consumer a consumer, to be called with the result of the successful future.
     */
    public static <T> void addSuccessCallback(ListenableFuture<T> future, final Consumer<? super T> consumer) {
        addCallback(future, consumer, null);
    }

    /**
     * A lambda-friendly way to attach a callback to a {@link ListenableFuture}. The callback will
     * only be run if the future fails.
     * @param future a ListenableFuture to attach the callback to.
     * @param consumer a consumer, to be called with the result of the failed future.
     */
    public static <T> void addFailureCallback(ListenableFuture<T> future, final Consumer<Throwable> consumer) {
        addCallback(future, null, consumer);
    }

    /**
     * Transform the input futures into a single future, using the provided
     * transform function. The transformation follows the same semantics as as
     * {@link Futures#transform(ListenableFuture, Function)} and the input
     * futures are combined using {@link Futures#allAsList}.
     *
     * @param a a ListenableFuture to combine
     * @param b a ListenableFuture to combine
     * @param function the implementation of the transform
     * @return a ListenableFuture holding the result of function.apply()
     */
    public static <Z, A, B> ListenableFuture<Z> syncTransform2(ListenableFuture<A> a, ListenableFuture<B> b,
            final Function2<Z, ? super A, ? super B> function) {
        return transform(Arrays.asList(a, b), new Function<List<Object>, Z>() {
            @Override
            public Z apply(List<Object> results) {
                return function.apply((A) results.get(0), (B) results.get(1));
            }
        });
    }

    /**
     * Implementations of this interface is used to synchronously transform the
     * input values into an output value.
     */
    public interface Function2<Z, A, B> {
        /**
         * Combine the inputs into the returned output
         *
         * @param a an input value
         * @param b an input value
         * @return a result of the combination of the input values.
         */
        Z apply(A a, B b);
    }

    /**
     * Transform the input futures into a single future, using the provided
     * transform function. The transformation follows the same semantics as as
     * {@link Futures#transform(ListenableFuture, AsyncFunction)} and the input
     * futures are combined using {@link Futures#allAsList}.
     *
     * @param a a ListenableFuture to combine
     * @param b a ListenableFuture to combine
     * @param function the implementation of the transform
     * @return a ListenableFuture holding the result of function.apply()
     */
    public static <Z, A, B> ListenableFuture<Z> asyncTransform2(ListenableFuture<A> a, ListenableFuture<B> b,
            final AsyncFunction2<Z, ? super A, ? super B> function) {
        return transform(Arrays.asList(a, b), new AsyncFunction<List<Object>, Z>() {
            @Override
            public ListenableFuture<Z> apply(List<Object> results) throws Exception {
                return function.apply((A) results.get(0), (B) results.get(1));
            }
        });
    }

    /**
     * Implementations of this interface is used in{@link #syncTransform2} to asynchronously
     * transform two values into a return value.
     */
    public interface AsyncFunction2<Z, A, B> {
        /**
         * Create and return a {@link ListenableFuture} that will execute combining a and b into
         * some sort of result.
         *
         * @param a the first input value.
         * @param b the second input value.
         * @return a ListenableFuture that will complete once the result of a and b is computed.
         */
        ListenableFuture<Z> apply(A a, B b) throws Exception;
    }

    /**
     * Transform the input futures into a single future, using the provided
     * transform function. The transformation follows the same semantics as as
     * {@link Futures#transform(ListenableFuture, Function)} and the input
     * futures are combined using {@link Futures#allAsList}.
     *
     * @param a a ListenableFuture to combine
     * @param b a ListenableFuture to combine
     * @param c a ListenableFuture to combine
     * @param function the implementation of the transform
     * @return a ListenableFuture holding the result of function.apply()
     */
    public static <Z, A, B, C> ListenableFuture<Z> syncTransform3(ListenableFuture<A> a, ListenableFuture<B> b,
            ListenableFuture<C> c, final Function3<Z, ? super A, ? super B, ? super C> function) {
        return transform(Arrays.asList(a, b, c), new Function<List<Object>, Z>() {
            @Override
            public Z apply(List<Object> results) {
                return function.apply((A) results.get(0), (B) results.get(1), (C) results.get(2));
            }
        });
    }

    /**
     * Implementations of this interface is used to synchronously transform the
     * input values into an output value.
     */
    public interface Function3<Z, A, B, C> {
        Z apply(A a, B b, C c);
    }

    /**
     * Transform the input futures into a single future, using the provided
     * transform function. The transformation follows the same semantics as as
     * {@link Futures#transform(ListenableFuture, AsyncFunction)} and the input
     * futures are combined using {@link Futures#allAsList}.
     *
     * @param a a ListenableFuture to combine
     * @param b a ListenableFuture to combine
     * @param c a ListenableFuture to combine
     * @param function the implementation of the transform
     * @return a ListenableFuture holding the result of function.apply()
     */
    public static <Z, A, B, C> ListenableFuture<Z> asyncTransform3(ListenableFuture<A> a, ListenableFuture<B> b,
            ListenableFuture<C> c, final AsyncFunction3<Z, ? super A, ? super B, ? super C> function) {
        return transform(Arrays.asList(a, b, c), new AsyncFunction<List<Object>, Z>() {
            @Override
            public ListenableFuture<Z> apply(List<Object> results) throws Exception {
                return function.apply((A) results.get(0), (B) results.get(1), (C) results.get(2));
            }
        });
    }

    public interface AsyncFunction3<Z, A, B, C> {
        ListenableFuture<Z> apply(A a, B b, C c) throws Exception;
    }

    /**
     * Transform the input futures into a single future, using the provided
     * transform function. The transformation follows the same semantics as as
     * {@link Futures#transform(ListenableFuture, Function)} and the input
     * futures are combined using {@link Futures#allAsList}.
     *
     * @param a a ListenableFuture to combine
     * @param b a ListenableFuture to combine
     * @param c a ListenableFuture to combine
     * @param d a ListenableFuture to combine
     * @param function the implementation of the transform
     * @return a ListenableFuture holding the result of function.apply()
     */
    public static <Z, A, B, C, D> ListenableFuture<Z> syncTransform4(ListenableFuture<A> a, ListenableFuture<B> b,
            ListenableFuture<C> c, ListenableFuture<D> d,
            final Function4<Z, ? super A, ? super B, ? super C, ? super D> function) {
        return transform(Arrays.asList(a, b, c, d), new Function<List<Object>, Z>() {
            @Override
            public Z apply(List<Object> results) {
                return function.apply((A) results.get(0), (B) results.get(1), (C) results.get(2),
                        (D) results.get(3));
            }
        });
    }

    /**
     * Implementations of this interface is used to synchronously transform the
     * input values into an output value.
     */
    public interface Function4<Z, A, B, C, D> {
        Z apply(A a, B b, C c, D d);
    }

    /**
     * Transform the input futures into a single future, using the provided
     * transform function. The transformation follows the same semantics as as
     * {@link Futures#transform(ListenableFuture, AsyncFunction)} and the input
     * futures are combined using {@link Futures#allAsList}.
     *
     * @param a a ListenableFuture to combine
     * @param b a ListenableFuture to combine
     * @param c a ListenableFuture to combine
     * @param d a ListenableFuture to combine
     * @param function the implementation of the transform
     * @return a ListenableFuture holding the result of function.apply()
     */
    public static <Z, A, B, C, D> ListenableFuture<Z> asyncTransform4(ListenableFuture<A> a, ListenableFuture<B> b,
            ListenableFuture<C> c, ListenableFuture<D> d,
            final AsyncFunction4<Z, ? super A, ? super B, ? super C, ? super D> function) {
        return transform(Arrays.asList(a, b, c, d), new AsyncFunction<List<Object>, Z>() {
            @Override
            public ListenableFuture<Z> apply(List<Object> results) throws Exception {
                return function.apply((A) results.get(0), (B) results.get(1), (C) results.get(2),
                        (D) results.get(3));
            }
        });
    }

    public interface AsyncFunction4<Z, A, B, C, D> {
        ListenableFuture<Z> apply(A a, B b, C c, D d) throws Exception;
    }

    /**
     * Transform the input futures into a single future, using the provided
     * transform function. The transformation follows the same semantics as as
     * {@link Futures#transform(ListenableFuture, Function)} and the input
     * futures are combined using {@link Futures#allAsList}.
     *
     * @param a a ListenableFuture to combine
     * @param b a ListenableFuture to combine
     * @param c a ListenableFuture to combine
     * @param d a ListenableFuture to combine
     * @param e a ListenableFuture to combine
     * @param function the implementation of the transform
     * @return a ListenableFuture holding the result of function.apply()
     */
    public static <Z, A, B, C, D, E> ListenableFuture<Z> syncTransform5(ListenableFuture<A> a,
            ListenableFuture<B> b, ListenableFuture<C> c, ListenableFuture<D> d, ListenableFuture<E> e,
            final Function5<Z, ? super A, ? super B, ? super C, ? super D, ? super E> function) {
        return transform(Arrays.asList(a, b, c, d, e), new Function<List<Object>, Z>() {
            @Override
            public Z apply(List<Object> results) {
                return function.apply((A) results.get(0), (B) results.get(1), (C) results.get(2),
                        (D) results.get(3), (E) results.get(4));
            }
        });
    }

    /**
     * Implementations of this interface is used to synchronously transform the
     * input values into an output value.
     */
    public interface Function5<Z, A, B, C, D, E> {
        Z apply(A a, B b, C c, D d, E e);
    }

    /**
     * Transform the input futures into a single future, using the provided
     * transform function. The transformation follows the same semantics as as
     * {@link Futures#transform(ListenableFuture, AsyncFunction)} and the input
     * futures are combined using {@link Futures#allAsList}.
     *
     * @param a a ListenableFuture to combine
     * @param b a ListenableFuture to combine
     * @param c a ListenableFuture to combine
     * @param d a ListenableFuture to combine
     * @param e a ListenableFuture to combine
     * @param function the implementation of the transform
     * @return a ListenableFuture holding the result of function.apply()
     */
    public static <Z, A, B, C, D, E> ListenableFuture<Z> asyncTransform5(ListenableFuture<A> a,
            ListenableFuture<B> b, ListenableFuture<C> c, ListenableFuture<D> d, ListenableFuture<E> e,
            final AsyncFunction5<Z, ? super A, ? super B, ? super C, ? super D, ? super E> function) {
        return transform(Arrays.asList(a, b, c, d, e), new AsyncFunction<List<Object>, Z>() {
            @Override
            public ListenableFuture<Z> apply(List<Object> results) throws Exception {
                return function.apply((A) results.get(0), (B) results.get(1), (C) results.get(2),
                        (D) results.get(3), (E) results.get(4));
            }
        });
    }

    public interface AsyncFunction5<Z, A, B, C, D, E> {
        ListenableFuture<Z> apply(A a, B b, C c, D d, E e) throws Exception;
    }

    /**
     * Transform the input futures into a single future, using the provided
     * transform function. The transformation follows the same semantics as as
     * {@link Futures#transform(ListenableFuture, Function)} and the input
     * futures are combined using {@link Futures#allAsList}.
     *
     * @param a a ListenableFuture to combine
     * @param b a ListenableFuture to combine
     * @param c a ListenableFuture to combine
     * @param d a ListenableFuture to combine
     * @param e a ListenableFuture to combine
     * @param f a ListenableFuture to combine
     * @param function the implementation of the transform
     * @return a ListenableFuture holding the result of function.apply()
     */
    public static <Z, A, B, C, D, E, F> ListenableFuture<Z> syncTransform6(ListenableFuture<A> a,
            ListenableFuture<B> b, ListenableFuture<C> c, ListenableFuture<D> d, ListenableFuture<E> e,
            ListenableFuture<F> f,
            final Function6<Z, ? super A, ? super B, ? super C, ? super D, ? super E, ? super F> function) {
        return transform(Arrays.asList(a, b, c, d, e, f), new Function<List<Object>, Z>() {
            @Override
            public Z apply(List<Object> results) {
                return function.apply((A) results.get(0), (B) results.get(1), (C) results.get(2),
                        (D) results.get(3), (E) results.get(4), (F) results.get(5));
            }
        });
    }

    /**
     * Implementations of this interface is used to synchronously transform the
     * input values into an output value.
     */
    public interface Function6<Z, A, B, C, D, E, F> {
        Z apply(A a, B b, C c, D d, E e, F f);
    }

    /**
     * Transform the input futures into a single future, using the provided
     * transform function. The transformation follows the same semantics as as
     * {@link Futures#transform(ListenableFuture, AsyncFunction)} and the input
     * futures are combined using {@link Futures#allAsList}.
     *
     * @param a a ListenableFuture to combine
     * @param b a ListenableFuture to combine
     * @param c a ListenableFuture to combine
     * @param d a ListenableFuture to combine
     * @param e a ListenableFuture to combine
     * @param f a ListenableFuture to combine
     * @param function the implementation of the transform
     * @return a ListenableFuture holding the result of function.apply()
     */
    public static <Z, A, B, C, D, E, F> ListenableFuture<Z> asyncTransform6(ListenableFuture<A> a,
            ListenableFuture<B> b, ListenableFuture<C> c, ListenableFuture<D> d, ListenableFuture<E> e,
            ListenableFuture<F> f,
            final AsyncFunction6<Z, ? super A, ? super B, ? super C, ? super D, ? super E, ? super F> function) {
        return transform(Arrays.asList(a, b, c, d, e, f), new AsyncFunction<List<Object>, Z>() {
            @Override
            public ListenableFuture<Z> apply(List<Object> results) throws Exception {
                return function.apply((A) results.get(0), (B) results.get(1), (C) results.get(2),
                        (D) results.get(3), (E) results.get(4), (F) results.get(5));
            }
        });
    }

    public interface AsyncFunction6<Z, A, B, C, D, E, F> {
        ListenableFuture<Z> apply(A a, B b, C c, D d, E e, F f) throws Exception;
    }

    private static <Z> ListenableFuture<Z> transform(final List<? extends ListenableFuture<?>> inputs,
            final Function<List<Object>, Z> function) {
        return Futures.transform(Futures.allAsList(inputs), function);
    }

    private static <Z> ListenableFuture<Z> transform(final List<? extends ListenableFuture<?>> inputs,
            final AsyncFunction<List<Object>, Z> function) {
        return Futures.transform(Futures.allAsList(inputs), function);
    }

    /**
     * <p>Transform a list of futures to a future that returns a joined result of them all.
     * The result can be used to get the joined results and ensures no future that were not part of
     * the join is accessed.</p>
     * @see #join(ListenableFuture...)
     */
    public static ListenableFuture<JoinedResults> join(List<? extends ListenableFuture<?>> inputs) {
        return Futures.transform(Futures.allAsList(inputs), new JoinedResults.Transform(inputs));
    }

    /**
     * <p>Transform a list of futures to a future that returns a joined result of them all.
     * The result can be used to get the joined results and ensures no future that were not part of
     * the join is accessed.</p>
     * <p>Example</p>
     * <pre>
     * {@code
     * final Future<String> first = Futures.immediateFuture("ok");
     * final Future<Integer> second = Futures.immediateFuture(1);
     * JoinedResults futures = FuturesExtra.join(first, second).get();
     * assertEquals("ok", futures.get(first));
     * assertEquals(1, futures.get(second));
     * }
     * </pre>
     */
    public static ListenableFuture<JoinedResults> join(ListenableFuture<?>... inputs) {
        List<? extends ListenableFuture<?>> list = Arrays.asList(inputs);
        return Futures.transform(Futures.allAsList(list), new JoinedResults.Transform(list));
    }

    public static <I, O> ListenableFuture<O> syncTransform(ListenableFuture<I> input,
            Function<? super I, ? extends O> function) {
        return Futures.transform(input, function);
    }

    public static <I, O> ListenableFuture<O> asyncTransform(ListenableFuture<I> input,
            AsyncFunction<? super I, ? extends O> function) {
        return Futures.transform(input, function);
    }

    /**
     * check that a future is completed.
     * @param future the future.
     * @throws IllegalStateException if the future is not completed.
     */
    public static <T> void checkCompleted(ListenableFuture<T> future) {
        if (!future.isDone()) {
            throw new IllegalStateException("future was not completed");
        }
    }

    /**
     * Get the value of a completed future.
     *
     * @param future a completed future.
     * @return the value of the future if it has one.
     * @throws IllegalStateException if the future is not completed.
     * @throws com.google.common.util.concurrent.UncheckedExecutionException if the future has failed
     */
    public static <T> T getCompleted(ListenableFuture<T> future) {
        checkCompleted(future);
        return Futures.getUnchecked(future);
    }

    /**
     * Get the exception of a completed future.
     *
     * @param future a completed future.
     * @return the exception of a future or null if no exception was thrown
     * @throws IllegalStateException if the future is not completed.
     */
    public static <T> Throwable getException(ListenableFuture<T> future) {
        checkCompleted(future);
        try {
            Uninterruptibles.getUninterruptibly(future);
            return null;
        } catch (ExecutionException e) {
            return e.getCause();
        }
    }
}