Skip to content

Instantly share code, notes, and snippets.

@rage-shadowman
Last active February 24, 2017 04:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rage-shadowman/eecf773585c7fa976cf087abbd117204 to your computer and use it in GitHub Desktop.
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.
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));
}
}
@rage-shadowman
Copy link
Author

rage-shadowman commented Feb 24, 2017

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 only Future but not CompletionStage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment