1 /*
2 * Copyright 2019 The gRPC 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 // gRPC Java code generation
18 //
19 // The generated code should be very light-weight in terms of size. Although
20 // less of a scaling problem than for protobuf messages, additions here are
21 // multiplied many times and could easily bloat binaries if added to without
22 // consideration. This is generally achieved by putting logic in io.grpc.stub
23 // and limiting the generated code to API surface and inputs.
24 //
25 // gRPC is compatible with old versions of the generated code. New versions of
26 // generated code are free to use new APIs, and thus require new gRPC versions.
27 // But this isn't a special rule and is more just that the generated code is
28 // best viewed as a library that depends on gRPC (as it may be used within a
29 // library), and it should adhere to the normal rules of such. Notably, it can
30 // only use stable API. Using experimental API, even if only exposing its own
31 // experimental API, is not safe as libraries do not get to choose the version
32 // of gRPC being used. The rule here is more "everything should behave like
33 // normal to users" or "we don't require users to generate code for their
34 // transitive dependency tree because OSS build tools don't support that."
35 //
36 // Java lite is designed for tools like Proguard to strip unused code. Be
37 // careful not to add new references to methods that would retain them even when
38 // unneeded. In particular, we want uncalled RPC methods to have their
39 // descriptors and code pruned.
40
41 #include "java_generator.h"
42
43 #include <algorithm>
44 #include <iostream>
45 #include <iterator>
46 #include <map>
47 #include <set>
48 #include <vector>
49 #include <google/protobuf/descriptor.h>
50 #include <google/protobuf/descriptor.pb.h>
51 #include <google/protobuf/io/printer.h>
52 #include <google/protobuf/io/zero_copy_stream.h>
53 #include <google/protobuf/stubs/common.h>
54
55 // Protobuf 3.21 changed the name of this file.
56 #if GOOGLE_PROTOBUF_VERSION >= 3021000
57 #include <google/protobuf/compiler/java/names.h>
58 #else
59 #include <google/protobuf/compiler/java/java_names.h>
60 #endif
61
62 // Stringify helpers used solely to cast GRPC_VERSION
63 #ifndef STR
64 #define STR(s) #s
65 #endif
66
67 #ifndef XSTR
68 #define XSTR(s) STR(s)
69 #endif
70
71 #ifdef ABSL_FALLTHROUGH_INTENDED
72 #define FALLTHROUGH ABSL_FALLTHROUGH_INTENDED
73 #else
74 #define FALLTHROUGH
75 #endif
76
77 namespace java_grpc_generator {
78
79 namespace protobuf = google::protobuf;
80
81 using protobuf::Descriptor;
82 using protobuf::FileDescriptor;
83 using protobuf::MethodDescriptor;
84 using protobuf::ServiceDescriptor;
85 using protobuf::SourceLocation;
86 using protobuf::io::Printer;
87 using std::to_string;
88
89 // java keywords from: https://docs.oracle.com/javase/specs/jls/se8/html/jls-3.html#jls-3.9
90 static std::set<std::string> java_keywords = {
91 "abstract",
92 "assert",
93 "boolean",
94 "break",
95 "byte",
96 "case",
97 "catch",
98 "char",
99 "class",
100 "const",
101 "continue",
102 "default",
103 "do",
104 "double",
105 "else",
106 "enum",
107 "extends",
108 "final",
109 "finally",
110 "float",
111 "for",
112 "goto",
113 "if",
114 "implements",
115 "import",
116 "instanceof",
117 "int",
118 "interface",
119 "long",
120 "native",
121 "new",
122 "package",
123 "private",
124 "protected",
125 "public",
126 "return",
127 "short",
128 "static",
129 "strictfp",
130 "super",
131 "switch",
132 "synchronized",
133 "this",
134 "throw",
135 "throws",
136 "transient",
137 "try",
138 "void",
139 "volatile",
140 "while",
141 // additional ones added by us
142 "true",
143 "false",
144 };
145
146 // Adjust a method name prefix identifier to follow the JavaBean spec:
147 // - decapitalize the first letter
148 // - remove embedded underscores & capitalize the following letter
149 // Finally, if the result is a reserved java keyword, append an underscore.
MixedLower(const std::string & word)150 static std::string MixedLower(const std::string& word) {
151 std::string w;
152 w += tolower(word[0]);
153 bool after_underscore = false;
154 for (size_t i = 1; i < word.length(); ++i) {
155 if (word[i] == '_') {
156 after_underscore = true;
157 } else {
158 w += after_underscore ? toupper(word[i]) : word[i];
159 after_underscore = false;
160 }
161 }
162 if (java_keywords.find(w) != java_keywords.end()) {
163 return w + "_";
164 }
165 return w;
166 }
167
168 // Converts to the identifier to the ALL_UPPER_CASE format.
169 // - An underscore is inserted where a lower case letter is followed by an
170 // upper case letter.
171 // - All letters are converted to upper case
ToAllUpperCase(const std::string & word)172 static std::string ToAllUpperCase(const std::string& word) {
173 std::string w;
174 for (size_t i = 0; i < word.length(); ++i) {
175 w += toupper(word[i]);
176 if ((i < word.length() - 1) && islower(word[i]) && isupper(word[i + 1])) {
177 w += '_';
178 }
179 }
180 return w;
181 }
182
LowerMethodName(const MethodDescriptor * method)183 static inline std::string LowerMethodName(const MethodDescriptor* method) {
184 return MixedLower(method->name());
185 }
186
MethodPropertiesFieldName(const MethodDescriptor * method)187 static inline std::string MethodPropertiesFieldName(const MethodDescriptor* method) {
188 return "METHOD_" + ToAllUpperCase(method->name());
189 }
190
MethodPropertiesGetterName(const MethodDescriptor * method)191 static inline std::string MethodPropertiesGetterName(const MethodDescriptor* method) {
192 return MixedLower("get_" + method->name() + "_method");
193 }
194
MethodIdFieldName(const MethodDescriptor * method)195 static inline std::string MethodIdFieldName(const MethodDescriptor* method) {
196 return "METHODID_" + ToAllUpperCase(method->name());
197 }
198
MessageFullJavaName(const Descriptor * desc)199 static inline std::string MessageFullJavaName(const Descriptor* desc) {
200 return protobuf::compiler::java::ClassName(desc);
201 }
202
203 // TODO(nmittler): Remove once protobuf includes javadoc methods in distribution.
204 template <typename ITR>
GrpcSplitStringToIteratorUsing(const std::string & full,const char * delim,ITR & result)205 static void GrpcSplitStringToIteratorUsing(const std::string& full,
206 const char* delim,
207 ITR& result) {
208 // Optimize the common case where delim is a single character.
209 if (delim[0] != '\0' && delim[1] == '\0') {
210 char c = delim[0];
211 const char* p = full.data();
212 const char* end = p + full.size();
213 while (p != end) {
214 if (*p == c) {
215 ++p;
216 } else {
217 const char* start = p;
218 while (++p != end && *p != c);
219 *result++ = std::string(start, p - start);
220 }
221 }
222 return;
223 }
224
225 std::string::size_type begin_index, end_index;
226 begin_index = full.find_first_not_of(delim);
227 while (begin_index != std::string::npos) {
228 end_index = full.find_first_of(delim, begin_index);
229 if (end_index == std::string::npos) {
230 *result++ = full.substr(begin_index);
231 return;
232 }
233 *result++ = full.substr(begin_index, (end_index - begin_index));
234 begin_index = full.find_first_not_of(delim, end_index);
235 }
236 }
237
238 // TODO(nmittler): Remove once protobuf includes javadoc methods in distribution.
GrpcSplitStringUsing(const std::string & full,const char * delim,std::vector<std::string> * result)239 static void GrpcSplitStringUsing(const std::string& full,
240 const char* delim,
241 std::vector<std::string>* result) {
242 std::back_insert_iterator< std::vector<std::string> > it(*result);
243 GrpcSplitStringToIteratorUsing(full, delim, it);
244 }
245
246 // TODO(nmittler): Remove once protobuf includes javadoc methods in distribution.
GrpcSplit(const std::string & full,const char * delim)247 static std::vector<std::string> GrpcSplit(const std::string& full, const char* delim) {
248 std::vector<std::string> result;
249 GrpcSplitStringUsing(full, delim, &result);
250 return result;
251 }
252
253 // TODO(nmittler): Remove once protobuf includes javadoc methods in distribution.
GrpcEscapeJavadoc(const std::string & input)254 static std::string GrpcEscapeJavadoc(const std::string& input) {
255 std::string result;
256 result.reserve(input.size() * 2);
257
258 char prev = '*';
259
260 for (std::string::size_type i = 0; i < input.size(); i++) {
261 char c = input[i];
262 switch (c) {
263 case '*':
264 // Avoid "/*".
265 if (prev == '/') {
266 result.append("*");
267 } else {
268 result.push_back(c);
269 }
270 break;
271 case '/':
272 // Avoid "*/".
273 if (prev == '*') {
274 result.append("/");
275 } else {
276 result.push_back(c);
277 }
278 break;
279 case '@':
280 // '@' starts javadoc tags including the @deprecated tag, which will
281 // cause a compile-time error if inserted before a declaration that
282 // does not have a corresponding @Deprecated annotation.
283 result.append("@");
284 break;
285 case '<':
286 // Avoid interpretation as HTML.
287 result.append("<");
288 break;
289 case '>':
290 // Avoid interpretation as HTML.
291 result.append(">");
292 break;
293 case '&':
294 // Avoid interpretation as HTML.
295 result.append("&");
296 break;
297 case '\\':
298 // Java interprets Unicode escape sequences anywhere!
299 result.append("\");
300 break;
301 default:
302 result.push_back(c);
303 break;
304 }
305
306 prev = c;
307 }
308
309 return result;
310 }
311
312 // TODO(nmittler): Remove once protobuf includes javadoc methods in distribution.
313 template <typename DescriptorType>
GrpcGetCommentsForDescriptor(const DescriptorType * descriptor)314 static std::string GrpcGetCommentsForDescriptor(const DescriptorType* descriptor) {
315 SourceLocation location;
316 if (descriptor->GetSourceLocation(&location)) {
317 return location.leading_comments.empty() ?
318 location.trailing_comments : location.leading_comments;
319 }
320 return std::string();
321 }
322
323 // TODO(nmittler): Remove once protobuf includes javadoc methods in distribution.
GrpcGetDocLines(const std::string & comments)324 static std::vector<std::string> GrpcGetDocLines(const std::string& comments) {
325 if (!comments.empty()) {
326 // TODO(kenton): Ideally we should parse the comment text as Markdown and
327 // write it back as HTML, but this requires a Markdown parser. For now
328 // we just use <pre> to get fixed-width text formatting.
329
330 // If the comment itself contains block comment start or end markers,
331 // HTML-escape them so that they don't accidentally close the doc comment.
332 std::string escapedComments = GrpcEscapeJavadoc(comments);
333
334 std::vector<std::string> lines = GrpcSplit(escapedComments, "\n");
335 while (!lines.empty() && lines.back().empty()) {
336 lines.pop_back();
337 }
338 return lines;
339 }
340 return std::vector<std::string>();
341 }
342
343 // TODO(nmittler): Remove once protobuf includes javadoc methods in distribution.
344 template <typename DescriptorType>
GrpcGetDocLinesForDescriptor(const DescriptorType * descriptor)345 static std::vector<std::string> GrpcGetDocLinesForDescriptor(const DescriptorType* descriptor) {
346 return GrpcGetDocLines(GrpcGetCommentsForDescriptor(descriptor));
347 }
348
349 enum StubType {
350 ASYNC_INTERFACE = 0,
351 BLOCKING_CLIENT_INTERFACE = 1,
352 FUTURE_CLIENT_INTERFACE = 2,
353 BLOCKING_SERVER_INTERFACE = 3,
354 ASYNC_CLIENT_IMPL = 4,
355 BLOCKING_CLIENT_IMPL = 5,
356 FUTURE_CLIENT_IMPL = 6,
357 ABSTRACT_CLASS = 7,
358 NONE = 8,
359 };
360
361 enum CallType {
362 ASYNC_CALL = 0,
363 BLOCKING_CALL = 1,
364 FUTURE_CALL = 2
365 };
366
367 // TODO(nmittler): Remove once protobuf includes javadoc methods in distribution.
GrpcWriteDocCommentBody(Printer * printer,const std::vector<std::string> & lines,bool surroundWithPreTag)368 static void GrpcWriteDocCommentBody(Printer* printer,
369 const std::vector<std::string>& lines,
370 bool surroundWithPreTag) {
371 if (!lines.empty()) {
372 if (surroundWithPreTag) {
373 printer->Print(" * <pre>\n");
374 }
375
376 for (size_t i = 0; i < lines.size(); i++) {
377 // Most lines should start with a space. Watch out for lines that start
378 // with a /, since putting that right after the leading asterisk will
379 // close the comment.
380 if (!lines[i].empty() && lines[i][0] == '/') {
381 printer->Print(" * $line$\n", "line", lines[i]);
382 } else {
383 printer->Print(" *$line$\n", "line", lines[i]);
384 }
385 }
386
387 if (surroundWithPreTag) {
388 printer->Print(" * </pre>\n");
389 }
390 }
391 }
392
393 // TODO(nmittler): Remove once protobuf includes javadoc methods in distribution.
GrpcWriteDocComment(Printer * printer,const std::string & comments)394 static void GrpcWriteDocComment(Printer* printer, const std::string& comments) {
395 printer->Print("/**\n");
396 std::vector<std::string> lines = GrpcGetDocLines(comments);
397 GrpcWriteDocCommentBody(printer, lines, false);
398 printer->Print(" */\n");
399 }
400
401 // For the non-interface classes add a description of use before the description from proto
GrpcWriteServiceDocComment(Printer * printer,const ServiceDescriptor * service,StubType type)402 static void GrpcWriteServiceDocComment(Printer* printer,
403 const ServiceDescriptor* service,
404 StubType type) {
405 printer->Print("/**\n");
406
407 std::map<std::string, std::string> vars = {{"service", service->name()}};
408 switch (type) {
409 case ASYNC_CLIENT_IMPL:
410 printer->Print(vars, " * A stub to allow clients to do asynchronous rpc calls to service $service$.\n");
411 break;
412 case BLOCKING_CLIENT_IMPL:
413 printer->Print(vars, " * A stub to allow clients to do synchronous rpc calls to service $service$.\n");
414 break;
415 case FUTURE_CLIENT_IMPL:
416 printer->Print(vars, " * A stub to allow clients to do ListenableFuture-style rpc calls to service $service$.\n");
417 break;
418 case ABSTRACT_CLASS:
419 printer->Print(vars, " * Base class for the server implementation of the service $service$.\n");
420 break;
421 default: ;
422 // No extra description
423 }
424
425 std::vector<std::string> lines = GrpcGetDocLinesForDescriptor(service);
426 GrpcWriteDocCommentBody(printer, lines, true);
427 printer->Print(" */\n");
428 }
429
430 // TODO(nmittler): Remove once protobuf includes javadoc methods in distribution.
GrpcWriteMethodDocComment(Printer * printer,const MethodDescriptor * method)431 void GrpcWriteMethodDocComment(Printer* printer,
432 const MethodDescriptor* method) {
433 // Deviating from protobuf to avoid extraneous docs
434 // (see https://github.com/google/protobuf/issues/1406);
435 printer->Print("/**\n");
436 std::vector<std::string> lines = GrpcGetDocLinesForDescriptor(method);
437 GrpcWriteDocCommentBody(printer, lines, true);
438 printer->Print(" */\n");
439 }
440
PrintMethodFields(const ServiceDescriptor * service,std::map<std::string,std::string> * vars,Printer * p,ProtoFlavor flavor)441 static void PrintMethodFields(
442 const ServiceDescriptor* service, std::map<std::string, std::string>* vars,
443 Printer* p, ProtoFlavor flavor) {
444 p->Print("// Static method descriptors that strictly reflect the proto.\n");
445 (*vars)["service_name"] = service->name();
446 for (int i = 0; i < service->method_count(); ++i) {
447 const MethodDescriptor* method = service->method(i);
448 (*vars)["arg_in_id"] = to_string(2 * i);
449 (*vars)["arg_out_id"] = to_string(2 * i + 1);
450 (*vars)["method_name"] = method->name();
451 (*vars)["input_type"] = MessageFullJavaName(method->input_type());
452 (*vars)["output_type"] = MessageFullJavaName(method->output_type());
453 (*vars)["method_field_name"] = MethodPropertiesFieldName(method);
454 (*vars)["method_new_field_name"] = MethodPropertiesGetterName(method);
455 (*vars)["method_method_name"] = MethodPropertiesGetterName(method);
456 bool client_streaming = method->client_streaming();
457 bool server_streaming = method->server_streaming();
458 if (client_streaming) {
459 if (server_streaming) {
460 (*vars)["method_type"] = "BIDI_STREAMING";
461 } else {
462 (*vars)["method_type"] = "CLIENT_STREAMING";
463 }
464 } else {
465 if (server_streaming) {
466 (*vars)["method_type"] = "SERVER_STREAMING";
467 } else {
468 (*vars)["method_type"] = "UNARY";
469 }
470 }
471
472 if (flavor == ProtoFlavor::LITE) {
473 (*vars)["ProtoUtils"] = "io.grpc.protobuf.lite.ProtoLiteUtils";
474 } else {
475 (*vars)["ProtoUtils"] = "io.grpc.protobuf.ProtoUtils";
476 }
477 p->Print(
478 *vars,
479 "private static volatile $MethodDescriptor$<$input_type$,\n"
480 " $output_type$> $method_new_field_name$;\n"
481 "\n"
482 "@$RpcMethod$(\n"
483 " fullMethodName = SERVICE_NAME + '/' + \"$method_name$\",\n"
484 " requestType = $input_type$.class,\n"
485 " responseType = $output_type$.class,\n"
486 " methodType = $MethodType$.$method_type$)\n"
487 "public static $MethodDescriptor$<$input_type$,\n"
488 " $output_type$> $method_method_name$() {\n"
489 " $MethodDescriptor$<$input_type$, $output_type$> $method_new_field_name$;\n"
490 " if (($method_new_field_name$ = $service_class_name$.$method_new_field_name$) == null) {\n"
491 " synchronized ($service_class_name$.class) {\n"
492 " if (($method_new_field_name$ = $service_class_name$.$method_new_field_name$) == null) {\n"
493 " $service_class_name$.$method_new_field_name$ = $method_new_field_name$ =\n"
494 " $MethodDescriptor$.<$input_type$, $output_type$>newBuilder()\n"
495 " .setType($MethodType$.$method_type$)\n"
496 " .setFullMethodName(generateFullMethodName(SERVICE_NAME, \"$method_name$\"))\n");
497
498 bool safe = method->options().idempotency_level()
499 == protobuf::MethodOptions_IdempotencyLevel_NO_SIDE_EFFECTS;
500 if (safe) {
501 p->Print(*vars, " .setSafe(true)\n");
502 } else {
503 bool idempotent = method->options().idempotency_level()
504 == protobuf::MethodOptions_IdempotencyLevel_IDEMPOTENT;
505 if (idempotent) {
506 p->Print(*vars, " .setIdempotent(true)\n");
507 }
508 }
509
510 p->Print(
511 *vars,
512 " .setSampledToLocalTracing(true)\n"
513 " .setRequestMarshaller($ProtoUtils$.marshaller(\n"
514 " $input_type$.getDefaultInstance()))\n"
515 " .setResponseMarshaller($ProtoUtils$.marshaller(\n"
516 " $output_type$.getDefaultInstance()))\n");
517
518 (*vars)["proto_method_descriptor_supplier"] = service->name() + "MethodDescriptorSupplier";
519 if (flavor == ProtoFlavor::NORMAL) {
520 p->Print(
521 *vars,
522 " .setSchemaDescriptor(new $proto_method_descriptor_supplier$(\"$method_name$\"))\n");
523 }
524
525 p->Print(
526 *vars,
527 " .build();\n");
528 p->Print(*vars,
529 " }\n"
530 " }\n"
531 " }\n"
532 " return $method_new_field_name$;\n"
533 "}\n"
534 "\n");
535 }
536 }
537
538 static void PrintBindServiceMethod(const ServiceDescriptor* service,
539 std::map<std::string, std::string>* vars,
540 Printer* p);
541
542 // Prints a StubFactory for given service / stub type.
PrintStubFactory(const ServiceDescriptor * service,std::map<std::string,std::string> * vars,Printer * p,StubType type)543 static void PrintStubFactory(
544 const ServiceDescriptor* service,
545 std::map<std::string, std::string>* vars,
546 Printer* p, StubType type) {
547 std::string stub_type_name;
548 switch (type) {
549 case ASYNC_CLIENT_IMPL:
550 stub_type_name = "";
551 break;
552 case FUTURE_CLIENT_IMPL:
553 stub_type_name = "Future";
554 break;
555 case BLOCKING_CLIENT_IMPL:
556 stub_type_name = "Blocking";
557 break;
558 default:
559 GRPC_CODEGEN_FAIL << "Cannot generate StubFactory for StubType: " << type;
560 }
561 (*vars)["stub_full_name"] = (*vars)["service_name"] + stub_type_name + "Stub";
562 p->Print(
563 *vars,
564 "$StubFactory$<$stub_full_name$> factory =\n"
565 " new $StubFactory$<$stub_full_name$>() {\n"
566 " @$Override$\n"
567 " public $stub_full_name$ newStub($Channel$ channel, $CallOptions$ callOptions) {\n"
568 " return new $stub_full_name$(channel, callOptions);\n"
569 " }\n"
570 " };\n");
571 }
572
573 // Prints a client interface or implementation class, or a server interface.
PrintStub(const ServiceDescriptor * service,std::map<std::string,std::string> * vars,Printer * p,StubType type)574 static void PrintStub(
575 const ServiceDescriptor* service,
576 std::map<std::string, std::string>* vars,
577 Printer* p, StubType type) {
578 const std::string service_name = service->name();
579 (*vars)["service_name"] = service_name;
580 std::string stub_name = service_name;
581 std::string stub_base_class_name = "AbstractStub";
582 CallType call_type;
583 bool interface = false;
584 switch (type) {
585 case ASYNC_INTERFACE:
586 call_type = ASYNC_CALL;
587 interface = true;
588 stub_name ="AsyncService";
589 break;
590 case ASYNC_CLIENT_IMPL:
591 call_type = ASYNC_CALL;
592 stub_name += "Stub";
593 stub_base_class_name = "AbstractAsyncStub";
594 break;
595 case BLOCKING_CLIENT_IMPL:
596 call_type = BLOCKING_CALL;
597 stub_name += "BlockingStub";
598 stub_base_class_name = "AbstractBlockingStub";
599 break;
600 case FUTURE_CLIENT_IMPL:
601 call_type = FUTURE_CALL;
602 stub_name += "FutureStub";
603 stub_base_class_name = "AbstractFutureStub";
604 break;
605 case BLOCKING_CLIENT_INTERFACE:
606 case FUTURE_CLIENT_INTERFACE:
607 GRPC_CODEGEN_FAIL << "Intentionally not creating StubType: " << type;
608 case ABSTRACT_CLASS:
609 GRPC_CODEGEN_FAIL << "Call PrintAbstractClassStub for ABSTRACT_CLASS";
610 default:
611 GRPC_CODEGEN_FAIL << "Cannot determine class name for StubType: " << type;
612 }
613 (*vars)["stub_name"] = stub_name;
614 (*vars)["stub_base_class_name"] = (*vars)[stub_base_class_name];
615
616 // Class head
617 GrpcWriteServiceDocComment(p, service, type);
618
619 if (service->options().deprecated()) {
620 p->Print(*vars, "@$Deprecated$\n");
621 }
622
623 if (interface) {
624 p->Print(
625 *vars,
626 "public interface $stub_name$ {\n");
627 } else {
628 p->Print(
629 *vars,
630 "public static final class $stub_name$\n"
631 " extends $stub_base_class_name$<$stub_name$> {\n");
632 }
633 p->Indent();
634
635 // Constructor and build() method
636 if (!interface) {
637 p->Print(
638 *vars,
639 "private $stub_name$(\n"
640 " $Channel$ channel, $CallOptions$ callOptions) {"
641 "\n");
642 p->Indent();
643 p->Print("super(channel, callOptions);\n");
644 p->Outdent();
645 p->Print("}\n\n");
646 p->Print(
647 *vars,
648 "@$Override$\n"
649 "protected $stub_name$ build(\n"
650 " $Channel$ channel, $CallOptions$ callOptions) {"
651 "\n");
652 p->Indent();
653 p->Print(
654 *vars,
655 "return new $stub_name$(channel, callOptions);\n");
656 p->Outdent();
657 p->Print("}\n");
658 }
659
660 // RPC methods
661 for (int i = 0; i < service->method_count(); ++i) {
662 const MethodDescriptor* method = service->method(i);
663 (*vars)["input_type"] = MessageFullJavaName(method->input_type());
664 (*vars)["output_type"] = MessageFullJavaName(method->output_type());
665 (*vars)["lower_method_name"] = LowerMethodName(method);
666 (*vars)["method_method_name"] = MethodPropertiesGetterName(method);
667 bool client_streaming = method->client_streaming();
668 bool server_streaming = method->server_streaming();
669
670 if (call_type == BLOCKING_CALL && client_streaming) {
671 // Blocking client interface with client streaming is not available
672 continue;
673 }
674
675 if (call_type == FUTURE_CALL && (client_streaming || server_streaming)) {
676 // Future interface doesn't support streaming.
677 continue;
678 }
679
680 // Method signature
681 p->Print("\n");
682 // TODO(nmittler): Replace with WriteMethodDocComment once included by the protobuf distro.
683 GrpcWriteMethodDocComment(p, method);
684
685 if (method->options().deprecated()) {
686 p->Print(*vars, "@$Deprecated$\n");
687 }
688
689 if (!interface) {
690 p->Print("public ");
691 } else {
692 p->Print("default ");
693 }
694 switch (call_type) {
695 case BLOCKING_CALL:
696 GRPC_CODEGEN_CHECK(!client_streaming)
697 << "Blocking client interface with client streaming is unavailable";
698 if (server_streaming) {
699 // Server streaming
700 p->Print(
701 *vars,
702 "$Iterator$<$output_type$> $lower_method_name$(\n"
703 " $input_type$ request)");
704 } else {
705 // Simple RPC
706 p->Print(
707 *vars,
708 "$output_type$ $lower_method_name$($input_type$ request)");
709 }
710 break;
711 case ASYNC_CALL:
712 if (client_streaming) {
713 // Bidirectional streaming or client streaming
714 p->Print(
715 *vars,
716 "$StreamObserver$<$input_type$> $lower_method_name$(\n"
717 " $StreamObserver$<$output_type$> responseObserver)");
718 } else {
719 // Server streaming or simple RPC
720 p->Print(
721 *vars,
722 "void $lower_method_name$($input_type$ request,\n"
723 " $StreamObserver$<$output_type$> responseObserver)");
724 }
725 break;
726 case FUTURE_CALL:
727 GRPC_CODEGEN_CHECK(!client_streaming && !server_streaming)
728 << "Future interface doesn't support streaming. "
729 << "client_streaming=" << client_streaming << ", "
730 << "server_streaming=" << server_streaming;
731 p->Print(
732 *vars,
733 "$ListenableFuture$<$output_type$> $lower_method_name$(\n"
734 " $input_type$ request)");
735 break;
736 }
737
738 // Method body.
739 p->Print(" {\n");
740 p->Indent();
741 if (interface && call_type == ASYNC_CALL) {
742 // NB: Skipping validation of service methods. If something is wrong, we wouldn't get to
743 // this point as compiler would return errors when generating service interface.
744 if (client_streaming) {
745 p->Print(
746 *vars,
747 "return io.grpc.stub.ServerCalls.asyncUnimplementedStreamingCall("
748 "$method_method_name$(), responseObserver);\n");
749 } else {
750 p->Print(
751 *vars,
752 "io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall("
753 "$method_method_name$(), responseObserver);\n");
754 }
755 } else if (!interface) {
756 switch (call_type) {
757 case BLOCKING_CALL:
758 GRPC_CODEGEN_CHECK(!client_streaming)
759 << "Blocking client streaming interface is not available";
760 if (server_streaming) {
761 (*vars)["calls_method"] = "io.grpc.stub.ClientCalls.blockingServerStreamingCall";
762 (*vars)["params"] = "request";
763 } else {
764 (*vars)["calls_method"] = "io.grpc.stub.ClientCalls.blockingUnaryCall";
765 (*vars)["params"] = "request";
766 }
767 p->Print(
768 *vars,
769 "return $calls_method$(\n"
770 " getChannel(), $method_method_name$(), getCallOptions(), $params$);\n");
771 break;
772 case ASYNC_CALL:
773 if (server_streaming) {
774 if (client_streaming) {
775 (*vars)["calls_method"] = "io.grpc.stub.ClientCalls.asyncBidiStreamingCall";
776 (*vars)["params"] = "responseObserver";
777 } else {
778 (*vars)["calls_method"] = "io.grpc.stub.ClientCalls.asyncServerStreamingCall";
779 (*vars)["params"] = "request, responseObserver";
780 }
781 } else {
782 if (client_streaming) {
783 (*vars)["calls_method"] = "io.grpc.stub.ClientCalls.asyncClientStreamingCall";
784 (*vars)["params"] = "responseObserver";
785 } else {
786 (*vars)["calls_method"] = "io.grpc.stub.ClientCalls.asyncUnaryCall";
787 (*vars)["params"] = "request, responseObserver";
788 }
789 }
790 (*vars)["last_line_prefix"] = client_streaming ? "return " : "";
791 p->Print(
792 *vars,
793 "$last_line_prefix$$calls_method$(\n"
794 " getChannel().newCall($method_method_name$(), getCallOptions()), $params$);\n");
795 break;
796 case FUTURE_CALL:
797 GRPC_CODEGEN_CHECK(!client_streaming && !server_streaming)
798 << "Future interface doesn't support streaming. "
799 << "client_streaming=" << client_streaming << ", "
800 << "server_streaming=" << server_streaming;
801 (*vars)["calls_method"] = "io.grpc.stub.ClientCalls.futureUnaryCall";
802 p->Print(
803 *vars,
804 "return $calls_method$(\n"
805 " getChannel().newCall($method_method_name$(), getCallOptions()), request);\n");
806 break;
807 }
808 } else {
809 GRPC_CODEGEN_FAIL << "Do not create Stub interfaces";
810 }
811 p->Outdent();
812 p->Print("}\n");
813 }
814
815 p->Outdent();
816 p->Print("}\n\n");
817 }
818
819
PrintAbstractClassStub(const ServiceDescriptor * service,std::map<std::string,std::string> * vars,Printer * p)820 static void PrintAbstractClassStub(
821 const ServiceDescriptor* service,
822 std::map<std::string, std::string>* vars,
823 Printer* p) {
824 const std::string service_name = service->name();
825 (*vars)["service_name"] = service_name;
826
827 GrpcWriteServiceDocComment(p, service, ABSTRACT_CLASS);
828 if (service->options().deprecated()) {
829 p->Print(*vars, "@$Deprecated$\n");
830 }
831 p->Print(
832 *vars,
833 "public static abstract class $service_name$ImplBase\n"
834 " implements $BindableService$, AsyncService {\n"
835 "\n"
836 " @$Override$ public final $ServerServiceDefinition$ bindService() {\n"
837 " return $service_class_name$.bindService(this);\n"
838 " }\n"
839 "}\n\n");
840 }
841
CompareMethodClientStreaming(const MethodDescriptor * method1,const MethodDescriptor * method2)842 static bool CompareMethodClientStreaming(const MethodDescriptor* method1,
843 const MethodDescriptor* method2)
844 {
845 return method1->client_streaming() < method2->client_streaming();
846 }
847
848 // Place all method invocations into a single class to reduce memory footprint
849 // on Android.
PrintMethodHandlerClass(const ServiceDescriptor * service,std::map<std::string,std::string> * vars,Printer * p)850 static void PrintMethodHandlerClass(const ServiceDescriptor* service,
851 std::map<std::string, std::string>* vars,
852 Printer* p) {
853 // Sort method ids based on client_streaming() so switch tables are compact.
854 std::vector<const MethodDescriptor*> sorted_methods(service->method_count());
855 for (int i = 0; i < service->method_count(); ++i) {
856 sorted_methods[i] = service->method(i);
857 }
858 stable_sort(sorted_methods.begin(), sorted_methods.end(),
859 CompareMethodClientStreaming);
860 for (size_t i = 0; i < sorted_methods.size(); i++) {
861 const MethodDescriptor* method = sorted_methods[i];
862 (*vars)["method_id"] = to_string(i);
863 (*vars)["method_id_name"] = MethodIdFieldName(method);
864 p->Print(
865 *vars,
866 "private static final int $method_id_name$ = $method_id$;\n");
867 }
868 p->Print("\n");
869 p->Print(
870 *vars,
871 "private static final class MethodHandlers<Req, Resp> implements\n"
872 " io.grpc.stub.ServerCalls.UnaryMethod<Req, Resp>,\n"
873 " io.grpc.stub.ServerCalls.ServerStreamingMethod<Req, Resp>,\n"
874 " io.grpc.stub.ServerCalls.ClientStreamingMethod<Req, Resp>,\n"
875 " io.grpc.stub.ServerCalls.BidiStreamingMethod<Req, Resp> {\n"
876 " private final AsyncService serviceImpl;\n"
877 " private final int methodId;\n"
878 "\n"
879 " MethodHandlers(AsyncService serviceImpl, int methodId) {\n"
880 " this.serviceImpl = serviceImpl;\n"
881 " this.methodId = methodId;\n"
882 " }\n\n");
883 p->Indent();
884 p->Print(
885 *vars,
886 "@$Override$\n"
887 "@java.lang.SuppressWarnings(\"unchecked\")\n"
888 "public void invoke(Req request, $StreamObserver$<Resp> responseObserver) {\n"
889 " switch (methodId) {\n");
890 p->Indent();
891 p->Indent();
892
893 for (int i = 0; i < service->method_count(); ++i) {
894 const MethodDescriptor* method = service->method(i);
895 if (method->client_streaming()) {
896 continue;
897 }
898 (*vars)["method_id_name"] = MethodIdFieldName(method);
899 (*vars)["lower_method_name"] = LowerMethodName(method);
900 (*vars)["input_type"] = MessageFullJavaName(method->input_type());
901 (*vars)["output_type"] = MessageFullJavaName(method->output_type());
902 p->Print(
903 *vars,
904 "case $method_id_name$:\n"
905 " serviceImpl.$lower_method_name$(($input_type$) request,\n"
906 " ($StreamObserver$<$output_type$>) responseObserver);\n"
907 " break;\n");
908 }
909 p->Print("default:\n"
910 " throw new AssertionError();\n");
911
912 p->Outdent();
913 p->Outdent();
914 p->Print(" }\n"
915 "}\n\n");
916
917 p->Print(
918 *vars,
919 "@$Override$\n"
920 "@java.lang.SuppressWarnings(\"unchecked\")\n"
921 "public $StreamObserver$<Req> invoke(\n"
922 " $StreamObserver$<Resp> responseObserver) {\n"
923 " switch (methodId) {\n");
924 p->Indent();
925 p->Indent();
926
927 for (int i = 0; i < service->method_count(); ++i) {
928 const MethodDescriptor* method = service->method(i);
929 if (!method->client_streaming()) {
930 continue;
931 }
932 (*vars)["method_id_name"] = MethodIdFieldName(method);
933 (*vars)["lower_method_name"] = LowerMethodName(method);
934 (*vars)["input_type"] = MessageFullJavaName(method->input_type());
935 (*vars)["output_type"] = MessageFullJavaName(method->output_type());
936 p->Print(
937 *vars,
938 "case $method_id_name$:\n"
939 " return ($StreamObserver$<Req>) serviceImpl.$lower_method_name$(\n"
940 " ($StreamObserver$<$output_type$>) responseObserver);\n");
941 }
942 p->Print("default:\n"
943 " throw new AssertionError();\n");
944
945 p->Outdent();
946 p->Outdent();
947 p->Print(" }\n"
948 "}\n");
949
950
951 p->Outdent();
952 p->Print("}\n\n");
953 }
954
PrintGetServiceDescriptorMethod(const ServiceDescriptor * service,std::map<std::string,std::string> * vars,Printer * p,ProtoFlavor flavor)955 static void PrintGetServiceDescriptorMethod(const ServiceDescriptor* service,
956 std::map<std::string, std::string>* vars,
957 Printer* p,
958 ProtoFlavor flavor) {
959 (*vars)["service_name"] = service->name();
960
961
962 if (flavor == ProtoFlavor::NORMAL) {
963 (*vars)["proto_base_descriptor_supplier"] = service->name() + "BaseDescriptorSupplier";
964 (*vars)["proto_file_descriptor_supplier"] = service->name() + "FileDescriptorSupplier";
965 (*vars)["proto_method_descriptor_supplier"] = service->name() + "MethodDescriptorSupplier";
966 (*vars)["proto_class_name"] = protobuf::compiler::java::ClassName(service->file());
967 p->Print(
968 *vars,
969 "private static abstract class $proto_base_descriptor_supplier$\n"
970 " implements $ProtoFileDescriptorSupplier$, $ProtoServiceDescriptorSupplier$ {\n"
971 " $proto_base_descriptor_supplier$() {}\n"
972 "\n"
973 " @$Override$\n"
974 " public com.google.protobuf.Descriptors.FileDescriptor getFileDescriptor() {\n"
975 " return $proto_class_name$.getDescriptor();\n"
976 " }\n"
977 "\n"
978 " @$Override$\n"
979 " public com.google.protobuf.Descriptors.ServiceDescriptor getServiceDescriptor() {\n"
980 " return getFileDescriptor().findServiceByName(\"$service_name$\");\n"
981 " }\n"
982 "}\n"
983 "\n"
984 "private static final class $proto_file_descriptor_supplier$\n"
985 " extends $proto_base_descriptor_supplier$ {\n"
986 " $proto_file_descriptor_supplier$() {}\n"
987 "}\n"
988 "\n"
989 "private static final class $proto_method_descriptor_supplier$\n"
990 " extends $proto_base_descriptor_supplier$\n"
991 " implements $ProtoMethodDescriptorSupplier$ {\n"
992 " private final String methodName;\n"
993 "\n"
994 " $proto_method_descriptor_supplier$(String methodName) {\n"
995 " this.methodName = methodName;\n"
996 " }\n"
997 "\n"
998 " @$Override$\n"
999 " public com.google.protobuf.Descriptors.MethodDescriptor getMethodDescriptor() {\n"
1000 " return getServiceDescriptor().findMethodByName(methodName);\n"
1001 " }\n"
1002 "}\n\n");
1003 }
1004
1005 p->Print(
1006 *vars,
1007 "private static volatile $ServiceDescriptor$ serviceDescriptor;\n\n");
1008
1009 p->Print(
1010 *vars,
1011 "public static $ServiceDescriptor$ getServiceDescriptor() {\n");
1012 p->Indent();
1013 p->Print(
1014 *vars,
1015 "$ServiceDescriptor$ result = serviceDescriptor;\n");
1016 p->Print("if (result == null) {\n");
1017 p->Indent();
1018 p->Print(
1019 *vars,
1020 "synchronized ($service_class_name$.class) {\n");
1021 p->Indent();
1022 p->Print("result = serviceDescriptor;\n");
1023 p->Print("if (result == null) {\n");
1024 p->Indent();
1025
1026 p->Print(
1027 *vars,
1028 "serviceDescriptor = result = $ServiceDescriptor$.newBuilder(SERVICE_NAME)");
1029 p->Indent();
1030 p->Indent();
1031 if (flavor == ProtoFlavor::NORMAL) {
1032 p->Print(
1033 *vars,
1034 "\n.setSchemaDescriptor(new $proto_file_descriptor_supplier$())");
1035 }
1036 for (int i = 0; i < service->method_count(); ++i) {
1037 const MethodDescriptor* method = service->method(i);
1038 (*vars)["method_method_name"] = MethodPropertiesGetterName(method);
1039 p->Print(*vars, "\n.addMethod($method_method_name$())");
1040 }
1041 p->Print("\n.build();\n");
1042 p->Outdent();
1043 p->Outdent();
1044
1045 p->Outdent();
1046 p->Print("}\n");
1047 p->Outdent();
1048 p->Print("}\n");
1049 p->Outdent();
1050 p->Print("}\n");
1051 p->Print("return result;\n");
1052 p->Outdent();
1053 p->Print("}\n");
1054 }
1055
PrintBindServiceMethod(const ServiceDescriptor * service,std::map<std::string,std::string> * vars,Printer * p)1056 static void PrintBindServiceMethod(const ServiceDescriptor* service,
1057 std::map<std::string, std::string>* vars,
1058 Printer* p) {
1059 (*vars)["service_name"] = service->name();
1060 p->Print(*vars,
1061 "public static final io.grpc.ServerServiceDefinition "
1062 "bindService(AsyncService service) {\n");
1063
1064 p->Indent();
1065 p->Print(*vars,
1066 "return "
1067 "$ServerServiceDefinition$.builder(getServiceDescriptor())\n");
1068 p->Indent();
1069 p->Indent();
1070 for (int i = 0; i < service->method_count(); ++i) {
1071 const MethodDescriptor* method = service->method(i);
1072 (*vars)["lower_method_name"] = LowerMethodName(method);
1073 (*vars)["method_method_name"] = MethodPropertiesGetterName(method);
1074 (*vars)["input_type"] = MessageFullJavaName(method->input_type());
1075 (*vars)["output_type"] = MessageFullJavaName(method->output_type());
1076 (*vars)["method_id_name"] = MethodIdFieldName(method);
1077 bool client_streaming = method->client_streaming();
1078 bool server_streaming = method->server_streaming();
1079 if (client_streaming) {
1080 if (server_streaming) {
1081 (*vars)["calls_method"] = "io.grpc.stub.ServerCalls.asyncBidiStreamingCall";
1082 } else {
1083 (*vars)["calls_method"] = "io.grpc.stub.ServerCalls.asyncClientStreamingCall";
1084 }
1085 } else {
1086 if (server_streaming) {
1087 (*vars)["calls_method"] = "io.grpc.stub.ServerCalls.asyncServerStreamingCall";
1088 } else {
1089 (*vars)["calls_method"] = "io.grpc.stub.ServerCalls.asyncUnaryCall";
1090 }
1091 }
1092 p->Print(*vars, ".addMethod(\n");
1093 p->Indent();
1094 p->Print(
1095 *vars,
1096 "$method_method_name$(),\n"
1097 "$calls_method$(\n");
1098 p->Indent();
1099 p->Print(
1100 *vars,
1101 "new MethodHandlers<\n"
1102 " $input_type$,\n"
1103 " $output_type$>(\n"
1104 " service, $method_id_name$)))\n");
1105 p->Outdent();
1106 p->Outdent();
1107 }
1108 p->Print(".build();\n");
1109 p->Outdent();
1110 p->Outdent();
1111 p->Outdent();
1112 p->Print("}\n\n");
1113 }
1114
PrintService(const ServiceDescriptor * service,std::map<std::string,std::string> * vars,Printer * p,ProtoFlavor flavor,bool disable_version)1115 static void PrintService(const ServiceDescriptor* service,
1116 std::map<std::string, std::string>* vars,
1117 Printer* p,
1118 ProtoFlavor flavor,
1119 bool disable_version) {
1120 (*vars)["service_name"] = service->name();
1121 (*vars)["file_name"] = service->file()->name();
1122 (*vars)["service_class_name"] = ServiceClassName(service);
1123 (*vars)["grpc_version"] = "";
1124 #ifdef GRPC_VERSION
1125 if (!disable_version) {
1126 (*vars)["grpc_version"] = " (version " XSTR(GRPC_VERSION) ")";
1127 }
1128 #endif
1129 // TODO(nmittler): Replace with WriteServiceDocComment once included by protobuf distro.
1130 GrpcWriteServiceDocComment(p, service, NONE);
1131 p->Print(
1132 *vars,
1133 "@$Generated$(\n"
1134 " value = \"by gRPC proto compiler$grpc_version$\",\n"
1135 " comments = \"Source: $file_name$\")\n"
1136 "@$GrpcGenerated$\n");
1137
1138 if (service->options().deprecated()) {
1139 p->Print(*vars, "@$Deprecated$\n");
1140 }
1141
1142 p->Print(
1143 *vars,
1144 "public final class $service_class_name$ {\n\n");
1145 p->Indent();
1146 p->Print(
1147 *vars,
1148 "private $service_class_name$() {}\n\n");
1149
1150 p->Print(
1151 *vars,
1152 "public static final String SERVICE_NAME = "
1153 "\"$Package$$service_name$\";\n\n");
1154
1155 PrintMethodFields(service, vars, p, flavor);
1156
1157 // TODO(nmittler): Replace with WriteDocComment once included by protobuf distro.
1158 GrpcWriteDocComment(p, " Creates a new async stub that supports all call types for the service");
1159 p->Print(
1160 *vars,
1161 "public static $service_name$Stub newStub($Channel$ channel) {\n");
1162 p->Indent();
1163 PrintStubFactory(service, vars, p, ASYNC_CLIENT_IMPL);
1164 p->Print(*vars, "return $service_name$Stub.newStub(factory, channel);\n");
1165 p->Outdent();
1166 p->Print("}\n\n");
1167
1168 // TODO(nmittler): Replace with WriteDocComment once included by protobuf distro.
1169 GrpcWriteDocComment(p, " Creates a new blocking-style stub that supports unary and streaming "
1170 "output calls on the service");
1171 p->Print(
1172 *vars,
1173 "public static $service_name$BlockingStub newBlockingStub(\n"
1174 " $Channel$ channel) {\n");
1175 p->Indent();
1176 PrintStubFactory(service, vars, p, BLOCKING_CLIENT_IMPL);
1177 p->Print(
1178 *vars,
1179 "return $service_name$BlockingStub.newStub(factory, channel);\n");
1180 p->Outdent();
1181 p->Print("}\n\n");
1182
1183 // TODO(nmittler): Replace with WriteDocComment once included by protobuf distro.
1184 GrpcWriteDocComment(p, " Creates a new ListenableFuture-style stub that supports unary calls "
1185 "on the service");
1186 p->Print(
1187 *vars,
1188 "public static $service_name$FutureStub newFutureStub(\n"
1189 " $Channel$ channel) {\n");
1190 p->Indent();
1191 PrintStubFactory(service, vars, p, FUTURE_CLIENT_IMPL);
1192 p->Print(
1193 *vars,
1194 "return $service_name$FutureStub.newStub(factory, channel);\n");
1195 p->Outdent();
1196 p->Print("}\n\n");
1197
1198 PrintStub(service, vars, p, ASYNC_INTERFACE);
1199 PrintAbstractClassStub(service, vars, p);
1200 PrintStub(service, vars, p, ASYNC_CLIENT_IMPL);
1201 PrintStub(service, vars, p, BLOCKING_CLIENT_IMPL);
1202 PrintStub(service, vars, p, FUTURE_CLIENT_IMPL);
1203
1204 PrintMethodHandlerClass(service, vars, p);
1205 PrintBindServiceMethod(service, vars, p);
1206 PrintGetServiceDescriptorMethod(service, vars, p, flavor);
1207 p->Outdent();
1208 p->Print("}\n");
1209 }
1210
PrintImports(Printer * p)1211 void PrintImports(Printer* p) {
1212 p->Print(
1213 "import static "
1214 "io.grpc.MethodDescriptor.generateFullMethodName;\n\n");
1215 }
1216
GenerateService(const ServiceDescriptor * service,protobuf::io::ZeroCopyOutputStream * out,ProtoFlavor flavor,bool disable_version)1217 void GenerateService(const ServiceDescriptor* service,
1218 protobuf::io::ZeroCopyOutputStream* out,
1219 ProtoFlavor flavor,
1220 bool disable_version) {
1221 // All non-generated classes must be referred by fully qualified names to
1222 // avoid collision with generated classes.
1223 std::map<std::string, std::string> vars;
1224 vars["String"] = "java.lang.String";
1225 vars["Deprecated"] = "java.lang.Deprecated";
1226 vars["Override"] = "java.lang.Override";
1227 vars["Channel"] = "io.grpc.Channel";
1228 vars["CallOptions"] = "io.grpc.CallOptions";
1229 vars["MethodType"] = "io.grpc.MethodDescriptor.MethodType";
1230 vars["ServerMethodDefinition"] =
1231 "io.grpc.ServerMethodDefinition";
1232 vars["BindableService"] = "io.grpc.BindableService";
1233 vars["ServerServiceDefinition"] =
1234 "io.grpc.ServerServiceDefinition";
1235 vars["ServiceDescriptor"] =
1236 "io.grpc.ServiceDescriptor";
1237 vars["ProtoFileDescriptorSupplier"] =
1238 "io.grpc.protobuf.ProtoFileDescriptorSupplier";
1239 vars["ProtoServiceDescriptorSupplier"] =
1240 "io.grpc.protobuf.ProtoServiceDescriptorSupplier";
1241 vars["ProtoMethodDescriptorSupplier"] =
1242 "io.grpc.protobuf.ProtoMethodDescriptorSupplier";
1243 vars["AbstractStub"] = "io.grpc.stub.AbstractStub";
1244 vars["AbstractAsyncStub"] = "io.grpc.stub.AbstractAsyncStub";
1245 vars["AbstractFutureStub"] = "io.grpc.stub.AbstractFutureStub";
1246 vars["AbstractBlockingStub"] = "io.grpc.stub.AbstractBlockingStub";
1247 vars["StubFactory"] = "io.grpc.stub.AbstractStub.StubFactory";
1248 vars["RpcMethod"] = "io.grpc.stub.annotations.RpcMethod";
1249 vars["MethodDescriptor"] = "io.grpc.MethodDescriptor";
1250 vars["StreamObserver"] = "io.grpc.stub.StreamObserver";
1251 vars["Iterator"] = "java.util.Iterator";
1252 vars["Generated"] = "javax.annotation.Generated";
1253 vars["GrpcGenerated"] = "io.grpc.stub.annotations.GrpcGenerated";
1254 vars["ListenableFuture"] =
1255 "com.google.common.util.concurrent.ListenableFuture";
1256
1257 Printer printer(out, '$');
1258 std::string package_name = ServiceJavaPackage(service->file());
1259 if (!package_name.empty()) {
1260 printer.Print(
1261 "package $package_name$;\n\n",
1262 "package_name", package_name);
1263 }
1264 PrintImports(&printer);
1265
1266 // Package string is used to fully qualify method names.
1267 vars["Package"] = service->file()->package();
1268 if (!vars["Package"].empty()) {
1269 vars["Package"].append(".");
1270 }
1271 PrintService(service, &vars, &printer, flavor, disable_version);
1272 }
1273
ServiceJavaPackage(const FileDescriptor * file)1274 std::string ServiceJavaPackage(const FileDescriptor* file) {
1275 std::string result = protobuf::compiler::java::ClassName(file);
1276 size_t last_dot_pos = result.find_last_of('.');
1277 if (last_dot_pos != std::string::npos) {
1278 result.resize(last_dot_pos);
1279 } else {
1280 result = "";
1281 }
1282 return result;
1283 }
1284
ServiceClassName(const ServiceDescriptor * service)1285 std::string ServiceClassName(const ServiceDescriptor* service) {
1286 return service->name() + "Grpc";
1287 }
1288
1289 } // namespace java_grpc_generator
1290