001
002package io.vrap.rmf.base.client.utils;
003
004import static java.util.stream.Collectors.toList;
005
006import java.time.Duration;
007import java.util.List;
008import java.util.concurrent.*;
009import java.util.function.Function;
010import java.util.stream.Collector;
011import java.util.stream.Collectors;
012import java.util.stream.Stream;
013
014import io.vrap.rmf.base.client.ApiHttpRequest;
015import io.vrap.rmf.base.client.error.RmfTimeoutException;
016
017public class ClientUtils {
018    /**
019     * Waits with a timeout for RESPONSES of a client wrapped in a {@link CompletionStage}.
020     * This method should not be used for other {@link CompletionStage}s since it is throwing {@link io.vrap.rmf.base.client.error.BaseException}s.
021     *
022     * @param completionStage the future monad to wait for
023     * @param request the request belonging to the completionStage
024     * @param duration the maximum duration to wait for this single request
025     * @param <T> type of the result for the request
026     * @return the wrapped value of {@code completionStage}
027     * @throws RmfTimeoutException if a timeout occurs
028     */
029    public static <T> T blockingWait(final CompletionStage<T> completionStage, final ApiHttpRequest request,
030            final Duration duration) {
031        return blockingWait(completionStage, request, duration.toMillis(), TimeUnit.MILLISECONDS);
032    }
033
034    /**
035     * Waits with a timeout for RESPONSES of a client wrapped in a {@link CompletionStage}.
036     * This method should not be used for other {@link CompletionStage}s since it is throwing {@link io.vrap.rmf.base.client.error.BaseException}s.
037     *
038     * @param completionStage the future monad to wait for
039     * @param duration the maximum duration to wait for this single request
040     * @param <T> type of the result for the request
041     * @return the wrapped value of {@code completionStage}
042     * @throws RmfTimeoutException if a timeout occurs
043     */
044    public static <T> T blockingWait(final CompletionStage<T> completionStage, final Duration duration) {
045        return blockingWait(completionStage, duration.toMillis(), TimeUnit.MILLISECONDS);
046    }
047
048    /**
049     * Waits with a timeout for RESPONSES of a client wrapped in a {@link CompletionStage}.
050     * This method should not be used for other {@link CompletionStage}s since it is throwing {@link io.vrap.rmf.base.client.error.BaseException}s.
051     *
052     * @param completionStage the future monad to wait for
053     * @param timeout the maximum time to wait for this single request
054     * @param unit the time unit of the timeout argument
055     * @param <T> type of the result for the request
056     * @return the wrapped value of {@code completionStage}
057     * @throws RmfTimeoutException if a timeout occurs
058     */
059    public static <T> T blockingWait(final CompletionStage<T> completionStage, final long timeout,
060            final TimeUnit unit) {
061        return blockingWait(completionStage, null, timeout, unit);
062    }
063
064    /**
065     * Waits with a timeout for RESPONSES of a client wrapped in a {@link CompletionStage}.
066     * This method should not be used for other {@link CompletionStage}s since it is throwing {@link io.vrap.rmf.base.client.error.BaseException}s.
067     *
068     * @param completionStage the future monad to wait for
069     * @param request the request belonging to the completionStage
070     * @param timeout the maximum time to wait for this single request
071     * @param unit the time unit of the timeout argument
072     * @param <T> type of the result for the request
073     * @return the wrapped value of {@code completionStage}
074     * @throws RmfTimeoutException if a timeout occurs
075     */
076    public static <T> T blockingWait(final CompletionStage<T> completionStage, final ApiHttpRequest request,
077            final long timeout, final TimeUnit unit) {
078        try {
079            return completionStage.toCompletableFuture().get(timeout, unit);
080        }
081        catch (InterruptedException e) {
082            Thread.currentThread().interrupt();
083            throw new CompletionException(e);
084        }
085        catch (ExecutionException e) {
086            final Throwable cause = e.getCause() != null ? e.getCause() : e;
087            throw cause instanceof RuntimeException ? (RuntimeException) cause : new CompletionException(cause);
088        }
089        catch (final TimeoutException e) {
090            throw new RmfTimeoutException(e, request);
091        }
092    }
093
094    public static <T> List<T> blockingWaitForEach(final Stream<? extends CompletionStage<T>> stream,
095            final Duration duration) {
096        return blockingWaitForEach(stream, duration.toMillis(), TimeUnit.MILLISECONDS);
097    }
098
099    public static <T> List<T> blockingWaitForEach(final Stream<? extends CompletionStage<T>> stream, final long timeout,
100            final TimeUnit unit) {
101        return stream.map(stage -> blockingWait(stage, timeout, unit)).collect(toList());
102    }
103
104    public static <T> List<T> blockingWaitForEach(final List<? extends CompletionStage<T>> list,
105            final Duration duration) {
106        return blockingWaitForEach(list, duration.toMillis(), TimeUnit.MILLISECONDS);
107    }
108
109    public static <T> List<T> blockingWaitForEach(final List<? extends CompletionStage<T>> list, final long timeout,
110            final TimeUnit unit) {
111        return blockingWaitForEach(list.stream(), timeout, unit);
112    }
113
114    public static <S extends CompletionStage<T>, T> Collector<S, ?, List<T>> blockingWaitForEachCollector(
115            final long timeout, final TimeUnit unit) {
116        final Function<CompletionStage<T>, T> mapper = stage -> blockingWait(stage, timeout, unit);
117        return Collectors.mapping(mapper, toList());
118    }
119
120    public static <S extends CompletionStage<T>, T> Collector<S, ?, List<T>> blockingWaitForEachCollector(
121            final Duration duration) {
122        return blockingWaitForEachCollector(duration.toMillis(), TimeUnit.MILLISECONDS);
123    }
124}