/*
* 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.ratelimiter.operator;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.github.resilience4j.ratelimiter.RateLimiter;
import io.github.resilience4j.ratelimiter.RequestNotPermitted;
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;
public class RateLimiterOperator<T> implements ObservableOperator<T, T>, FlowableOperator<T, T>, SingleOperator<T, T> {
private static final Logger LOG = LoggerFactory.getLogger(RateLimiterOperator.class);
private final RateLimiter rateLimiter;
private RateLimiterOperator(RateLimiter rateLimiter) {
this.rateLimiter = rateLimiter;
}
/**
* Creates a RateLimiterOperator.
*
* @param rateLimiter the RateLimiter
* @param <T> the value type of the upstream and downstream
* @return a RateLimiterOperator
*/
public static <T> RateLimiterOperator<T> of(RateLimiter rateLimiter) {
return new RateLimiterOperator<>(rateLimiter);
}
/**
* {@inheritDoc}
*/
@Override
public Subscriber<? super T> apply(Subscriber<? super T> childSubscriber) throws Exception {
return new RateLimiterSubscriber(childSubscriber);
}
/**
* {@inheritDoc}
*/
@Override
public Observer<? super T> apply(Observer<? super T> childObserver) throws Exception {
return new RateLimiterObserver(childObserver);
}
/**
* {@inheritDoc}
*/
@Override
public SingleObserver<? super T> apply(SingleObserver<? super T> childObserver) throws Exception {
return new RateLimiterSingleObserver(childObserver);
}
private final class RateLimiterSubscriber implements Subscriber<T>, Subscription {
private final Subscriber<? super T> childSubscriber;
private Subscription subscription;
private AtomicBoolean cancelled = new AtomicBoolean(false);
RateLimiterSubscriber(Subscriber<? super T> childSubscriber) {
this.childSubscriber = childSubscriber;
}
/**
* {@inheritDoc}
*/
@Override
public void onSubscribe(Subscription subscription) {
this.subscription = subscription;
LOG.debug("onSubscribe");
if (rateLimiter.getPermission(rateLimiter.getRateLimiterConfig().getTimeoutDuration())) {
childSubscriber.onSubscribe(this);
} else {
subscription.cancel();
childSubscriber.onSubscribe(this);
childSubscriber.onError(new RequestNotPermitted("Request not permitted for limiter: " + rateLimiter.getName()));
}
}
/**
* {@inheritDoc}
*/
@Override
public void onNext(T event) {
LOG.debug("onNext: {}", event);
if (!isCancelled()) {
if (rateLimiter.getPermission(rateLimiter.getRateLimiterConfig().getTimeoutDuration())) {
childSubscriber.onNext(event);
} else {
subscription.cancel();
childSubscriber.onError(new RequestNotPermitted("Request not permitted for limiter: " + rateLimiter.getName()));
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void onError(Throwable e) {
LOG.debug("onError", e);
if (!isCancelled()) {
childSubscriber.onError(e);
}
}
/**
* {@inheritDoc}
*/
@Override
public void onComplete() {
LOG.debug("onComplete");
if (!isCancelled()) {
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 RateLimiterObserver implements Observer<T>, Disposable {
private final Observer<? super T> childObserver;
private Disposable disposable;
private AtomicBoolean cancelled = new AtomicBoolean(false);
RateLimiterObserver(Observer<? super T> childObserver) {
this.childObserver = childObserver;
}
/**
* {@inheritDoc}
*/
@Override
public void onSubscribe(Disposable disposable) {
this.disposable = disposable;
LOG.debug("onSubscribe");
if (rateLimiter.getPermission(rateLimiter.getRateLimiterConfig().getTimeoutDuration())) {
childObserver.onSubscribe(this);
} else {
disposable.dispose();
childObserver.onSubscribe(this);
childObserver.onError(new RequestNotPermitted("Request not permitted for limiter: " + rateLimiter.getName()));
}
}
/**
* {@inheritDoc}
*/
@Override
public void onNext(T event) {
LOG.debug("onNext: {}", event);
if (!isDisposed()) {
if (rateLimiter.getPermission(rateLimiter.getRateLimiterConfig().getTimeoutDuration())) {
childObserver.onNext(event);
} else {
disposable.dispose();
childObserver.onError(new RequestNotPermitted("Request not permitted for limiter: " + rateLimiter.getName()));
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void onError(Throwable e) {
LOG.debug("onError", e);
if (!isDisposed()) {
childObserver.onError(e);
}
}
/**
* {@inheritDoc}
*/
@Override
public void onComplete() {
LOG.debug("onComplete");
if (!isDisposed()) {
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 RateLimiterSingleObserver implements SingleObserver<T>, Disposable {
private final SingleObserver<? super T> childObserver;
private Disposable disposable;
private AtomicBoolean cancelled = new AtomicBoolean(false);
RateLimiterSingleObserver(SingleObserver<? super T> childObserver) {
this.childObserver = childObserver;
}
/**
* {@inheritDoc}
*/
@Override
public void onSubscribe(Disposable disposable) {
this.disposable = disposable;
LOG.debug("onSubscribe");
if (rateLimiter.getPermission(rateLimiter.getRateLimiterConfig().getTimeoutDuration())) {
childObserver.onSubscribe(this);
} else {
disposable.dispose();
childObserver.onSubscribe(this);
childObserver.onError(new RequestNotPermitted("Request not permitted for limiter: " + rateLimiter.getName()));
}
}
/**
* {@inheritDoc}
*/
@Override
public void onError(Throwable e) {
LOG.debug("onError", e);
if (!isDisposed()) {
childObserver.onError(e);
}
}
/**
* {@inheritDoc}
*/
@Override
public void onSuccess(T value) {
LOG.debug("onComplete");
if (!isDisposed()) {
childObserver.onSuccess(value);
}
}
/**
* {@inheritDoc}
*/
@Override
public void dispose() {
if (!cancelled.get()) {
cancelled.set(true);
disposable.dispose();
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean isDisposed() {
return cancelled.get();
}
}
}