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

org.lognet.springboot.grpc.GRpcServicesRegistry Maven / Gradle / Ivy

The newest version!
// Generated by delombok at Wed Sep 27 05:27:18 UTC 2023
package org.lognet.springboot.grpc;

import io.grpc.BindableService;
import io.grpc.MethodDescriptor;
import io.grpc.ServerInterceptor;
import io.grpc.ServerServiceDefinition;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.MethodParameter;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.function.SingletonSupplier;

public class GRpcServicesRegistry implements InitializingBean, ApplicationContextAware {

  public static class GrpcServiceMethod {
    private BindableService service;
    private Method method;

    @java.lang.SuppressWarnings("all")
    GrpcServiceMethod(final BindableService service, final Method method) {
      this.service = service;
      this.method = method;
    }

    @java.lang.SuppressWarnings("all")
    public static class GrpcServiceMethodBuilder {
      @java.lang.SuppressWarnings("all")
      private BindableService service;

      @java.lang.SuppressWarnings("all")
      private Method method;

      @java.lang.SuppressWarnings("all")
      GrpcServiceMethodBuilder() {}

      /**
       * @return {@code this}.
       */
      @java.lang.SuppressWarnings("all")
      public GRpcServicesRegistry.GrpcServiceMethod.GrpcServiceMethodBuilder service(
          final BindableService service) {
        this.service = service;
        return this;
      }

      /**
       * @return {@code this}.
       */
      @java.lang.SuppressWarnings("all")
      public GRpcServicesRegistry.GrpcServiceMethod.GrpcServiceMethodBuilder method(
          final Method method) {
        this.method = method;
        return this;
      }

      @java.lang.SuppressWarnings("all")
      public GRpcServicesRegistry.GrpcServiceMethod build() {
        return new GRpcServicesRegistry.GrpcServiceMethod(this.service, this.method);
      }

      @java.lang.Override
      @java.lang.SuppressWarnings("all")
      public java.lang.String toString() {
        return "GRpcServicesRegistry.GrpcServiceMethod.GrpcServiceMethodBuilder(service="
            + this.service
            + ", method="
            + this.method
            + ")";
      }
    }

    @java.lang.SuppressWarnings("all")
    public static GRpcServicesRegistry.GrpcServiceMethod.GrpcServiceMethodBuilder builder() {
      return new GRpcServicesRegistry.GrpcServiceMethod.GrpcServiceMethodBuilder();
    }

    @java.lang.SuppressWarnings("all")
    public BindableService getService() {
      return this.service;
    }

    @java.lang.SuppressWarnings("all")
    public Method getMethod() {
      return this.method;
    }
  }

  private ApplicationContext applicationContext;
  private Supplier> beanNameToServiceBean;
  private Supplier> serviceNameToServiceBean;
  private Supplier> grpcGlobalInterceptors;
  private Supplier, GrpcServiceMethod>> descriptorToServiceMethod;
  private Supplier>> methodToDescriptor;

  /**
   * @return service name to grpc service bean
   */
  public Map getServiceNameToServiceBeanMap() {
    return serviceNameToServiceBean.get();
  }

  /**
   * @return spring bean name to grpc service bean
   */
  public Map getBeanNameToServiceBeanMap() {
    return beanNameToServiceBean.get();
  }

  Collection getGlobalInterceptors() {
    return grpcGlobalInterceptors.get();
  }

  public GrpcServiceMethod getGrpServiceMethod(MethodDescriptor descriptor) {
    return descriptorToServiceMethod.get().get(descriptor);
  }

  public MethodDescriptor getMethodDescriptor(Method method) {
    return methodToDescriptor.get().get(method);
  }

  private  Map getBeanNamesByTypeWithAnnotation(
      Class annotationType, Class beanType) {
    return applicationContext.getBeansWithAnnotation(annotationType).entrySet().stream()
        .filter(e -> beanType.isInstance(e.getValue()))
        .collect(Collectors.toMap(Map.Entry::getKey, e -> beanType.cast(e.getValue())));
  }

  @Override
  public void afterPropertiesSet() throws Exception {
    descriptorToServiceMethod = SingletonSupplier.of(this::descriptorToServiceMethod);
    methodToDescriptor =
        SingletonSupplier.of(
            () ->
                descriptorToServiceMethod.get().entrySet().stream()
                    .collect(Collectors.toMap(e -> e.getValue().getMethod(), Map.Entry::getKey)));
    beanNameToServiceBean =
        SingletonSupplier.of(
            () -> getBeanNamesByTypeWithAnnotation(GRpcService.class, BindableService.class));
    serviceNameToServiceBean =
        SingletonSupplier.of(
            () ->
                beanNameToServiceBean.get().values().stream()
                    .collect(
                        Collectors.toMap(
                            s -> s.bindService().getServiceDescriptor().getName(),
                            Function.identity())));
    grpcGlobalInterceptors =
        SingletonSupplier.of(
            () ->
                getBeanNamesByTypeWithAnnotation(
                        GRpcGlobalInterceptor.class, ServerInterceptor.class)
                    .values());
  }

  @Override
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    this.applicationContext = applicationContext;
  }

  private Map, GrpcServiceMethod> descriptorToServiceMethod() {
    final Map, GrpcServiceMethod> map = new HashMap<>();
    Function filterFactory =
        name -> method -> method.getName().equalsIgnoreCase(name.replaceAll("_", ""));
    Predicate firstArgIsMono =
        m -> "reactor.core.publisher.Mono".equals(m.getParameterTypes()[0].getName());
    Predicate singleArg = m -> 1 == m.getParameterCount();
    for (BindableService service : getBeanNameToServiceBeanMap().values()) {
      final ServerServiceDefinition serviceDefinition = service.bindService();
      for (MethodDescriptor d : serviceDefinition.getServiceDescriptor().getMethods()) {
        Class abstractBaseClass = service.getClass();
        while (!Modifier.isAbstract(abstractBaseClass.getModifiers())) {
          abstractBaseClass = abstractBaseClass.getSuperclass();
        }
        final Set methods =
            MethodIntrospector.selectMethods(
                abstractBaseClass, filterFactory.apply(d.getBareMethodName()));
        switch (methods.size()) {
          case 0:
            throw new IllegalStateException(
                "Method "
                    + d.getBareMethodName()
                    + "not found in service "
                    + serviceDefinition.getServiceDescriptor().getName());
          case 1:
            map.put(
                d,
                GrpcServiceMethod.builder()
                    .service(service)
                    .method(methods.iterator().next())
                    .build());
            break;
          default:
            if (2 == methods.size()) {
              Optional methodWithMono = // grpcMethod(Mono arg)
                  methods.stream().filter(singleArg.and(firstArgIsMono)).findFirst();
              Optional methodPure = // grpcMethod(Payload arg)
                  methods.stream().filter(singleArg.and(firstArgIsMono.negate())).findFirst();
              Class finalAbstractBaseClass = abstractBaseClass;
              Boolean typesAreEqual =
                  methodWithMono
                      .map(
                          m ->
                              ((ParameterizedType)
                                      new MethodParameter(m, 0)
                                          .withContainingClass(finalAbstractBaseClass)
                                          .getGenericParameterType())
                                  .getActualTypeArguments()[0])
                      .map(
                          t -> t.equals(methodPure.map(m -> m.getParameterTypes()[0]).orElse(null)))
                      .orElse(false);
              if (typesAreEqual) {
                map.put(
                    d,
                    GrpcServiceMethod.builder()
                        .service(service)
                        .method(methodWithMono.get())
                        .build());
                break;
              }
            }
            throw new IllegalStateException(
                "Ambiguous method "
                    + d.getBareMethodName()
                    + " in service "
                    + serviceDefinition.getServiceDescriptor().getName());
        }
      }
    }
    return Collections.unmodifiableMap(map);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy