io.github.resilience4j.retry.transformer.RetryTransformer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of resilience4j-rxjava2 Show documentation
Show all versions of resilience4j-rxjava2 Show documentation
Resilience4j is a lightweight, easy-to-use fault tolerance library designed for Java8 and functional programming
/*
* 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 super T> actual;
private final SubscriptionArbiter sa;
private final Publisher extends T> source;
private final Retry.Context context;
private long remaining;
RetrySubscriber(Subscriber super T> actual, long count,
SubscriptionArbiter sa, Publisher extends T> 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