/* * * Copyright 2017: Robert Winkler * * 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.circuitbreaker.operator; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.github.resilience4j.circuitbreaker.CircuitBreaker; import io.github.resilience4j.circuitbreaker.CircuitBreakerOpenException; import io.github.resilience4j.core.StopWatch; import io.reactivex.FlowableOperator; import io.reactivex.ObservableOperator; import io.reactivex.Observer; import io.reactivex.SingleObserver; import io.reactivex.SingleOperator; import io.reactivex.disposables.Disposable; import java.util.concurrent.atomic.AtomicBoolean; /** * A RxJava operator which protects an Observable or Flowable by a CircuitBreaker * * @param <T> the value type of the upstream and downstream */ public class CircuitBreakerOperator<T> implements ObservableOperator<T, T>, FlowableOperator<T, T>, SingleOperator<T, T> { private static final Logger LOG = LoggerFactory.getLogger(CircuitBreakerOperator.class); private final CircuitBreaker circuitBreaker; private CircuitBreakerOperator(CircuitBreaker circuitBreaker) { this.circuitBreaker = circuitBreaker; } /** * Creates a CircuitBreakerOperator. * * @param circuitBreaker the CircuitBreaker * @param <T> the value type of the upstream and downstream * @return a CircuitBreakerOperator */ public static <T> CircuitBreakerOperator<T> of(CircuitBreaker circuitBreaker) { return new CircuitBreakerOperator<>(circuitBreaker); } /** * {@inheritDoc} */ @Override public Subscriber<? super T> apply(Subscriber<? super T> childSubscriber) throws Exception { return new CircuitBreakerSubscriber(childSubscriber); } /** * {@inheritDoc} */ @Override public Observer<? super T> apply(Observer<? super T> childObserver) throws Exception { return new CircuitBreakerObserver(childObserver); } /** * {@inheritDoc} */ @Override public SingleObserver<? super T> apply(SingleObserver<? super T> childObserver) throws Exception { return new CircuitBreakerSingleObserver(childObserver); } private final class CircuitBreakerSubscriber implements Subscriber<T>, Subscription { private final Subscriber<? super T> childSubscriber; private Subscription subscription; private AtomicBoolean cancelled = new AtomicBoolean(false); private StopWatch stopWatch; CircuitBreakerSubscriber(Subscriber<? super T> childSubscriber) { this.childSubscriber = childSubscriber; } /** * {@inheritDoc} */ @Override public void onSubscribe(Subscription subscription) { this.subscription = subscription; LOG.debug("onSubscribe"); if (circuitBreaker.isCallPermitted()) { stopWatch = StopWatch.start(circuitBreaker.getName()); childSubscriber.onSubscribe(this); } else { subscription.cancel(); childSubscriber.onSubscribe(this); childSubscriber.onError(new CircuitBreakerOpenException( String.format("CircuitBreaker '%s' is open", circuitBreaker.getName()))); } } /** * {@inheritDoc} */ @Override public void onNext(T event) { LOG.debug("onNext: {}", event); if (!isCancelled()) { childSubscriber.onNext(event); } } /** * {@inheritDoc} */ @Override public void onError(Throwable e) { LOG.debug("onError", e); if (!isCancelled()) { circuitBreaker.onError(stopWatch.stop().getProcessingDuration().toNanos(), e); childSubscriber.onError(e); } } /** * {@inheritDoc} */ @Override public void onComplete() { LOG.debug("onComplete"); if (!isCancelled()) { circuitBreaker.onSuccess(stopWatch.stop().getProcessingDuration().toNanos()); childSubscriber.onComplete(); } } /** * {@inheritDoc} */ @Override public void request(long n) { subscription.request(n); } /** * {@inheritDoc} */ @Override public void cancel() { if (!cancelled.get()) { cancelled.set(true); subscription.cancel(); } } public boolean isCancelled() { return cancelled.get(); } } private final class CircuitBreakerObserver implements Observer<T>, Disposable { private final Observer<? super T> childObserver; private Disposable disposable; private AtomicBoolean cancelled = new AtomicBoolean(false); private StopWatch stopWatch; CircuitBreakerObserver(Observer<? super T> childObserver) { this.childObserver = childObserver; } /** * {@inheritDoc} */ @Override public void onSubscribe(Disposable disposable) { this.disposable = disposable; LOG.debug("onSubscribe"); if (circuitBreaker.isCallPermitted()) { stopWatch = StopWatch.start(circuitBreaker.getName()); childObserver.onSubscribe(this); } else { disposable.dispose(); childObserver.onSubscribe(this); childObserver.onError(new CircuitBreakerOpenException( String.format("CircuitBreaker '%s' is open", circuitBreaker.getName()))); } } /** * {@inheritDoc} */ @Override public void onNext(T event) { LOG.debug("onNext: {}", event); if (!isDisposed()) { childObserver.onNext(event); } } /** * {@inheritDoc} */ @Override public void onError(Throwable e) { LOG.debug("onError", e); if (!isDisposed()) { circuitBreaker.onError(stopWatch.stop().getProcessingDuration().toNanos(), e); childObserver.onError(e); } } /** * {@inheritDoc} */ @Override public void onComplete() { LOG.debug("onComplete"); if (!isDisposed()) { circuitBreaker.onSuccess(stopWatch.stop().getProcessingDuration().toNanos()); childObserver.onComplete(); } } /** * {@inheritDoc} */ @Override public void dispose() { if (!cancelled.get()) { cancelled.set(true); disposable.dispose(); } } /** * {@inheritDoc} */ @Override public boolean isDisposed() { return cancelled.get(); } } private class CircuitBreakerSingleObserver implements SingleObserver<T>, Disposable { private final SingleObserver<? super T> childObserver; private Disposable disposable; private AtomicBoolean cancelled = new AtomicBoolean(false); private StopWatch stopWatch; CircuitBreakerSingleObserver(SingleObserver<? super T> childObserver) { this.childObserver = childObserver; } /** * {@inheritDoc} */ @Override public void onSubscribe(Disposable disposable) { this.disposable = disposable; LOG.debug("onSubscribe"); if (circuitBreaker.isCallPermitted()) { stopWatch = StopWatch.start(circuitBreaker.getName()); childObserver.onSubscribe(this); } else { disposable.dispose(); childObserver.onSubscribe(this); childObserver.onError(new CircuitBreakerOpenException( String.format("CircuitBreaker '%s' is open", circuitBreaker.getName()))); } } /** * {@inheritDoc} */ @Override public void onError(Throwable e) { LOG.debug("onError", e); if (!isDisposed()) { circuitBreaker.onError(stopWatch.stop().getProcessingDuration().toNanos(), e); childObserver.onError(e); } } /** * {@inheritDoc} */ @Override public void onSuccess(T value) { LOG.debug("onComplete"); if (!isDisposed()) { circuitBreaker.onSuccess(stopWatch.stop().getProcessingDuration().toNanos()); childObserver.onSuccess(value); } } /** * {@inheritDoc} */ @Override public void dispose() { if (!cancelled.get()) { cancelled.set(true); disposable.dispose(); } } /** * {@inheritDoc} */ @Override public boolean isDisposed() { return cancelled.get(); } } }