Java tutorial
/* 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); } } }; } }