1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // https://developers.google.com/protocol-buffers/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 //     * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 //     * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 // Author: kenton@google.com (Kenton Varda)
32 //  Based on original Protocol Buffers design by
33 //  Sanjay Ghemawat, Jeff Dean, and others.
34 
35 #include <google/protobuf/compiler/java/java_service.h>
36 
37 #include <google/protobuf/compiler/java/java_context.h>
38 #include <google/protobuf/compiler/java/java_doc_comment.h>
39 #include <google/protobuf/compiler/java/java_helpers.h>
40 #include <google/protobuf/compiler/java/java_name_resolver.h>
41 #include <google/protobuf/io/printer.h>
42 #include <google/protobuf/stubs/strutil.h>
43 
44 
45 namespace google {
46 namespace protobuf {
47 namespace compiler {
48 namespace java {
49 
ServiceGenerator(const ServiceDescriptor * descriptor)50 ServiceGenerator::ServiceGenerator(const ServiceDescriptor* descriptor)
51     : descriptor_(descriptor) {}
52 
~ServiceGenerator()53 ServiceGenerator::~ServiceGenerator() {}
54 
55 // ===================================================================
ImmutableServiceGenerator(const ServiceDescriptor * descriptor,Context * context)56 ImmutableServiceGenerator::ImmutableServiceGenerator(
57     const ServiceDescriptor* descriptor, Context* context)
58     : ServiceGenerator(descriptor),
59       context_(context),
60       name_resolver_(context->GetNameResolver()) {}
61 
~ImmutableServiceGenerator()62 ImmutableServiceGenerator::~ImmutableServiceGenerator() {}
63 
Generate(io::Printer * printer)64 void ImmutableServiceGenerator::Generate(io::Printer* printer) {
65   bool is_own_file = IsOwnFile(descriptor_, /* immutable = */ true);
66   WriteServiceDocComment(printer, descriptor_);
67   MaybePrintGeneratedAnnotation(context_, printer, descriptor_,
68                                 /* immutable = */ true);
69   printer->Print(
70       "public $static$ abstract class $classname$\n"
71       "    implements com.google.protobuf.Service {\n",
72       "static", is_own_file ? "" : "static", "classname", descriptor_->name());
73   printer->Indent();
74 
75   printer->Print("protected $classname$() {}\n\n", "classname",
76                  descriptor_->name());
77 
78   GenerateInterface(printer);
79 
80   GenerateNewReflectiveServiceMethod(printer);
81   GenerateNewReflectiveBlockingServiceMethod(printer);
82 
83   GenerateAbstractMethods(printer);
84 
85   // Generate getDescriptor() and getDescriptorForType().
86   printer->Print(
87       "public static final\n"
88       "    com.google.protobuf.Descriptors.ServiceDescriptor\n"
89       "    getDescriptor() {\n"
90       "  return $file$.getDescriptor().getServices().get($index$);\n"
91       "}\n",
92       "file", name_resolver_->GetImmutableClassName(descriptor_->file()),
93       "index", StrCat(descriptor_->index()));
94   GenerateGetDescriptorForType(printer);
95 
96   // Generate more stuff.
97   GenerateCallMethod(printer);
98   GenerateGetPrototype(REQUEST, printer);
99   GenerateGetPrototype(RESPONSE, printer);
100   GenerateStub(printer);
101   GenerateBlockingStub(printer);
102 
103   // Add an insertion point.
104   printer->Print(
105       "\n"
106       "// @@protoc_insertion_point(class_scope:$full_name$)\n",
107       "full_name", descriptor_->full_name());
108 
109   printer->Outdent();
110   printer->Print("}\n\n");
111 }
112 
GenerateGetDescriptorForType(io::Printer * printer)113 void ImmutableServiceGenerator::GenerateGetDescriptorForType(
114     io::Printer* printer) {
115   printer->Print(
116       "public final com.google.protobuf.Descriptors.ServiceDescriptor\n"
117       "    getDescriptorForType() {\n"
118       "  return getDescriptor();\n"
119       "}\n");
120 }
121 
GenerateInterface(io::Printer * printer)122 void ImmutableServiceGenerator::GenerateInterface(io::Printer* printer) {
123   printer->Print("public interface Interface {\n");
124   printer->Indent();
125   GenerateAbstractMethods(printer);
126   printer->Outdent();
127   printer->Print("}\n\n");
128 }
129 
GenerateNewReflectiveServiceMethod(io::Printer * printer)130 void ImmutableServiceGenerator::GenerateNewReflectiveServiceMethod(
131     io::Printer* printer) {
132   printer->Print(
133       "public static com.google.protobuf.Service newReflectiveService(\n"
134       "    final Interface impl) {\n"
135       "  return new $classname$() {\n",
136       "classname", descriptor_->name());
137   printer->Indent();
138   printer->Indent();
139 
140   for (int i = 0; i < descriptor_->method_count(); i++) {
141     const MethodDescriptor* method = descriptor_->method(i);
142     printer->Print("@java.lang.Override\n");
143     GenerateMethodSignature(printer, method, IS_CONCRETE);
144     printer->Print(
145         " {\n"
146         "  impl.$method$(controller, request, done);\n"
147         "}\n\n",
148         "method", UnderscoresToCamelCase(method));
149   }
150 
151   printer->Outdent();
152   printer->Print("};\n");
153   printer->Outdent();
154   printer->Print("}\n\n");
155 }
156 
GenerateNewReflectiveBlockingServiceMethod(io::Printer * printer)157 void ImmutableServiceGenerator::GenerateNewReflectiveBlockingServiceMethod(
158     io::Printer* printer) {
159   printer->Print(
160       "public static com.google.protobuf.BlockingService\n"
161       "    newReflectiveBlockingService(final BlockingInterface impl) {\n"
162       "  return new com.google.protobuf.BlockingService() {\n");
163   printer->Indent();
164   printer->Indent();
165 
166   GenerateGetDescriptorForType(printer);
167 
168   GenerateCallBlockingMethod(printer);
169   GenerateGetPrototype(REQUEST, printer);
170   GenerateGetPrototype(RESPONSE, printer);
171 
172   printer->Outdent();
173   printer->Print("};\n");
174   printer->Outdent();
175   printer->Print("}\n\n");
176 }
177 
GenerateAbstractMethods(io::Printer * printer)178 void ImmutableServiceGenerator::GenerateAbstractMethods(io::Printer* printer) {
179   for (int i = 0; i < descriptor_->method_count(); i++) {
180     const MethodDescriptor* method = descriptor_->method(i);
181     WriteMethodDocComment(printer, method);
182     GenerateMethodSignature(printer, method, IS_ABSTRACT);
183     printer->Print(";\n\n");
184   }
185 }
186 
GetOutput(const MethodDescriptor * method)187 std::string ImmutableServiceGenerator::GetOutput(
188     const MethodDescriptor* method) {
189   return name_resolver_->GetImmutableClassName(method->output_type());
190 }
191 
GenerateCallMethod(io::Printer * printer)192 void ImmutableServiceGenerator::GenerateCallMethod(io::Printer* printer) {
193   printer->Print(
194       "\n"
195       "public final void callMethod(\n"
196       "    com.google.protobuf.Descriptors.MethodDescriptor method,\n"
197       "    com.google.protobuf.RpcController controller,\n"
198       "    com.google.protobuf.Message request,\n"
199       "    com.google.protobuf.RpcCallback<\n"
200       "      com.google.protobuf.Message> done) {\n"
201       "  if (method.getService() != getDescriptor()) {\n"
202       "    throw new java.lang.IllegalArgumentException(\n"
203       "      \"Service.callMethod() given method descriptor for wrong \" +\n"
204       "      \"service type.\");\n"
205       "  }\n"
206       "  switch(method.getIndex()) {\n");
207   printer->Indent();
208   printer->Indent();
209 
210   for (int i = 0; i < descriptor_->method_count(); i++) {
211     const MethodDescriptor* method = descriptor_->method(i);
212     std::map<std::string, std::string> vars;
213     vars["index"] = StrCat(i);
214     vars["method"] = UnderscoresToCamelCase(method);
215     vars["input"] = name_resolver_->GetImmutableClassName(method->input_type());
216     vars["output"] = GetOutput(method);
217     printer->Print(
218         vars,
219         "case $index$:\n"
220         "  this.$method$(controller, ($input$)request,\n"
221         "    com.google.protobuf.RpcUtil.<$output$>specializeCallback(\n"
222         "      done));\n"
223         "  return;\n");
224   }
225 
226   printer->Print(
227       "default:\n"
228       "  throw new java.lang.AssertionError(\"Can't get here.\");\n");
229 
230   printer->Outdent();
231   printer->Outdent();
232 
233   printer->Print(
234       "  }\n"
235       "}\n"
236       "\n");
237 }
238 
GenerateCallBlockingMethod(io::Printer * printer)239 void ImmutableServiceGenerator::GenerateCallBlockingMethod(
240     io::Printer* printer) {
241   printer->Print(
242       "\n"
243       "public final com.google.protobuf.Message callBlockingMethod(\n"
244       "    com.google.protobuf.Descriptors.MethodDescriptor method,\n"
245       "    com.google.protobuf.RpcController controller,\n"
246       "    com.google.protobuf.Message request)\n"
247       "    throws com.google.protobuf.ServiceException {\n"
248       "  if (method.getService() != getDescriptor()) {\n"
249       "    throw new java.lang.IllegalArgumentException(\n"
250       "      \"Service.callBlockingMethod() given method descriptor for \" +\n"
251       "      \"wrong service type.\");\n"
252       "  }\n"
253       "  switch(method.getIndex()) {\n");
254   printer->Indent();
255   printer->Indent();
256 
257   for (int i = 0; i < descriptor_->method_count(); i++) {
258     const MethodDescriptor* method = descriptor_->method(i);
259     std::map<std::string, std::string> vars;
260     vars["index"] = StrCat(i);
261     vars["method"] = UnderscoresToCamelCase(method);
262     vars["input"] = name_resolver_->GetImmutableClassName(method->input_type());
263     vars["output"] = GetOutput(method);
264     printer->Print(vars,
265                    "case $index$:\n"
266                    "  return impl.$method$(controller, ($input$)request);\n");
267   }
268 
269   printer->Print(
270       "default:\n"
271       "  throw new java.lang.AssertionError(\"Can't get here.\");\n");
272 
273   printer->Outdent();
274   printer->Outdent();
275 
276   printer->Print(
277       "  }\n"
278       "}\n"
279       "\n");
280 }
281 
GenerateGetPrototype(RequestOrResponse which,io::Printer * printer)282 void ImmutableServiceGenerator::GenerateGetPrototype(RequestOrResponse which,
283                                                      io::Printer* printer) {
284   /*
285    * TODO(cpovirk): The exception message says "Service.foo" when it may be
286    * "BlockingService.foo."  Consider fixing.
287    */
288   printer->Print(
289       "public final com.google.protobuf.Message\n"
290       "    get$request_or_response$Prototype(\n"
291       "    com.google.protobuf.Descriptors.MethodDescriptor method) {\n"
292       "  if (method.getService() != getDescriptor()) {\n"
293       "    throw new java.lang.IllegalArgumentException(\n"
294       "      \"Service.get$request_or_response$Prototype() given method \" +\n"
295       "      \"descriptor for wrong service type.\");\n"
296       "  }\n"
297       "  switch(method.getIndex()) {\n",
298       "request_or_response", (which == REQUEST) ? "Request" : "Response");
299   printer->Indent();
300   printer->Indent();
301 
302   for (int i = 0; i < descriptor_->method_count(); i++) {
303     const MethodDescriptor* method = descriptor_->method(i);
304     std::map<std::string, std::string> vars;
305     vars["index"] = StrCat(i);
306     vars["type"] =
307         (which == REQUEST)
308             ? name_resolver_->GetImmutableClassName(method->input_type())
309             : GetOutput(method);
310     printer->Print(vars,
311                    "case $index$:\n"
312                    "  return $type$.getDefaultInstance();\n");
313   }
314 
315   printer->Print(
316       "default:\n"
317       "  throw new java.lang.AssertionError(\"Can't get here.\");\n");
318 
319   printer->Outdent();
320   printer->Outdent();
321 
322   printer->Print(
323       "  }\n"
324       "}\n"
325       "\n");
326 }
327 
GenerateStub(io::Printer * printer)328 void ImmutableServiceGenerator::GenerateStub(io::Printer* printer) {
329   printer->Print(
330       "public static Stub newStub(\n"
331       "    com.google.protobuf.RpcChannel channel) {\n"
332       "  return new Stub(channel);\n"
333       "}\n"
334       "\n"
335       "public static final class Stub extends $classname$ implements Interface "
336       "{"
337       "\n",
338       "classname", name_resolver_->GetImmutableClassName(descriptor_));
339   printer->Indent();
340 
341   printer->Print(
342       "private Stub(com.google.protobuf.RpcChannel channel) {\n"
343       "  this.channel = channel;\n"
344       "}\n"
345       "\n"
346       "private final com.google.protobuf.RpcChannel channel;\n"
347       "\n"
348       "public com.google.protobuf.RpcChannel getChannel() {\n"
349       "  return channel;\n"
350       "}\n");
351 
352   for (int i = 0; i < descriptor_->method_count(); i++) {
353     const MethodDescriptor* method = descriptor_->method(i);
354     printer->Print("\n");
355     GenerateMethodSignature(printer, method, IS_CONCRETE);
356     printer->Print(" {\n");
357     printer->Indent();
358 
359     std::map<std::string, std::string> vars;
360     vars["index"] = StrCat(i);
361     vars["output"] = GetOutput(method);
362     printer->Print(vars,
363                    "channel.callMethod(\n"
364                    "  getDescriptor().getMethods().get($index$),\n"
365                    "  controller,\n"
366                    "  request,\n"
367                    "  $output$.getDefaultInstance(),\n"
368                    "  com.google.protobuf.RpcUtil.generalizeCallback(\n"
369                    "    done,\n"
370                    "    $output$.class,\n"
371                    "    $output$.getDefaultInstance()));\n");
372 
373     printer->Outdent();
374     printer->Print("}\n");
375   }
376 
377   printer->Outdent();
378   printer->Print(
379       "}\n"
380       "\n");
381 }
382 
GenerateBlockingStub(io::Printer * printer)383 void ImmutableServiceGenerator::GenerateBlockingStub(io::Printer* printer) {
384   printer->Print(
385       "public static BlockingInterface newBlockingStub(\n"
386       "    com.google.protobuf.BlockingRpcChannel channel) {\n"
387       "  return new BlockingStub(channel);\n"
388       "}\n"
389       "\n");
390 
391   printer->Print("public interface BlockingInterface {");
392   printer->Indent();
393 
394   for (int i = 0; i < descriptor_->method_count(); i++) {
395     const MethodDescriptor* method = descriptor_->method(i);
396     GenerateBlockingMethodSignature(printer, method);
397     printer->Print(";\n");
398   }
399 
400   printer->Outdent();
401   printer->Print(
402       "}\n"
403       "\n");
404 
405   printer->Print(
406       "private static final class BlockingStub implements BlockingInterface "
407       "{\n");
408   printer->Indent();
409 
410   printer->Print(
411       "private BlockingStub(com.google.protobuf.BlockingRpcChannel channel) {\n"
412       "  this.channel = channel;\n"
413       "}\n"
414       "\n"
415       "private final com.google.protobuf.BlockingRpcChannel channel;\n");
416 
417   for (int i = 0; i < descriptor_->method_count(); i++) {
418     const MethodDescriptor* method = descriptor_->method(i);
419     GenerateBlockingMethodSignature(printer, method);
420     printer->Print(" {\n");
421     printer->Indent();
422 
423     std::map<std::string, std::string> vars;
424     vars["index"] = StrCat(i);
425     vars["output"] = GetOutput(method);
426     printer->Print(vars,
427                    "return ($output$) channel.callBlockingMethod(\n"
428                    "  getDescriptor().getMethods().get($index$),\n"
429                    "  controller,\n"
430                    "  request,\n"
431                    "  $output$.getDefaultInstance());\n");
432 
433     printer->Outdent();
434     printer->Print(
435         "}\n"
436         "\n");
437   }
438 
439   printer->Outdent();
440   printer->Print("}\n");
441 }
442 
GenerateMethodSignature(io::Printer * printer,const MethodDescriptor * method,IsAbstract is_abstract)443 void ImmutableServiceGenerator::GenerateMethodSignature(
444     io::Printer* printer, const MethodDescriptor* method,
445     IsAbstract is_abstract) {
446   std::map<std::string, std::string> vars;
447   vars["name"] = UnderscoresToCamelCase(method);
448   vars["input"] = name_resolver_->GetImmutableClassName(method->input_type());
449   vars["output"] = GetOutput(method);
450   vars["abstract"] = (is_abstract == IS_ABSTRACT) ? "abstract" : "";
451   printer->Print(vars,
452                  "public $abstract$ void $name$(\n"
453                  "    com.google.protobuf.RpcController controller,\n"
454                  "    $input$ request,\n"
455                  "    com.google.protobuf.RpcCallback<$output$> done)");
456 }
457 
GenerateBlockingMethodSignature(io::Printer * printer,const MethodDescriptor * method)458 void ImmutableServiceGenerator::GenerateBlockingMethodSignature(
459     io::Printer* printer, const MethodDescriptor* method) {
460   std::map<std::string, std::string> vars;
461   vars["method"] = UnderscoresToCamelCase(method);
462   vars["input"] = name_resolver_->GetImmutableClassName(method->input_type());
463   vars["output"] = GetOutput(method);
464   printer->Print(vars,
465                  "\n"
466                  "public $output$ $method$(\n"
467                  "    com.google.protobuf.RpcController controller,\n"
468                  "    $input$ request)\n"
469                  "    throws com.google.protobuf.ServiceException");
470 }
471 
472 }  // namespace java
473 }  // namespace compiler
474 }  // namespace protobuf
475 }  // namespace google
476