/* * 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<T> implements Promise<T> { public static final Promise<Void> NULL = Promise.value(null); private final Upstream<T> upstream; public DefaultPromise(Upstream<T> upstream) { this.upstream = upstream; } @Override public void then(final Action<? super T> then) { ThreadBinding.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<T>() { @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<? super T> downstream) { ThreadBinding.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<? super T> 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 <O> Promise<O> transform(Function<? super Upstream<? extends T>, ? extends Upstream<O>> upstreamTransformer) { try { return new DefaultPromise<>(upstreamTransformer.apply(upstream)); } catch (Exception e) { throw Exceptions.uncheck(e); } } public static <T> void retryAttempt(int attemptNum, int maxAttempts, Upstream<? extends T> up, Downstream<? super T> down, BiFunction<? super Integer, ? super Throwable, Promise<Duration>> onError) throws Exception { up.connect(down.onError(e -> { if (attemptNum > maxAttempts) { down.error(e); } else { Promise<Duration> delay; try { delay = onError.apply(attemptNum, e); } catch (Throwable errorHandlerError) { if (errorHandlerError != e) { errorHandlerError.addSuppressed(e); } down.error(errorHandlerError); return; } delay.connect(new Downstream<Duration>() { @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(); } }); } })); } }