io.github.resilience4j.ratelimiter.autoconfigure.RateLimiterAspect.java Source code

Java tutorial

Introduction

Here is the source code for io.github.resilience4j.ratelimiter.autoconfigure.RateLimiterAspect.java

Source

/*
 * Copyright 2017 Bohdan Storozhuk
 *
 * 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.autoconfigure;

import io.github.resilience4j.ratelimiter.RateLimiterConfig;
import io.github.resilience4j.ratelimiter.RateLimiterRegistry;
import io.github.resilience4j.ratelimiter.annotation.RateLimiter;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;

import java.lang.reflect.Method;

/**
 * This Spring AOP aspect intercepts all methods which are annotated with a {@link RateLimiter} annotation.
 * The aspect protects an annotated method with a RateLimiter. The RateLimiterRegistry is used to retrieve an instance of a RateLimiter for
 * a specific backend.
 */

@Aspect
public class RateLimiterAspect implements Ordered {
    private static final Logger logger = LoggerFactory.getLogger(RateLimiterAspect.class);
    public static final String RATE_LIMITER_RECEIVED = "Created or retrieved rate limiter '{}' with period: '{}'; limit for period: '{}'; timeout: '{}'; method: '{}'";

    private final RateLimiterRegistry rateLimiterRegistry;
    private final RateLimiterProperties properties;

    public RateLimiterAspect(RateLimiterRegistry rateLimiterRegistry, RateLimiterProperties properties) {
        this.rateLimiterRegistry = rateLimiterRegistry;
        this.properties = properties;
    }

    /**
     * Method used as pointcut
     * @param rateLimiter - matched annotation
     */
    @Pointcut(value = "@within(rateLimiter) || @annotation(rateLimiter)", argNames = "rateLimiter")
    public void matchAnnotatedClassOrMethod(RateLimiter rateLimiter) {
        // Method used as pointcut
    }

    @Around(value = "matchAnnotatedClassOrMethod(limitedService)", argNames = "proceedingJoinPoint, limitedService")
    public Object rateLimiterAroundAdvice(ProceedingJoinPoint proceedingJoinPoint, RateLimiter limitedService)
            throws Throwable {
        RateLimiter targetService = limitedService;
        Method method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();
        String methodName = method.getDeclaringClass().getName() + "#" + method.getName();
        if (targetService == null) {
            targetService = getRateLimiterAnnotation(proceedingJoinPoint);
        }
        String name = targetService.name();
        io.github.resilience4j.ratelimiter.RateLimiter rateLimiter = getOrCreateRateLimiter(methodName, name);
        return handleJoinPoint(proceedingJoinPoint, rateLimiter, methodName);
    }

    private io.github.resilience4j.ratelimiter.RateLimiter getOrCreateRateLimiter(String methodName, String name) {
        io.github.resilience4j.ratelimiter.RateLimiter rateLimiter = rateLimiterRegistry.rateLimiter(name);

        if (logger.isDebugEnabled()) {
            RateLimiterConfig rateLimiterConfig = rateLimiter.getRateLimiterConfig();
            logger.debug(RATE_LIMITER_RECEIVED, name, rateLimiterConfig.getLimitRefreshPeriod(),
                    rateLimiterConfig.getLimitForPeriod(), rateLimiterConfig.getTimeoutDuration(), methodName);
        }

        return rateLimiter;
    }

    private RateLimiter getRateLimiterAnnotation(ProceedingJoinPoint proceedingJoinPoint) {
        RateLimiter rateLimiter = null;
        Class<?> targetClass = proceedingJoinPoint.getTarget().getClass();
        if (targetClass.isAnnotationPresent(RateLimiter.class)) {
            rateLimiter = targetClass.getAnnotation(RateLimiter.class);
            if (rateLimiter == null) {
                rateLimiter = targetClass.getDeclaredAnnotation(RateLimiter.class);
            }
            if (rateLimiter == null) {
                logger.debug("TargetClass has no declared annotation 'RateLimiter'");
            }
        }
        return rateLimiter;
    }

    private Object handleJoinPoint(ProceedingJoinPoint proceedingJoinPoint,
            io.github.resilience4j.ratelimiter.RateLimiter rateLimiter, String methodName) throws Throwable {
        try {
            io.github.resilience4j.ratelimiter.RateLimiter.waitForPermission(rateLimiter);
            return proceedingJoinPoint.proceed();
        } catch (Exception exception) {
            if (logger.isDebugEnabled()) {
                logger.debug("Invocation of method '" + methodName + "' failed!", exception);
            }
            throw exception;
        }
    }

    @Override
    public int getOrder() {
        return properties.getRateLimiterAspectOrder();
    }
}