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

io.github.resilience4j.retry.transformer.RetryTransformer 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
/*
 * Copyright 2017 Dan Maas
 *
 * 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 io.github.resilience4j.retry.transformer;

import io.github.resilience4j.retry.Retry;
import io.reactivex.BackpressureStrategy;
import io.reactivex.Flowable;
import io.reactivex.FlowableTransformer;
import io.reactivex.Observable;
import io.reactivex.ObservableSource;
import io.reactivex.ObservableTransformer;
import io.reactivex.Single;
import io.reactivex.SingleSource;
import io.reactivex.SingleTransformer;
import io.reactivex.internal.subscriptions.SubscriptionArbiter;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.atomic.AtomicInteger;

public class RetryTransformer implements FlowableTransformer, ObservableTransformer, SingleTransformer {

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

    private final Retry retry;

    private RetryTransformer(Retry retry) {
        this.retry = retry;
    }

    /**
     * Creates a RetryOperator.
     *
     * @param retry the Retry
     * @param    the value type of the upstream and downstream
     * @return a RetryOperator
     */
    public static  RetryTransformer of(Retry retry) {
        return new RetryTransformer<>(retry);
    }

    @Override
    public Publisher apply(Flowable upstream) {
        return Flowable.fromPublisher(downstream -> {
            SubscriptionArbiter sa = new SubscriptionArbiter();
            downstream.onSubscribe(sa);
            RetrySubscriber repeatSubscriber = new RetrySubscriber<>(downstream, retry.getRetryConfig().getMaxAttempts(), sa, upstream, retry);
            upstream.subscribe(repeatSubscriber);
        });
    }

    @Override
    public ObservableSource apply(Observable upstream) {
        return Observable.fromPublisher(downstream -> {
            Flowable flowable = upstream.toFlowable(BackpressureStrategy.BUFFER);
            SubscriptionArbiter sa = new SubscriptionArbiter();
            downstream.onSubscribe(sa);
            RetrySubscriber retrySubscriber = new RetrySubscriber<>(downstream, retry.getRetryConfig().getMaxAttempts(), sa, flowable, retry);
            flowable.subscribe(retrySubscriber);
        });
    }

    @Override
    public SingleSource apply(Single upstream) {
        return Single.fromPublisher(downstream -> {
            Flowable flowable = upstream.toFlowable();
            SubscriptionArbiter sa = new SubscriptionArbiter();
            downstream.onSubscribe(sa);
            RetrySubscriber retrySubscriber = new RetrySubscriber<>(downstream, retry.getRetryConfig().getMaxAttempts(), sa, flowable, retry);
            flowable.subscribe(retrySubscriber);
        });
    }

    static final class RetrySubscriber extends AtomicInteger implements Subscriber {

        private final Subscriber actual;
        private final SubscriptionArbiter sa;
        private final Publisher source;
        private final Retry.Context context;
        private long remaining;
        RetrySubscriber(Subscriber actual, long count,
                         SubscriptionArbiter sa, Publisher source,
                         Retry retry) {
            this.actual = actual;
            this.sa = sa;
            this.source = source;
            this.context = retry.context();
            this.remaining = count;
        }

        @Override
        public void onSubscribe(Subscription s) {
            if (LOG.isDebugEnabled()) {
                LOG.info("onSubscribe");
            }
            sa.setSubscription(s);
        }

        @Override
        public void onNext(T t) {
            if (LOG.isDebugEnabled()) {
                LOG.info("onNext");
            }
            context.onSuccess();
            actual.onNext(t);
            sa.produced(1L);
        }
        @Override
        public void onError(Throwable t) {
            if (LOG.isDebugEnabled()) {
                LOG.info("onError");
            }
            long r = remaining;
            if (r != Long.MAX_VALUE) {
                remaining = r - 1;
            }
            if (r == 0) {
                actual.onError(t);
            } else {
                try {
                    context.onError((Exception) t);
                    subscribeNext();
                } catch (Throwable t2) {
                    actual.onError(t2);
                }
            }
        }

        @Override
        public void onComplete() {
            if (LOG.isDebugEnabled()) {
                LOG.info("onComplete");
            }
            actual.onComplete();
        }

        /**
         * Subscribes to the source again via trampolining.
         */
        private void subscribeNext() {
            if (getAndIncrement() == 0) {
                int missed = 1;
                for (;;) {
                    if (sa.isCancelled()) {
                        return;
                    }
                    source.subscribe(this);

                    missed = addAndGet(-missed);
                    if (missed == 0) {
                        break;
                    }
                }
            }
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy