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

ratpack.exec.internal.DefaultExecControl Maven / Gradle / Ivy

There is a newer version: 2.0.0-rc-1
Show newest version
/*
 * Copyright 2014 the original author or authors.
 *
 * 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 ratpack.exec.internal;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import io.netty.channel.EventLoop;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import org.slf4j.Logger;
import ratpack.exec.*;
import ratpack.func.Action;
import ratpack.func.BiAction;
import ratpack.func.Block;
import ratpack.registry.RegistrySpec;
import ratpack.stream.Streams;
import ratpack.stream.TransformablePublisher;
import ratpack.util.Exceptions;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;

import static ratpack.func.Action.noop;

public class DefaultExecControl implements ExecControl, ExecControlInternal {

  private static final Logger LOGGER = ExecutionBacking.LOGGER;
  private static final BiAction LOG_UNCAUGHT = (o, t) -> LOGGER.error("Uncaught execution exception", t);
  private static final int MAX_ERRORS_THRESHOLD = 5;

  private final ExecController execController;
  private ImmutableList interceptors = ImmutableList.of();

  public DefaultExecControl(ExecController execController) {
    this.execController = execController;
  }

  @Override
  public void setDefaultInterceptors(ImmutableList interceptors) {
    this.interceptors = interceptors;
  }

  @Override
  public Execution getExecution() throws UnmanagedThreadException {
    return ExecutionBacking.require().getExecution();
  }

  @Override
  public ExecController getController() {
    return execController;
  }

  @Override
  public void addInterceptor(ExecInterceptor execInterceptor, Block continuation) throws Exception {
    ExecutionBacking backing = ExecutionBacking.require();
    backing.addInterceptor(execInterceptor);
    backing.intercept(ExecInterceptor.ExecType.COMPUTE, Collections.singletonList(execInterceptor).iterator(), continuation);
  }

  @Override
  public ExecBuilder fork() {
    return new ExecBuilder() {
      private BiAction onError = LOG_UNCAUGHT;
      private Action onComplete = noop();
      private Action onStart = noop();
      private Action registry = noop();
      private EventLoop eventLoop = execController.getEventLoopGroup().next();

      @Override
      public ExecBuilder eventLoop(EventLoop eventLoop) {
        this.eventLoop = eventLoop;
        return this;
      }

      @Override
      public ExecBuilder onError(BiAction onError) {
        List seen = Lists.newLinkedList();
        this.onError = (e, t) -> {
          if (seen.size() < MAX_ERRORS_THRESHOLD) {
            seen.add(t);
            onError.execute(e, t);
          } else {
            seen.forEach(t::addSuppressed);
            LOGGER.error("Error handler " + onError + "reached maximum error threshold (might be caught in an error loop)", t);
          }
        };
        return this;
      }

      @Override
      public ExecBuilder onError(Action onError) {
        return onError((e, t) -> onError.execute(t));
      }

      @Override
      public ExecBuilder onComplete(Action onComplete) {
        this.onComplete = onComplete;
        return this;
      }

      @Override
      public ExecBuilder onStart(Action onStart) {
        this.onStart = onStart;
        return this;
      }

      @Override
      public ExecBuilder register(Action action) {
        this.registry = action;
        return this;
      }

      @Override
      public void start(Action action) {
        if (eventLoop.inEventLoop() && ExecutionBacking.get() == null) {
          Exceptions.uncheck(() -> new ExecutionBacking(execController, eventLoop, interceptors, registry, action, onError, onStart, onComplete));
        } else {
          eventLoop.submit(() ->
              new ExecutionBacking(execController, eventLoop, interceptors, registry, action, onError, onStart, onComplete)
          );
        }
      }
    };
  }

  @Override
  public  Promise promise(Action> action) {
    return directPromise(upstream(action));
  }

  @Override
  public  Promise blocking(final Callable blockingOperation) {
    return directPromise(downstream -> {
      ExecutionBacking backing = ExecutionBacking.require();
      backing.streamSubscribe(streamHandle ->
          CompletableFuture.supplyAsync(
            new Supplier>() {
              Result result;

              @Override
              public Result get() {
                try {
                  ExecutionBacking.THREAD_BINDING.set(backing);
                  backing.intercept(ExecInterceptor.ExecType.BLOCKING, backing.getAllInterceptors().iterator(), () -> {
                    T value = blockingOperation.call();
                    result = Result.success(value);
                  });
                  return result;
                } catch (Exception e) {
                  return Result.error(e);
                } finally {
                  ExecutionBacking.THREAD_BINDING.remove();
                }
              }
            }, execController.getBlockingExecutor()
          ).thenAcceptAsync(v ->
              streamHandle.complete(() ->
                downstream.accept(v)
              ),
            backing.getEventLoop()
          )
      );
    });
  }

  private  Promise directPromise(Upstream upstream) {
    return new DefaultPromise<>(upstream);
  }

  public  TransformablePublisher stream(Publisher publisher) {
    return Streams.transformable(subscriber -> ExecutionBacking.require().streamSubscribe((handle) ->
        publisher.subscribe(new Subscriber() {
          @Override
          public void onSubscribe(final Subscription subscription) {
            handle.event(() ->
                subscriber.onSubscribe(subscription)
            );
          }

          @Override
          public void onNext(final T element) {
            handle.event(() -> subscriber.onNext(element));
          }

          @Override
          public void onComplete() {
            handle.complete(subscriber::onComplete);
          }

          @Override
          public void onError(final Throwable cause) {
            handle.complete(() -> subscriber.onError(cause));
          }
        })
    ));
  }

  public static  Upstream upstream(Action> action) {
    return downstream -> ExecutionBacking.require().streamSubscribe((streamHandle) -> {
      final AtomicBoolean fulfilled = new AtomicBoolean();
      try {
        action.execute(new Fulfiller() {
          @Override
          public void error(Throwable throwable) {
            if (!fulfilled.compareAndSet(false, true)) {
              LOGGER.error("", new OverlappingExecutionException("promise already fulfilled", throwable));
              return;
            }

            streamHandle.complete(() -> downstream.error(throwable));
          }

          @Override
          public void success(T value) {
            if (!fulfilled.compareAndSet(false, true)) {
              LOGGER.error("", new OverlappingExecutionException("promise already fulfilled"));
              return;
            }

            streamHandle.complete(() -> downstream.success(value));
          }
        });
      } catch (Throwable throwable) {
        if (!fulfilled.compareAndSet(false, true)) {
          LOGGER.error("", new OverlappingExecutionException("exception thrown after promise was fulfilled", throwable));
        } else {
          streamHandle.complete(() -> downstream.error(throwable));
        }
      }
    });
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy