1 /* 2 * Copyright (C) 2016 The Dagger Authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package dagger.grpc.server.processor; 18 19 import static com.google.auto.common.MoreElements.hasModifiers; 20 import static com.google.common.collect.ImmutableList.toImmutableList; 21 import static com.squareup.javapoet.MethodSpec.methodBuilder; 22 import static com.squareup.javapoet.TypeSpec.anonymousClassBuilder; 23 import static com.squareup.javapoet.TypeSpec.classBuilder; 24 import static javax.lang.model.element.Modifier.FINAL; 25 import static javax.lang.model.element.Modifier.PUBLIC; 26 import static javax.lang.model.element.Modifier.STATIC; 27 import static javax.lang.model.util.ElementFilter.fieldsIn; 28 import static javax.lang.model.util.ElementFilter.methodsIn; 29 30 import com.google.common.collect.ImmutableList; 31 import com.squareup.javapoet.CodeBlock; 32 import com.squareup.javapoet.MethodSpec; 33 import com.squareup.javapoet.ParameterSpec; 34 import com.squareup.javapoet.ParameterizedTypeName; 35 import com.squareup.javapoet.TypeName; 36 import com.squareup.javapoet.TypeSpec; 37 import dagger.grpc.server.GrpcService; 38 import java.util.List; 39 import java.util.function.Function; 40 import javax.lang.model.element.Element; 41 import javax.lang.model.element.ExecutableElement; 42 import javax.lang.model.element.VariableElement; 43 import javax.lang.model.type.TypeMirror; 44 45 /** 46 * An object that generates the proxying service definition module for a {@link 47 * GrpcService}-annotated service implementation. 48 */ 49 final class ProxyModuleGenerator extends SourceGenerator { 50 51 private final GrpcServiceModel grpcServiceModel; 52 ProxyModuleGenerator(GrpcServiceModel grpcServiceModel)53 ProxyModuleGenerator(GrpcServiceModel grpcServiceModel) { 54 super(grpcServiceModel.packageName()); 55 this.grpcServiceModel = grpcServiceModel; 56 } 57 58 @Override createType()59 protected TypeSpec createType() { 60 TypeSpec.Builder proxyModule = 61 classBuilder(grpcServiceModel.proxyModuleName) 62 .addModifiers(PUBLIC, FINAL) 63 .addJavadoc( 64 "Install this module in the {@link $T @Singleton} server component.\n", 65 JavaxInject.singleton().type); 66 grpcServiceModel.generatedAnnotation().ifPresent(proxyModule::addAnnotation); 67 return proxyModule 68 .addAnnotation(Dagger.module()) 69 .addMethod(provideServiceDefinitionContribution()) 70 .addMethod(provideServiceDefinitionFactory()) 71 .build(); 72 } 73 74 /** 75 * Returns the {@link dagger.Provides @Provides} method for the proxying {@link 76 * io.grpc.ServerServiceDefinition}. 77 */ provideServiceDefinitionContribution()78 private MethodSpec provideServiceDefinitionContribution() { 79 MethodSpec.Builder method = 80 methodBuilder("serviceDefinition") 81 .addAnnotation(Dagger.provides()) 82 .addAnnotation(Dagger.intoSet()) 83 .addAnnotation(JavaxInject.singleton()) 84 .addModifiers(STATIC) 85 .returns(IoGrpc.SERVER_SERVICE_DEFINITION) 86 .addParameter( 87 ParameterSpec.builder( 88 Dagger.GrpcServer.SERVICE_DEFINITION_FACTORY, "serviceDefinitionFactory") 89 .addAnnotation(grpcServiceModel.forGrpcService()) 90 .build()) 91 .addCode( 92 "return $T.builder($T.SERVICE_NAME)", 93 IoGrpc.SERVER_SERVICE_DEFINITION, 94 grpcServiceModel.grpcClass()); 95 for (CodeBlock methodDescriptor : methodDescriptors()) { 96 method.addCode( 97 ".addMethod($T.proxyMethod($L, serviceDefinitionFactory))", 98 Dagger.GrpcServer.PROXY_SERVER_CALL_HANDLER, 99 methodDescriptor); 100 } 101 method.addCode(".build();"); 102 return method.build(); 103 } 104 105 /** 106 * Returns the {@link io.grpc.MethodDescriptor} references from the class enclosing the service 107 * interface. 108 * 109 * <p>Looks first for public static methods (new in 1.8), and then for public static fields if it 110 * finds none. 111 */ methodDescriptors()112 private ImmutableList<CodeBlock> methodDescriptors() { 113 ImmutableList<CodeBlock> staticMethodCalls = 114 findMethodDescriptors( 115 methodsIn(grpcServiceModel.grpcClass().getEnclosedElements()), 116 ExecutableElement::getReturnType, 117 method -> 118 CodeBlock.of("$T.$N()", grpcServiceModel.grpcClass(), method.getSimpleName())); 119 if (!staticMethodCalls.isEmpty()) { 120 return staticMethodCalls; 121 } 122 return findMethodDescriptors( 123 fieldsIn(grpcServiceModel.grpcClass().getEnclosedElements()), 124 VariableElement::asType, 125 field -> CodeBlock.of("$T.$N", grpcServiceModel.grpcClass(), field.getSimpleName())); 126 } 127 findMethodDescriptors( List<E> elements, Function<? super E, TypeMirror> elementType, Function<? super E, CodeBlock> elementReference)128 private <E extends Element> ImmutableList<CodeBlock> findMethodDescriptors( 129 List<E> elements, 130 Function<? super E, TypeMirror> elementType, 131 Function<? super E, CodeBlock> elementReference) { 132 return elements 133 .stream() 134 .filter(hasModifiers(PUBLIC, STATIC)::apply) 135 .filter( 136 method -> { 137 TypeName typeName = TypeName.get(elementType.apply(method)); 138 return typeName instanceof ParameterizedTypeName 139 && ((ParameterizedTypeName) typeName).rawType.equals(IoGrpc.METHOD_DESCRIPTOR); 140 }) 141 .map(elementReference) 142 .collect(toImmutableList()); 143 } 144 145 /** 146 * Returns the {@link dagger.Provides @Provides} method for the {@link 147 * dagger.grpc.server.ProxyServerCallHandler.ServiceDefinitionFactory} used by the proxy. 148 */ provideServiceDefinitionFactory()149 private MethodSpec provideServiceDefinitionFactory() { 150 return methodBuilder("serviceDefinitionFactory") 151 .addAnnotation(Dagger.provides()) 152 .addAnnotation(grpcServiceModel.forGrpcService()) 153 .addModifiers(STATIC) 154 .returns(Dagger.GrpcServer.SERVICE_DEFINITION_FACTORY) 155 .addParameter(grpcServiceModel.serviceDefinitionTypeFactoryName, "factory", FINAL) 156 .addStatement("return $L", anonymousServiceDefinitionFactory()) 157 .build(); 158 } 159 160 /** 161 * Returns the anonymous inner class that implements the {@link 162 * dagger.grpc.server.ProxyServerCallHandler.ServiceDefinitionFactory} used by the proxy. 163 */ anonymousServiceDefinitionFactory()164 private TypeSpec anonymousServiceDefinitionFactory() { 165 return anonymousClassBuilder("") 166 .addSuperinterface(Dagger.GrpcServer.SERVICE_DEFINITION_FACTORY) 167 .addMethod( 168 methodBuilder("getServiceDefinition") 169 .addAnnotation(Override.class) 170 .addModifiers(PUBLIC) 171 .returns(IoGrpc.SERVER_SERVICE_DEFINITION) 172 .addParameter(IoGrpc.METADATA, "headers") 173 .addStatement( 174 "return factory.grpcService(new $T(headers)).$N()", 175 Dagger.GrpcServer.GRPC_CALL_METADATA_MODULE, 176 grpcServiceModel.subcomponentServiceDefinitionMethodName()) 177 .build()) 178 .build(); 179 } 180 } 181