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

io.github.resilience4j.timelimiter.internal.TimeLimiterImpl Maven / Gradle / Ivy

Go to download

Resilience4j is a lightweight, easy-to-use fault tolerance library designed for Java8 and functional programming

There is a newer version: 2.2.0
Show newest version
package io.github.resilience4j.timelimiter.internal;

import io.github.resilience4j.timelimiter.TimeLimiter;
import io.github.resilience4j.timelimiter.TimeLimiterConfig;
import io.github.resilience4j.timelimiter.event.TimeLimiterEvent;
import io.github.resilience4j.timelimiter.event.TimeLimiterOnErrorEvent;
import io.github.resilience4j.timelimiter.event.TimeLimiterOnSuccessEvent;
import io.github.resilience4j.timelimiter.event.TimeLimiterOnTimeoutEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.*;
import java.util.function.Supplier;

import static java.util.Collections.emptyMap;

public class TimeLimiterImpl implements TimeLimiter {

    private static final Logger LOG = LoggerFactory.getLogger(TimeLimiterImpl.class);

    private final String name;
    private final Map tags;
    private final TimeLimiterConfig timeLimiterConfig;
    private final TimeLimiterEventProcessor eventProcessor;

    public TimeLimiterImpl(String name, TimeLimiterConfig timeLimiterConfig) {
        this(name, timeLimiterConfig, emptyMap());

    }

    public TimeLimiterImpl(String name, TimeLimiterConfig timeLimiterConfig, Map tags) {
        this.name = name;
        this.tags = Objects.requireNonNull(tags, "Tags must not be null");
        this.timeLimiterConfig = timeLimiterConfig;
        this.eventProcessor = new TimeLimiterEventProcessor();
    }


    @Override
    public > Callable decorateFutureSupplier(Supplier futureSupplier) {
        return () -> {
            Future future = futureSupplier.get();
            try {
                T result = future.get(getTimeLimiterConfig().getTimeoutDuration().toMillis(),
                    TimeUnit.MILLISECONDS);
                onSuccess();
                return result;
            } catch (TimeoutException e) {
                TimeoutException timeoutException = TimeLimiter.createdTimeoutExceptionWithName(name, e);
                onError(timeoutException);
                if (getTimeLimiterConfig().shouldCancelRunningFuture()) {
                    future.cancel(true);
                }
                throw timeoutException;
            } catch (ExecutionException e) {
                Throwable t = e.getCause();
                if (t == null) {
                    onError(e);
                    throw e;
                }
                onError(t);
                if (t instanceof Error) {
                    throw (Error) t;
                }
                throw (Exception) t;
            }
        };
    }

    @Override
    public > Supplier> decorateCompletionStage(
        ScheduledExecutorService scheduler, Supplier supplier) {

        return () -> {
            CompletableFuture future = supplier.get().toCompletableFuture();
            ScheduledFuture timeoutFuture =
                Timeout
                    .of(future, scheduler, name, getTimeLimiterConfig().getTimeoutDuration().toMillis(),
                        TimeUnit.MILLISECONDS);

            return future.whenComplete((result, throwable) -> {
                // complete
                if (result != null) {
                    if (!timeoutFuture.isDone()) {
                        timeoutFuture.cancel(false);
                    }
                    onSuccess();
                }

                // exceptionally
                if (throwable != null) {
                    if (throwable instanceof CompletionException) {
                        Throwable cause = throwable.getCause();
                        onError(cause);
                    } else if (throwable instanceof ExecutionException) {
                        Throwable cause = throwable.getCause();
                        if (cause == null) {
                            onError(throwable);
                        } else {
                            onError(cause);
                        }
                    } else {
                        onError(throwable);
                    }
                }
            });
        };
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public Map getTags() {
        return tags;
    }

    @Override
    public TimeLimiterConfig getTimeLimiterConfig() {
        return timeLimiterConfig;
    }

    @Override
    public EventPublisher getEventPublisher() {
        return eventProcessor;
    }

    @Override
    public void onSuccess() {
        if (!eventProcessor.hasConsumers()) {
            return;
        }
        publishEvent(new TimeLimiterOnSuccessEvent(name));
    }

    @Override
    public void onError(Throwable throwable) {
        if (throwable instanceof TimeoutException) {
            onTimeout();
        } else {
            onFailure(throwable);
        }
    }

    private void onTimeout() {
        if (!eventProcessor.hasConsumers()) {
            return;
        }
        publishEvent(new TimeLimiterOnTimeoutEvent(name));
    }

    private void onFailure(Throwable throwable) {
        if (!eventProcessor.hasConsumers()) {
            return;
        }
        publishEvent(new TimeLimiterOnErrorEvent(name, throwable));
    }

    private void publishEvent(TimeLimiterEvent event) {
        try {
            eventProcessor.consumeEvent(event);
            LOG.debug("Event {} published: {}", event.getEventType(), event);
        } catch (Exception e) {
            LOG.warn("Failed to handle event {}", event.getEventType(), e);
        }
    }

    /**
     * Completes CompletableFuture with {@link TimeoutException}.
     */
    static final class Timeout {

        private Timeout() {
        }

        static ScheduledFuture of(
            CompletableFuture future, ScheduledExecutorService scheduler, String name, long delay,
            TimeUnit unit) {
            return scheduler.schedule(() -> {
                if (future != null && !future.isDone()) {
                    future.completeExceptionally(TimeLimiter.createdTimeoutExceptionWithName(name, null));
                }
            }, delay, unit);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy