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
44namespace google {
45namespace protobuf {
46namespace compiler {
47namespace java {
48
49ServiceGenerator::ServiceGenerator(const ServiceDescriptor* descriptor)
50  : descriptor_(descriptor) {}
51
52ServiceGenerator::~ServiceGenerator() {}
53
54// ===================================================================
55ImmutableServiceGenerator::ImmutableServiceGenerator(
56    const ServiceDescriptor* descriptor, Context* context)
57    : ServiceGenerator(descriptor), context_(context),
58    name_resolver_(context->GetNameResolver()) {}
59
60ImmutableServiceGenerator::~ImmutableServiceGenerator() {}
61
62void ImmutableServiceGenerator::Generate(io::Printer* printer) {
63  bool is_own_file =
64    MultipleJavaFiles(descriptor_->file(), /* immutable = */ true);
65  WriteServiceDocComment(printer, descriptor_);
66  printer->Print(
67    "public $static$ abstract class $classname$\n"
68    "    implements com.google.protobuf.Service {\n",
69    "static", is_own_file ? "" : "static",
70    "classname", descriptor_->name());
71  printer->Indent();
72
73  printer->Print(
74    "protected $classname$() {}\n\n",
75    "classname", descriptor_->name());
76
77  GenerateInterface(printer);
78
79  GenerateNewReflectiveServiceMethod(printer);
80  GenerateNewReflectiveBlockingServiceMethod(printer);
81
82  GenerateAbstractMethods(printer);
83
84  // Generate getDescriptor() and getDescriptorForType().
85  printer->Print(
86    "public static final\n"
87    "    com.google.protobuf.Descriptors.ServiceDescriptor\n"
88    "    getDescriptor() {\n"
89    "  return $file$.getDescriptor().getServices().get($index$);\n"
90    "}\n",
91    "file", name_resolver_->GetImmutableClassName(descriptor_->file()),
92    "index", SimpleItoa(descriptor_->index()));
93  GenerateGetDescriptorForType(printer);
94
95  // Generate more stuff.
96  GenerateCallMethod(printer);
97  GenerateGetPrototype(REQUEST, printer);
98  GenerateGetPrototype(RESPONSE, printer);
99  GenerateStub(printer);
100  GenerateBlockingStub(printer);
101
102  // Add an insertion point.
103  printer->Print(
104    "\n"
105    "// @@protoc_insertion_point(class_scope:$full_name$)\n",
106    "full_name", descriptor_->full_name());
107
108  printer->Outdent();
109  printer->Print("}\n\n");
110}
111
112void ImmutableServiceGenerator::GenerateGetDescriptorForType(
113    io::Printer* printer) {
114  printer->Print(
115    "public final com.google.protobuf.Descriptors.ServiceDescriptor\n"
116    "    getDescriptorForType() {\n"
117    "  return getDescriptor();\n"
118    "}\n");
119}
120
121void ImmutableServiceGenerator::GenerateInterface(io::Printer* printer) {
122  printer->Print("public interface Interface {\n");
123  printer->Indent();
124  GenerateAbstractMethods(printer);
125  printer->Outdent();
126  printer->Print("}\n\n");
127}
128
129void ImmutableServiceGenerator::GenerateNewReflectiveServiceMethod(
130    io::Printer* printer) {
131  printer->Print(
132    "public static com.google.protobuf.Service newReflectiveService(\n"
133    "    final Interface impl) {\n"
134    "  return new $classname$() {\n",
135    "classname", descriptor_->name());
136  printer->Indent();
137  printer->Indent();
138
139  for (int i = 0; i < descriptor_->method_count(); i++) {
140    const MethodDescriptor* method = descriptor_->method(i);
141    printer->Print("@java.lang.Override\n");
142    GenerateMethodSignature(printer, method, IS_CONCRETE);
143    printer->Print(
144      " {\n"
145      "  impl.$method$(controller, request, done);\n"
146      "}\n\n",
147      "method", UnderscoresToCamelCase(method));
148  }
149
150  printer->Outdent();
151  printer->Print("};\n");
152  printer->Outdent();
153  printer->Print("}\n\n");
154}
155
156void ImmutableServiceGenerator::GenerateNewReflectiveBlockingServiceMethod(
157    io::Printer* printer) {
158  printer->Print(
159    "public static com.google.protobuf.BlockingService\n"
160    "    newReflectiveBlockingService(final BlockingInterface impl) {\n"
161    "  return new com.google.protobuf.BlockingService() {\n");
162  printer->Indent();
163  printer->Indent();
164
165  GenerateGetDescriptorForType(printer);
166
167  GenerateCallBlockingMethod(printer);
168  GenerateGetPrototype(REQUEST, printer);
169  GenerateGetPrototype(RESPONSE, printer);
170
171  printer->Outdent();
172  printer->Print("};\n");
173  printer->Outdent();
174  printer->Print("}\n\n");
175}
176
177void ImmutableServiceGenerator::GenerateAbstractMethods(io::Printer* printer) {
178  for (int i = 0; i < descriptor_->method_count(); i++) {
179    const MethodDescriptor* method = descriptor_->method(i);
180    WriteMethodDocComment(printer, method);
181    GenerateMethodSignature(printer, method, IS_ABSTRACT);
182    printer->Print(";\n\n");
183  }
184}
185
186void ImmutableServiceGenerator::GenerateCallMethod(io::Printer* printer) {
187  printer->Print(
188    "\n"
189    "public final void callMethod(\n"
190    "    com.google.protobuf.Descriptors.MethodDescriptor method,\n"
191    "    com.google.protobuf.RpcController controller,\n"
192    "    com.google.protobuf.Message request,\n"
193    "    com.google.protobuf.RpcCallback<\n"
194    "      com.google.protobuf.Message> done) {\n"
195    "  if (method.getService() != getDescriptor()) {\n"
196    "    throw new java.lang.IllegalArgumentException(\n"
197    "      \"Service.callMethod() given method descriptor for wrong \" +\n"
198    "      \"service type.\");\n"
199    "  }\n"
200    "  switch(method.getIndex()) {\n");
201  printer->Indent();
202  printer->Indent();
203
204  for (int i = 0; i < descriptor_->method_count(); i++) {
205    const MethodDescriptor* method = descriptor_->method(i);
206    map<string, string> vars;
207    vars["index"] = SimpleItoa(i);
208    vars["method"] = UnderscoresToCamelCase(method);
209    vars["input"] = name_resolver_->GetImmutableClassName(
210        method->input_type());
211    vars["output"] = name_resolver_->GetImmutableClassName(
212        method->output_type());
213    printer->Print(vars,
214      "case $index$:\n"
215      "  this.$method$(controller, ($input$)request,\n"
216      "    com.google.protobuf.RpcUtil.<$output$>specializeCallback(\n"
217      "      done));\n"
218      "  return;\n");
219  }
220
221  printer->Print(
222    "default:\n"
223    "  throw new java.lang.AssertionError(\"Can't get here.\");\n");
224
225  printer->Outdent();
226  printer->Outdent();
227
228  printer->Print(
229    "  }\n"
230    "}\n"
231    "\n");
232}
233
234void ImmutableServiceGenerator::GenerateCallBlockingMethod(
235    io::Printer* printer) {
236  printer->Print(
237    "\n"
238    "public final com.google.protobuf.Message callBlockingMethod(\n"
239    "    com.google.protobuf.Descriptors.MethodDescriptor method,\n"
240    "    com.google.protobuf.RpcController controller,\n"
241    "    com.google.protobuf.Message request)\n"
242    "    throws com.google.protobuf.ServiceException {\n"
243    "  if (method.getService() != getDescriptor()) {\n"
244    "    throw new java.lang.IllegalArgumentException(\n"
245    "      \"Service.callBlockingMethod() given method descriptor for \" +\n"
246    "      \"wrong service type.\");\n"
247    "  }\n"
248    "  switch(method.getIndex()) {\n");
249  printer->Indent();
250  printer->Indent();
251
252  for (int i = 0; i < descriptor_->method_count(); i++) {
253    const MethodDescriptor* method = descriptor_->method(i);
254    map<string, string> vars;
255    vars["index"] = SimpleItoa(i);
256    vars["method"] = UnderscoresToCamelCase(method);
257    vars["input"] = name_resolver_->GetImmutableClassName(
258        method->input_type());
259    vars["output"] = name_resolver_->GetImmutableClassName(
260        method->output_type());
261    printer->Print(vars,
262      "case $index$:\n"
263      "  return impl.$method$(controller, ($input$)request);\n");
264  }
265
266  printer->Print(
267    "default:\n"
268    "  throw new java.lang.AssertionError(\"Can't get here.\");\n");
269
270  printer->Outdent();
271  printer->Outdent();
272
273  printer->Print(
274    "  }\n"
275    "}\n"
276    "\n");
277}
278
279void ImmutableServiceGenerator::GenerateGetPrototype(RequestOrResponse which,
280                                            io::Printer* printer) {
281  /*
282   * TODO(cpovirk): The exception message says "Service.foo" when it may be
283   * "BlockingService.foo."  Consider fixing.
284   */
285  printer->Print(
286    "public final com.google.protobuf.Message\n"
287    "    get$request_or_response$Prototype(\n"
288    "    com.google.protobuf.Descriptors.MethodDescriptor method) {\n"
289    "  if (method.getService() != getDescriptor()) {\n"
290    "    throw new java.lang.IllegalArgumentException(\n"
291    "      \"Service.get$request_or_response$Prototype() given method \" +\n"
292    "      \"descriptor for wrong service type.\");\n"
293    "  }\n"
294    "  switch(method.getIndex()) {\n",
295    "request_or_response", (which == REQUEST) ? "Request" : "Response");
296  printer->Indent();
297  printer->Indent();
298
299  for (int i = 0; i < descriptor_->method_count(); i++) {
300    const MethodDescriptor* method = descriptor_->method(i);
301    map<string, string> vars;
302    vars["index"] = SimpleItoa(i);
303    vars["type"] = name_resolver_->GetImmutableClassName(
304      (which == REQUEST) ? method->input_type() : method->output_type());
305    printer->Print(vars,
306      "case $index$:\n"
307      "  return $type$.getDefaultInstance();\n");
308  }
309
310  printer->Print(
311    "default:\n"
312    "  throw new java.lang.AssertionError(\"Can't get here.\");\n");
313
314  printer->Outdent();
315  printer->Outdent();
316
317  printer->Print(
318    "  }\n"
319    "}\n"
320    "\n");
321}
322
323void ImmutableServiceGenerator::GenerateStub(io::Printer* printer) {
324  printer->Print(
325    "public static Stub newStub(\n"
326    "    com.google.protobuf.RpcChannel channel) {\n"
327    "  return new Stub(channel);\n"
328    "}\n"
329    "\n"
330    "public static final class Stub extends $classname$ implements Interface {"
331    "\n",
332    "classname", name_resolver_->GetImmutableClassName(descriptor_));
333  printer->Indent();
334
335  printer->Print(
336    "private Stub(com.google.protobuf.RpcChannel channel) {\n"
337    "  this.channel = channel;\n"
338    "}\n"
339    "\n"
340    "private final com.google.protobuf.RpcChannel channel;\n"
341    "\n"
342    "public com.google.protobuf.RpcChannel getChannel() {\n"
343    "  return channel;\n"
344    "}\n");
345
346  for (int i = 0; i < descriptor_->method_count(); i++) {
347    const MethodDescriptor* method = descriptor_->method(i);
348    printer->Print("\n");
349    GenerateMethodSignature(printer, method, IS_CONCRETE);
350    printer->Print(" {\n");
351    printer->Indent();
352
353    map<string, string> vars;
354    vars["index"] = SimpleItoa(i);
355    vars["output"] = name_resolver_->GetImmutableClassName(
356        method->output_type());
357    printer->Print(vars,
358      "channel.callMethod(\n"
359      "  getDescriptor().getMethods().get($index$),\n"
360      "  controller,\n"
361      "  request,\n"
362      "  $output$.getDefaultInstance(),\n"
363      "  com.google.protobuf.RpcUtil.generalizeCallback(\n"
364      "    done,\n"
365      "    $output$.class,\n"
366      "    $output$.getDefaultInstance()));\n");
367
368    printer->Outdent();
369    printer->Print("}\n");
370  }
371
372  printer->Outdent();
373  printer->Print(
374    "}\n"
375    "\n");
376}
377
378void ImmutableServiceGenerator::GenerateBlockingStub(io::Printer* printer) {
379  printer->Print(
380    "public static BlockingInterface newBlockingStub(\n"
381    "    com.google.protobuf.BlockingRpcChannel channel) {\n"
382    "  return new BlockingStub(channel);\n"
383    "}\n"
384    "\n");
385
386  printer->Print(
387    "public interface BlockingInterface {");
388  printer->Indent();
389
390  for (int i = 0; i < descriptor_->method_count(); i++) {
391    const MethodDescriptor* method = descriptor_->method(i);
392    GenerateBlockingMethodSignature(printer, method);
393    printer->Print(";\n");
394  }
395
396  printer->Outdent();
397  printer->Print(
398    "}\n"
399    "\n");
400
401  printer->Print(
402    "private static final class BlockingStub implements BlockingInterface {\n");
403  printer->Indent();
404
405  printer->Print(
406    "private BlockingStub(com.google.protobuf.BlockingRpcChannel channel) {\n"
407    "  this.channel = channel;\n"
408    "}\n"
409    "\n"
410    "private final com.google.protobuf.BlockingRpcChannel channel;\n");
411
412  for (int i = 0; i < descriptor_->method_count(); i++) {
413    const MethodDescriptor* method = descriptor_->method(i);
414    GenerateBlockingMethodSignature(printer, method);
415    printer->Print(" {\n");
416    printer->Indent();
417
418    map<string, string> vars;
419    vars["index"] = SimpleItoa(i);
420    vars["output"] = name_resolver_->GetImmutableClassName(
421        method->output_type());
422    printer->Print(vars,
423      "return ($output$) channel.callBlockingMethod(\n"
424      "  getDescriptor().getMethods().get($index$),\n"
425      "  controller,\n"
426      "  request,\n"
427      "  $output$.getDefaultInstance());\n");
428
429    printer->Outdent();
430    printer->Print(
431      "}\n"
432      "\n");
433  }
434
435  printer->Outdent();
436  printer->Print("}\n");
437}
438
439void ImmutableServiceGenerator::GenerateMethodSignature(io::Printer* printer,
440                                               const MethodDescriptor* method,
441                                               IsAbstract is_abstract) {
442  map<string, string> vars;
443  vars["name"] = UnderscoresToCamelCase(method);
444  vars["input"] = name_resolver_->GetImmutableClassName(method->input_type());
445  vars["output"] = name_resolver_->GetImmutableClassName(method->output_type());
446  vars["abstract"] = (is_abstract == IS_ABSTRACT) ? "abstract" : "";
447  printer->Print(vars,
448    "public $abstract$ void $name$(\n"
449    "    com.google.protobuf.RpcController controller,\n"
450    "    $input$ request,\n"
451    "    com.google.protobuf.RpcCallback<$output$> done)");
452}
453
454void ImmutableServiceGenerator::GenerateBlockingMethodSignature(
455    io::Printer* printer,
456    const MethodDescriptor* method) {
457  map<string, string> vars;
458  vars["method"] = UnderscoresToCamelCase(method);
459  vars["input"] = name_resolver_->GetImmutableClassName(method->input_type());
460  vars["output"] = name_resolver_->GetImmutableClassName(method->output_type());
461  printer->Print(vars,
462    "\n"
463    "public $output$ $method$(\n"
464    "    com.google.protobuf.RpcController controller,\n"
465    "    $input$ request)\n"
466    "    throws com.google.protobuf.ServiceException");
467}
468
469}  // namespace java
470}  // namespace compiler
471}  // namespace protobuf
472}  // namespace google
473