Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ public abstract class AbstractGenerator {
private static final MustacheFactory MUSTACHE_FACTORY = new DefaultMustacheFactory();
private static final int SERVICE_NUMBER_OF_PATHS = 2;
private static final int METHOD_NUMBER_OF_PATHS = 4;
private static final String GOOGLE_PROTOBUF_EMPTY_PROTO_TYPE = ".google.protobuf.Empty";
private static final String GOOGLE_PROTOBUF_EMPTY_JAVA_TYPE = "com.google.protobuf.Empty";

protected abstract String getClassPrefix();

Expand Down Expand Up @@ -184,11 +186,17 @@ private MethodContext buildMethodContext(
MethodContext methodContext = new MethodContext();
methodContext.originMethodName = methodProto.getName();
methodContext.methodName = lowerCaseFirst(methodProto.getName());
methodContext.inputType = typeMap.toJavaTypeName(methodProto.getInputType());
methodContext.outputType = typeMap.toJavaTypeName(methodProto.getOutputType());
methodContext.inputType = toJavaTypeName(methodProto.getInputType(), typeMap);
methodContext.outputType = toJavaTypeName(methodProto.getOutputType(), typeMap);
methodContext.deprecated = methodProto.getOptions().getDeprecated();
methodContext.isManyInput = methodProto.getClientStreaming();
methodContext.isManyOutput = methodProto.getServerStreaming();
methodContext.isEmptyOutput = GOOGLE_PROTOBUF_EMPTY_PROTO_TYPE.equals(methodProto.getOutputType());
methodContext.returnType = methodContext.isEmptyOutput
&& !methodContext.isManyInput
&& !methodContext.isManyOutput
? "void"
: methodContext.outputType;
methodContext.methodNumber = methodNumber;

// compile google.api.http option
Expand Down Expand Up @@ -251,6 +259,13 @@ private MethodContext buildMethodContext(
return methodContext;
}

private String toJavaTypeName(String protoTypeName, ProtoTypeMap typeMap) {
if (GOOGLE_PROTOBUF_EMPTY_PROTO_TYPE.equals(protoTypeName)) {
return GOOGLE_PROTOBUF_EMPTY_JAVA_TYPE;
}
return typeMap.toJavaTypeName(protoTypeName);
}

private HttpRule parseHttpRule(MethodDescriptorProto methodProto) {
HttpRule rule = null;
// check methodProto have options
Expand Down Expand Up @@ -383,6 +398,10 @@ public List<MethodContext> unaryMethods() {
.collect(Collectors.toList());
}

public boolean hasEmptyUnaryMethods() {
return unaryMethods().stream().anyMatch(m -> m.isEmptyOutput);
}

public List<MethodContext> serverStreamingMethods() {
return methods.stream()
.filter(m -> !m.isManyInput && m.isManyOutput)
Expand Down Expand Up @@ -413,14 +432,16 @@ public List<MethodContext> methods() {
*/
private static class MethodContext {

// CHECKSTYLE DISABLE VisibilityModifier FOR 10 LINES
// CHECKSTYLE DISABLE VisibilityModifier FOR 12 LINES
public String originMethodName;
public String methodName;
public String inputType;
public String outputType;
public String returnType;
public boolean deprecated;
public boolean isManyInput;
public boolean isManyOutput;
public boolean isEmptyOutput;
public String reactiveCallsMethodName;
public String grpcCallsMethodName;
public int methodNumber;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public interface {{interfaceClassName}} extends org.apache.dubbo.rpc.model.Dubbo
{{#hasMapping}}
@Mapping(method = HttpMethods.{{httpMethod}}, path = "{{path}}")
{{/hasMapping}}
{{outputType}} {{methodName}}({{#needRequestAnnotation}}@GRequest{{#body}}("{{body}}"){{/body}} {{/needRequestAnnotation}}{{inputType}} request);
{{returnType}} {{methodName}}({{#needRequestAnnotation}}@GRequest{{#body}}("{{body}}"){{/body}} {{/needRequestAnnotation}}{{inputType}} request);

CompletableFuture<{{outputType}}> {{methodName}}Async({{#needRequestAnnotation}}@GRequest {{/needRequestAnnotation}}{{inputType}} request);
{{/unaryMethods}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,16 @@ public final class {{className}} {
{{#unaryMethods}}

@Override
{{#isEmptyOutput}}
public void {{methodName}}({{inputType}} request){
StubInvocationUtil.unaryCall(invoker, {{methodName}}Method, request);
}
{{/isEmptyOutput}}
{{^isEmptyOutput}}
public {{outputType}} {{methodName}}({{inputType}} request){
return StubInvocationUtil.unaryCall(invoker, {{methodName}}Method, request);
}
{{/isEmptyOutput}}

public CompletableFuture<{{outputType}}> {{methodName}}Async({{inputType}} request){
return StubInvocationUtil.unaryCall(invoker, {{methodName}}AsyncMethod, request);
Expand Down Expand Up @@ -181,12 +188,37 @@ public final class {{className}} {
}
};
}
{{#hasEmptyUnaryMethods}}
private <T, R> BiConsumer<T, StreamObserver<R>> syncToAsyncVoid(java.util.function.Consumer<T> syncFun,
R response) {
return new BiConsumer<T, StreamObserver<R>>() {
@Override
public void accept(T t, StreamObserver<R> observer) {
try {
syncFun.accept(t);
observer.onNext(response);
observer.onCompleted();
} catch (Throwable e) {
observer.onError(e);
}
}
};
}
{{/hasEmptyUnaryMethods}}
{{#unaryMethods}}

@Override
{{#isEmptyOutput}}
public CompletableFuture<{{outputType}}> {{methodName}}Async({{inputType}} request){
{{methodName}}(request);
return CompletableFuture.completedFuture({{outputType}}.getDefaultInstance());
}
{{/isEmptyOutput}}
{{^isEmptyOutput}}
public CompletableFuture<{{outputType}}> {{methodName}}Async({{inputType}} request){
return CompletableFuture.completedFuture({{methodName}}(request));
}
{{/isEmptyOutput}}
{{/unaryMethods}}

// This server stream type unary method is <b>only</b> used for generated stub to support async unary method.
Expand Down Expand Up @@ -221,7 +253,12 @@ public final class {{className}} {
{{#unaryMethods}}
BiConsumer<{{inputType}}, StreamObserver<{{outputType}}>> {{methodName}}Func = this::{{methodName}};
handlers.put({{methodName}}Method.getMethodName(), new UnaryStubMethodHandler<>({{methodName}}Func));
{{#isEmptyOutput}}
BiConsumer<{{inputType}}, StreamObserver<{{outputType}}>> {{methodName}}AsyncFunc = syncToAsyncVoid(this::{{methodName}}, {{outputType}}.getDefaultInstance());
{{/isEmptyOutput}}
{{^isEmptyOutput}}
BiConsumer<{{inputType}}, StreamObserver<{{outputType}}>> {{methodName}}AsyncFunc = syncToAsync(this::{{methodName}});
{{/isEmptyOutput}}
handlers.put({{methodName}}ProxyAsyncMethod.getMethodName(), new UnaryStubMethodHandler<>({{methodName}}AsyncFunc));
{{/unaryMethods}}
{{#serverStreamingMethods}}
Expand All @@ -239,7 +276,7 @@ public final class {{className}} {
{{#unaryMethods}}

@Override
public {{outputType}} {{methodName}}({{inputType}} request){
public {{returnType}} {{methodName}}({{inputType}} request){
throw unimplementedMethodException({{methodName}}Method);
}
{{/unaryMethods}}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.gen.tri;

import java.util.List;

import com.google.protobuf.DescriptorProtos.DescriptorProto;
import com.google.protobuf.DescriptorProtos.FileDescriptorProto;
import com.google.protobuf.DescriptorProtos.FileOptions;
import com.google.protobuf.DescriptorProtos.MethodDescriptorProto;
import com.google.protobuf.DescriptorProtos.ServiceDescriptorProto;
import com.google.protobuf.compiler.PluginProtos.CodeGeneratorRequest;
import com.google.protobuf.compiler.PluginProtos.CodeGeneratorResponse;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

class Dubbo3TripleGeneratorTest {

@Test
void generateVoidMethodForGoogleProtobufEmptyOutput() {
List<CodeGeneratorResponse.File> files = new Dubbo3TripleGenerator().generateFiles(emptyOutputRequest());

String interfaceContent =
findFile(files, "org/apache/dubbo/test/EmptyService.java").getContent();
String stubContent = findFile(files, "org/apache/dubbo/test/DubboEmptyServiceTriple.java")
.getContent();

assertTrue(interfaceContent.contains("void ack("), interfaceContent);
assertTrue(
interfaceContent.contains("CompletableFuture<com.google.protobuf.Empty> ackAsync("), interfaceContent);
assertTrue(stubContent.contains("public void ack("), stubContent);
assertTrue(stubContent.contains("StubInvocationUtil.unaryCall(invoker, ackMethod, request);"));
assertTrue(stubContent.contains("com.google.protobuf.Empty::parseFrom"));
assertTrue(stubContent.contains("syncToAsyncVoid(this::ack"));
assertTrue(stubContent.contains(
"CompletableFuture.completedFuture(com.google.protobuf.Empty.getDefaultInstance());"));
}

@Test
void generateMessageReturnMethodForNormalUnaryOutput() {
List<CodeGeneratorResponse.File> files = new Dubbo3TripleGenerator().generateFiles(normalOutputRequest());

String interfaceContent =
findFile(files, "org/apache/dubbo/test/EchoService.java").getContent();
String stubContent = findFile(files, "org/apache/dubbo/test/DubboEchoServiceTriple.java")
.getContent();

assertTrue(interfaceContent.contains("org.apache.dubbo.test.SampleResponse echo("), interfaceContent);
assertTrue(
interfaceContent.contains("CompletableFuture<org.apache.dubbo.test.SampleResponse> echoAsync("),
interfaceContent);
assertTrue(stubContent.contains("public org.apache.dubbo.test.SampleResponse echo("), stubContent);
assertTrue(stubContent.contains("return StubInvocationUtil.unaryCall(invoker, echoMethod, request);"));
assertFalse(stubContent.contains("syncToAsyncVoid("), stubContent);
}

private CodeGeneratorResponse.File findFile(List<CodeGeneratorResponse.File> files, String name) {
return files.stream()
.filter(file -> name.equals(file.getName()))
.findFirst()
.orElseThrow(() -> new AssertionError("Generated file not found: " + name));
}

private CodeGeneratorRequest emptyOutputRequest() {
FileDescriptorProto sampleProto = FileDescriptorProto.newBuilder()
.setName("sample.proto")
.setPackage("org.apache.dubbo.test")
.addDependency("google/protobuf/empty.proto")
.setOptions(FileOptions.newBuilder()
.setJavaPackage("org.apache.dubbo.test")
.setJavaMultipleFiles(true))
.addMessageType(DescriptorProto.newBuilder().setName("SampleRequest"))
.addService(ServiceDescriptorProto.newBuilder()
.setName("EmptyService")
.addMethod(MethodDescriptorProto.newBuilder()
.setName("Ack")
.setInputType(".org.apache.dubbo.test.SampleRequest")
.setOutputType(".google.protobuf.Empty")))
.build();

return CodeGeneratorRequest.newBuilder()
.addFileToGenerate("sample.proto")
.addProtoFile(sampleProto)
.build();
}

private CodeGeneratorRequest normalOutputRequest() {
FileDescriptorProto sampleProto = FileDescriptorProto.newBuilder()
.setName("normal.proto")
.setPackage("org.apache.dubbo.test")
.setOptions(FileOptions.newBuilder()
.setJavaPackage("org.apache.dubbo.test")
.setJavaMultipleFiles(true))
.addMessageType(DescriptorProto.newBuilder().setName("SampleRequest"))
.addMessageType(DescriptorProto.newBuilder().setName("SampleResponse"))
.addService(ServiceDescriptorProto.newBuilder()
.setName("EchoService")
.addMethod(MethodDescriptorProto.newBuilder()
.setName("Echo")
.setInputType(".org.apache.dubbo.test.SampleRequest")
.setOutputType(".org.apache.dubbo.test.SampleResponse")))
.build();

return CodeGeneratorRequest.newBuilder()
.addFileToGenerate("normal.proto")
.addProtoFile(sampleProto)
.build();
}
}
Loading