com.tinspx.util.concurrent.FutureUtils.java Source code

Java tutorial

Introduction

Here is the source code for com.tinspx.util.concurrent.FutureUtils.java

Source

/* Copyright (C) 2013-2014 Ian Teune <ian.teune@gmail.com>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
package com.tinspx.util.concurrent;

import com.google.common.base.Function;
import static com.google.common.base.Preconditions.*;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.AbstractFuture;
import com.google.common.util.concurrent.AsyncFunction;
import com.google.common.util.concurrent.ForwardingListenableFuture;
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.ListenableScheduledFuture;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.SettableFuture;
import java.util.List;
import java.util.concurrent.Delayed;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import lombok.EqualsAndHashCode;
import lombok.NonNull;
import lombok.ToString;

/**
 * Basic {@link ListenableFuture} utility functions.
 *
 * @see Futures
 * @author Ian
 */
public class FutureUtils {
    private FutureUtils() {
    }

    /**
     * Cancels all Futures in {@code futures} with the
     * {@code mayInterruptIfRunning} argument. null Futures in {@code futures}
     * are ignored and will not cause a NPE
     *
     * @param mayInterruptIfRunning argument for {@link Future#cancel(boolean)}
     * @param futures the Futures to cancel, may have null elements
     */
    public static void cancelNullableFutures(boolean mayInterruptIfRunning, Future<?>... futures) {
        for (Future<?> f : futures) {
            if (f != null) {
                f.cancel(mayInterruptIfRunning);
            }
        }
    }

    /**
     * Cancels all Futures in {@code futures} with the
     * {@code mayInterruptIfRunning} argument. null Futures in {@code futures}
     * are ignored and will not cause a NPE
     *
     * @param mayInterruptIfRunning argument for {@link Future#cancel(boolean)}
     * @param futures the Futures to cancel, may have null elements
     */
    public static void cancelNullableFutures(boolean mayInterruptIfRunning, Iterable<? extends Future<?>> futures) {
        for (Future<?> f : futures) {
            if (f != null) {
                f.cancel(mayInterruptIfRunning);
            }
        }
    }

    /**
     * Combines multiple Futures into a single ListenableFuture whose result is
     * a List of the ListenableFutures in {@code futures}. The returned Future's
     * List always contains all of the Futures in {@code futures}, regardless of
     * whether they failed or succeeded.
     * <p>
     * The returned future will always succeed. It completes once all the
     * Futures in {@code futures} are complete (through either success or
     * failure).
     * <p>
     * If the returned Future is canceled, it will attempt to cancel all Futures
     * in {@code futures}.
     *
     * @param futures the Futures to combine
     * @return a ListenableFuture that whose result is a List of all Futures in
     * {@code futures} and always succeeds once all the Futures in
     * {@code futures} are complete
     */
    public static <T extends ListenableFuture<?>> ListenableFuture<List<T>> combine(Iterable<T> futures) {
        return FutureCollection.create(futures);
    }

    static class FutureCollection<T extends ListenableFuture<?>> extends AbstractFuture<List<T>>
            implements Runnable {

        static <T extends ListenableFuture<?>> FutureCollection<T> create(Iterable<T> futures) {
            FutureCollection<T> fc = new FutureCollection<T>(futures);
            fc.addListeners();
            return fc;
        }

        final List<T> futures;
        final AtomicInteger count;

        @SuppressWarnings("LeakingThisInConstructor")
        public FutureCollection(Iterable<T> f) {
            futures = ImmutableList.copyOf(f);
            count = futures.isEmpty() ? null : new AtomicInteger(futures.size());
        }

        private void addListeners() {
            if (futures.isEmpty()) {
                set(futures);
            } else {
                for (ListenableFuture<?> future : futures) {
                    future.addListener(this, MoreExecutors.directExecutor());
                }
            }
        }

        @Override
        public void run() {
            if (count.decrementAndGet() == 0) {
                set(futures);
            }
        }

        @Override
        public boolean cancel(boolean mayInterruptIfRunning) {
            if (super.cancel(mayInterruptIfRunning)) {
                for (T future : futures) {
                    future.cancel(mayInterruptIfRunning);
                }
                return true;
            }
            return false;
        }
    }

    /**
     * Links {@code link} to {@code source}. The result or exception from
     * {@code source} is set on {@code link}. This is a one-way link, so
     * a result set on {@code source} is also set on {@code link}, but not
     * vice versa (setting a result/exception on {@code link} will have no
     * effect on {@code source}).
     *
     * @param source the result/exception source
     * @param link the Future to be linked to {@code source}, the
     * result/exception of {@code source} is set directly on this Future
     */
    public static <T> void linkFutures(@NonNull ListenableFuture<T> source,
            @NonNull SettableFuture<? super T> link) {
        if (source != link) {
            Futures.addCallback(source, new FutureLinker<T>(link));
        }
    }

    /**
     * Links {@code link} to {@code source}. The result or exception from
     * {@code source} is set on {@code link}. This is a one-way link, so a
     * result set on {@code source} is also set on {@code link}, but not vice
     * versa (setting a result/exception on {@code link} will have no effect on
     * {@code source}).
     * <p>
     * {@code executor} controls which thread
     * {@link SettableFuture#set(Object) set} or
     * {@link SettableFuture#setException(Throwable) setException} is called on
     * {@code link}.
     *
     * @param source the result/exception source
     * @param link the Future to be linked to {@code source}, the
     * result/exception of {@code source} is set directly on this Future
     * @param executor the Executor on which the result of {@code source} is set
     * on {@code link}
     */
    public static <T> void linkFutures(@NonNull ListenableFuture<T> source, @NonNull SettableFuture<? super T> link,
            @NonNull Executor executor) {
        if (source != link) {
            Futures.addCallback(source, new FutureLinker<T>(link), executor);
        }
    }

    static class FutureLinker<T> implements FutureCallback<T> {
        final SettableFuture<? super T> future;

        public FutureLinker(SettableFuture<? super T> future) {
            this.future = checkNotNull(future);
        }

        @Override
        public void onSuccess(T result) {
            future.set(result);
        }

        @Override
        public void onFailure(Throwable t) {
            future.setException(t);
        }
    }

    /**
     * Returns a {@code ListenableScheduledFuture} that forwards all
     * {@link Delayed} methods to the {@code scheduledDelegate} future. All
     * {@code ListenableFuture} methods are forwarded to
     * {@code listenableDelegate}. Canceling the returned future cancels both
     * futures but returns the result of canceling {@code listenableDelegate}.
     * If {@code listenableDelegate} is canceled or fails,
     * {@code scheduledDelegate} is canceled. If {@code listenableDelegate}
     * succeeds, no action is taken on {@code scheduledDelegate}.
     * {@code listenableDelegate} and {@code scheduledDelegate} may be the same
     * reference.
     * <p>
     * This function was designed for the use case of decorating a
     * {@link ScheduledExecutorService}. The {@code ScheduledFuture} comes from
     * the decorated {@code ScheduledExecutorService} and the
     * {@code ListenableFuture} is some internal Future for the task.
     */
    public static <T> ListenableScheduledFuture<T> delegateListenableScheduledFuture(
            ListenableFuture<T> listenableDelegate, ScheduledFuture<?> scheduledDelegate) {
        DelegateListenableScheduledFuture<T> d = new DelegateListenableScheduledFuture<T>(listenableDelegate,
                scheduledDelegate);
        Futures.addCallback(listenableDelegate, d);
        return d;
    }

    /**
     * See {@link #delegateListenableScheduledFuture(ListenableFuture, ScheduledFuture)}.
     * <p>
     * This class is intended to be used when {@code scheduled} controls when
     * the computation {@code delegate} <i>begins</i>. Either {@code scheduled}
     * or {@code delegate} may complete first.
     */
    @EqualsAndHashCode(callSuper = false)
    @ToString
    static class DelegateListenableScheduledFuture<T> extends ForwardingListenableFuture<T>
            implements ListenableScheduledFuture<T>, FutureCallback<T> {

        final ListenableFuture<T> delegate;
        final ScheduledFuture<?> scheduled;

        /**
         * MUST be created through
         * {@link #delegateListenableScheduledFuture(ListenableFuture, ScheduledFuture)}.
         * After construction, this instance MUST be added as a callback to
         * {@code delegate}.
         */
        DelegateListenableScheduledFuture(ListenableFuture<T> delegate, ScheduledFuture<?> scheduled) {
            this.delegate = checkNotNull(delegate);
            this.scheduled = checkNotNull(scheduled);
        }

        @Override
        protected ListenableFuture<T> delegate() {
            return delegate;
        }

        @Override
        public boolean cancel(boolean mayInterruptIfRunning) {
            //Notes: scheduled controls when the computation of delegate begins.
            //either scheduled or delegate may complete first. scheduled is
            //canceled for the sole purpose of stopping the computation
            //of delegate if it has not begun already. delegate is canceled as
            //it is the actual future and isCancelled() forwards to delegate.
            if (scheduled != delegate) {
                scheduled.cancel(mayInterruptIfRunning);
            }
            return delegate.cancel(mayInterruptIfRunning);
        }

        @Override
        public long getDelay(TimeUnit unit) {
            return scheduled.getDelay(unit);
        }

        @Override
        public int compareTo(Delayed o) {
            return scheduled.compareTo(o);
        }

        @Override
        public void onSuccess(T result) {
        }

        @Override
        public void onFailure(Throwable t) {
            if (scheduled != delegate) {
                scheduled.cancel(false);
            }
        }
    }

    /**
     * Returns the result of calling
     * {@link AsyncFunction#apply(java.lang.Object) function.apply(input)}. Any
     * {@code Throwable} thrown from {@code apply} is returned in an failed
     * {@code ListenableFuture} instead of throwing the {@code Exception}.
     */
    @SuppressWarnings({ "BroadCatchBlock", "TooBroadCatch", "unchecked" })
    public static <I, O> ListenableFuture<O> apply(AsyncFunction<? super I, ? extends O> function, I input) {
        checkNotNull(function);
        try {
            return (ListenableFuture<O>) function.apply(input);
        } catch (Throwable ex) {
            return Futures.immediateFailedFuture(ex);
        }
    }

    /**
     * Links the failure of {@code source} to {@code cancelWhenSourceFails}.
     * When {@code source} fails, {@code cancelWhenSourceFails} is canceled with
     * the {@code mayInterruptIfRunning} argument. If {@code source} succeeds,
     * no action is taken on {@code cancelWhenSourceFails}. {@code cancelWhenSourceFails}
     * will be canceled immediately on a direct Executor; same as
     * {@link #linkFailure(ListenableFuture, Future, boolean, Executor)} using
     * {@link MoreExecutors#directExecutor()} as the Executor.
     * 
     * @param source the Future whose failure is propagated to
     * {@code cancelWhenSourceFails}
     * @param cancelWhenSourceFails canceled when {@code source} fails
     * @param mayInterruptIfRunning the argument to
     * {@link Future#cancel(boolean)} if {@code cancelWhenSourceFails} is
     * canceled.
     */
    public static void linkFailure(ListenableFuture<?> source, Future<?> cancelWhenSourceFails,
            boolean mayInterruptIfRunning) {
        linkFailure(source, cancelWhenSourceFails, mayInterruptIfRunning, MoreExecutors.directExecutor());
    }

    /**
     * Links the failure of {@code source} to {@code cancelWhenSourceFails}.
     * When {@code source} fails, {@code cancelWhenSourceFails} is canceled with
     * the {@code mayInterruptIfRunning} argument. If {@code source} succeeds,
     * no action is taken on {@code cancelWhenSourceFails}.
     *
     * @param source the Future whose failure is propagated to
     * {@code cancelWhenSourceFails}
     * @param cancelWhenSourceFails canceled when {@code source} fails
     * @param mayInterruptIfRunning the argument to
     * {@link Future#cancel(boolean)} if {@code cancelWhenSourceFails} is
     * canceled.
     * @param executor the Executor on which {@code cancelWhenSourceFails} will
     * be canceled. Typically this can be done on a direct executor.
     */
    public static void linkFailure(@NonNull ListenableFuture<?> source,
            @NonNull final Future<?> cancelWhenSourceFails, final boolean mayInterruptIfRunning,
            @NonNull Executor executor) {
        if (source == cancelWhenSourceFails) {
            return;
        }
        Futures.addCallback(source, new FutureCallback<Object>() {
            @Override
            public void onSuccess(Object result) {
            }

            @Override
            public void onFailure(Throwable t) {
                cancelWhenSourceFails.cancel(mayInterruptIfRunning);
            }
        }, executor);
    }

    static class FutureResultFunction<T> implements Function<Future<T>, T> {
        static final FutureResultFunction<Object> INSTANCE = new FutureResultFunction<Object>();

        @Override
        @SuppressWarnings({ "BroadCatchBlock", "TooBroadCatch", "UseSpecificCatch" })
        public T apply(Future<T> input) {
            try {
                return input.get();
            } catch (Exception ex) {
                throw Throwables.propagate(ex);
            }
        }

        @Override
        public String toString() {
            return "Future.get()";
        }
    }

    /**
     * Returns a Function that calls {@link Future#get()} to convert the Future
     * to its result. Any Exception thrown by {@link Future#get()} will be
     * propagated as a RuntimeException.
     */
    @SuppressWarnings("unchecked")
    public static <T> Function<Future<T>, T> resultFunction() {
        return (Function<Future<T>, T>) FutureResultFunction.INSTANCE;
    }

    /**
     * Returns a {@code FutureCallback} that forwards to {@code callback} but
     * suppresses and notifies {@code handler} of any uncaught
     * {@code Throwable}.
     * 
     * @since 2.0
     */
    public static <T> FutureCallback<T> notifyAndSuppress(final @NonNull FutureCallback<T> callback,
            final @NonNull Thread.UncaughtExceptionHandler handler) {
        return new FutureCallback<T>() {
            @Override
            public void onSuccess(T result) {
                try {
                    callback.onSuccess(result);
                } catch (Throwable ex) {
                    handler.uncaughtException(Thread.currentThread(), ex);
                }
            }

            @Override
            public void onFailure(Throwable t) {
                try {
                    callback.onFailure(t);
                } catch (Throwable ex) {
                    handler.uncaughtException(Thread.currentThread(), ex);
                }
            }
        };
    }
}