All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.tascalate.concurrent.AbstractCompletableTask Maven / Gradle / Ivy

Go to download

Implementation of blocking (IO-Bound) cancellable java.util.concurrent.CompletionStage and related extensions to java.util.concurrent.ExecutorService-s

There is a newer version: 0.7.1
Show newest version
/**
 * Original work: copyright 2009-2015 Lukáš Křečan 
 * https://github.com/lukas-krecan/completion-stage
 * 
 * This class is based on the work create by Lukáš Křečan 
 * under the Apache License, Version 2.0. Please see 
 * https://github.com/lukas-krecan/completion-stage/blob/completion-stage-0.0.9/src/main/java/net/javacrumbs/completionstage/SimpleCompletionStage.java
 * 
 * Modified work: copyright 2015-2017 Valery Silaev (http://vsilaev.com)
 * 
 * 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 net.tascalate.concurrent;

import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RunnableFuture;
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;

/**
 * Base superclass for both root and intermediate {@link Promise}-s that
 * represent blocking long-running tasks
 * 
 * @author vsilaev
 *
 * @param 
 *   a type of the successfully executed task result   
 */
abstract class AbstractCompletableTask extends PromiseAdapter implements Promise {

    private final CallbackRegistry callbackRegistry = new CallbackRegistry<>();
    protected final RunnableFuture task;
    protected final Callable action;

    protected AbstractCompletableTask(Executor defaultExecutor, Callable action) {
        super(defaultExecutor);
        this.action = action;
        this.task = new StageTransition(action);
    }

    abstract Runnable setupTransition(Callable code);

    @Override
    public boolean cancel(boolean mayInterruptIfRunning) {
        if (task.cancel(mayInterruptIfRunning)) {
            onError(new CancellationException());
            return true;
        } else {
            return false;
        }
    }

    @Override
    public boolean isCancelled() {
        return task.isCancelled();
    }

    @Override
    public boolean isDone() {
        return task.isDone();
    }

    @Override
    public T get() throws InterruptedException, ExecutionException {
        try {
            return task.get();
        } catch (ExecutionException ex) {
            throw rewrapExecutionException(ex);
        }
    }

    @Override
    public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
        try {
            return task.get(timeout, unit);
        } catch (ExecutionException ex) {
            throw rewrapExecutionException(ex);
        }
    }

    boolean onSuccess(T result) {
        return callbackRegistry.success(result);
    }

    boolean onError(Throwable ex) {
        return callbackRegistry.failure(ex);
    }

    class StageTransition extends FutureTask {
        StageTransition(Callable callable) {
            super(callable);
        }

        @Override
        protected void set(T v) {
            super.set(v);
            onSuccess(v);
        };

        @Override
        protected void setException(Throwable t) {
            super.setException(t);
            onError(t);
        };
    }

    @Override
    public  Promise thenApplyAsync(Function fn, Executor executor) {
        AbstractCompletableTask nextStage = internalCreateCompletionStage(executor);
        addCallbacks(nextStage, fn, executor);
        return nextStage;
    }

    @Override
    public Promise thenAcceptAsync(Consumer action, Executor executor) {
        return thenApplyAsync(consumerAsFunction(action), executor);
    }

    @Override
    public Promise thenRunAsync(Runnable action, Executor executor) {
        return thenApplyAsync(runnableAsFunction(action), executor);
    }

    @Override
    public  Promise thenCombineAsync(CompletionStage other,
                                              BiFunction fn, 
                                              Executor executor) {

        return thenCompose(result1 -> other.thenApplyAsync(result2 -> fn.apply(result1, result2), executor));
    }

    @Override
    public  Promise thenAcceptBothAsync(CompletionStage other,
                                                 BiConsumer action, 
                                                 Executor executor) {
        return thenCombineAsync(other,
                // transform BiConsumer to BiFunction
                (t, u) -> {
                    action.accept(t, u);
                    return null;
                }, executor);
    }

    @Override
    public Promise runAfterBothAsync(CompletionStage other, 
                                           Runnable action, 
                                           Executor executor) {
        return thenCombineAsync(other,
                // transform Runnable to BiFunction
                (t, r) -> {
                    action.run();
                    return null;
                }, executor);
    }

    @Override
    public  Promise applyToEitherAsync(CompletionStage other, 
                                             Function fn,
                                             Executor executor) {

        return doApplyToEitherAsync(this, other, fn, executor);
    }

    @Override
    public Promise acceptEitherAsync(CompletionStage other,
                                           Consumer action,
                                           Executor executor) {

        return applyToEitherAsync(other, consumerAsFunction(action), executor);
    }

    @Override
    public Promise runAfterEitherAsync(CompletionStage other, 
                                             Runnable action, 
                                             Executor executor) {
        
        return doApplyToEitherAsync(this, other, runnableAsFunction(action), executor);
    }

    @Override
    public  Promise thenComposeAsync(Function> fn, Executor executor) {

        AbstractCompletableTask nextStage = internalCreateCompletionStage(executor);
        AbstractCompletableTask tempStage = internalCreateCompletionStage(executor);

        // We must ALWAYS run through the execution
        // of nextStage.task when this nextStage is
        // exposed to the client, even in a "trivial" case:
        // Success path, just return value
        // Failure path, just re-throw exception
        Executor helperExecutor = SAME_THREAD_EXECUTOR;
        BiConsumer moveToNextStage = (r, e) -> {
            if (null == e)
                runDirectly(nextStage, Function.identity(), r, helperExecutor);
            else
                runDirectly(nextStage, AbstractCompletableTask::forwardException, e, helperExecutor);
        };

        // Important -- tempStage is the target here
        addCallbacks(
            tempStage, 
            consumerAsFunction(r -> fn.apply(r).whenComplete(moveToNextStage)), 
            e -> { moveToNextStage.accept(null, e); return null; }, /* must-have if fn.apply above failed */
            executor
        );

        return nextStage;
    }

    @Override
    public Promise exceptionally(Function fn) {
        AbstractCompletableTask nextStage = internalCreateCompletionStage(getDefaultExecutor());
        addCallbacks(nextStage, Function.identity(), fn, SAME_THREAD_EXECUTOR);
        return nextStage;
    }

    @Override
    public Promise whenCompleteAsync(BiConsumer action, Executor executor) {
        AbstractCompletableTask nextStage = internalCreateCompletionStage(getDefaultExecutor());
        addCallbacks(
            nextStage, 
            result -> {
                action.accept(result, null);
                return result;
            }, 
            failure -> {
                try {
                    action.accept(null, failure);
                    return forwardException(failure);
                } catch (Throwable e) {
                    return forwardException(e);
                }
            }, 
            executor
        );
        return nextStage;
    }

    @Override
    public  Promise handleAsync(BiFunction fn, Executor executor) {
        AbstractCompletableTask nextStage = internalCreateCompletionStage(executor);
        addCallbacks(
            nextStage, 
            result -> fn.apply(result, null),
            // exceptions are treated as success
            error -> fn.apply(null, error), executor
        );
        return nextStage;
    }

    @Override
    public CompletableFuture toCompletableFuture() {
        CompletableFuture completableFuture = new CompletableFuture<>();
        // nextStage is CompletableFuture rather than AbstractCompletableTask
        // so trigger completion on ad-hoc runnable rather than on
        // nextStage.task
        Function, Runnable> setup = c -> () -> {
            try {
                c.call();
            } catch (final Throwable ex) {
                completableFuture.completeExceptionally(ex);
            }
        };
        addCallbacks(
            setup, 
            consumerAsFunction(completableFuture::complete),
            consumerAsFunction(completableFuture::completeExceptionally), 
            SAME_THREAD_EXECUTOR
        );
        return completableFuture;
    }

    abstract protected  AbstractCompletableTask createCompletionStage(Executor executor);

    /**
     * This method exists just to reconcile generics when called from
     * {@link #runAfterEitherAsync} which has unexpected type of parameter
     * "other". The alternative is to ignore compiler warning.
     */
    private  Promise doApplyToEitherAsync(CompletionStage first,
                                                   CompletionStage second, 
                                                   Function fn, 
                                                   Executor executor) {

        AbstractCompletableTask nextStage = internalCreateCompletionStage(executor);

        // Next stage is not exposed to the client, so we can
        // short-circuit its initiation - just fire callbacks
        // without task execution (unlike as in other methods,
        // event in thenComposeAsync with its ad-hoc execution)

        // In certain sense, nextStage here is bogus: neither
        // of Future-defined methods are functional.
        BiConsumer action = (result, failure) -> {
            if (failure == null) {
                nextStage.onSuccess(result);
            } else {
                nextStage.onError(wrapException(failure));
            }
        };
        // only the first result is accepted by completion stage,
        // the other one is ignored
        first.whenComplete(action);
        second.whenComplete(action);

        return nextStage.thenApplyAsync(fn, executor);
    }

    private  AbstractCompletableTask internalCreateCompletionStage(Executor executor) {
        // Preserve default async executor, or use user-supplied executor as default
        // But don't let SAME_THREAD_EXECUTOR to be a default async executor
        return createCompletionStage(executor == SAME_THREAD_EXECUTOR ? getDefaultExecutor() : executor);
    }

    private static  Function consumerAsFunction(Consumer action) {
        return result -> {
            action.accept(result);
            return null;
        };
    }

    private static  Function runnableAsFunction(Runnable action) {
        return result -> {
            action.run();
            return null;
        };
    }

    private static  U forwardException(Throwable e) {
        throw wrapException(e);
    }

    private static CompletionException wrapException(Throwable e) {
        if (e instanceof CompletionException) {
            return (CompletionException) e;
        } else {
            return new CompletionException(e);
        }
    }
    
    private static ExecutionException rewrapExecutionException(ExecutionException ex) {
        if (ex.getCause() instanceof CompletionException) {
            Throwable completionExceptionReason = ex.getCause().getCause();
            if (null != completionExceptionReason) {
                return new ExecutionException(ex.getMessage(), completionExceptionReason);
            }
        }
        return ex;
    }    

    private  void addCallbacks(AbstractCompletableTask targetStage,
                                  Function successCallback, 
                                  Executor executor) {
        
        addCallbacks(targetStage, successCallback, AbstractCompletableTask::forwardException, executor);
    }

    private  void addCallbacks(AbstractCompletableTask targetStage,
                                  Function successCallback, 
                                  Function failureCallback,
                                  Executor executor) {
        
        addCallbacks(targetStage::setupTransition, successCallback, failureCallback, executor);
    }

    private  void addCallbacks(Function, ? extends Runnable> targetSetup,
                                  Function successCallback, 
                                  Function failureCallback,
                                  Executor executor) {
        
        callbackRegistry.addCallbacks(targetSetup, successCallback, failureCallback, executor);
    }

    private static  void runDirectly(AbstractCompletableTask targetStage,
                                           Function callback, 
                                           S value, 
                                           Executor executor) {

        CallbackRegistry.callCallback(targetStage::setupTransition, callback, value, executor);
    }
}