1 /* 2 * Copyright 2016 Google Inc. All rights reserved. 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 #include "java_generator.h" 18 19 #include <algorithm> 20 #include <iostream> 21 #include <iterator> 22 #include <map> 23 #include <utility> 24 #include <vector> 25 26 // just to get flatbuffer_version_string() 27 #include <flatbuffers/flatbuffers.h> 28 #include <flatbuffers/util.h> 29 #define to_string flatbuffers::NumToString 30 31 // Stringify helpers used solely to cast GRPC_VERSION 32 #ifndef STR 33 #define STR(s) #s 34 #endif 35 36 #ifndef XSTR 37 #define XSTR(s) STR(s) 38 #endif 39 40 41 typedef grpc_generator::Printer Printer; 42 typedef std::map<grpc::string, grpc::string> VARS; 43 typedef grpc_generator::Service ServiceDescriptor; 44 typedef grpc_generator::CommentHolder 45 DescriptorType; // base class of all 'descriptors' 46 typedef grpc_generator::Method MethodDescriptor; 47 48 namespace grpc_java_generator { 49 typedef std::string string; 50 // Generates imports for the service 51 void GenerateImports(grpc_generator::File* file, 52 grpc_generator::Printer* printer, VARS& vars) { 53 vars["filename"] = file->filename(); 54 printer->Print( 55 vars, 56 "//Generated by flatc compiler (version $flatc_version$)\n"); 57 printer->Print("//If you make any local changes, they will be lost\n"); 58 printer->Print(vars, "//source: $filename$.fbs\n\n"); 59 printer->Print(vars, "package $Package$;\n\n"); 60 vars["Package"] = vars["Package"] + "."; 61 if (!file->additional_headers().empty()) { 62 printer->Print(file->additional_headers().c_str()); 63 printer->Print("\n\n"); 64 } 65 } 66 67 // Adjust a method name prefix identifier to follow the JavaBean spec: 68 // - decapitalize the first letter 69 // - remove embedded underscores & capitalize the following letter 70 static string MixedLower(const string& word) { 71 string w; 72 w += static_cast<string::value_type>(tolower(word[0])); 73 bool after_underscore = false; 74 for (size_t i = 1; i < word.length(); ++i) { 75 if (word[i] == '_') { 76 after_underscore = true; 77 } else { 78 w += after_underscore ? static_cast<string::value_type>(toupper(word[i])) 79 : word[i]; 80 after_underscore = false; 81 } 82 } 83 return w; 84 } 85 86 // Converts to the identifier to the ALL_UPPER_CASE format. 87 // - An underscore is inserted where a lower case letter is followed by an 88 // upper case letter. 89 // - All letters are converted to upper case 90 static string ToAllUpperCase(const string& word) { 91 string w; 92 for (size_t i = 0; i < word.length(); ++i) { 93 w += static_cast<string::value_type>(toupper(word[i])); 94 if ((i < word.length() - 1) && islower(word[i]) && isupper(word[i + 1])) { 95 w += '_'; 96 } 97 } 98 return w; 99 } 100 101 static inline string LowerMethodName(const MethodDescriptor* method) { 102 return MixedLower(method->name()); 103 } 104 105 static inline string MethodPropertiesFieldName(const MethodDescriptor* method) { 106 return "METHOD_" + ToAllUpperCase(method->name()); 107 } 108 109 static inline string MethodPropertiesGetterName( 110 const MethodDescriptor* method) { 111 return MixedLower("get_" + method->name() + "_method"); 112 } 113 114 static inline string MethodIdFieldName(const MethodDescriptor* method) { 115 return "METHODID_" + ToAllUpperCase(method->name()); 116 } 117 118 static inline string JavaClassName(VARS& vars, const string& name) { 119 // string name = google::protobuf::compiler::java::ClassName(desc); 120 return vars["Package"] + name; 121 } 122 123 static inline string ServiceClassName(const string& service_name) { 124 return service_name + "Grpc"; 125 } 126 127 // TODO(nmittler): Remove once protobuf includes javadoc methods in 128 // distribution. 129 template <typename ITR> 130 static void GrpcSplitStringToIteratorUsing(const string& full, 131 const char* delim, ITR& result) { 132 // Optimize the common case where delim is a single character. 133 if (delim[0] != '\0' && delim[1] == '\0') { 134 char c = delim[0]; 135 const char* p = full.data(); 136 const char* end = p + full.size(); 137 while (p != end) { 138 if (*p == c) { 139 ++p; 140 } else { 141 const char* start = p; 142 while (++p != end && *p != c) 143 ; 144 *result++ = string(start, p - start); 145 } 146 } 147 return; 148 } 149 150 string::size_type begin_index, end_index; 151 begin_index = full.find_first_not_of(delim); 152 while (begin_index != string::npos) { 153 end_index = full.find_first_of(delim, begin_index); 154 if (end_index == string::npos) { 155 *result++ = full.substr(begin_index); 156 return; 157 } 158 *result++ = full.substr(begin_index, (end_index - begin_index)); 159 begin_index = full.find_first_not_of(delim, end_index); 160 } 161 } 162 163 static void GrpcSplitStringUsing(const string& full, const char* delim, 164 std::vector<string>* result) { 165 std::back_insert_iterator<std::vector<string>> it(*result); 166 GrpcSplitStringToIteratorUsing(full, delim, it); 167 } 168 169 static std::vector<string> GrpcSplit(const string& full, const char* delim) { 170 std::vector<string> result; 171 GrpcSplitStringUsing(full, delim, &result); 172 return result; 173 } 174 175 // TODO(nmittler): Remove once protobuf includes javadoc methods in 176 // distribution. 177 static string GrpcEscapeJavadoc(const string& input) { 178 string result; 179 result.reserve(input.size() * 2); 180 181 char prev = '*'; 182 183 for (string::size_type i = 0; i < input.size(); i++) { 184 char c = input[i]; 185 switch (c) { 186 case '*': 187 // Avoid "/*". 188 if (prev == '/') { 189 result.append("*"); 190 } else { 191 result.push_back(c); 192 } 193 break; 194 case '/': 195 // Avoid "*/". 196 if (prev == '*') { 197 result.append("/"); 198 } else { 199 result.push_back(c); 200 } 201 break; 202 case '@': 203 // '@' starts javadoc tags including the @deprecated tag, which will 204 // cause a compile-time error if inserted before a declaration that 205 // does not have a corresponding @Deprecated annotation. 206 result.append("@"); 207 break; 208 case '<': 209 // Avoid interpretation as HTML. 210 result.append("<"); 211 break; 212 case '>': 213 // Avoid interpretation as HTML. 214 result.append(">"); 215 break; 216 case '&': 217 // Avoid interpretation as HTML. 218 result.append("&"); 219 break; 220 case '\\': 221 // Java interprets Unicode escape sequences anywhere! 222 result.append("\"); 223 break; 224 default: 225 result.push_back(c); 226 break; 227 } 228 229 prev = c; 230 } 231 232 return result; 233 } 234 235 static std::vector<string> GrpcGetDocLines(const string& comments) { 236 if (!comments.empty()) { 237 // TODO(kenton): Ideally we should parse the comment text as Markdown and 238 // write it back as HTML, but this requires a Markdown parser. For now 239 // we just use <pre> to get fixed-width text formatting. 240 241 // If the comment itself contains block comment start or end markers, 242 // HTML-escape them so that they don't accidentally close the doc comment. 243 string escapedComments = GrpcEscapeJavadoc(comments); 244 245 std::vector<string> lines = GrpcSplit(escapedComments, "\n"); 246 while (!lines.empty() && lines.back().empty()) { 247 lines.pop_back(); 248 } 249 return lines; 250 } 251 return std::vector<string>(); 252 } 253 254 static std::vector<string> GrpcGetDocLinesForDescriptor( 255 const DescriptorType* descriptor) { 256 return descriptor->GetAllComments(); 257 // return GrpcGetDocLines(descriptor->GetLeadingComments("///")); 258 } 259 260 static void GrpcWriteDocCommentBody(Printer* printer, VARS& vars, 261 const std::vector<string>& lines, 262 bool surroundWithPreTag) { 263 if (!lines.empty()) { 264 if (surroundWithPreTag) { 265 printer->Print(" * <pre>\n"); 266 } 267 268 for (size_t i = 0; i < lines.size(); i++) { 269 // Most lines should start with a space. Watch out for lines that start 270 // with a /, since putting that right after the leading asterisk will 271 // close the comment. 272 vars["line"] = lines[i]; 273 if (!lines[i].empty() && lines[i][0] == '/') { 274 printer->Print(vars, " * $line$\n"); 275 } else { 276 printer->Print(vars, " *$line$\n"); 277 } 278 } 279 280 if (surroundWithPreTag) { 281 printer->Print(" * </pre>\n"); 282 } 283 } 284 } 285 286 static void GrpcWriteDocComment(Printer* printer, VARS& vars, 287 const string& comments) { 288 printer->Print("/**\n"); 289 std::vector<string> lines = GrpcGetDocLines(comments); 290 GrpcWriteDocCommentBody(printer, vars, lines, false); 291 printer->Print(" */\n"); 292 } 293 294 static void GrpcWriteServiceDocComment(Printer* printer, VARS& vars, 295 const ServiceDescriptor* service) { 296 printer->Print("/**\n"); 297 std::vector<string> lines = GrpcGetDocLinesForDescriptor(service); 298 GrpcWriteDocCommentBody(printer, vars, lines, true); 299 printer->Print(" */\n"); 300 } 301 302 void GrpcWriteMethodDocComment(Printer* printer, VARS& vars, 303 const MethodDescriptor* method) { 304 printer->Print("/**\n"); 305 std::vector<string> lines = GrpcGetDocLinesForDescriptor(method); 306 GrpcWriteDocCommentBody(printer, vars, lines, true); 307 printer->Print(" */\n"); 308 } 309 310 //outputs static singleton extractor for type stored in "extr_type" and "extr_type_name" vars 311 static void PrintTypeExtractor(Printer* p, VARS& vars) { 312 p->Print( 313 vars, 314 "private static volatile FlatbuffersUtils.FBExtactor<$extr_type$> " 315 "extractorOf$extr_type_name$;\n" 316 "private static FlatbuffersUtils.FBExtactor<$extr_type$> " 317 "getExtractorOf$extr_type_name$() {\n" 318 " if (extractorOf$extr_type_name$ != null) return " 319 "extractorOf$extr_type_name$;\n" 320 " synchronized ($service_class_name$.class) {\n" 321 " if (extractorOf$extr_type_name$ != null) return " 322 "extractorOf$extr_type_name$;\n" 323 " extractorOf$extr_type_name$ = new " 324 "FlatbuffersUtils.FBExtactor<$extr_type$>() {\n" 325 " public $extr_type$ extract (ByteBuffer buffer) {\n" 326 " return " 327 "$extr_type$.getRootAs$extr_type_name$(buffer);\n" 328 " }\n" 329 " };\n" 330 " return extractorOf$extr_type_name$;\n" 331 " }\n" 332 "}\n\n"); 333 } 334 static void PrintMethodFields(Printer* p, VARS& vars, 335 const ServiceDescriptor* service) { 336 p->Print("// Static method descriptors that strictly reflect the proto.\n"); 337 vars["service_name"] = service->name(); 338 339 //set of names of rpc input- and output- types that were already encountered. 340 //this is needed to avoid duplicating type extractor since it's possible that 341 //the same type is used as an input or output type of more than a single RPC method 342 std::set<std::string> encounteredTypes; 343 344 for (int i = 0; i < service->method_count(); ++i) { 345 auto method = service->method(i); 346 vars["arg_in_id"] = to_string(2L * i); //trying to make msvc 10 happy 347 vars["arg_out_id"] = to_string(2L * i + 1); 348 vars["method_name"] = method->name(); 349 vars["input_type_name"] = method->get_input_type_name(); 350 vars["output_type_name"] = method->get_output_type_name(); 351 vars["input_type"] = JavaClassName(vars, method->get_input_type_name()); 352 vars["output_type"] = JavaClassName(vars, method->get_output_type_name()); 353 vars["method_field_name"] = MethodPropertiesFieldName(method.get()); 354 vars["method_new_field_name"] = MethodPropertiesGetterName(method.get()); 355 vars["method_method_name"] = MethodPropertiesGetterName(method.get()); 356 bool client_streaming = method->ClientStreaming() || method->BidiStreaming(); 357 bool server_streaming = method->ServerStreaming() || method->BidiStreaming(); 358 if (client_streaming) { 359 if (server_streaming) { 360 vars["method_type"] = "BIDI_STREAMING"; 361 } else { 362 vars["method_type"] = "CLIENT_STREAMING"; 363 } 364 } else { 365 if (server_streaming) { 366 vars["method_type"] = "SERVER_STREAMING"; 367 } else { 368 vars["method_type"] = "UNARY"; 369 } 370 } 371 372 p->Print( 373 vars, 374 "@$ExperimentalApi$(\"https://github.com/grpc/grpc-java/issues/" 375 "1901\")\n" 376 "@$Deprecated$ // Use {@link #$method_method_name$()} instead. \n" 377 "public static final $MethodDescriptor$<$input_type$,\n" 378 " $output_type$> $method_field_name$ = $method_method_name$();\n" 379 "\n" 380 "private static volatile $MethodDescriptor$<$input_type$,\n" 381 " $output_type$> $method_new_field_name$;\n" 382 "\n"); 383 384 if (encounteredTypes.insert(vars["input_type_name"]).second) { 385 vars["extr_type"] = vars["input_type"]; 386 vars["extr_type_name"] = vars["input_type_name"]; 387 PrintTypeExtractor(p, vars); 388 } 389 390 if (encounteredTypes.insert(vars["output_type_name"]).second) { 391 vars["extr_type"] = vars["output_type"]; 392 vars["extr_type_name"] = vars["output_type_name"]; 393 PrintTypeExtractor(p, vars); 394 } 395 396 p->Print( 397 vars, 398 "@$ExperimentalApi$(\"https://github.com/grpc/grpc-java/issues/" 399 "1901\")\n" 400 "public static $MethodDescriptor$<$input_type$,\n" 401 " $output_type$> $method_method_name$() {\n" 402 " $MethodDescriptor$<$input_type$, $output_type$> " 403 "$method_new_field_name$;\n" 404 " if (($method_new_field_name$ = " 405 "$service_class_name$.$method_new_field_name$) == null) {\n" 406 " synchronized ($service_class_name$.class) {\n" 407 " if (($method_new_field_name$ = " 408 "$service_class_name$.$method_new_field_name$) == null) {\n" 409 " $service_class_name$.$method_new_field_name$ = " 410 "$method_new_field_name$ = \n" 411 " $MethodDescriptor$.<$input_type$, " 412 "$output_type$>newBuilder()\n" 413 " .setType($MethodType$.$method_type$)\n" 414 " .setFullMethodName(generateFullMethodName(\n" 415 " \"$Package$$service_name$\", \"$method_name$\"))\n" 416 " .setSampledToLocalTracing(true)\n" 417 " .setRequestMarshaller(FlatbuffersUtils.marshaller(\n" 418 " $input_type$.class, " 419 "getExtractorOf$input_type_name$()))\n" 420 " .setResponseMarshaller(FlatbuffersUtils.marshaller(\n" 421 " $output_type$.class, " 422 "getExtractorOf$output_type_name$()))\n"); 423 424 // vars["proto_method_descriptor_supplier"] = service->name() + 425 // "MethodDescriptorSupplier"; 426 p->Print(vars, " .setSchemaDescriptor(null)\n"); 427 //" .setSchemaDescriptor(new 428 //$proto_method_descriptor_supplier$(\"$method_name$\"))\n"); 429 430 p->Print(vars, " .build();\n"); 431 p->Print(vars, 432 " }\n" 433 " }\n" 434 " }\n" 435 " return $method_new_field_name$;\n" 436 "}\n"); 437 438 p->Print("\n"); 439 } 440 } 441 enum StubType { 442 ASYNC_INTERFACE = 0, 443 BLOCKING_CLIENT_INTERFACE = 1, 444 FUTURE_CLIENT_INTERFACE = 2, 445 BLOCKING_SERVER_INTERFACE = 3, 446 ASYNC_CLIENT_IMPL = 4, 447 BLOCKING_CLIENT_IMPL = 5, 448 FUTURE_CLIENT_IMPL = 6, 449 ABSTRACT_CLASS = 7, 450 }; 451 452 enum CallType { ASYNC_CALL = 0, BLOCKING_CALL = 1, FUTURE_CALL = 2 }; 453 454 static void PrintBindServiceMethodBody(Printer* p, VARS& vars, 455 const ServiceDescriptor* service); 456 457 // Prints a client interface or implementation class, or a server interface. 458 static void PrintStub(Printer* p, VARS& vars, const ServiceDescriptor* service, 459 StubType type) { 460 const string service_name = service->name(); 461 vars["service_name"] = service_name; 462 vars["abstract_name"] = service_name + "ImplBase"; 463 string stub_name = service_name; 464 string client_name = service_name; 465 CallType call_type = ASYNC_CALL; 466 bool impl_base = false; 467 bool interface = false; 468 switch (type) { 469 case ABSTRACT_CLASS: 470 call_type = ASYNC_CALL; 471 impl_base = true; 472 break; 473 case ASYNC_CLIENT_IMPL: 474 call_type = ASYNC_CALL; 475 stub_name += "Stub"; 476 break; 477 case BLOCKING_CLIENT_INTERFACE: 478 interface = true; 479 FLATBUFFERS_FALLTHROUGH(); // fall thru 480 case BLOCKING_CLIENT_IMPL: 481 call_type = BLOCKING_CALL; 482 stub_name += "BlockingStub"; 483 client_name += "BlockingClient"; 484 break; 485 case FUTURE_CLIENT_INTERFACE: 486 interface = true; 487 FLATBUFFERS_FALLTHROUGH(); // fall thru 488 case FUTURE_CLIENT_IMPL: 489 call_type = FUTURE_CALL; 490 stub_name += "FutureStub"; 491 client_name += "FutureClient"; 492 break; 493 case ASYNC_INTERFACE: 494 call_type = ASYNC_CALL; 495 interface = true; 496 break; 497 default: 498 GRPC_CODEGEN_FAIL << "Cannot determine class name for StubType: " << type; 499 } 500 vars["stub_name"] = stub_name; 501 vars["client_name"] = client_name; 502 503 // Class head 504 if (!interface) { 505 GrpcWriteServiceDocComment(p, vars, service); 506 } 507 if (impl_base) { 508 p->Print(vars, 509 "public static abstract class $abstract_name$ implements " 510 "$BindableService$ {\n"); 511 } else { 512 p->Print(vars, 513 "public static final class $stub_name$ extends " 514 "$AbstractStub$<$stub_name$> {\n"); 515 } 516 p->Indent(); 517 518 // Constructor and build() method 519 if (!impl_base && !interface) { 520 p->Print(vars, "private $stub_name$($Channel$ channel) {\n"); 521 p->Indent(); 522 p->Print("super(channel);\n"); 523 p->Outdent(); 524 p->Print("}\n\n"); 525 p->Print(vars, 526 "private $stub_name$($Channel$ channel,\n" 527 " $CallOptions$ callOptions) {\n"); 528 p->Indent(); 529 p->Print("super(channel, callOptions);\n"); 530 p->Outdent(); 531 p->Print("}\n\n"); 532 p->Print(vars, 533 "@$Override$\n" 534 "protected $stub_name$ build($Channel$ channel,\n" 535 " $CallOptions$ callOptions) {\n"); 536 p->Indent(); 537 p->Print(vars, "return new $stub_name$(channel, callOptions);\n"); 538 p->Outdent(); 539 p->Print("}\n"); 540 } 541 542 // RPC methods 543 for (int i = 0; i < service->method_count(); ++i) { 544 auto method = service->method(i); 545 vars["input_type"] = JavaClassName(vars, method->get_input_type_name()); 546 vars["output_type"] = JavaClassName(vars, method->get_output_type_name()); 547 vars["lower_method_name"] = LowerMethodName(&*method); 548 vars["method_method_name"] = MethodPropertiesGetterName(&*method); 549 bool client_streaming = method->ClientStreaming() || method->BidiStreaming(); 550 bool server_streaming = method->ServerStreaming() || method->BidiStreaming(); 551 552 if (call_type == BLOCKING_CALL && client_streaming) { 553 // Blocking client interface with client streaming is not available 554 continue; 555 } 556 557 if (call_type == FUTURE_CALL && (client_streaming || server_streaming)) { 558 // Future interface doesn't support streaming. 559 continue; 560 } 561 562 // Method signature 563 p->Print("\n"); 564 // TODO(nmittler): Replace with WriteMethodDocComment once included by the 565 // protobuf distro. 566 if (!interface) { 567 GrpcWriteMethodDocComment(p, vars, &*method); 568 } 569 p->Print("public "); 570 switch (call_type) { 571 case BLOCKING_CALL: 572 GRPC_CODEGEN_CHECK(!client_streaming) 573 << "Blocking client interface with client streaming is unavailable"; 574 if (server_streaming) { 575 // Server streaming 576 p->Print(vars, 577 "$Iterator$<$output_type$> $lower_method_name$(\n" 578 " $input_type$ request)"); 579 } else { 580 // Simple RPC 581 p->Print(vars, 582 "$output_type$ $lower_method_name$($input_type$ request)"); 583 } 584 break; 585 case ASYNC_CALL: 586 if (client_streaming) { 587 // Bidirectional streaming or client streaming 588 p->Print(vars, 589 "$StreamObserver$<$input_type$> $lower_method_name$(\n" 590 " $StreamObserver$<$output_type$> responseObserver)"); 591 } else { 592 // Server streaming or simple RPC 593 p->Print(vars, 594 "void $lower_method_name$($input_type$ request,\n" 595 " $StreamObserver$<$output_type$> responseObserver)"); 596 } 597 break; 598 case FUTURE_CALL: 599 GRPC_CODEGEN_CHECK(!client_streaming && !server_streaming) 600 << "Future interface doesn't support streaming. " 601 << "client_streaming=" << client_streaming << ", " 602 << "server_streaming=" << server_streaming; 603 p->Print(vars, 604 "$ListenableFuture$<$output_type$> $lower_method_name$(\n" 605 " $input_type$ request)"); 606 break; 607 } 608 609 if (interface) { 610 p->Print(";\n"); 611 continue; 612 } 613 // Method body. 614 p->Print(" {\n"); 615 p->Indent(); 616 if (impl_base) { 617 switch (call_type) { 618 // NB: Skipping validation of service methods. If something is wrong, 619 // we wouldn't get to this point as compiler would return errors when 620 // generating service interface. 621 case ASYNC_CALL: 622 if (client_streaming) { 623 p->Print(vars, 624 "return " 625 "asyncUnimplementedStreamingCall($method_method_name$(), " 626 "responseObserver);\n"); 627 } else { 628 p->Print(vars, 629 "asyncUnimplementedUnaryCall($method_method_name$(), " 630 "responseObserver);\n"); 631 } 632 break; 633 default: 634 break; 635 } 636 } else if (!interface) { 637 switch (call_type) { 638 case BLOCKING_CALL: 639 GRPC_CODEGEN_CHECK(!client_streaming) 640 << "Blocking client streaming interface is not available"; 641 if (server_streaming) { 642 vars["calls_method"] = "blockingServerStreamingCall"; 643 vars["params"] = "request"; 644 } else { 645 vars["calls_method"] = "blockingUnaryCall"; 646 vars["params"] = "request"; 647 } 648 p->Print(vars, 649 "return $calls_method$(\n" 650 " getChannel(), $method_method_name$(), " 651 "getCallOptions(), $params$);\n"); 652 break; 653 case ASYNC_CALL: 654 if (server_streaming) { 655 if (client_streaming) { 656 vars["calls_method"] = "asyncBidiStreamingCall"; 657 vars["params"] = "responseObserver"; 658 } else { 659 vars["calls_method"] = "asyncServerStreamingCall"; 660 vars["params"] = "request, responseObserver"; 661 } 662 } else { 663 if (client_streaming) { 664 vars["calls_method"] = "asyncClientStreamingCall"; 665 vars["params"] = "responseObserver"; 666 } else { 667 vars["calls_method"] = "asyncUnaryCall"; 668 vars["params"] = "request, responseObserver"; 669 } 670 } 671 vars["last_line_prefix"] = client_streaming ? "return " : ""; 672 p->Print(vars, 673 "$last_line_prefix$$calls_method$(\n" 674 " getChannel().newCall($method_method_name$(), " 675 "getCallOptions()), $params$);\n"); 676 break; 677 case FUTURE_CALL: 678 GRPC_CODEGEN_CHECK(!client_streaming && !server_streaming) 679 << "Future interface doesn't support streaming. " 680 << "client_streaming=" << client_streaming << ", " 681 << "server_streaming=" << server_streaming; 682 vars["calls_method"] = "futureUnaryCall"; 683 p->Print(vars, 684 "return $calls_method$(\n" 685 " getChannel().newCall($method_method_name$(), " 686 "getCallOptions()), request);\n"); 687 break; 688 } 689 } 690 p->Outdent(); 691 p->Print("}\n"); 692 } 693 694 if (impl_base) { 695 p->Print("\n"); 696 p->Print( 697 vars, 698 "@$Override$ public final $ServerServiceDefinition$ bindService() {\n"); 699 vars["instance"] = "this"; 700 PrintBindServiceMethodBody(p, vars, service); 701 p->Print("}\n"); 702 } 703 704 p->Outdent(); 705 p->Print("}\n\n"); 706 } 707 708 static bool CompareMethodClientStreaming( 709 const std::unique_ptr<const grpc_generator::Method>& method1, 710 const std::unique_ptr<const grpc_generator::Method>& method2) { 711 return method1->ClientStreaming() < method2->ClientStreaming(); 712 } 713 714 // Place all method invocations into a single class to reduce memory footprint 715 // on Android. 716 static void PrintMethodHandlerClass(Printer* p, VARS& vars, 717 const ServiceDescriptor* service) { 718 // Sort method ids based on ClientStreaming() so switch tables are compact. 719 std::vector<std::unique_ptr<const grpc_generator::Method>> sorted_methods( 720 service->method_count()); 721 for (int i = 0; i < service->method_count(); ++i) { 722 sorted_methods[i] = service->method(i); 723 } 724 stable_sort(sorted_methods.begin(), sorted_methods.end(), 725 CompareMethodClientStreaming); 726 for (size_t i = 0; i < sorted_methods.size(); i++) { 727 auto& method = sorted_methods[i]; 728 vars["method_id"] = to_string(i); 729 vars["method_id_name"] = MethodIdFieldName(&*method); 730 p->Print(vars, 731 "private static final int $method_id_name$ = $method_id$;\n"); 732 } 733 p->Print("\n"); 734 vars["service_name"] = service->name() + "ImplBase"; 735 p->Print(vars, 736 "private static final class MethodHandlers<Req, Resp> implements\n" 737 " io.grpc.stub.ServerCalls.UnaryMethod<Req, Resp>,\n" 738 " io.grpc.stub.ServerCalls.ServerStreamingMethod<Req, Resp>,\n" 739 " io.grpc.stub.ServerCalls.ClientStreamingMethod<Req, Resp>,\n" 740 " io.grpc.stub.ServerCalls.BidiStreamingMethod<Req, Resp> {\n" 741 " private final $service_name$ serviceImpl;\n" 742 " private final int methodId;\n" 743 "\n" 744 " MethodHandlers($service_name$ serviceImpl, int methodId) {\n" 745 " this.serviceImpl = serviceImpl;\n" 746 " this.methodId = methodId;\n" 747 " }\n\n"); 748 p->Indent(); 749 p->Print(vars, 750 "@$Override$\n" 751 "@java.lang.SuppressWarnings(\"unchecked\")\n" 752 "public void invoke(Req request, $StreamObserver$<Resp> " 753 "responseObserver) {\n" 754 " switch (methodId) {\n"); 755 p->Indent(); 756 p->Indent(); 757 758 for (int i = 0; i < service->method_count(); ++i) { 759 auto method = service->method(i); 760 if (method->ClientStreaming() || method->BidiStreaming()) { 761 continue; 762 } 763 vars["method_id_name"] = MethodIdFieldName(&*method); 764 vars["lower_method_name"] = LowerMethodName(&*method); 765 vars["input_type"] = JavaClassName(vars, method->get_input_type_name()); 766 vars["output_type"] = JavaClassName(vars, method->get_output_type_name()); 767 p->Print(vars, 768 "case $method_id_name$:\n" 769 " serviceImpl.$lower_method_name$(($input_type$) request,\n" 770 " ($StreamObserver$<$output_type$>) responseObserver);\n" 771 " break;\n"); 772 } 773 p->Print( 774 "default:\n" 775 " throw new AssertionError();\n"); 776 777 p->Outdent(); 778 p->Outdent(); 779 p->Print( 780 " }\n" 781 "}\n\n"); 782 783 p->Print(vars, 784 "@$Override$\n" 785 "@java.lang.SuppressWarnings(\"unchecked\")\n" 786 "public $StreamObserver$<Req> invoke(\n" 787 " $StreamObserver$<Resp> responseObserver) {\n" 788 " switch (methodId) {\n"); 789 p->Indent(); 790 p->Indent(); 791 792 for (int i = 0; i < service->method_count(); ++i) { 793 auto method = service->method(i); 794 if (!(method->ClientStreaming() || method->BidiStreaming())) { 795 continue; 796 } 797 vars["method_id_name"] = MethodIdFieldName(&*method); 798 vars["lower_method_name"] = LowerMethodName(&*method); 799 vars["input_type"] = JavaClassName(vars, method->get_input_type_name()); 800 vars["output_type"] = JavaClassName(vars, method->get_output_type_name()); 801 p->Print( 802 vars, 803 "case $method_id_name$:\n" 804 " return ($StreamObserver$<Req>) serviceImpl.$lower_method_name$(\n" 805 " ($StreamObserver$<$output_type$>) responseObserver);\n"); 806 } 807 p->Print( 808 "default:\n" 809 " throw new AssertionError();\n"); 810 811 p->Outdent(); 812 p->Outdent(); 813 p->Print( 814 " }\n" 815 "}\n"); 816 817 p->Outdent(); 818 p->Print("}\n\n"); 819 } 820 821 static void PrintGetServiceDescriptorMethod(Printer* p, VARS& vars, 822 const ServiceDescriptor* service) { 823 vars["service_name"] = service->name(); 824 // vars["proto_base_descriptor_supplier"] = service->name() + 825 // "BaseDescriptorSupplier"; vars["proto_file_descriptor_supplier"] = 826 // service->name() + "FileDescriptorSupplier"; 827 // vars["proto_method_descriptor_supplier"] = service->name() + 828 // "MethodDescriptorSupplier"; vars["proto_class_name"] = 829 // google::protobuf::compiler::java::ClassName(service->file()); 830 // p->Print( 831 // vars, 832 // "private static abstract class 833 // $proto_base_descriptor_supplier$\n" " implements 834 // $ProtoFileDescriptorSupplier$, 835 // $ProtoServiceDescriptorSupplier$ {\n" " 836 // $proto_base_descriptor_supplier$() {}\n" 837 // "\n" 838 // " @$Override$\n" 839 // " public com.google.protobuf.Descriptors.FileDescriptor 840 // getFileDescriptor() {\n" " return 841 // $proto_class_name$.getDescriptor();\n" " }\n" 842 // "\n" 843 // " @$Override$\n" 844 // " public com.google.protobuf.Descriptors.ServiceDescriptor 845 // getServiceDescriptor() {\n" " return 846 // getFileDescriptor().findServiceByName(\"$service_name$\");\n" 847 // " }\n" 848 // "}\n" 849 // "\n" 850 // "private static final class 851 // $proto_file_descriptor_supplier$\n" " extends 852 // $proto_base_descriptor_supplier$ {\n" " 853 // $proto_file_descriptor_supplier$() {}\n" 854 // "}\n" 855 // "\n" 856 // "private static final class 857 // $proto_method_descriptor_supplier$\n" " extends 858 // $proto_base_descriptor_supplier$\n" " implements 859 // $ProtoMethodDescriptorSupplier$ {\n" " private final 860 // String methodName;\n" 861 // "\n" 862 // " $proto_method_descriptor_supplier$(String methodName) 863 // {\n" " this.methodName = methodName;\n" " }\n" 864 // "\n" 865 // " @$Override$\n" 866 // " public com.google.protobuf.Descriptors.MethodDescriptor 867 // getMethodDescriptor() {\n" " return 868 // getServiceDescriptor().findMethodByName(methodName);\n" " 869 // }\n" 870 // "}\n\n"); 871 872 p->Print( 873 vars, 874 "private static volatile $ServiceDescriptor$ serviceDescriptor;\n\n"); 875 876 p->Print(vars, 877 "public static $ServiceDescriptor$ getServiceDescriptor() {\n"); 878 p->Indent(); 879 p->Print(vars, "$ServiceDescriptor$ result = serviceDescriptor;\n"); 880 p->Print("if (result == null) {\n"); 881 p->Indent(); 882 p->Print(vars, "synchronized ($service_class_name$.class) {\n"); 883 p->Indent(); 884 p->Print("result = serviceDescriptor;\n"); 885 p->Print("if (result == null) {\n"); 886 p->Indent(); 887 888 p->Print(vars, 889 "serviceDescriptor = result = " 890 "$ServiceDescriptor$.newBuilder(SERVICE_NAME)"); 891 p->Indent(); 892 p->Indent(); 893 p->Print(vars, "\n.setSchemaDescriptor(null)"); 894 for (int i = 0; i < service->method_count(); ++i) { 895 auto method = service->method(i); 896 vars["method_method_name"] = MethodPropertiesGetterName(&*method); 897 p->Print(vars, "\n.addMethod($method_method_name$())"); 898 } 899 p->Print("\n.build();\n"); 900 p->Outdent(); 901 p->Outdent(); 902 903 p->Outdent(); 904 p->Print("}\n"); 905 p->Outdent(); 906 p->Print("}\n"); 907 p->Outdent(); 908 p->Print("}\n"); 909 p->Print("return result;\n"); 910 p->Outdent(); 911 p->Print("}\n"); 912 } 913 914 static void PrintBindServiceMethodBody(Printer* p, VARS& vars, 915 const ServiceDescriptor* service) { 916 vars["service_name"] = service->name(); 917 p->Indent(); 918 p->Print(vars, 919 "return " 920 "$ServerServiceDefinition$.builder(getServiceDescriptor())\n"); 921 p->Indent(); 922 p->Indent(); 923 for (int i = 0; i < service->method_count(); ++i) { 924 auto method = service->method(i); 925 vars["lower_method_name"] = LowerMethodName(&*method); 926 vars["method_method_name"] = MethodPropertiesGetterName(&*method); 927 vars["input_type"] = JavaClassName(vars, method->get_input_type_name()); 928 vars["output_type"] = JavaClassName(vars, method->get_output_type_name()); 929 vars["method_id_name"] = MethodIdFieldName(&*method); 930 bool client_streaming = method->ClientStreaming() || method->BidiStreaming(); 931 bool server_streaming = method->ServerStreaming() || method->BidiStreaming(); 932 if (client_streaming) { 933 if (server_streaming) { 934 vars["calls_method"] = "asyncBidiStreamingCall"; 935 } else { 936 vars["calls_method"] = "asyncClientStreamingCall"; 937 } 938 } else { 939 if (server_streaming) { 940 vars["calls_method"] = "asyncServerStreamingCall"; 941 } else { 942 vars["calls_method"] = "asyncUnaryCall"; 943 } 944 } 945 p->Print(vars, ".addMethod(\n"); 946 p->Indent(); 947 p->Print(vars, 948 "$method_method_name$(),\n" 949 "$calls_method$(\n"); 950 p->Indent(); 951 p->Print(vars, 952 "new MethodHandlers<\n" 953 " $input_type$,\n" 954 " $output_type$>(\n" 955 " $instance$, $method_id_name$)))\n"); 956 p->Outdent(); 957 p->Outdent(); 958 } 959 p->Print(".build();\n"); 960 p->Outdent(); 961 p->Outdent(); 962 p->Outdent(); 963 } 964 965 static void PrintService(Printer* p, VARS& vars, 966 const ServiceDescriptor* service, 967 bool disable_version) { 968 vars["service_name"] = service->name(); 969 vars["service_class_name"] = ServiceClassName(service->name()); 970 vars["grpc_version"] = ""; 971 #ifdef GRPC_VERSION 972 if (!disable_version) { 973 vars["grpc_version"] = " (version " XSTR(GRPC_VERSION) ")"; 974 } 975 #else 976 (void)disable_version; 977 #endif 978 // TODO(nmittler): Replace with WriteServiceDocComment once included by 979 // protobuf distro. 980 GrpcWriteServiceDocComment(p, vars, service); 981 p->Print(vars, 982 "@$Generated$(\n" 983 " value = \"by gRPC proto compiler$grpc_version$\",\n" 984 " comments = \"Source: $file_name$.fbs\")\n" 985 "public final class $service_class_name$ {\n\n"); 986 p->Indent(); 987 p->Print(vars, "private $service_class_name$() {}\n\n"); 988 989 p->Print(vars, 990 "public static final String SERVICE_NAME = " 991 "\"$Package$$service_name$\";\n\n"); 992 993 PrintMethodFields(p, vars, service); 994 995 // TODO(nmittler): Replace with WriteDocComment once included by protobuf 996 // distro. 997 GrpcWriteDocComment( 998 p, vars, 999 " Creates a new async stub that supports all call types for the service"); 1000 p->Print(vars, 1001 "public static $service_name$Stub newStub($Channel$ channel) {\n"); 1002 p->Indent(); 1003 p->Print(vars, "return new $service_name$Stub(channel);\n"); 1004 p->Outdent(); 1005 p->Print("}\n\n"); 1006 1007 // TODO(nmittler): Replace with WriteDocComment once included by protobuf 1008 // distro. 1009 GrpcWriteDocComment( 1010 p, vars, 1011 " Creates a new blocking-style stub that supports unary and streaming " 1012 "output calls on the service"); 1013 p->Print(vars, 1014 "public static $service_name$BlockingStub newBlockingStub(\n" 1015 " $Channel$ channel) {\n"); 1016 p->Indent(); 1017 p->Print(vars, "return new $service_name$BlockingStub(channel);\n"); 1018 p->Outdent(); 1019 p->Print("}\n\n"); 1020 1021 // TODO(nmittler): Replace with WriteDocComment once included by protobuf 1022 // distro. 1023 GrpcWriteDocComment( 1024 p, vars, 1025 " Creates a new ListenableFuture-style stub that supports unary calls " 1026 "on the service"); 1027 p->Print(vars, 1028 "public static $service_name$FutureStub newFutureStub(\n" 1029 " $Channel$ channel) {\n"); 1030 p->Indent(); 1031 p->Print(vars, "return new $service_name$FutureStub(channel);\n"); 1032 p->Outdent(); 1033 p->Print("}\n\n"); 1034 1035 PrintStub(p, vars, service, ABSTRACT_CLASS); 1036 PrintStub(p, vars, service, ASYNC_CLIENT_IMPL); 1037 PrintStub(p, vars, service, BLOCKING_CLIENT_IMPL); 1038 PrintStub(p, vars, service, FUTURE_CLIENT_IMPL); 1039 1040 PrintMethodHandlerClass(p, vars, service); 1041 PrintGetServiceDescriptorMethod(p, vars, service); 1042 p->Outdent(); 1043 p->Print("}\n"); 1044 } 1045 1046 void PrintStaticImports(Printer* p) { 1047 p->Print( 1048 "import java.nio.ByteBuffer;\n" 1049 "import static " 1050 "io.grpc.MethodDescriptor.generateFullMethodName;\n" 1051 "import static " 1052 "io.grpc.stub.ClientCalls.asyncBidiStreamingCall;\n" 1053 "import static " 1054 "io.grpc.stub.ClientCalls.asyncClientStreamingCall;\n" 1055 "import static " 1056 "io.grpc.stub.ClientCalls.asyncServerStreamingCall;\n" 1057 "import static " 1058 "io.grpc.stub.ClientCalls.asyncUnaryCall;\n" 1059 "import static " 1060 "io.grpc.stub.ClientCalls.blockingServerStreamingCall;\n" 1061 "import static " 1062 "io.grpc.stub.ClientCalls.blockingUnaryCall;\n" 1063 "import static " 1064 "io.grpc.stub.ClientCalls.futureUnaryCall;\n" 1065 "import static " 1066 "io.grpc.stub.ServerCalls.asyncBidiStreamingCall;\n" 1067 "import static " 1068 "io.grpc.stub.ServerCalls.asyncClientStreamingCall;\n" 1069 "import static " 1070 "io.grpc.stub.ServerCalls.asyncServerStreamingCall;\n" 1071 "import static " 1072 "io.grpc.stub.ServerCalls.asyncUnaryCall;\n" 1073 "import static " 1074 "io.grpc.stub.ServerCalls.asyncUnimplementedStreamingCall;\n" 1075 "import static " 1076 "io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall;\n\n"); 1077 } 1078 1079 void GenerateService(const grpc_generator::Service* service, 1080 grpc_generator::Printer* printer, VARS& vars, 1081 bool disable_version) { 1082 // All non-generated classes must be referred by fully qualified names to 1083 // avoid collision with generated classes. 1084 vars["String"] = "java.lang.String"; 1085 vars["Deprecated"] = "java.lang.Deprecated"; 1086 vars["Override"] = "java.lang.Override"; 1087 vars["Channel"] = "io.grpc.Channel"; 1088 vars["CallOptions"] = "io.grpc.CallOptions"; 1089 vars["MethodType"] = "io.grpc.MethodDescriptor.MethodType"; 1090 vars["ServerMethodDefinition"] = "io.grpc.ServerMethodDefinition"; 1091 vars["BindableService"] = "io.grpc.BindableService"; 1092 vars["ServerServiceDefinition"] = "io.grpc.ServerServiceDefinition"; 1093 vars["ServiceDescriptor"] = "io.grpc.ServiceDescriptor"; 1094 vars["ProtoFileDescriptorSupplier"] = 1095 "io.grpc.protobuf.ProtoFileDescriptorSupplier"; 1096 vars["ProtoServiceDescriptorSupplier"] = 1097 "io.grpc.protobuf.ProtoServiceDescriptorSupplier"; 1098 vars["ProtoMethodDescriptorSupplier"] = 1099 "io.grpc.protobuf.ProtoMethodDescriptorSupplier"; 1100 vars["AbstractStub"] = "io.grpc.stub.AbstractStub"; 1101 vars["MethodDescriptor"] = "io.grpc.MethodDescriptor"; 1102 vars["NanoUtils"] = "io.grpc.protobuf.nano.NanoUtils"; 1103 vars["StreamObserver"] = "io.grpc.stub.StreamObserver"; 1104 vars["Iterator"] = "java.util.Iterator"; 1105 vars["Generated"] = "javax.annotation.Generated"; 1106 vars["ListenableFuture"] = 1107 "com.google.common.util.concurrent.ListenableFuture"; 1108 vars["ExperimentalApi"] = "io.grpc.ExperimentalApi"; 1109 1110 PrintStaticImports(printer); 1111 1112 PrintService(printer, vars, service, disable_version); 1113 } 1114 1115 grpc::string GenerateServiceSource( 1116 grpc_generator::File* file, const grpc_generator::Service* service, 1117 grpc_java_generator::Parameters* parameters) { 1118 grpc::string out; 1119 auto printer = file->CreatePrinter(&out); 1120 VARS vars; 1121 vars["flatc_version"] = grpc::string( 1122 FLATBUFFERS_STRING(FLATBUFFERS_VERSION_MAJOR) "." FLATBUFFERS_STRING( 1123 FLATBUFFERS_VERSION_MINOR) "." FLATBUFFERS_STRING(FLATBUFFERS_VERSION_REVISION)); 1124 1125 vars["file_name"] = file->filename(); 1126 1127 if (!parameters->package_name.empty()) { 1128 vars["Package"] = parameters->package_name; // ServiceJavaPackage(service); 1129 } 1130 GenerateImports(file, &*printer, vars); 1131 GenerateService(service, &*printer, vars, false); 1132 return out; 1133 } 1134 1135 } // namespace grpc_java_generator 1136