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

com.github.netty.protocol.nrpc.RpcMethod Maven / Gradle / Ivy

The newest version!
package com.github.netty.protocol.nrpc;

import com.github.netty.annotation.NRpcMethod;
import com.github.netty.core.util.ClassFileMethodToParameterNamesFunction;
import com.github.netty.core.util.LoggerFactoryX;
import com.github.netty.core.util.LoggerX;
import com.github.netty.core.util.ReflectUtil;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Future;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Rpc Method
 *
 * @author wangzihao
 */
public class RpcMethod {
    private static final LoggerX LOGGERX = LoggerFactoryX.getLogger(RpcMethod.class);
    private static final Class JDK9_PUBLISHER_CLASS;
    private static final Class REACTIVE_PUBLISHER_CLASS;
    private static final Class RXJAVA3_OBSERVABLE_CLASS;
    private static final Class RXJAVA3_FLOWABLE_CLASS;

    static {
        JDK9_PUBLISHER_CLASS = classForName("java.util.concurrent.Flow.Publisher");
        REACTIVE_PUBLISHER_CLASS = classForName("org.reactivestreams.Publisher");
        RXJAVA3_OBSERVABLE_CLASS = classForName("io.reactivex.rxjava3.core.Observable");
        RXJAVA3_FLOWABLE_CLASS = classForName("io.reactivex.rxjava3.core.Flowable");
    }

    private final String methodName;
    private final Method method;
    private final Class[] parameterTypes;
    private final String[] parameterNames;
    private final Type genericReturnType;
    private final Type chunkGenericReturnType;
    private final INSTANCE instance;
    private final NRpcMethod methodAnnotation;
    private final boolean returnChunkCompletionFlag;
    private final boolean returnCompletionStageFlag;
    private final boolean returnFutureFlag;
    private final boolean returnRxjava3FlowableFlag;
    private final boolean returnRxjava3ObservableFlag;
    private final boolean returnTypeJdk9PublisherFlag;
    private final boolean returnTypeReactivePublisherFlag;
    private final boolean innerMethodFlag;
    private final String methodDescriptorName;
    private final String parameterTypeDescriptorName;
    private final MethodHandle methodHandle;
    private final int parameterCount;
    private String loggerName;

    private RpcMethod(INSTANCE instance, Method method, String[] parameterNames, String methodName,
                      NRpcMethod methodAnnotation,
                      boolean returnTypeJdk9PublisherFlag, boolean returnTypeReactivePublisherFlag,
                      boolean returnRxjava3ObservableFlag, boolean returnRxjava3FlowableFlag) {
        this.instance = instance;
        this.method = method;
        this.methodName = methodName;
        this.parameterNames = parameterNames;
        this.methodAnnotation = methodAnnotation;
        this.returnTypeJdk9PublisherFlag = returnTypeJdk9PublisherFlag;
        this.returnTypeReactivePublisherFlag = returnTypeReactivePublisherFlag;
        this.returnRxjava3ObservableFlag = returnRxjava3ObservableFlag;
        this.returnRxjava3FlowableFlag = returnRxjava3FlowableFlag;
        this.returnChunkCompletionFlag = RpcClientChunkCompletableFuture.class.isAssignableFrom(method.getReturnType());
        this.returnCompletionStageFlag = CompletionStage.class.isAssignableFrom(method.getReturnType());
        this.returnFutureFlag = Future.class.isAssignableFrom(method.getReturnType());
        this.parameterTypes = method.getParameterTypes();
        if (isReturnAsync()) {
            this.genericReturnType = getParameterizedType(method, 0);
        } else {
            this.genericReturnType = method.getGenericReturnType();
        }
        if (returnChunkCompletionFlag) {
            this.chunkGenericReturnType = getParameterizedType(method, 1);
        } else {
            this.chunkGenericReturnType = null;
        }
        this.loggerName = method.getDeclaringClass().getName() + "-" + method.getName();
        this.innerMethodFlag = RpcServerInstance.isRpcInnerClass(method.getDeclaringClass());
        this.parameterTypeDescriptorName = Stream.of(parameterTypes)
                .map(Class::getSimpleName)
                .collect(Collectors.joining(","));
        this.methodDescriptorName = getMethodDescriptorName(method);
        this.parameterCount = method.getParameterCount();
        MethodHandle methodHandle;
        try {
            MethodHandles.Lookup publicLookup = MethodHandles.publicLookup();
            MethodType methodType = MethodType.methodType(method.getReturnType(), method.getParameterTypes());
            methodHandle = publicLookup.findVirtual(method.getDeclaringClass(), method.getName(), methodType);
        } catch (NoSuchMethodException | IllegalAccessException e) {
            methodHandle = null;
        }
        this.methodHandle = methodHandle;
    }

    public static String getMethodDescriptorName(Method method) {
        return method.getName();
    }

    public static  Map> getMethodMap(Class source) throws UnsupportedOperationException {
        return getMethodMap(null, source, null, Method::getName, false);
    }

    public static  Map> getMethodMap(INSTANCE instance, Class source, Function methodToParameterNamesFunction, Function methodToNameFunction, boolean overwriteCheck) throws UnsupportedOperationException {
        Map> methodMap = new HashMap<>(6);
        if (methodToParameterNamesFunction != null && methodToNameFunction != null) {
            Class[] interfaceClasses = ReflectUtil.getInterfaces(source);
            for (Class interfaceClass : interfaceClasses) {
                initMethodsMap(instance, interfaceClass, methodMap, methodToParameterNamesFunction, methodToNameFunction, overwriteCheck);
            }
        }
        if (!source.isInterface()) {
            initMethodsMap(instance, source, methodMap, new ClassFileMethodToParameterNamesFunction(), methodToNameFunction, overwriteCheck);
        }
        return methodMap;
    }

    private static  void initMethodsMap(INSTANCE instance, Class source, Map> methodMap, Function methodToParameterNamesFunction, Function methodToNameFunction, boolean overwriteCheck) throws UnsupportedOperationException {
        Method[] methods = source.isInterface() ? source.getDeclaredMethods() : source.getMethods();
        for (Method method : methods) {
            Class declaringClass = method.getDeclaringClass();
            if (declaringClass == Object.class) {
                continue;
            }
            String[] parameterNames;
            try {
                parameterNames = methodToParameterNamesFunction.apply(method);
            } catch (IllegalStateException e) {
                LOGGERX.warn("skip init method. source={}, method={}, cause={}", source.getSimpleName(), method, e.toString());
                continue;
            }
            if (method.getParameterCount() != parameterNames.length) {
                continue;
            }

            String methodName = methodToNameFunction.apply(method);
            boolean isReturnTypeJdk9Publisher = isReturnType(JDK9_PUBLISHER_CLASS, method);
            boolean isReturnTypeReactivePublisher = isReturnType(REACTIVE_PUBLISHER_CLASS, method);
            boolean isReturnRxjava3ObservableFlag = isReturnType(RXJAVA3_OBSERVABLE_CLASS, method);
            boolean isReturnRxjava3FlowableFlag = isReturnType(RXJAVA3_FLOWABLE_CLASS, method);
            NRpcMethod methodAnnotation = method.getDeclaredAnnotation(NRpcMethod.class);
            RpcMethod newMethod = new RpcMethod<>(instance, method, parameterNames, methodName,
                    methodAnnotation, isReturnTypeJdk9Publisher, isReturnTypeReactivePublisher,
                    isReturnRxjava3ObservableFlag, isReturnRxjava3FlowableFlag);
            RpcMethod oldMethod = methodMap.put(newMethod.getMethodDescriptorName(), newMethod);
            boolean existOverwrite = oldMethod != null;
            if (existOverwrite && overwriteCheck && !Objects.equals(oldMethod, newMethod)) {
                String message = "Please rename method! In the non-rigorous public method calls, public method name needs to be unique. You can change to any non public method." +
                        "\n" + method.getDeclaringClass().getName() + ", old=" + oldMethod + ", new=" + newMethod;
                throw new UnsupportedOperationException(message);
            }

            if (!Objects.equals(methodName, newMethod.getMethodDescriptorName())) {
                methodMap.put(methodName, newMethod);
            }
        }
    }

    private static boolean isReturnType(Class type, Method method) {
        return Objects.equals(type, method.getReturnType());
    }

    private static Type getParameterizedType(Method method, int index) {
        Type genericReturnType = method.getGenericReturnType();
        if (genericReturnType instanceof ParameterizedType) {
            return ((ParameterizedType) genericReturnType).getActualTypeArguments()[index];
        }
        throw new IllegalStateException("If the method returns the type of Publisher class, you must add generics " +
                method.getDeclaringClass().getSimpleName() + "], method=[" + method.getName() + "]");
    }

    private static Class classForName(String className) {
        Class clazz;
        try {
            clazz = Class.forName(className);
        } catch (Throwable e) {
            clazz = null;
        }
        return clazz;
    }

    public int getParameterCount() {
        return parameterCount;
    }

    public Method getMethod() {
        return method;
    }

    public LoggerX getLog() {
        return LoggerFactoryX.getLogger(loggerName);
    }

    public MethodHandle getMethodHandle() {
        return methodHandle;
    }

    public NRpcMethod getMethodAnnotation() {
        return methodAnnotation;
    }

    public Integer getTimeout() {
        return methodAnnotation != null ? methodAnnotation.timeout() : null;
    }

    public boolean isTimeoutInterrupt() {
        return methodAnnotation != null && methodAnnotation.timeoutInterrupt();
    }

    public boolean isInnerMethodFlag() {
        return innerMethodFlag;
    }

    public boolean isReturnCompletionStageFlag() {
        return returnCompletionStageFlag;
    }

    public boolean isReturnFutureFlag() {
        return returnFutureFlag;
    }

    public boolean isReturnRxjava3FlowableFlag() {
        return returnRxjava3FlowableFlag;
    }

    public boolean isReturnRxjava3ObservableFlag() {
        return returnRxjava3ObservableFlag;
    }

    public boolean isReturnChunkCompletionFlag() {
        return returnChunkCompletionFlag;
    }

    public boolean isReturnAsync() {
        return isReturnTypeReactivePublisherFlag()
                || isReturnFutureFlag()
                || isReturnCompletionStageFlag()
                || isReturnChunkCompletionFlag()
                || isReturnRxjava3ObservableFlag()
                || isReturnRxjava3FlowableFlag()
                || isReturnTypeJdk9PublisherFlag();
    }

    public boolean isReturnTypeJdk9PublisherFlag() {
        return returnTypeJdk9PublisherFlag;
    }

    public boolean isReturnTypeReactivePublisherFlag() {
        return returnTypeReactivePublisherFlag;
    }

    public Class[] getParameterTypes() {
        return parameterTypes;
    }

    public Type getGenericReturnType() {
        return genericReturnType;
    }

    public Type getChunkGenericReturnType() {
        return chunkGenericReturnType;
    }

    public boolean isReturnVoid() {
        return genericReturnType == void.class || genericReturnType == Void.class;
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof RpcMethod)) {
            return false;
        }
        RpcMethod that = (RpcMethod) obj;
        if (this.parameterTypes.length != that.parameterTypes.length) {
            return false;
        }
        for (int i = 0; i < this.parameterTypes.length; i++) {
            if (this.parameterTypes[i] != that.parameterTypes[i]) {
                return false;
            }
        }
        return this.parameterNames.length == that.parameterNames.length;
    }

    public String getMethodDescriptorName() {
        return methodDescriptorName;
    }

    public Class getReturnType() {
        return method.getReturnType();
    }

    public String getMethodName() {
        return methodName;
    }

    public Object invoke(Object instance, Object[] args) throws Throwable {
        if (methodHandle != null) {
            Object[] methodHandleArgs = new Object[parameterCount + 1];
            methodHandleArgs[0] = instance;
            System.arraycopy(args, 0, methodHandleArgs, 1, args.length);
            return methodHandle.invokeWithArguments(methodHandleArgs);
        } else {
            return method.invoke(instance, args);
        }
    }

    public String getParameterTypeDescriptorName() {
        return parameterTypeDescriptorName;
    }

    public String[] getParameterNames() {
        return parameterNames;
    }

    public INSTANCE getInstance() {
        return instance;
    }

    @Override
    public String toString() {
        return "RpcMethod{public " + getMethodDescriptorName() + "(" + getParameterTypeDescriptorName() + ")" + '}';
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy