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