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

org.lognet.springboot.grpc.autoconfigure.GRpcAutoConfiguration Maven / Gradle / Ivy

The newest version!
package org.lognet.springboot.grpc.autoconfigure;

import io.grpc.ServerBuilder;
import io.grpc.inprocess.InProcessServerBuilder;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import org.lognet.springboot.grpc.FailureHandlingSupport;
import org.lognet.springboot.grpc.GRpcGlobalInterceptor;
import org.lognet.springboot.grpc.GRpcServerBuilderConfigurer;
import org.lognet.springboot.grpc.GRpcServerRunner;
import org.lognet.springboot.grpc.GRpcService;
import org.lognet.springboot.grpc.GRpcServicesRegistry;
import org.lognet.springboot.grpc.health.DefaultHealthStatusService;
import org.lognet.springboot.grpc.recovery.GRpcExceptionHandlerInterceptor;
import org.lognet.springboot.grpc.recovery.GRpcExceptionHandlerMethodResolver;
import org.lognet.springboot.grpc.recovery.GRpcServiceAdvice;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration;
import org.springframework.boot.context.properties.ConfigurationPropertiesBindHandlerAdvisor;
import org.springframework.boot.context.properties.ConfigurationPropertiesBinding;
import org.springframework.boot.context.properties.bind.AbstractBindHandler;
import org.springframework.boot.context.properties.bind.BindContext;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.convert.converter.Converter;

/** Created by alexf on 25-Jan-16. */
@AutoConfigureOrder
@AutoConfigureAfter(ValidationAutoConfiguration.class)
@ConditionalOnBean(annotation = GRpcService.class)
@Import({
  GRpcValidationConfiguration.class,
  NettyServerBuilderSelector.class,
  DefaultHealthStatusService.class
})
@Configuration
public class GRpcAutoConfiguration {

  @Bean
  @OnGrpcServerEnabled
  public GRpcServerRunner grpcServerRunner(
      @Qualifier("grpcInternalConfigurator") Consumer> configurator,
      ServerBuilder serverBuilder) {
    return new GRpcServerRunner(configurator, serverBuilder);
  }

  @Bean
  @ConditionalOnProperty(prefix = "grpc", name = "in-process-server-name")
  public GRpcServerRunner grpcInprocessServerRunner(
      @Qualifier("grpcInternalConfigurator") Consumer> configurator,
      GRpcServerProperties gRpcServerProperties) {
    return new GRpcServerRunner(
        configurator,
        InProcessServerBuilder.forName(gRpcServerProperties.getInProcessServerName()));
  }

  @Bean
  public GRpcServicesRegistry grpcServicesRegistry() {
    return new GRpcServicesRegistry();
  }

  @Bean
  public GRpcExceptionHandlerMethodResolver exceptionHandlerMethodResolver(
      GRpcServicesRegistry gRpcServicesRegistry, ApplicationContext applicationContext) {
    final Collection advices =
        applicationContext.getBeansWithAnnotation(GRpcServiceAdvice.class).values();
    return new GRpcExceptionHandlerMethodResolver(gRpcServicesRegistry, advices);
  }

  @Bean
  public FailureHandlingSupport failureHandlingSupport(
      GRpcExceptionHandlerMethodResolver methodResolver) {
    return new FailureHandlingSupport(methodResolver);
  }

  @Bean
  @GRpcGlobalInterceptor
  public GRpcExceptionHandlerInterceptor exceptionHandlerInterceptor(
      FailureHandlingSupport failureHandlingSupport,
      GRpcExceptionHandlerMethodResolver methodResolver,
      GRpcServerProperties serverProperties) {
    return new GRpcExceptionHandlerInterceptor(
        methodResolver, failureHandlingSupport, serverProperties);
  }

  @Bean
  @ConditionalOnMissingBean(GRpcServerBuilderConfigurer.class)
  public GRpcServerBuilderConfigurer serverBuilderConfigurer() {
    return new GRpcServerBuilderConfigurer();
  }

  @Bean
  public GRpcServerProperties gRpcServerProperties() {
    return new GRpcServerProperties();
  }

  @ConditionalOnMissingClass("org.springframework.cloud.consul.discovery.ConsulDiscoveryProperties")
  @Bean
  public ConfigurationPropertiesBindHandlerAdvisor skipConsulDiscoveryBinding() {
    return bindHandler ->
        new AbstractBindHandler(bindHandler) {
          private final ConfigurationPropertyName grpcConsulConfigProperty =
              ConfigurationPropertyName.of("grpc.consul");

          @Override
          public  Bindable onStart(
              ConfigurationPropertyName name, Bindable target, BindContext context) {
            // otherwise, it will try to instantiate grpc.consul property and discovery field class
            // doesn't exist
            return grpcConsulConfigProperty.equals(name)
                ? null
                : super.onStart(name, target, context);
          }
        };
  }

  @Bean(name = "grpcInternalConfigurator")
  public Consumer> configurator(
      List configurers, GRpcServerProperties grpcServerProperties) {
    return serverBuilder -> {
      if (grpcServerProperties.isEnabled()) {
        Optional.ofNullable(grpcServerProperties.getSecurity())
            .ifPresent(
                s -> {
                  boolean setupSecurity = Optional.ofNullable(s.getCertChain()).isPresent();
                  if (setupSecurity != Optional.ofNullable(s.getPrivateKey()).isPresent()) {
                    throw new BeanCreationException(
                        "Both  gRPC  TLS 'certChain' and 'privateKey' should be configured. One of them is null. ");
                  }
                  if (setupSecurity) {
                    try {
                      serverBuilder.useTransportSecurity(
                          s.getCertChain().getInputStream(), s.getPrivateKey().getInputStream());
                    } catch (IOException e) {
                      throw new BeanCreationException("Failed to setup security", e);
                    }
                  }
                });
      }
      configurers.forEach(c -> c.configure(serverBuilder));
    };
  }

  @Bean
  @ConfigurationPropertiesBinding
  public static Converter socketAddressConverter() {
    return new Converter() {
      @Override
      public InetSocketAddress convert(String source) {
        final String[] chunks = source.split(":");
        int port;
        switch (chunks.length) {
          case 1:
            port = GRpcServerProperties.DEFAULT_GRPC_PORT;
            break;
          case 2:
            port = Integer.parseInt(chunks[1]);
            if (port < 1) {
              port = 0;
            }
            break;
          default:
            throw new IllegalArgumentException(source + " can't be converted to socket address");
        }

        return new InetSocketAddress(chunks[0], port);
      }
    };
  }
}