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

grpcstarter.server.feature.exceptionhandling.annotation.GrpcExceptionHandlerMethod Maven / Gradle / Ivy

There is a newer version: 3.4.1
Show newest version
package grpcstarter.server.feature.exceptionhandling.annotation;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.grpc.Metadata;
import io.grpc.ServerCall;
import io.grpc.Status;
import io.grpc.StatusException;
import io.grpc.StatusRuntimeException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import lombok.Getter;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.OrderUtils;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

/**
 * @author Freeman
 */
@Getter
class GrpcExceptionHandlerMethod {

    private final Object bean;
    private final Integer beanOrder;
    private final Method method;
    private final Class[] exceptions;

    @SuppressFBWarnings("CT_CONSTRUCTOR_THROW")
    public GrpcExceptionHandlerMethod(Object bean, Method method) {
        this.bean = bean;
        checkMethod(method);
        this.method = method;
        this.exceptions = getExceptionsThatCanBeHandled(method);
        this.beanOrder = OrderUtils.getOrder(AopProxyUtils.ultimateTargetClass(bean));
    }

    private static void checkMethod(Method method) {
        checkMethodReturnType(method);
        checkMethodParameters(method);
    }

    private static void checkMethodReturnType(Method method) {
        Class returnType = method.getReturnType();
        if (!StatusRuntimeException.class.isAssignableFrom(returnType)
                && !StatusException.class.isAssignableFrom(returnType)
                && !Status.class.isAssignableFrom(returnType)
                && !Throwable.class.isAssignableFrom(returnType)) {
            throw new IllegalStateException(
                    "The method annotated with @GrpcExceptionHandler must return StatusRuntimeException, StatusException, Status or Throwable");
        }
    }

    private static void checkMethodParameters(Method method) {
        Class[] parameterTypes = method.getParameterTypes();
        for (Class parameterType : parameterTypes) {
            if (!ServerCall.class.isAssignableFrom(parameterType)
                    && !Metadata.class.isAssignableFrom(parameterType)
                    && !Throwable.class.isAssignableFrom(parameterType)) {
                throw new IllegalArgumentException(
                        "Unsupported parameter type of the method annotated with @GrpcExceptionHandler: "
                                + parameterType.getName());
            }
        }
    }

    @SuppressWarnings("unchecked")
    private static Class[] getExceptionsThatCanBeHandled(Method method) {
        GrpcExceptionHandler anno = AnnotationUtils.findAnnotation(method, GrpcExceptionHandler.class);
        Assert.notNull(anno, "The method must be annotated with @GrpcExceptionHandler");
        if (!ObjectUtils.isEmpty(anno.value())) {
            // find the first Throwable parameter,
            Optional parameter = Arrays.stream(method.getParameters())
                    .filter(param -> Throwable.class.isAssignableFrom(param.getType()))
                    .findFirst();
            // check if it is assignable from all the exception types
            parameter.ifPresent(value -> Arrays.stream(anno.value()).forEach(type -> {
                if (!value.getType().isAssignableFrom(type)) {
                    throw new IllegalStateException(String.format(
                            "The parameter of the method annotated with @GrpcExceptionHandler must be assignable from all the exception types, but '%s' is not assignable from '%s' on method %s",
                            value.getType().getSimpleName(), type.getSimpleName(), formatMethod(method)));
                }
            }));
            return anno.value();
        }
        List parameters = Arrays.stream(method.getParameters())
                .filter(param -> Throwable.class.isAssignableFrom(param.getType()))
                .collect(Collectors.toList());
        if (parameters.size() != 1) {
            throw new IllegalStateException(
                    "The method annotated with @GrpcExceptionHandler must have only one Throwable parameter: "
                            + formatMethod(method));
        }
        return new Class[] {parameters.get(0).getType()};
    }

    private static String formatMethod(Method method) {
        return method.getDeclaringClass().getSimpleName() + "#" + method.getName();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy