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

ratpack.exec.internal.DefaultPromise 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 ratpack.exec.*;
import ratpack.func.Action;
import ratpack.func.BiFunction;
import ratpack.func.Block;
import ratpack.func.Function;
import ratpack.util.Exceptions;

import java.time.Duration;

public class DefaultPromise implements Promise {

  public static final Promise NULL = Promise.value(null);

  private final Upstream upstream;

  public DefaultPromise(Upstream upstream) {
    this.upstream = upstream;
  }

  @Override
  public void then(final Action then) {
    ExecThreadBinding.requireComputeThread("Promise.then() can only be called on a compute thread (use Blocking.on() to use a promise on a blocking thread)");
    doConnect(new Downstream() {
      @Override
      public void success(T value) {
        try {
          then.execute(value);
        } catch (Throwable e) {
          throwError(e);
        }
      }

      @Override
      public void error(Throwable throwable) {
        throwError(throwable);
      }

      @Override
      public void complete() {

      }
    });
  }

  @Override
  public void connect(Downstream downstream) {
    ExecThreadBinding.requireComputeThread("Promise.connect() can only be called on a compute thread (use Blocking.on() to use a promise on a blocking thread)");
    doConnect(downstream);
  }

  private void doConnect(Downstream downstream) {
    try {
      upstream.connect(downstream);
    } catch (ExecutionException e) {
      throw e;
    } catch (Exception e) {
      throwError(e);
    }
  }

  public static void throwError(Throwable throwable) {
    DefaultExecution.require().delimit(Action.throwException(), h -> h.resume(Block.throwException(throwable)));
  }

  @Override
  public  Promise transform(Function, ? extends Upstream> upstreamTransformer) {
    try {
      return new DefaultPromise<>(upstreamTransformer.apply(upstream));
    } catch (Exception e) {
      throw Exceptions.uncheck(e);
    }
  }


  public static  void retryAttempt(int attemptNum, int maxAttempts, Upstream up, Downstream down, BiFunction> onError) throws Exception {
    up.connect(down.onError(e -> {
      if (attemptNum > maxAttempts) {
        down.error(e);
      } else {
        Promise delay;
        try {
          delay = onError.apply(attemptNum, e);
        } catch (Throwable errorHandlerError) {
          if (errorHandlerError != e) {
            errorHandlerError.addSuppressed(e);
          }
          down.error(errorHandlerError);
          return;
        }

        delay.connect(new Downstream() {
          @Override
          public void success(Duration value) {
            Execution.sleep(value, () ->
              retryAttempt(attemptNum + 1, maxAttempts, up, down, onError)
            );
          }

          @Override
          public void error(Throwable throwable) {
            down.error(throwable);
          }

          @Override
          public void complete() {
            down.complete();
          }
        });
      }
    }));
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy