• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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