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/descriptor.pb.h>
43 #include <google/protobuf/stubs/strutil.h>
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), context_(context),
59     name_resolver_(context->GetNameResolver()) {}
60 
~ImmutableServiceGenerator()61 ImmutableServiceGenerator::~ImmutableServiceGenerator() {}
62 
Generate(io::Printer * printer)63 void ImmutableServiceGenerator::Generate(io::Printer* printer) {
64   bool is_own_file =
65     MultipleJavaFiles(descriptor_->file(), /* immutable = */ true);
66   WriteServiceDocComment(printer, descriptor_);
67   printer->Print(
68     "public $static$ abstract class $classname$\n"
69     "    implements com.google.protobuf.Service {\n",
70     "static", is_own_file ? "" : "static",
71     "classname", descriptor_->name());
72   printer->Indent();
73 
74   printer->Print(
75     "protected $classname$() {}\n\n",
76     "classname", 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", SimpleItoa(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 
GenerateCallMethod(io::Printer * printer)187 void ImmutableServiceGenerator::GenerateCallMethod(io::Printer* printer) {
188   printer->Print(
189     "\n"
190     "public final void callMethod(\n"
191     "    com.google.protobuf.Descriptors.MethodDescriptor method,\n"
192     "    com.google.protobuf.RpcController controller,\n"
193     "    com.google.protobuf.Message request,\n"
194     "    com.google.protobuf.RpcCallback<\n"
195     "      com.google.protobuf.Message> done) {\n"
196     "  if (method.getService() != getDescriptor()) {\n"
197     "    throw new java.lang.IllegalArgumentException(\n"
198     "      \"Service.callMethod() given method descriptor for wrong \" +\n"
199     "      \"service type.\");\n"
200     "  }\n"
201     "  switch(method.getIndex()) {\n");
202   printer->Indent();
203   printer->Indent();
204 
205   for (int i = 0; i < descriptor_->method_count(); i++) {
206     const MethodDescriptor* method = descriptor_->method(i);
207     map<string, string> vars;
208     vars["index"] = SimpleItoa(i);
209     vars["method"] = UnderscoresToCamelCase(method);
210     vars["input"] = name_resolver_->GetImmutableClassName(
211         method->input_type());
212     vars["output"] = name_resolver_->GetImmutableClassName(
213         method->output_type());
214     printer->Print(vars,
215       "case $index$:\n"
216       "  this.$method$(controller, ($input$)request,\n"
217       "    com.google.protobuf.RpcUtil.<$output$>specializeCallback(\n"
218       "      done));\n"
219       "  return;\n");
220   }
221 
222   printer->Print(
223     "default:\n"
224     "  throw new java.lang.AssertionError(\"Can't get here.\");\n");
225 
226   printer->Outdent();
227   printer->Outdent();
228 
229   printer->Print(
230     "  }\n"
231     "}\n"
232     "\n");
233 }
234 
GenerateCallBlockingMethod(io::Printer * printer)235 void ImmutableServiceGenerator::GenerateCallBlockingMethod(
236     io::Printer* printer) {
237   printer->Print(
238     "\n"
239     "public final com.google.protobuf.Message callBlockingMethod(\n"
240     "    com.google.protobuf.Descriptors.MethodDescriptor method,\n"
241     "    com.google.protobuf.RpcController controller,\n"
242     "    com.google.protobuf.Message request)\n"
243     "    throws com.google.protobuf.ServiceException {\n"
244     "  if (method.getService() != getDescriptor()) {\n"
245     "    throw new java.lang.IllegalArgumentException(\n"
246     "      \"Service.callBlockingMethod() given method descriptor for \" +\n"
247     "      \"wrong service type.\");\n"
248     "  }\n"
249     "  switch(method.getIndex()) {\n");
250   printer->Indent();
251   printer->Indent();
252 
253   for (int i = 0; i < descriptor_->method_count(); i++) {
254     const MethodDescriptor* method = descriptor_->method(i);
255     map<string, string> vars;
256     vars["index"] = SimpleItoa(i);
257     vars["method"] = UnderscoresToCamelCase(method);
258     vars["input"] = name_resolver_->GetImmutableClassName(
259         method->input_type());
260     vars["output"] = name_resolver_->GetImmutableClassName(
261         method->output_type());
262     printer->Print(vars,
263       "case $index$:\n"
264       "  return impl.$method$(controller, ($input$)request);\n");
265   }
266 
267   printer->Print(
268     "default:\n"
269     "  throw new java.lang.AssertionError(\"Can't get here.\");\n");
270 
271   printer->Outdent();
272   printer->Outdent();
273 
274   printer->Print(
275     "  }\n"
276     "}\n"
277     "\n");
278 }
279 
GenerateGetPrototype(RequestOrResponse which,io::Printer * printer)280 void ImmutableServiceGenerator::GenerateGetPrototype(RequestOrResponse which,
281                                             io::Printer* printer) {
282   /*
283    * TODO(cpovirk): The exception message says "Service.foo" when it may be
284    * "BlockingService.foo."  Consider fixing.
285    */
286   printer->Print(
287     "public final com.google.protobuf.Message\n"
288     "    get$request_or_response$Prototype(\n"
289     "    com.google.protobuf.Descriptors.MethodDescriptor method) {\n"
290     "  if (method.getService() != getDescriptor()) {\n"
291     "    throw new java.lang.IllegalArgumentException(\n"
292     "      \"Service.get$request_or_response$Prototype() given method \" +\n"
293     "      \"descriptor for wrong service type.\");\n"
294     "  }\n"
295     "  switch(method.getIndex()) {\n",
296     "request_or_response", (which == REQUEST) ? "Request" : "Response");
297   printer->Indent();
298   printer->Indent();
299 
300   for (int i = 0; i < descriptor_->method_count(); i++) {
301     const MethodDescriptor* method = descriptor_->method(i);
302     map<string, string> vars;
303     vars["index"] = SimpleItoa(i);
304     vars["type"] = name_resolver_->GetImmutableClassName(
305       (which == REQUEST) ? method->input_type() : method->output_type());
306     printer->Print(vars,
307       "case $index$:\n"
308       "  return $type$.getDefaultInstance();\n");
309   }
310 
311   printer->Print(
312     "default:\n"
313     "  throw new java.lang.AssertionError(\"Can't get here.\");\n");
314 
315   printer->Outdent();
316   printer->Outdent();
317 
318   printer->Print(
319     "  }\n"
320     "}\n"
321     "\n");
322 }
323 
GenerateStub(io::Printer * printer)324 void ImmutableServiceGenerator::GenerateStub(io::Printer* printer) {
325   printer->Print(
326     "public static Stub newStub(\n"
327     "    com.google.protobuf.RpcChannel channel) {\n"
328     "  return new Stub(channel);\n"
329     "}\n"
330     "\n"
331     "public static final class Stub extends $classname$ implements Interface {"
332     "\n",
333     "classname", name_resolver_->GetImmutableClassName(descriptor_));
334   printer->Indent();
335 
336   printer->Print(
337     "private Stub(com.google.protobuf.RpcChannel channel) {\n"
338     "  this.channel = channel;\n"
339     "}\n"
340     "\n"
341     "private final com.google.protobuf.RpcChannel channel;\n"
342     "\n"
343     "public com.google.protobuf.RpcChannel getChannel() {\n"
344     "  return channel;\n"
345     "}\n");
346 
347   for (int i = 0; i < descriptor_->method_count(); i++) {
348     const MethodDescriptor* method = descriptor_->method(i);
349     printer->Print("\n");
350     GenerateMethodSignature(printer, method, IS_CONCRETE);
351     printer->Print(" {\n");
352     printer->Indent();
353 
354     map<string, string> vars;
355     vars["index"] = SimpleItoa(i);
356     vars["output"] = name_resolver_->GetImmutableClassName(
357         method->output_type());
358     printer->Print(vars,
359       "channel.callMethod(\n"
360       "  getDescriptor().getMethods().get($index$),\n"
361       "  controller,\n"
362       "  request,\n"
363       "  $output$.getDefaultInstance(),\n"
364       "  com.google.protobuf.RpcUtil.generalizeCallback(\n"
365       "    done,\n"
366       "    $output$.class,\n"
367       "    $output$.getDefaultInstance()));\n");
368 
369     printer->Outdent();
370     printer->Print("}\n");
371   }
372 
373   printer->Outdent();
374   printer->Print(
375     "}\n"
376     "\n");
377 }
378 
GenerateBlockingStub(io::Printer * printer)379 void ImmutableServiceGenerator::GenerateBlockingStub(io::Printer* printer) {
380   printer->Print(
381     "public static BlockingInterface newBlockingStub(\n"
382     "    com.google.protobuf.BlockingRpcChannel channel) {\n"
383     "  return new BlockingStub(channel);\n"
384     "}\n"
385     "\n");
386 
387   printer->Print(
388     "public interface BlockingInterface {");
389   printer->Indent();
390 
391   for (int i = 0; i < descriptor_->method_count(); i++) {
392     const MethodDescriptor* method = descriptor_->method(i);
393     GenerateBlockingMethodSignature(printer, method);
394     printer->Print(";\n");
395   }
396 
397   printer->Outdent();
398   printer->Print(
399     "}\n"
400     "\n");
401 
402   printer->Print(
403     "private static final class BlockingStub implements BlockingInterface {\n");
404   printer->Indent();
405 
406   printer->Print(
407     "private BlockingStub(com.google.protobuf.BlockingRpcChannel channel) {\n"
408     "  this.channel = channel;\n"
409     "}\n"
410     "\n"
411     "private final com.google.protobuf.BlockingRpcChannel channel;\n");
412 
413   for (int i = 0; i < descriptor_->method_count(); i++) {
414     const MethodDescriptor* method = descriptor_->method(i);
415     GenerateBlockingMethodSignature(printer, method);
416     printer->Print(" {\n");
417     printer->Indent();
418 
419     map<string, string> vars;
420     vars["index"] = SimpleItoa(i);
421     vars["output"] = name_resolver_->GetImmutableClassName(
422         method->output_type());
423     printer->Print(vars,
424       "return ($output$) channel.callBlockingMethod(\n"
425       "  getDescriptor().getMethods().get($index$),\n"
426       "  controller,\n"
427       "  request,\n"
428       "  $output$.getDefaultInstance());\n");
429 
430     printer->Outdent();
431     printer->Print(
432       "}\n"
433       "\n");
434   }
435 
436   printer->Outdent();
437   printer->Print("}\n");
438 }
439 
GenerateMethodSignature(io::Printer * printer,const MethodDescriptor * method,IsAbstract is_abstract)440 void ImmutableServiceGenerator::GenerateMethodSignature(io::Printer* printer,
441                                                const MethodDescriptor* method,
442                                                IsAbstract is_abstract) {
443   map<string, string> vars;
444   vars["name"] = UnderscoresToCamelCase(method);
445   vars["input"] = name_resolver_->GetImmutableClassName(method->input_type());
446   vars["output"] = name_resolver_->GetImmutableClassName(method->output_type());
447   vars["abstract"] = (is_abstract == IS_ABSTRACT) ? "abstract" : "";
448   printer->Print(vars,
449     "public $abstract$ void $name$(\n"
450     "    com.google.protobuf.RpcController controller,\n"
451     "    $input$ request,\n"
452     "    com.google.protobuf.RpcCallback<$output$> done)");
453 }
454 
GenerateBlockingMethodSignature(io::Printer * printer,const MethodDescriptor * method)455 void ImmutableServiceGenerator::GenerateBlockingMethodSignature(
456     io::Printer* printer,
457     const MethodDescriptor* method) {
458   map<string, string> vars;
459   vars["method"] = UnderscoresToCamelCase(method);
460   vars["input"] = name_resolver_->GetImmutableClassName(method->input_type());
461   vars["output"] = name_resolver_->GetImmutableClassName(method->output_type());
462   printer->Print(vars,
463     "\n"
464     "public $output$ $method$(\n"
465     "    com.google.protobuf.RpcController controller,\n"
466     "    $input$ request)\n"
467     "    throws com.google.protobuf.ServiceException");
468 }
469 
470 }  // namespace java
471 }  // namespace compiler
472 }  // namespace protobuf
473 }  // namespace google
474