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