| /* | |
| * Copyright 2019 The gRPC Authors | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| // gRPC Java code generation | |
| // | |
| // The generated code should be very light-weight in terms of size. Although | |
| // less of a scaling problem than for protobuf messages, additions here are | |
| // multiplied many times and could easily bloat binaries if added to without | |
| // consideration. This is generally achieved by putting logic in io.grpc.stub | |
| // and limiting the generated code to API surface and inputs. | |
| // | |
| // gRPC is compatible with old versions of the generated code. New versions of | |
| // generated code are free to use new APIs, and thus require new gRPC versions. | |
| // But this isn't a special rule and is more just that the generated code is | |
| // best viewed as a library that depends on gRPC (as it may be used within a | |
| // library), and it should adhere to the normal rules of such. Notably, it can | |
| // only use stable API. Using experimental API, even if only exposing its own | |
| // experimental API, is not safe as libraries do not get to choose the version | |
| // of gRPC being used. The rule here is more "everything should behave like | |
| // normal to users" or "we don't require users to generate code for their | |
| // transitive dependency tree because OSS build tools don't support that." | |
| // | |
| // Java lite is designed for tools like Proguard to strip unused code. Be | |
| // careful not to add new references to methods that would retain them even when | |
| // unneeded. In particular, we want uncalled RPC methods to have their | |
| // descriptors and code pruned. | |
| #include "java_generator.h" | |
| #include <algorithm> | |
| #include <iostream> | |
| #include <iterator> | |
| #include <map> | |
| #include <set> | |
| #include <vector> | |
| #include "absl/strings/escaping.h" | |
| #include <google/protobuf/descriptor.h> | |
| #include <google/protobuf/descriptor.pb.h> | |
| #include <google/protobuf/io/printer.h> | |
| #include <google/protobuf/io/zero_copy_stream.h> | |
| #include <google/protobuf/stubs/common.h> | |
| // Protobuf 3.21 changed the name of this file. | |
| #if GOOGLE_PROTOBUF_VERSION >= 3021000 | |
| #include <google/protobuf/compiler/java/names.h> | |
| #else | |
| #include <google/protobuf/compiler/java/java_names.h> | |
| #endif | |
| // Stringify helpers used solely to cast GRPC_VERSION | |
| #ifndef STR | |
| #define STR(s) #s | |
| #endif | |
| #ifndef XSTR | |
| #define XSTR(s) STR(s) | |
| #endif | |
| #ifdef ABSL_FALLTHROUGH_INTENDED | |
| #define FALLTHROUGH ABSL_FALLTHROUGH_INTENDED | |
| #else | |
| #define FALLTHROUGH | |
| #endif | |
| namespace java_grpc_generator { | |
| namespace protobuf = google::protobuf; | |
| using protobuf::Descriptor; | |
| using protobuf::FileDescriptor; | |
| using protobuf::MethodDescriptor; | |
| using protobuf::ServiceDescriptor; | |
| using protobuf::SourceLocation; | |
| using protobuf::io::Printer; | |
| using std::to_string; | |
| // java keywords from: https://docs.oracle.com/javase/specs/jls/se8/html/jls-3.html#jls-3.9 | |
| static std::set<std::string> java_keywords = { | |
| "abstract", | |
| "assert", | |
| "boolean", | |
| "break", | |
| "byte", | |
| "case", | |
| "catch", | |
| "char", | |
| "class", | |
| "const", | |
| "continue", | |
| "default", | |
| "do", | |
| "double", | |
| "else", | |
| "enum", | |
| "extends", | |
| "final", | |
| "finally", | |
| "float", | |
| "for", | |
| "goto", | |
| "if", | |
| "implements", | |
| "import", | |
| "instanceof", | |
| "int", | |
| "interface", | |
| "long", | |
| "native", | |
| "new", | |
| "package", | |
| "private", | |
| "protected", | |
| "public", | |
| "return", | |
| "short", | |
| "static", | |
| "strictfp", | |
| "super", | |
| "switch", | |
| "synchronized", | |
| "this", | |
| "throw", | |
| "throws", | |
| "transient", | |
| "try", | |
| "void", | |
| "volatile", | |
| "while", | |
| // additional ones added by us | |
| "true", | |
| "false", | |
| }; | |
| // Methods on java.lang.Object that take no arguments. | |
| static std::set<std::string> java_object_methods = { | |
| "clone", | |
| "finalize", | |
| "getClass", | |
| "hashCode", | |
| "notify", | |
| "notifyAll", | |
| "toString", | |
| "wait", | |
| }; | |
| // Adjust a method name prefix identifier to follow the JavaBean spec: | |
| // - decapitalize the first letter | |
| // - remove embedded underscores & capitalize the following letter | |
| // Finally, if the result is a reserved java keyword or an Object method, | |
| // append an underscore. | |
| static std::string MixedLower(std::string word, bool mangle_object_methods = false) { | |
| std::string w; | |
| w += tolower(word[0]); | |
| bool after_underscore = false; | |
| for (size_t i = 1; i < word.length(); ++i) { | |
| if (word[i] == '_') { | |
| after_underscore = true; | |
| } else { | |
| w += after_underscore ? toupper(word[i]) : word[i]; | |
| after_underscore = false; | |
| } | |
| } | |
| if (java_keywords.find(w) != java_keywords.end() || | |
| (mangle_object_methods && | |
| java_object_methods.find(w) != java_object_methods.end())) { | |
| return w + "_"; | |
| } | |
| return w; | |
| } | |
| // Converts to the identifier to the ALL_UPPER_CASE format. | |
| // - An underscore is inserted where a lower case letter is followed by an | |
| // upper case letter. | |
| // - All letters are converted to upper case | |
| static std::string ToAllUpperCase(std::string word) { | |
| std::string w; | |
| for (size_t i = 0; i < word.length(); ++i) { | |
| w += toupper(word[i]); | |
| if ((i < word.length() - 1) && islower(word[i]) && isupper(word[i + 1])) { | |
| w += '_'; | |
| } | |
| } | |
| return w; | |
| } | |
| static inline std::string LowerMethodName(const MethodDescriptor* method, | |
| bool mangle_object_methods = false) { | |
| return MixedLower(std::string(method->name()), mangle_object_methods); | |
| } | |
| static inline std::string MethodPropertiesFieldName(const MethodDescriptor* method) { | |
| return "METHOD_" + ToAllUpperCase(std::string(method->name())); | |
| } | |
| static inline std::string MethodPropertiesGetterName(const MethodDescriptor* method) { | |
| return MixedLower("get_" + std::string(method->name()) + "_method"); | |
| } | |
| static inline std::string MethodIdFieldName(const MethodDescriptor* method) { | |
| return "METHODID_" + ToAllUpperCase(std::string(method->name())); | |
| } | |
| static inline std::string MessageFullJavaName(const Descriptor* desc) { | |
| return protobuf::compiler::java::QualifiedClassName(desc); | |
| } | |
| // TODO(nmittler): Remove once protobuf includes javadoc methods in distribution. | |
| template <typename ITR> | |
| static void GrpcSplitStringToIteratorUsing(const std::string& full, | |
| const char* delim, | |
| ITR& result) { | |
| // Optimize the common case where delim is a single character. | |
| if (delim[0] != '\0' && delim[1] == '\0') { | |
| char c = delim[0]; | |
| const char* p = full.data(); | |
| const char* end = p + full.size(); | |
| while (p != end) { | |
| if (*p == c) { | |
| ++p; | |
| } else { | |
| const char* start = p; | |
| while (++p != end && *p != c); | |
| *result++ = std::string(start, p - start); | |
| } | |
| } | |
| return; | |
| } | |
| std::string::size_type begin_index, end_index; | |
| begin_index = full.find_first_not_of(delim); | |
| while (begin_index != std::string::npos) { | |
| end_index = full.find_first_of(delim, begin_index); | |
| if (end_index == std::string::npos) { | |
| *result++ = full.substr(begin_index); | |
| return; | |
| } | |
| *result++ = full.substr(begin_index, (end_index - begin_index)); | |
| begin_index = full.find_first_not_of(delim, end_index); | |
| } | |
| } | |
| // TODO(nmittler): Remove once protobuf includes javadoc methods in distribution. | |
| static void GrpcSplitStringUsing(const std::string& full, | |
| const char* delim, | |
| std::vector<std::string>* result) { | |
| std::back_insert_iterator< std::vector<std::string> > it(*result); | |
| GrpcSplitStringToIteratorUsing(full, delim, it); | |
| } | |
| // TODO(nmittler): Remove once protobuf includes javadoc methods in distribution. | |
| static std::vector<std::string> GrpcSplit(const std::string& full, const char* delim) { | |
| std::vector<std::string> result; | |
| GrpcSplitStringUsing(full, delim, &result); | |
| return result; | |
| } | |
| // TODO(nmittler): Remove once protobuf includes javadoc methods in distribution. | |
| static std::string GrpcEscapeJavadoc(const std::string& input) { | |
| std::string result; | |
| result.reserve(input.size() * 2); | |
| char prev = '*'; | |
| for (std::string::size_type i = 0; i < input.size(); i++) { | |
| char c = input[i]; | |
| switch (c) { | |
| case '*': | |
| // Avoid "/*". | |
| if (prev == '/') { | |
| result.append("*"); | |
| } else { | |
| result.push_back(c); | |
| } | |
| break; | |
| case '/': | |
| // Avoid "*/". | |
| if (prev == '*') { | |
| result.append("/"); | |
| } else { | |
| result.push_back(c); | |
| } | |
| break; | |
| case '@': | |
| // '@' starts javadoc tags including the @deprecated tag, which will | |
| // cause a compile-time error if inserted before a declaration that | |
| // does not have a corresponding @Deprecated annotation. | |
| result.append("@"); | |
| break; | |
| case '<': | |
| // Avoid interpretation as HTML. | |
| result.append("<"); | |
| break; | |
| case '>': | |
| // Avoid interpretation as HTML. | |
| result.append(">"); | |
| break; | |
| case '&': | |
| // Avoid interpretation as HTML. | |
| result.append("&"); | |
| break; | |
| case '\\': | |
| // Java interprets Unicode escape sequences anywhere! | |
| result.append("\"); | |
| break; | |
| default: | |
| result.push_back(c); | |
| break; | |
| } | |
| prev = c; | |
| } | |
| return result; | |
| } | |
| // TODO(nmittler): Remove once protobuf includes javadoc methods in distribution. | |
| template <typename DescriptorType> | |
| static std::string GrpcGetCommentsForDescriptor(const DescriptorType* descriptor) { | |
| SourceLocation location; | |
| if (descriptor->GetSourceLocation(&location)) { | |
| return location.leading_comments.empty() ? | |
| location.trailing_comments : location.leading_comments; | |
| } | |
| return std::string(); | |
| } | |
| // TODO(nmittler): Remove once protobuf includes javadoc methods in distribution. | |
| static std::vector<std::string> GrpcGetDocLines(const std::string& comments) { | |
| if (!comments.empty()) { | |
| // TODO(kenton): Ideally we should parse the comment text as Markdown and | |
| // write it back as HTML, but this requires a Markdown parser. For now | |
| // we just use <pre> to get fixed-width text formatting. | |
| // If the comment itself contains block comment start or end markers, | |
| // HTML-escape them so that they don't accidentally close the doc comment. | |
| std::string escapedComments = GrpcEscapeJavadoc(comments); | |
| std::vector<std::string> lines = GrpcSplit(escapedComments, "\n"); | |
| while (!lines.empty() && lines.back().empty()) { | |
| lines.pop_back(); | |
| } | |
| return lines; | |
| } | |
| return std::vector<std::string>(); | |
| } | |
| // TODO(nmittler): Remove once protobuf includes javadoc methods in distribution. | |
| template <typename DescriptorType> | |
| static std::vector<std::string> GrpcGetDocLinesForDescriptor(const DescriptorType* descriptor) { | |
| return GrpcGetDocLines(GrpcGetCommentsForDescriptor(descriptor)); | |
| } | |
| enum StubType { | |
| ASYNC_INTERFACE = 0, | |
| BLOCKING_CLIENT_INTERFACE = 1, | |
| FUTURE_CLIENT_INTERFACE = 2, | |
| BLOCKING_SERVER_INTERFACE = 3, | |
| ASYNC_CLIENT_IMPL = 4, | |
| BLOCKING_CLIENT_IMPL = 5, | |
| FUTURE_CLIENT_IMPL = 6, | |
| ABSTRACT_CLASS = 7, | |
| BLOCKING_V2_CLIENT_IMPL = 8, | |
| NONE = 999, | |
| }; | |
| enum CallType { | |
| ASYNC_CALL = 0, | |
| BLOCKING_CALL = 1, | |
| FUTURE_CALL = 2, | |
| BLOCKING_V2_CALL = 3, | |
| }; | |
| // TODO(nmittler): Remove once protobuf includes javadoc methods in distribution. | |
| static void GrpcWriteDocCommentBody(Printer* printer, | |
| const std::vector<std::string>& lines, | |
| bool surroundWithPreTag) { | |
| if (!lines.empty()) { | |
| if (surroundWithPreTag) { | |
| printer->Print(" * <pre>\n"); | |
| } | |
| for (size_t i = 0; i < lines.size(); i++) { | |
| // Most lines should start with a space. Watch out for lines that start | |
| // with a /, since putting that right after the leading asterisk will | |
| // close the comment. | |
| if (!lines[i].empty() && lines[i][0] == '/') { | |
| printer->Print(" * $line$\n", "line", lines[i]); | |
| } else { | |
| printer->Print(" *$line$\n", "line", lines[i]); | |
| } | |
| } | |
| if (surroundWithPreTag) { | |
| printer->Print(" * </pre>\n"); | |
| } | |
| } | |
| } | |
| // TODO(nmittler): Remove once protobuf includes javadoc methods in distribution. | |
| static void GrpcWriteDocComment(Printer* printer, const std::string& comments) { | |
| printer->Print("/**\n"); | |
| std::vector<std::string> lines = GrpcGetDocLines(comments); | |
| GrpcWriteDocCommentBody(printer, lines, false); | |
| printer->Print(" */\n"); | |
| } | |
| // For the non-interface classes add a description of use before the description from proto | |
| static void GrpcWriteServiceDocComment(Printer* printer, | |
| const ServiceDescriptor* service, | |
| StubType type) { | |
| printer->Print("/**\n"); | |
| std::map<std::string, std::string> vars = {{"service", std::string(service->name())}}; | |
| switch (type) { | |
| case ASYNC_CLIENT_IMPL: | |
| printer->Print(vars, " * A stub to allow clients to do asynchronous rpc calls to service $service$.\n"); | |
| break; | |
| case BLOCKING_CLIENT_IMPL: | |
| printer->Print(vars, " * A stub to allow clients to do limited synchronous rpc calls to service $service$.\n"); | |
| break; | |
| case BLOCKING_V2_CLIENT_IMPL: | |
| printer->Print(vars, " * A stub to allow clients to do synchronous rpc calls to service $service$.\n"); | |
| break; | |
| case FUTURE_CLIENT_IMPL: | |
| printer->Print(vars, " * A stub to allow clients to do ListenableFuture-style rpc calls to service $service$.\n"); | |
| break; | |
| case ABSTRACT_CLASS: | |
| printer->Print(vars, " * Base class for the server implementation of the service $service$.\n"); | |
| break; | |
| default: ; | |
| // No extra description | |
| } | |
| std::vector<std::string> lines = GrpcGetDocLinesForDescriptor(service); | |
| GrpcWriteDocCommentBody(printer, lines, true); | |
| printer->Print(" */\n"); | |
| } | |
| // TODO(nmittler): Remove once protobuf includes javadoc methods in distribution. | |
| void GrpcWriteMethodDocComment(Printer* printer, | |
| const MethodDescriptor* method) { | |
| // Deviating from protobuf to avoid extraneous docs | |
| // (see https://github.com/google/protobuf/issues/1406); | |
| printer->Print("/**\n"); | |
| std::vector<std::string> lines = GrpcGetDocLinesForDescriptor(method); | |
| GrpcWriteDocCommentBody(printer, lines, true); | |
| printer->Print(" */\n"); | |
| } | |
| static void PrintMethodFields( | |
| const ServiceDescriptor* service, std::map<std::string, std::string>* vars, | |
| Printer* p, ProtoFlavor flavor) { | |
| p->Print("// Static method descriptors that strictly reflect the proto.\n"); | |
| (*vars)["service_name"] = service->name(); | |
| for (int i = 0; i < service->method_count(); ++i) { | |
| const MethodDescriptor* method = service->method(i); | |
| (*vars)["arg_in_id"] = to_string(2 * i); | |
| (*vars)["arg_out_id"] = to_string(2 * i + 1); | |
| (*vars)["method_name"] = method->name(); | |
| (*vars)["input_type"] = MessageFullJavaName(method->input_type()); | |
| (*vars)["output_type"] = MessageFullJavaName(method->output_type()); | |
| (*vars)["method_field_name"] = MethodPropertiesFieldName(method); | |
| (*vars)["method_new_field_name"] = MethodPropertiesGetterName(method); | |
| (*vars)["method_method_name"] = MethodPropertiesGetterName(method); | |
| bool client_streaming = method->client_streaming(); | |
| bool server_streaming = method->server_streaming(); | |
| if (client_streaming) { | |
| if (server_streaming) { | |
| (*vars)["method_type"] = "BIDI_STREAMING"; | |
| } else { | |
| (*vars)["method_type"] = "CLIENT_STREAMING"; | |
| } | |
| } else { | |
| if (server_streaming) { | |
| (*vars)["method_type"] = "SERVER_STREAMING"; | |
| } else { | |
| (*vars)["method_type"] = "UNARY"; | |
| } | |
| } | |
| if (flavor == ProtoFlavor::LITE) { | |
| (*vars)["ProtoUtils"] = "io.grpc.protobuf.lite.ProtoLiteUtils"; | |
| } else { | |
| (*vars)["ProtoUtils"] = "io.grpc.protobuf.ProtoUtils"; | |
| } | |
| p->Print( | |
| *vars, | |
| "private static volatile $MethodDescriptor$<$input_type$,\n" | |
| " $output_type$> $method_new_field_name$;\n" | |
| "\n" | |
| "@$RpcMethod$(\n" | |
| " fullMethodName = SERVICE_NAME + '/' + \"$method_name$\",\n" | |
| " requestType = $input_type$.class,\n" | |
| " responseType = $output_type$.class,\n" | |
| " methodType = $MethodType$.$method_type$)\n" | |
| "public static $MethodDescriptor$<$input_type$,\n" | |
| " $output_type$> $method_method_name$() {\n" | |
| " $MethodDescriptor$<$input_type$, $output_type$> $method_new_field_name$;\n" | |
| " if (($method_new_field_name$ = $service_class_name$.$method_new_field_name$) == null) {\n" | |
| " synchronized ($service_class_name$.class) {\n" | |
| " if (($method_new_field_name$ = $service_class_name$.$method_new_field_name$) == null) {\n" | |
| " $service_class_name$.$method_new_field_name$ = $method_new_field_name$ =\n" | |
| " $MethodDescriptor$.<$input_type$, $output_type$>newBuilder()\n" | |
| " .setType($MethodType$.$method_type$)\n" | |
| " .setFullMethodName(generateFullMethodName(SERVICE_NAME, \"$method_name$\"))\n"); | |
| bool safe = method->options().idempotency_level() | |
| == protobuf::MethodOptions_IdempotencyLevel_NO_SIDE_EFFECTS; | |
| if (safe) { | |
| p->Print(*vars, " .setSafe(true)\n"); | |
| } else { | |
| bool idempotent = method->options().idempotency_level() | |
| == protobuf::MethodOptions_IdempotencyLevel_IDEMPOTENT; | |
| if (idempotent) { | |
| p->Print(*vars, " .setIdempotent(true)\n"); | |
| } | |
| } | |
| p->Print( | |
| *vars, | |
| " .setSampledToLocalTracing(true)\n" | |
| " .setRequestMarshaller($ProtoUtils$.marshaller(\n" | |
| " $input_type$.getDefaultInstance()))\n" | |
| " .setResponseMarshaller($ProtoUtils$.marshaller(\n" | |
| " $output_type$.getDefaultInstance()))\n"); | |
| (*vars)["proto_method_descriptor_supplier"] | |
| = std::string(service->name()) + "MethodDescriptorSupplier"; | |
| if (flavor == ProtoFlavor::NORMAL) { | |
| p->Print( | |
| *vars, | |
| " .setSchemaDescriptor(new $proto_method_descriptor_supplier$(\"$method_name$\"))\n"); | |
| } | |
| p->Print( | |
| *vars, | |
| " .build();\n"); | |
| p->Print(*vars, | |
| " }\n" | |
| " }\n" | |
| " }\n" | |
| " return $method_new_field_name$;\n" | |
| "}\n" | |
| "\n"); | |
| } | |
| } | |
| static void PrintBindServiceMethod(const ServiceDescriptor* service, | |
| std::map<std::string, std::string>* vars, | |
| Printer* p); | |
| // Prints a StubFactory for given service / stub type. | |
| static void PrintStubFactory( | |
| const ServiceDescriptor* service, | |
| std::map<std::string, std::string>* vars, | |
| Printer* p, StubType type) { | |
| std::string stub_type_name; | |
| switch (type) { | |
| case ASYNC_CLIENT_IMPL: | |
| stub_type_name = ""; | |
| break; | |
| case FUTURE_CLIENT_IMPL: | |
| stub_type_name = "Future"; | |
| break; | |
| case BLOCKING_CLIENT_IMPL: | |
| stub_type_name = "Blocking"; | |
| break; | |
| case BLOCKING_V2_CLIENT_IMPL: | |
| stub_type_name = "BlockingV2"; | |
| break; | |
| default: | |
| GRPC_CODEGEN_FAIL << "Cannot generate StubFactory for StubType: " << type; | |
| } | |
| (*vars)["stub_full_name"] = (*vars)["service_name"] + stub_type_name + "Stub"; | |
| p->Print( | |
| *vars, | |
| "$StubFactory$<$stub_full_name$> factory =\n" | |
| " new $StubFactory$<$stub_full_name$>() {\n" | |
| " @$Override$\n" | |
| " public $stub_full_name$ newStub($Channel$ channel, $CallOptions$ callOptions) {\n" | |
| " return new $stub_full_name$(channel, callOptions);\n" | |
| " }\n" | |
| " };\n"); | |
| } | |
| // Prints a client interface or implementation class, or a server interface. | |
| static void PrintStub( | |
| const ServiceDescriptor* service, | |
| std::map<std::string, std::string>* vars, | |
| Printer* p, StubType type) { | |
| std::string service_name = std::string(service->name()); | |
| (*vars)["service_name"] = service_name; | |
| std::string stub_name = service_name; | |
| std::string stub_base_class_name = "AbstractStub"; | |
| CallType call_type; | |
| bool interface = false; | |
| switch (type) { | |
| case ASYNC_INTERFACE: | |
| call_type = ASYNC_CALL; | |
| interface = true; | |
| stub_name ="AsyncService"; | |
| break; | |
| case ASYNC_CLIENT_IMPL: | |
| call_type = ASYNC_CALL; | |
| stub_name += "Stub"; | |
| stub_base_class_name = "AbstractAsyncStub"; | |
| break; | |
| case BLOCKING_CLIENT_IMPL: | |
| call_type = BLOCKING_CALL; | |
| stub_name += "BlockingStub"; | |
| stub_base_class_name = "AbstractBlockingStub"; | |
| break; | |
| case BLOCKING_V2_CLIENT_IMPL: | |
| call_type = BLOCKING_V2_CALL; | |
| stub_name += "BlockingV2Stub"; | |
| stub_base_class_name = "AbstractBlockingStub"; | |
| break; | |
| case FUTURE_CLIENT_IMPL: | |
| call_type = FUTURE_CALL; | |
| stub_name += "FutureStub"; | |
| stub_base_class_name = "AbstractFutureStub"; | |
| break; | |
| case BLOCKING_CLIENT_INTERFACE: | |
| case FUTURE_CLIENT_INTERFACE: | |
| GRPC_CODEGEN_FAIL << "Intentionally not creating StubType: " << type; | |
| case ABSTRACT_CLASS: | |
| GRPC_CODEGEN_FAIL << "Call PrintAbstractClassStub for ABSTRACT_CLASS"; | |
| default: | |
| GRPC_CODEGEN_FAIL << "Cannot determine class name for StubType: " << type; | |
| } | |
| (*vars)["stub_name"] = stub_name; | |
| (*vars)["stub_base_class_name"] = (*vars)[stub_base_class_name]; | |
| // Class head | |
| GrpcWriteServiceDocComment(p, service, type); | |
| if (service->options().deprecated()) { | |
| p->Print(*vars, "@$Deprecated$\n"); | |
| } | |
| if (interface) { | |
| p->Print( | |
| *vars, | |
| "public interface $stub_name$ {\n"); | |
| } else { | |
| p->Print( | |
| *vars, | |
| "public static final class $stub_name$\n" | |
| " extends $stub_base_class_name$<$stub_name$> {\n"); | |
| } | |
| p->Indent(); | |
| // Constructor and build() method | |
| if (!interface) { | |
| p->Print( | |
| *vars, | |
| "private $stub_name$(\n" | |
| " $Channel$ channel, $CallOptions$ callOptions) {" | |
| "\n"); | |
| p->Indent(); | |
| p->Print("super(channel, callOptions);\n"); | |
| p->Outdent(); | |
| p->Print("}\n\n"); | |
| p->Print( | |
| *vars, | |
| "@$Override$\n" | |
| "protected $stub_name$ build(\n" | |
| " $Channel$ channel, $CallOptions$ callOptions) {" | |
| "\n"); | |
| p->Indent(); | |
| p->Print( | |
| *vars, | |
| "return new $stub_name$(channel, callOptions);\n"); | |
| p->Outdent(); | |
| p->Print("}\n"); | |
| } | |
| // RPC methods | |
| for (int i = 0; i < service->method_count(); ++i) { | |
| const MethodDescriptor* method = service->method(i); | |
| (*vars)["input_type"] = MessageFullJavaName(method->input_type()); | |
| (*vars)["output_type"] = MessageFullJavaName(method->output_type()); | |
| bool client_streaming = method->client_streaming(); | |
| bool server_streaming = method->server_streaming(); | |
| bool mangle_object_methods = (call_type == BLOCKING_V2_CALL && client_streaming) | |
| || (call_type == BLOCKING_CALL && client_streaming && server_streaming); | |
| (*vars)["lower_method_name"] = LowerMethodName(method, mangle_object_methods); | |
| (*vars)["method_method_name"] = MethodPropertiesGetterName(method); | |
| if (call_type == BLOCKING_CALL && client_streaming) { | |
| // Blocking client interface with client streaming is not available | |
| continue; | |
| } | |
| if (call_type == FUTURE_CALL && (client_streaming || server_streaming)) { | |
| // Future interface doesn't support streaming. | |
| continue; | |
| } | |
| // Method signature | |
| p->Print("\n"); | |
| GrpcWriteMethodDocComment(p, method); | |
| if (method->options().deprecated()) { | |
| p->Print(*vars, "@$Deprecated$\n"); | |
| } | |
| if ((call_type == BLOCKING_CALL && client_streaming && server_streaming) | |
| || (call_type == BLOCKING_V2_CALL && (client_streaming || server_streaming))) { | |
| p->Print(*vars, "@io.grpc.ExperimentalApi(\"https://github.com/grpc/grpc-java/issues/10918\")\n"); | |
| } | |
| if (!interface) { | |
| p->Print("public "); | |
| } else { | |
| p->Print("default "); | |
| } | |
| switch (call_type) { | |
| case BLOCKING_CALL: | |
| GRPC_CODEGEN_CHECK(!client_streaming) | |
| << "Blocking client interface with client streaming is unavailable"; | |
| if (client_streaming && server_streaming) { | |
| p->Print( | |
| *vars, | |
| "$BlockingClientCall$<$input_type$, $output_type$>\n" | |
| " $lower_method_name$()"); | |
| } else if (server_streaming) { | |
| // Server streaming | |
| p->Print( | |
| *vars, | |
| "$Iterator$<$output_type$> $lower_method_name$(\n" | |
| " $input_type$ request)"); | |
| } else { | |
| // Simple RPC | |
| p->Print( | |
| *vars, | |
| "$output_type$ $lower_method_name$($input_type$ request)"); | |
| } | |
| break; | |
| case BLOCKING_V2_CALL: | |
| if (client_streaming) { // Both Bidi and Client Streaming | |
| p->Print( | |
| *vars, | |
| "$BlockingClientCall$<$input_type$, $output_type$>\n" | |
| " $lower_method_name$()"); | |
| } else if (server_streaming) { | |
| // Server streaming | |
| p->Print( | |
| *vars, | |
| "$BlockingClientCall$<?, $output_type$>\n" | |
| " $lower_method_name$($input_type$ request)"); | |
| } else { | |
| // Simple RPC | |
| (*vars)["throws_decl"] = " throws io.grpc.StatusException"; | |
| p->Print( | |
| *vars, | |
| "$output_type$ $lower_method_name$($input_type$ request)$throws_decl$"); | |
| } | |
| break; | |
| case ASYNC_CALL: | |
| if (client_streaming) { | |
| // Bidirectional streaming or client streaming | |
| p->Print( | |
| *vars, | |
| "$StreamObserver$<$input_type$> $lower_method_name$(\n" | |
| " $StreamObserver$<$output_type$> responseObserver)"); | |
| } else { | |
| // Server streaming or simple RPC | |
| p->Print( | |
| *vars, | |
| "void $lower_method_name$($input_type$ request,\n" | |
| " $StreamObserver$<$output_type$> responseObserver)"); | |
| } | |
| break; | |
| case FUTURE_CALL: | |
| GRPC_CODEGEN_CHECK(!client_streaming && !server_streaming) | |
| << "Future interface doesn't support streaming. " | |
| << "client_streaming=" << client_streaming << ", " | |
| << "server_streaming=" << server_streaming; | |
| p->Print( | |
| *vars, | |
| "$ListenableFuture$<$output_type$> $lower_method_name$(\n" | |
| " $input_type$ request)"); | |
| break; | |
| } | |
| // Method body. | |
| p->Print(" {\n"); | |
| p->Indent(); | |
| if (interface && call_type == ASYNC_CALL) { | |
| // NB: Skipping validation of service methods. If something is wrong, we wouldn't get to | |
| // this point as compiler would return errors when generating service interface. | |
| if (client_streaming) { | |
| p->Print( | |
| *vars, | |
| "return io.grpc.stub.ServerCalls.asyncUnimplementedStreamingCall(" | |
| "$method_method_name$(), responseObserver);\n"); | |
| } else { | |
| p->Print( | |
| *vars, | |
| "io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(" | |
| "$method_method_name$(), responseObserver);\n"); | |
| } | |
| } else if (!interface) { | |
| switch (call_type) { | |
| case BLOCKING_CALL: | |
| GRPC_CODEGEN_CHECK(!client_streaming) | |
| << "Blocking client and bidi streaming interface are not available"; | |
| if (server_streaming) { | |
| (*vars)["calls_method"] = "io.grpc.stub.ClientCalls.blockingServerStreamingCall"; | |
| (*vars)["params"] = "request"; | |
| } else { | |
| (*vars)["calls_method"] = "io.grpc.stub.ClientCalls.blockingUnaryCall"; | |
| (*vars)["params"] = "request"; | |
| } | |
| p->Print( | |
| *vars, | |
| "return $calls_method$(\n" | |
| " getChannel(), $method_method_name$(), getCallOptions(), $params$);\n"); | |
| break; | |
| case BLOCKING_V2_CALL: | |
| if (client_streaming) { // client and bidi streaming | |
| if (server_streaming) { | |
| (*vars)["calls_method"] = "io.grpc.stub.ClientCalls.blockingBidiStreamingCall"; | |
| } else { | |
| (*vars)["calls_method"] = "io.grpc.stub.ClientCalls.blockingClientStreamingCall"; | |
| } | |
| p->Print( | |
| *vars, | |
| "return $calls_method$(\n" | |
| " getChannel(), $method_method_name$(), getCallOptions());\n"); | |
| } else { // server streaming and unary | |
| (*vars)["params"] = "request"; | |
| if (server_streaming) { | |
| (*vars)["calls_method"] = "io.grpc.stub.ClientCalls.blockingV2ServerStreamingCall"; | |
| } else { | |
| (*vars)["calls_method"] = "io.grpc.stub.ClientCalls.blockingV2UnaryCall"; | |
| (*vars)["throws_decl"] = " throws io.grpc.StatusException"; | |
| } | |
| p->Print( | |
| *vars, | |
| "return $calls_method$(\n" | |
| " getChannel(), $method_method_name$(), getCallOptions(), $params$);\n"); | |
| } | |
| break; | |
| case ASYNC_CALL: | |
| if (server_streaming) { | |
| if (client_streaming) { | |
| (*vars)["calls_method"] = "io.grpc.stub.ClientCalls.asyncBidiStreamingCall"; | |
| (*vars)["params"] = "responseObserver"; | |
| } else { | |
| (*vars)["calls_method"] = "io.grpc.stub.ClientCalls.asyncServerStreamingCall"; | |
| (*vars)["params"] = "request, responseObserver"; | |
| } | |
| } else { | |
| if (client_streaming) { | |
| (*vars)["calls_method"] = "io.grpc.stub.ClientCalls.asyncClientStreamingCall"; | |
| (*vars)["params"] = "responseObserver"; | |
| } else { | |
| (*vars)["calls_method"] = "io.grpc.stub.ClientCalls.asyncUnaryCall"; | |
| (*vars)["params"] = "request, responseObserver"; | |
| } | |
| } | |
| (*vars)["last_line_prefix"] = client_streaming ? "return " : ""; | |
| p->Print( | |
| *vars, | |
| "$last_line_prefix$$calls_method$(\n" | |
| " getChannel().newCall($method_method_name$(), getCallOptions()), $params$);\n"); | |
| break; | |
| case FUTURE_CALL: | |
| GRPC_CODEGEN_CHECK(!client_streaming && !server_streaming) | |
| << "Future interface doesn't support streaming. " | |
| << "client_streaming=" << client_streaming << ", " | |
| << "server_streaming=" << server_streaming; | |
| (*vars)["calls_method"] = "io.grpc.stub.ClientCalls.futureUnaryCall"; | |
| p->Print( | |
| *vars, | |
| "return $calls_method$(\n" | |
| " getChannel().newCall($method_method_name$(), getCallOptions()), request);\n"); | |
| break; | |
| } | |
| } else { | |
| GRPC_CODEGEN_FAIL << "Do not create Stub interfaces"; | |
| } | |
| p->Outdent(); | |
| p->Print("}\n"); | |
| } | |
| p->Outdent(); | |
| p->Print("}\n\n"); | |
| } | |
| static void PrintAbstractClassStub( | |
| const ServiceDescriptor* service, | |
| std::map<std::string, std::string>* vars, | |
| Printer* p) { | |
| (*vars)["service_name"] = service->name(); | |
| GrpcWriteServiceDocComment(p, service, ABSTRACT_CLASS); | |
| if (service->options().deprecated()) { | |
| p->Print(*vars, "@$Deprecated$\n"); | |
| } | |
| p->Print( | |
| *vars, | |
| "public static abstract class $service_name$ImplBase\n" | |
| " implements $BindableService$, AsyncService {\n" | |
| "\n" | |
| " @$Override$ public final $ServerServiceDefinition$ bindService() {\n" | |
| " return $service_class_name$.bindService(this);\n" | |
| " }\n" | |
| "}\n\n"); | |
| } | |
| static bool CompareMethodClientStreaming(const MethodDescriptor* method1, | |
| const MethodDescriptor* method2) | |
| { | |
| return method1->client_streaming() < method2->client_streaming(); | |
| } | |
| // Place all method invocations into a single class to reduce memory footprint | |
| // on Android. | |
| static void PrintMethodHandlerClass(const ServiceDescriptor* service, | |
| std::map<std::string, std::string>* vars, | |
| Printer* p) { | |
| // Sort method ids based on client_streaming() so switch tables are compact. | |
| std::vector<const MethodDescriptor*> sorted_methods(service->method_count()); | |
| for (int i = 0; i < service->method_count(); ++i) { | |
| sorted_methods[i] = service->method(i); | |
| } | |
| stable_sort(sorted_methods.begin(), sorted_methods.end(), | |
| CompareMethodClientStreaming); | |
| for (size_t i = 0; i < sorted_methods.size(); i++) { | |
| const MethodDescriptor* method = sorted_methods[i]; | |
| (*vars)["method_id"] = to_string(i); | |
| (*vars)["method_id_name"] = MethodIdFieldName(method); | |
| p->Print( | |
| *vars, | |
| "private static final int $method_id_name$ = $method_id$;\n"); | |
| } | |
| p->Print("\n"); | |
| p->Print( | |
| *vars, | |
| "private static final class MethodHandlers<Req, Resp> implements\n" | |
| " io.grpc.stub.ServerCalls.UnaryMethod<Req, Resp>,\n" | |
| " io.grpc.stub.ServerCalls.ServerStreamingMethod<Req, Resp>,\n" | |
| " io.grpc.stub.ServerCalls.ClientStreamingMethod<Req, Resp>,\n" | |
| " io.grpc.stub.ServerCalls.BidiStreamingMethod<Req, Resp> {\n" | |
| " private final AsyncService serviceImpl;\n" | |
| " private final int methodId;\n" | |
| "\n" | |
| " MethodHandlers(AsyncService serviceImpl, int methodId) {\n" | |
| " this.serviceImpl = serviceImpl;\n" | |
| " this.methodId = methodId;\n" | |
| " }\n\n"); | |
| p->Indent(); | |
| p->Print( | |
| *vars, | |
| "@$Override$\n" | |
| "@java.lang.SuppressWarnings(\"unchecked\")\n" | |
| "public void invoke(Req request, $StreamObserver$<Resp> responseObserver) {\n" | |
| " switch (methodId) {\n"); | |
| p->Indent(); | |
| p->Indent(); | |
| for (int i = 0; i < service->method_count(); ++i) { | |
| const MethodDescriptor* method = service->method(i); | |
| if (method->client_streaming()) { | |
| continue; | |
| } | |
| (*vars)["method_id_name"] = MethodIdFieldName(method); | |
| (*vars)["lower_method_name"] = LowerMethodName(method); | |
| (*vars)["input_type"] = MessageFullJavaName(method->input_type()); | |
| (*vars)["output_type"] = MessageFullJavaName(method->output_type()); | |
| p->Print( | |
| *vars, | |
| "case $method_id_name$:\n" | |
| " serviceImpl.$lower_method_name$(($input_type$) request,\n" | |
| " ($StreamObserver$<$output_type$>) responseObserver);\n" | |
| " break;\n"); | |
| } | |
| p->Print("default:\n" | |
| " throw new AssertionError();\n"); | |
| p->Outdent(); | |
| p->Outdent(); | |
| p->Print(" }\n" | |
| "}\n\n"); | |
| p->Print( | |
| *vars, | |
| "@$Override$\n" | |
| "@java.lang.SuppressWarnings(\"unchecked\")\n" | |
| "public $StreamObserver$<Req> invoke(\n" | |
| " $StreamObserver$<Resp> responseObserver) {\n" | |
| " switch (methodId) {\n"); | |
| p->Indent(); | |
| p->Indent(); | |
| for (int i = 0; i < service->method_count(); ++i) { | |
| const MethodDescriptor* method = service->method(i); | |
| if (!method->client_streaming()) { | |
| continue; | |
| } | |
| (*vars)["method_id_name"] = MethodIdFieldName(method); | |
| (*vars)["lower_method_name"] = LowerMethodName(method); | |
| (*vars)["input_type"] = MessageFullJavaName(method->input_type()); | |
| (*vars)["output_type"] = MessageFullJavaName(method->output_type()); | |
| p->Print( | |
| *vars, | |
| "case $method_id_name$:\n" | |
| " return ($StreamObserver$<Req>) serviceImpl.$lower_method_name$(\n" | |
| " ($StreamObserver$<$output_type$>) responseObserver);\n"); | |
| } | |
| p->Print("default:\n" | |
| " throw new AssertionError();\n"); | |
| p->Outdent(); | |
| p->Outdent(); | |
| p->Print(" }\n" | |
| "}\n"); | |
| p->Outdent(); | |
| p->Print("}\n\n"); | |
| } | |
| static void PrintGetServiceDescriptorMethod(const ServiceDescriptor* service, | |
| std::map<std::string, std::string>* vars, | |
| Printer* p, | |
| ProtoFlavor flavor) { | |
| std::string service_name = std::string(service->name()); | |
| (*vars)["service_name"] = service_name; | |
| if (flavor == ProtoFlavor::NORMAL) { | |
| (*vars)["proto_base_descriptor_supplier"] = service_name + "BaseDescriptorSupplier"; | |
| (*vars)["proto_file_descriptor_supplier"] = service_name + "FileDescriptorSupplier"; | |
| (*vars)["proto_method_descriptor_supplier"] = service_name + "MethodDescriptorSupplier"; | |
| (*vars)["proto_class_name"] = protobuf::compiler::java::QualifiedClassName(service->file()); | |
| p->Print( | |
| *vars, | |
| "private static abstract class $proto_base_descriptor_supplier$\n" | |
| " implements $ProtoFileDescriptorSupplier$, $ProtoServiceDescriptorSupplier$ {\n" | |
| " $proto_base_descriptor_supplier$() {}\n" | |
| "\n" | |
| " @$Override$\n" | |
| " public com.google.protobuf.Descriptors.FileDescriptor getFileDescriptor() {\n" | |
| " return $proto_class_name$.getDescriptor();\n" | |
| " }\n" | |
| "\n" | |
| " @$Override$\n" | |
| " public com.google.protobuf.Descriptors.ServiceDescriptor getServiceDescriptor() {\n" | |
| " return getFileDescriptor().findServiceByName(\"$service_name$\");\n" | |
| " }\n" | |
| "}\n" | |
| "\n" | |
| "private static final class $proto_file_descriptor_supplier$\n" | |
| " extends $proto_base_descriptor_supplier$ {\n" | |
| " $proto_file_descriptor_supplier$() {}\n" | |
| "}\n" | |
| "\n" | |
| "private static final class $proto_method_descriptor_supplier$\n" | |
| " extends $proto_base_descriptor_supplier$\n" | |
| " implements $ProtoMethodDescriptorSupplier$ {\n" | |
| " private final $String$ methodName;\n" | |
| "\n" | |
| " $proto_method_descriptor_supplier$($String$ methodName) {\n" | |
| " this.methodName = methodName;\n" | |
| " }\n" | |
| "\n" | |
| " @$Override$\n" | |
| " public com.google.protobuf.Descriptors.MethodDescriptor getMethodDescriptor() {\n" | |
| " return getServiceDescriptor().findMethodByName(methodName);\n" | |
| " }\n" | |
| "}\n\n"); | |
| } | |
| p->Print( | |
| *vars, | |
| "private static volatile $ServiceDescriptor$ serviceDescriptor;\n\n"); | |
| p->Print( | |
| *vars, | |
| "public static $ServiceDescriptor$ getServiceDescriptor() {\n"); | |
| p->Indent(); | |
| p->Print( | |
| *vars, | |
| "$ServiceDescriptor$ result = serviceDescriptor;\n"); | |
| p->Print("if (result == null) {\n"); | |
| p->Indent(); | |
| p->Print( | |
| *vars, | |
| "synchronized ($service_class_name$.class) {\n"); | |
| p->Indent(); | |
| p->Print("result = serviceDescriptor;\n"); | |
| p->Print("if (result == null) {\n"); | |
| p->Indent(); | |
| p->Print( | |
| *vars, | |
| "serviceDescriptor = result = $ServiceDescriptor$.newBuilder(SERVICE_NAME)"); | |
| p->Indent(); | |
| p->Indent(); | |
| if (flavor == ProtoFlavor::NORMAL) { | |
| p->Print( | |
| *vars, | |
| "\n.setSchemaDescriptor(new $proto_file_descriptor_supplier$())"); | |
| } | |
| for (int i = 0; i < service->method_count(); ++i) { | |
| const MethodDescriptor* method = service->method(i); | |
| (*vars)["method_method_name"] = MethodPropertiesGetterName(method); | |
| p->Print(*vars, "\n.addMethod($method_method_name$())"); | |
| } | |
| p->Print("\n.build();\n"); | |
| p->Outdent(); | |
| p->Outdent(); | |
| p->Outdent(); | |
| p->Print("}\n"); | |
| p->Outdent(); | |
| p->Print("}\n"); | |
| p->Outdent(); | |
| p->Print("}\n"); | |
| p->Print("return result;\n"); | |
| p->Outdent(); | |
| p->Print("}\n"); | |
| } | |
| static void PrintBindServiceMethod(const ServiceDescriptor* service, | |
| std::map<std::string, std::string>* vars, | |
| Printer* p) { | |
| (*vars)["service_name"] = service->name(); | |
| p->Print(*vars, | |
| "public static final io.grpc.ServerServiceDefinition " | |
| "bindService(AsyncService service) {\n"); | |
| p->Indent(); | |
| p->Print(*vars, | |
| "return " | |
| "$ServerServiceDefinition$.builder(getServiceDescriptor())\n"); | |
| p->Indent(); | |
| p->Indent(); | |
| for (int i = 0; i < service->method_count(); ++i) { | |
| const MethodDescriptor* method = service->method(i); | |
| (*vars)["lower_method_name"] = LowerMethodName(method); | |
| (*vars)["method_method_name"] = MethodPropertiesGetterName(method); | |
| (*vars)["input_type"] = MessageFullJavaName(method->input_type()); | |
| (*vars)["output_type"] = MessageFullJavaName(method->output_type()); | |
| (*vars)["method_id_name"] = MethodIdFieldName(method); | |
| bool client_streaming = method->client_streaming(); | |
| bool server_streaming = method->server_streaming(); | |
| if (client_streaming) { | |
| if (server_streaming) { | |
| (*vars)["calls_method"] = "io.grpc.stub.ServerCalls.asyncBidiStreamingCall"; | |
| } else { | |
| (*vars)["calls_method"] = "io.grpc.stub.ServerCalls.asyncClientStreamingCall"; | |
| } | |
| } else { | |
| if (server_streaming) { | |
| (*vars)["calls_method"] = "io.grpc.stub.ServerCalls.asyncServerStreamingCall"; | |
| } else { | |
| (*vars)["calls_method"] = "io.grpc.stub.ServerCalls.asyncUnaryCall"; | |
| } | |
| } | |
| p->Print(*vars, ".addMethod(\n"); | |
| p->Indent(); | |
| p->Print( | |
| *vars, | |
| "$method_method_name$(),\n" | |
| "$calls_method$(\n"); | |
| p->Indent(); | |
| p->Print( | |
| *vars, | |
| "new MethodHandlers<\n" | |
| " $input_type$,\n" | |
| " $output_type$>(\n" | |
| " service, $method_id_name$)))\n"); | |
| p->Outdent(); | |
| p->Outdent(); | |
| } | |
| p->Print(".build();\n"); | |
| p->Outdent(); | |
| p->Outdent(); | |
| p->Outdent(); | |
| p->Print("}\n\n"); | |
| } | |
| static void PrintService(const ServiceDescriptor* service, | |
| std::map<std::string, std::string>* vars, | |
| Printer* p, | |
| ProtoFlavor flavor, | |
| bool disable_version, | |
| GeneratedAnnotation generated_annotation) { | |
| (*vars)["service_name"] = service->name(); | |
| (*vars)["file_name"] = absl::Utf8SafeCEscape(service->file()->name()); | |
| (*vars)["service_class_name"] = ServiceClassName(service); | |
| (*vars)["grpc_version"] = ""; | |
| #ifdef GRPC_VERSION | |
| if (!disable_version) { | |
| (*vars)["grpc_version"] = " (version " XSTR(GRPC_VERSION) ")"; | |
| } | |
| #endif | |
| // TODO(nmittler): Replace with WriteServiceDocComment once included by protobuf distro. | |
| GrpcWriteServiceDocComment(p, service, NONE); | |
| if (generated_annotation == GeneratedAnnotation::JAVAX) { | |
| p->Print( | |
| *vars, | |
| "@javax.annotation.Generated(\n" | |
| " value = \"by gRPC proto compiler$grpc_version$\",\n" | |
| " comments = \"Source: $file_name$\")\n" | |
| "@$GrpcGenerated$\n"); | |
| } else { // GeneratedAnnotation::OMIT | |
| p->Print( | |
| *vars, | |
| "@$GrpcGenerated$\n"); | |
| } | |
| if (service->options().deprecated()) { | |
| p->Print(*vars, "@$Deprecated$\n"); | |
| } | |
| p->Print( | |
| *vars, | |
| "public final class $service_class_name$ {\n\n"); | |
| p->Indent(); | |
| p->Print( | |
| *vars, | |
| "private $service_class_name$() {}\n\n"); | |
| p->Print( | |
| *vars, | |
| "public static final $String$ SERVICE_NAME = " | |
| "\"$Package$$service_name$\";\n\n"); | |
| PrintMethodFields(service, vars, p, flavor); | |
| // TODO(nmittler): Replace with WriteDocComment once included by protobuf distro. | |
| GrpcWriteDocComment(p, " Creates a new async stub that supports all call types for the service"); | |
| p->Print( | |
| *vars, | |
| "public static $service_name$Stub newStub($Channel$ channel) {\n"); | |
| p->Indent(); | |
| PrintStubFactory(service, vars, p, ASYNC_CLIENT_IMPL); | |
| p->Print(*vars, "return $service_name$Stub.newStub(factory, channel);\n"); | |
| p->Outdent(); | |
| p->Print("}\n\n"); | |
| // TODO(nmittler): Replace with WriteDocComment once included by protobuf distro. | |
| GrpcWriteDocComment(p, " Creates a new blocking-style stub that supports all types of calls " | |
| "on the service"); | |
| p->Print( | |
| *vars, | |
| "public static $service_name$BlockingV2Stub newBlockingV2Stub(\n" | |
| " $Channel$ channel) {\n"); | |
| p->Indent(); | |
| PrintStubFactory(service, vars, p, BLOCKING_V2_CLIENT_IMPL); | |
| p->Print( | |
| *vars, | |
| "return $service_name$BlockingV2Stub.newStub(factory, channel);\n"); | |
| p->Outdent(); | |
| p->Print("}\n\n"); | |
| // TODO(nmittler): Replace with WriteDocComment once included by protobuf distro. | |
| GrpcWriteDocComment(p, " Creates a new blocking-style stub that supports unary and streaming " | |
| "output calls on the service"); | |
| p->Print( | |
| *vars, | |
| "public static $service_name$BlockingStub newBlockingStub(\n" | |
| " $Channel$ channel) {\n"); | |
| p->Indent(); | |
| PrintStubFactory(service, vars, p, BLOCKING_CLIENT_IMPL); | |
| p->Print( | |
| *vars, | |
| "return $service_name$BlockingStub.newStub(factory, channel);\n"); | |
| p->Outdent(); | |
| p->Print("}\n\n"); | |
| // TODO(nmittler): Replace with WriteDocComment once included by protobuf distro. | |
| GrpcWriteDocComment(p, " Creates a new ListenableFuture-style stub that supports unary calls " | |
| "on the service"); | |
| p->Print( | |
| *vars, | |
| "public static $service_name$FutureStub newFutureStub(\n" | |
| " $Channel$ channel) {\n"); | |
| p->Indent(); | |
| PrintStubFactory(service, vars, p, FUTURE_CLIENT_IMPL); | |
| p->Print( | |
| *vars, | |
| "return $service_name$FutureStub.newStub(factory, channel);\n"); | |
| p->Outdent(); | |
| p->Print("}\n\n"); | |
| PrintStub(service, vars, p, ASYNC_INTERFACE); | |
| PrintAbstractClassStub(service, vars, p); | |
| PrintStub(service, vars, p, ASYNC_CLIENT_IMPL); | |
| PrintStub(service, vars, p, BLOCKING_V2_CLIENT_IMPL); | |
| PrintStub(service, vars, p, BLOCKING_CLIENT_IMPL); | |
| PrintStub(service, vars, p, FUTURE_CLIENT_IMPL); | |
| PrintMethodHandlerClass(service, vars, p); | |
| PrintBindServiceMethod(service, vars, p); | |
| PrintGetServiceDescriptorMethod(service, vars, p, flavor); | |
| p->Outdent(); | |
| p->Print("}\n"); | |
| } | |
| void PrintImports(Printer* p) { | |
| p->Print( | |
| "import static " | |
| "io.grpc.MethodDescriptor.generateFullMethodName;\n\n"); | |
| } | |
| void GenerateService(const ServiceDescriptor* service, | |
| protobuf::io::ZeroCopyOutputStream* out, | |
| ProtoFlavor flavor, | |
| bool disable_version, | |
| GeneratedAnnotation generated_annotation) { | |
| // All non-generated classes must be referred by fully qualified names to | |
| // avoid collision with generated classes. | |
| std::map<std::string, std::string> vars; | |
| vars["String"] = "java.lang.String"; | |
| vars["Deprecated"] = "java.lang.Deprecated"; | |
| vars["Override"] = "java.lang.Override"; | |
| vars["Channel"] = "io.grpc.Channel"; | |
| vars["CallOptions"] = "io.grpc.CallOptions"; | |
| vars["MethodType"] = "io.grpc.MethodDescriptor.MethodType"; | |
| vars["ServerMethodDefinition"] = | |
| "io.grpc.ServerMethodDefinition"; | |
| vars["BindableService"] = "io.grpc.BindableService"; | |
| vars["ServerServiceDefinition"] = | |
| "io.grpc.ServerServiceDefinition"; | |
| vars["ServiceDescriptor"] = | |
| "io.grpc.ServiceDescriptor"; | |
| vars["ProtoFileDescriptorSupplier"] = | |
| "io.grpc.protobuf.ProtoFileDescriptorSupplier"; | |
| vars["ProtoServiceDescriptorSupplier"] = | |
| "io.grpc.protobuf.ProtoServiceDescriptorSupplier"; | |
| vars["ProtoMethodDescriptorSupplier"] = | |
| "io.grpc.protobuf.ProtoMethodDescriptorSupplier"; | |
| vars["AbstractStub"] = "io.grpc.stub.AbstractStub"; | |
| vars["AbstractAsyncStub"] = "io.grpc.stub.AbstractAsyncStub"; | |
| vars["AbstractFutureStub"] = "io.grpc.stub.AbstractFutureStub"; | |
| vars["AbstractBlockingStub"] = "io.grpc.stub.AbstractBlockingStub"; | |
| vars["StubFactory"] = "io.grpc.stub.AbstractStub.StubFactory"; | |
| vars["RpcMethod"] = "io.grpc.stub.annotations.RpcMethod"; | |
| vars["MethodDescriptor"] = "io.grpc.MethodDescriptor"; | |
| vars["StreamObserver"] = "io.grpc.stub.StreamObserver"; | |
| vars["BlockingClientCall"] = "io.grpc.stub.BlockingClientCall"; | |
| vars["Iterator"] = "java.util.Iterator"; | |
| vars["GrpcGenerated"] = "io.grpc.stub.annotations.GrpcGenerated"; | |
| vars["ListenableFuture"] = | |
| "com.google.common.util.concurrent.ListenableFuture"; | |
| Printer printer(out, '$'); | |
| std::string package_name = ServiceJavaPackage(service->file()); | |
| if (!package_name.empty()) { | |
| printer.Print( | |
| "package $package_name$;\n\n", | |
| "package_name", package_name); | |
| } | |
| PrintImports(&printer); | |
| // Package string is used to fully qualify method names. | |
| vars["Package"] = service->file()->package(); | |
| if (!vars["Package"].empty()) { | |
| vars["Package"].append("."); | |
| } | |
| PrintService(service, &vars, &printer, flavor, disable_version, generated_annotation); | |
| } | |
| std::string ServiceJavaPackage(const FileDescriptor* file) { | |
| std::string result = protobuf::compiler::java::QualifiedClassName(file); | |
| size_t last_dot_pos = result.find_last_of('.'); | |
| if (last_dot_pos != std::string::npos) { | |
| result.resize(last_dot_pos); | |
| } else { | |
| result = ""; | |
| } | |
| return result; | |
| } | |
| std::string ServiceClassName(const ServiceDescriptor* service) { | |
| return std::string(service->name()) + "Grpc"; | |
| } | |
| } // namespace java_grpc_generator |