Last active
February 24, 2017 04:10
-
-
Save rage-shadowman/eecf773585c7fa976cf087abbd117204 to your computer and use it in GitHub Desktop.
An attempt at a safe CompletionStage/Future class. You will need to use CompletableFuture in your own code and complete it (either successfully or exceptionally) as you normally would, but then you can wrap it in a SafePromise to return something less dangerous to your callers.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.rage.shadowman.promise; | |
import java.util.concurrent.CompletableFuture; | |
import java.util.concurrent.CompletionStage; | |
import java.util.concurrent.ExecutionException; | |
import java.util.concurrent.Executor; | |
import java.util.concurrent.Future; | |
import java.util.concurrent.TimeUnit; | |
import java.util.concurrent.TimeoutException; | |
import java.util.function.BiConsumer; | |
import java.util.function.BiFunction; | |
import java.util.function.Consumer; | |
import java.util.function.Function; | |
/** | |
* ChainableFuture is basically a {@link Future} and {@link CompletionStage} without exposing the dangerous bits that | |
* you don't want your clients to have access to, such as {@link CompletionStage#toCompletableFuture} (which would | |
* expose {@link CompletableFuture#complete} and {@link CompletableFuture#completeExceptionally} methods). | |
*/ | |
public class ChainableFuture<T> | |
implements CompletionStage<T>, Future<T> | |
{ | |
private final CompletableFuture<T> unsafe; | |
public ChainableFuture (CompletableFuture<T> unsafe) | |
{ | |
this.unsafe = unsafe; | |
} | |
@SuppressWarnings("unchecked") | |
private <U> ChainableFuture<U> wrap (CompletableFuture<U> completableFuture) | |
{ | |
if (completableFuture == unsafe) | |
return (ChainableFuture<U>)this; | |
return new ChainableFuture<>(completableFuture); | |
} | |
private static <T> CompletionStage<T> unwrap (CompletionStage<T> completionStage) | |
{ | |
if (completionStage instanceof ChainableFuture) | |
return ((ChainableFuture<T>)completionStage).unsafe; | |
return completionStage; | |
} | |
// useful static methods from CompletableFuture | |
public static ChainableFuture<Void> completedFuture () | |
{ | |
return new ChainableFuture<>(CompletableFuture.completedFuture(null)); | |
} | |
public static <T> ChainableFuture<T> completedFuture (T value) | |
{ | |
return new ChainableFuture<>(CompletableFuture.completedFuture(value)); | |
} | |
public static <T> ChainableFuture<T> exceptedFuture (Throwable thrown) | |
{ | |
CompletableFuture<T> unsafeFuture = new CompletableFuture<>(); | |
unsafeFuture.completeExceptionally(thrown); | |
return new ChainableFuture<>(unsafeFuture); | |
} | |
public static ChainableFuture<Void> allOf (Collection<? extends ChainableFuture<?>> safeFutures) | |
{ | |
ChainableFuture<?>[] array = safeFutures.toArray(new ChainableFuture<?>[safeFutures.size()]); | |
return allOf(array); | |
} | |
public static ChainableFuture<Void> allOf (ChainableFuture<?>... safeFutures) | |
{ | |
CompletableFuture<?>[] unsafeFutures = new CompletableFuture[safeFutures.length]; | |
for (int i = 0; i < unsafeFutures.length; ++i) | |
unsafeFutures[i] = safeFutures[i].unsafe; | |
return new ChainableFuture<>(CompletableFuture.allOf(unsafeFutures)); | |
} | |
// unsafe methods that should not be exposed via CompletionStage | |
@Override | |
public CompletableFuture<T> toCompletableFuture () | |
{ | |
throw new UnsupportedOperationException("ChainableFuture cannot does not support calls to unsafe toCompletableFuture method"); | |
} | |
// safe wrapped Future calls | |
@Override | |
public boolean cancel (boolean mayInterruptIfRunning) | |
{ | |
return unsafe.cancel(mayInterruptIfRunning); | |
} | |
@Override | |
public boolean isCancelled () | |
{ | |
return unsafe.isCancelled(); | |
} | |
@Override | |
public boolean isDone () | |
{ | |
return unsafe.isDone(); | |
} | |
@Override | |
public T get () | |
throws InterruptedException, ExecutionException | |
{ | |
return unsafe.get(); | |
} | |
@Override | |
public T get (long timeout, TimeUnit unit) | |
throws InterruptedException, ExecutionException, TimeoutException | |
{ | |
return unsafe.get(timeout, unit); | |
} | |
// safe wrapped CompletionStage calls | |
@Override | |
public <U> ChainableFuture<U> thenApply ( | |
Function<? super T, ? extends U> fn) | |
{ | |
return wrap(unsafe.thenApply(fn)); | |
} | |
@Override | |
public <U> ChainableFuture<U> thenApplyAsync ( | |
Function<? super T, ? extends U> fn) | |
{ | |
return wrap(unsafe.thenApplyAsync(fn)); | |
} | |
@Override | |
public <U> ChainableFuture<U> thenApplyAsync ( | |
Function<? super T, ? extends U> fn, | |
Executor executor) | |
{ | |
return wrap(unsafe.thenApplyAsync(fn, executor)); | |
} | |
@Override | |
public ChainableFuture<Void> thenAccept ( | |
Consumer<? super T> action) | |
{ | |
return wrap(unsafe.thenAccept(action)); | |
} | |
@Override | |
public ChainableFuture<Void> thenAcceptAsync ( | |
Consumer<? super T> action) | |
{ | |
return wrap(unsafe.thenAcceptAsync(action)); | |
} | |
@Override | |
public ChainableFuture<Void> thenAcceptAsync ( | |
Consumer<? super T> action, | |
Executor executor) | |
{ | |
return wrap(unsafe.thenAcceptAsync(action, executor)); | |
} | |
@Override | |
public ChainableFuture<Void> thenRun ( | |
Runnable action) | |
{ | |
return wrap(unsafe.thenRun(action)); | |
} | |
@Override | |
public ChainableFuture<Void> thenRunAsync ( | |
Runnable action) | |
{ | |
return wrap(unsafe.thenRunAsync(action)); | |
} | |
@Override | |
public ChainableFuture<Void> thenRunAsync ( | |
Runnable action, | |
Executor executor) | |
{ | |
return wrap(unsafe.thenRunAsync(action, executor)); | |
} | |
@Override | |
public <U, V> ChainableFuture<V> thenCombine ( | |
CompletionStage<? extends U> other, | |
BiFunction<? super T, ? super U, ? extends V> fn) | |
{ | |
CompletionStage<? extends U> unwrapped = unwrap(other); | |
return wrap(unsafe.thenCombine(unwrapped, fn)); | |
} | |
@Override | |
public <U, V> ChainableFuture<V> thenCombineAsync ( | |
CompletionStage<? extends U> other, | |
BiFunction<? super T, ? super U, ? extends V> fn) | |
{ | |
CompletionStage<? extends U> unwrapped = unwrap(other); | |
return wrap(unsafe.thenCombineAsync(unwrapped, fn)); | |
} | |
@Override | |
public <U, V> ChainableFuture<V> thenCombineAsync ( | |
CompletionStage<? extends U> other, | |
BiFunction<? super T, ? super U, ? extends V> fn, | |
Executor executor) | |
{ | |
CompletionStage<? extends U> unwrapped = unwrap(other); | |
return wrap(unsafe.thenCombineAsync(unwrapped, fn, executor)); | |
} | |
@Override | |
public <U> ChainableFuture<Void> thenAcceptBoth ( | |
CompletionStage<? extends U> other, | |
BiConsumer<? super T, ? super U> action) | |
{ | |
CompletionStage<? extends U> unwrapped = unwrap(other); | |
return wrap(unsafe.thenAcceptBoth(unwrapped, action)); | |
} | |
@Override | |
public <U> ChainableFuture<Void> thenAcceptBothAsync ( | |
CompletionStage<? extends U> other, | |
BiConsumer<? super T, ? super U> action) | |
{ | |
CompletionStage<? extends U> unwrapped = unwrap(other); | |
return wrap(unsafe.thenAcceptBothAsync(unwrapped, action)); | |
} | |
@Override | |
public <U> ChainableFuture<Void> thenAcceptBothAsync ( | |
CompletionStage<? extends U> other, | |
BiConsumer<? super T, ? super U> action, | |
Executor executor) | |
{ | |
CompletionStage<? extends U> unwrapped = unwrap(other); | |
return wrap(unsafe.thenAcceptBothAsync(unwrapped, action, executor)); | |
} | |
@Override | |
public ChainableFuture<Void> runAfterBoth ( | |
CompletionStage<?> other, | |
Runnable action) | |
{ | |
CompletionStage<?> unwrapped = unwrap(other); | |
return wrap(unsafe.runAfterBoth(unwrapped, action)); | |
} | |
@Override | |
public ChainableFuture<Void> runAfterBothAsync ( | |
CompletionStage<?> other, | |
Runnable action) | |
{ | |
CompletionStage<?> unwrapped = unwrap(other); | |
return wrap(unsafe.runAfterBothAsync(unwrapped, action)); | |
} | |
@Override | |
public ChainableFuture<Void> runAfterBothAsync ( | |
CompletionStage<?> other, | |
Runnable action, Executor executor) | |
{ | |
CompletionStage<?> unwrapped = unwrap(other); | |
return wrap(unsafe.runAfterBothAsync(unwrapped, action, executor)); | |
} | |
@Override | |
public <U> ChainableFuture<U> applyToEither ( | |
CompletionStage<? extends T> other, | |
Function<? super T, U> fn) | |
{ | |
CompletionStage<? extends T> unwrapped = unwrap(other); | |
return wrap(unsafe.applyToEither(unwrapped, fn)); | |
} | |
@Override | |
public <U> ChainableFuture<U> applyToEitherAsync ( | |
CompletionStage<? extends T> other, | |
Function<? super T, U> fn) | |
{ | |
CompletionStage<? extends T> unwrapped = unwrap(other); | |
return wrap(unsafe.applyToEitherAsync(unwrapped, fn)); | |
} | |
@Override | |
public <U> ChainableFuture<U> applyToEitherAsync ( | |
CompletionStage<? extends T> other, | |
Function<? super T, U> fn, | |
Executor executor) | |
{ | |
CompletionStage<? extends T> unwrapped = unwrap(other); | |
return wrap(unsafe.applyToEitherAsync(unwrapped, fn, executor)); | |
} | |
@Override | |
public ChainableFuture<Void> acceptEither ( | |
CompletionStage<? extends T> other, | |
Consumer<? super T> action) | |
{ | |
CompletionStage<? extends T> unwrapped = unwrap(other); | |
return wrap(unsafe.acceptEither(unwrapped, action)); | |
} | |
@Override | |
public ChainableFuture<Void> acceptEitherAsync ( | |
CompletionStage<? extends T> other, | |
Consumer<? super T> action) | |
{ | |
CompletionStage<? extends T> unwrapped = unwrap(other); | |
return wrap(unsafe.acceptEitherAsync(unwrapped, action)); | |
} | |
@Override | |
public ChainableFuture<Void> acceptEitherAsync ( | |
CompletionStage<? extends T> other, | |
Consumer<? super T> action, | |
Executor executor) | |
{ | |
CompletionStage<? extends T> unwrapped = unwrap(other); | |
return wrap(unsafe.acceptEitherAsync(unwrapped, action, executor)); | |
} | |
@Override | |
public ChainableFuture<Void> runAfterEither ( | |
CompletionStage<?> other, | |
Runnable action) | |
{ | |
CompletionStage<?> unwrapped = unwrap(other); | |
return wrap(unsafe.runAfterEither(unwrapped, action)); | |
} | |
@Override | |
public ChainableFuture<Void> runAfterEitherAsync ( | |
CompletionStage<?> other, | |
Runnable action) | |
{ | |
CompletionStage<?> unwrapped = unwrap(other); | |
return wrap(unsafe.runAfterEitherAsync(unwrapped, action)); | |
} | |
@Override | |
public ChainableFuture<Void> runAfterEitherAsync ( | |
CompletionStage<?> other, | |
Runnable action, | |
Executor executor) | |
{ | |
CompletionStage<?> unwrapped = unwrap(other); | |
return wrap(unsafe.runAfterEitherAsync(unwrapped, action, executor)); | |
} | |
@Override | |
public <U> ChainableFuture<U> thenCompose ( | |
Function<? super T, ? extends CompletionStage<U>> fn) | |
{ | |
return wrap(unsafe.thenCompose(fn)); | |
} | |
@Override | |
public <U> ChainableFuture<U> thenComposeAsync ( | |
Function<? super T, ? extends CompletionStage<U>> fn) | |
{ | |
return wrap(unsafe.thenComposeAsync(fn)); | |
} | |
@Override | |
public <U> ChainableFuture<U> thenComposeAsync ( | |
Function<? super T, ? extends CompletionStage<U>> fn, | |
Executor executor) | |
{ | |
return wrap(unsafe.thenComposeAsync(fn, executor)); | |
} | |
@Override | |
public ChainableFuture<T> exceptionally ( | |
Function<Throwable, ? extends T> fn) | |
{ | |
return wrap(unsafe.exceptionally(fn)); | |
} | |
@Override | |
public ChainableFuture<T> whenComplete ( | |
BiConsumer<? super T, ? super Throwable> action) | |
{ | |
return wrap(unsafe.whenComplete(action)); | |
} | |
@Override | |
public ChainableFuture<T> whenCompleteAsync ( | |
BiConsumer<? super T, ? super Throwable> action) | |
{ | |
return wrap(unsafe.whenCompleteAsync(action)); | |
} | |
@Override | |
public ChainableFuture<T> whenCompleteAsync ( | |
BiConsumer<? super T, ? super Throwable> action, | |
Executor executor) | |
{ | |
return wrap(unsafe.whenCompleteAsync(action, executor)); | |
} | |
@Override | |
public <U> ChainableFuture<U> handle ( | |
BiFunction<? super T, Throwable, ? extends U> fn) | |
{ | |
return wrap(unsafe.handle(fn)); | |
} | |
@Override | |
public <U> ChainableFuture<U> handleAsync ( | |
BiFunction<? super T, Throwable, ? extends U> fn) | |
{ | |
return wrap(unsafe.handleAsync(fn)); | |
} | |
@Override | |
public <U> ChainableFuture<U> handleAsync ( | |
BiFunction<? super T, Throwable, ? extends U> fn, | |
Executor executor) | |
{ | |
return wrap(unsafe.handleAsync(fn, executor)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Sadly, this does not work when passed into a CompletableFuture (non-ChainedFuture class) method that depends on a call to the
toCompletableFuture()
method. So, not implementing CompletionStage is your best bet here if you can get away with creating your own class that extends onlyFuture
but notCompletionStage
.