diff --git a/dependencies.yml b/dependencies.yml index 75894668566..35cf632c2fe 100644 --- a/dependencies.yml +++ b/dependencies.yml @@ -197,6 +197,8 @@ io.grpc: - io.netty:netty-handler-proxy - io.netty:netty-transport - io.netty:netty-tcnative-boringssl-static + grpc-kotlin-stub: { version: &GRPC_KOTLIN_VERSION '0.1.1' } + protoc-gen-grpc-kotlin: { version: *GRPC_KOTLIN_VERSION } io.micrometer: micrometer-core: @@ -420,7 +422,7 @@ org.jctools: - from: org.jctools to: com.linecorp.armeria.internal.shaded.jctools -# If you want to change `org.jetbrains.kotlin:kotlin-allopen` version, +# If you want to change `org.jetbrains.kotlin:kotlin-allopen` version, # you also need to change `org.jetbrains.kotlin.jvm` version in `build.gradle`. org.jetbrains.kotlin: kotlin-allopen: { version: &KOTLIN_VERSION '1.3.71' } diff --git a/examples/grpc-kotlin/build.gradle.kts b/examples/grpc-kotlin/build.gradle.kts index 0693821b223..0889366a363 100644 --- a/examples/grpc-kotlin/build.gradle.kts +++ b/examples/grpc-kotlin/build.gradle.kts @@ -13,10 +13,9 @@ buildscript { plugins { application id("org.jetbrains.kotlin.jvm") + id("org.jlleitschuh.gradle.ktlint") version "9.2.1" } -apply(plugin = "org.jlleitschuh.gradle.ktlint") - application { mainClassName = "example.armeria.grpc.kotlin.MainKt" } @@ -29,8 +28,10 @@ dependencies { runtimeOnly("org.slf4j:slf4j-simple") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") - implementation("org.jetbrains.kotlin:kotlin-reflect") - implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") + implementation(kotlin("reflect")) + implementation(kotlin("stdlib-jdk8")) + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core") + implementation("io.grpc:grpc-kotlin-stub") testImplementation("jakarta.annotation:jakarta.annotation-api") testImplementation("net.javacrumbs.json-unit:json-unit-fluent") @@ -44,3 +45,25 @@ tasks.withType { freeCompilerArgs = listOf("-Xjsr305=strict", "-java-parameters") } } + +// TODO(ikhoon): gRPC-Kotlin compiler does not run well in Windows. The generated stub files are added +// intentionally. Remove this once gRPC-Kotlin compiler supports Windows as well. +kotlin { + sourceSets["main"].apply { + kotlin.srcDir("gen-src/main/kotlinGrpc") + } +} + +ktlint { + filter { + exclude { element -> element.file.path.contains("gen-src") } + } +} + +tasks.clean { + for (path in delete) { + if (path == project.ext["genSrcDir"]) { + delete.remove(path) + } + } +} diff --git a/examples/grpc-kotlin/gen-src/main/grpc/example/armeria/grpc/kotlin/HelloServiceGrpc.java b/examples/grpc-kotlin/gen-src/main/grpc/example/armeria/grpc/kotlin/HelloServiceGrpc.java new file mode 100644 index 00000000000..e234a69922e --- /dev/null +++ b/examples/grpc-kotlin/gen-src/main/grpc/example/armeria/grpc/kotlin/HelloServiceGrpc.java @@ -0,0 +1,619 @@ +package example.armeria.grpc.kotlin; + +import static io.grpc.MethodDescriptor.generateFullMethodName; +import static io.grpc.stub.ClientCalls.asyncBidiStreamingCall; +import static io.grpc.stub.ClientCalls.asyncClientStreamingCall; +import static io.grpc.stub.ClientCalls.asyncServerStreamingCall; +import static io.grpc.stub.ClientCalls.asyncUnaryCall; +import static io.grpc.stub.ClientCalls.blockingServerStreamingCall; +import static io.grpc.stub.ClientCalls.blockingUnaryCall; +import static io.grpc.stub.ClientCalls.futureUnaryCall; +import static io.grpc.stub.ServerCalls.asyncBidiStreamingCall; +import static io.grpc.stub.ServerCalls.asyncClientStreamingCall; +import static io.grpc.stub.ServerCalls.asyncServerStreamingCall; +import static io.grpc.stub.ServerCalls.asyncUnaryCall; +import static io.grpc.stub.ServerCalls.asyncUnimplementedStreamingCall; +import static io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall; + +/** + */ +@javax.annotation.Generated( + value = "by gRPC proto compiler (version 1.28.0)", + comments = "Source: hello.proto") +public final class HelloServiceGrpc { + + private HelloServiceGrpc() {} + + public static final String SERVICE_NAME = "example.grpc.hello.HelloService"; + + // Static method descriptors that strictly reflect the proto. + private static volatile io.grpc.MethodDescriptor getHelloMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "Hello", + requestType = example.armeria.grpc.kotlin.Hello.HelloRequest.class, + responseType = example.armeria.grpc.kotlin.Hello.HelloReply.class, + methodType = io.grpc.MethodDescriptor.MethodType.UNARY) + public static io.grpc.MethodDescriptor getHelloMethod() { + io.grpc.MethodDescriptor getHelloMethod; + if ((getHelloMethod = HelloServiceGrpc.getHelloMethod) == null) { + synchronized (HelloServiceGrpc.class) { + if ((getHelloMethod = HelloServiceGrpc.getHelloMethod) == null) { + HelloServiceGrpc.getHelloMethod = getHelloMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.UNARY) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "Hello")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + example.armeria.grpc.kotlin.Hello.HelloRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + example.armeria.grpc.kotlin.Hello.HelloReply.getDefaultInstance())) + .setSchemaDescriptor(new HelloServiceMethodDescriptorSupplier("Hello")) + .build(); + } + } + } + return getHelloMethod; + } + + private static volatile io.grpc.MethodDescriptor getLazyHelloMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "LazyHello", + requestType = example.armeria.grpc.kotlin.Hello.HelloRequest.class, + responseType = example.armeria.grpc.kotlin.Hello.HelloReply.class, + methodType = io.grpc.MethodDescriptor.MethodType.UNARY) + public static io.grpc.MethodDescriptor getLazyHelloMethod() { + io.grpc.MethodDescriptor getLazyHelloMethod; + if ((getLazyHelloMethod = HelloServiceGrpc.getLazyHelloMethod) == null) { + synchronized (HelloServiceGrpc.class) { + if ((getLazyHelloMethod = HelloServiceGrpc.getLazyHelloMethod) == null) { + HelloServiceGrpc.getLazyHelloMethod = getLazyHelloMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.UNARY) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "LazyHello")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + example.armeria.grpc.kotlin.Hello.HelloRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + example.armeria.grpc.kotlin.Hello.HelloReply.getDefaultInstance())) + .setSchemaDescriptor(new HelloServiceMethodDescriptorSupplier("LazyHello")) + .build(); + } + } + } + return getLazyHelloMethod; + } + + private static volatile io.grpc.MethodDescriptor getBlockingHelloMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "BlockingHello", + requestType = example.armeria.grpc.kotlin.Hello.HelloRequest.class, + responseType = example.armeria.grpc.kotlin.Hello.HelloReply.class, + methodType = io.grpc.MethodDescriptor.MethodType.UNARY) + public static io.grpc.MethodDescriptor getBlockingHelloMethod() { + io.grpc.MethodDescriptor getBlockingHelloMethod; + if ((getBlockingHelloMethod = HelloServiceGrpc.getBlockingHelloMethod) == null) { + synchronized (HelloServiceGrpc.class) { + if ((getBlockingHelloMethod = HelloServiceGrpc.getBlockingHelloMethod) == null) { + HelloServiceGrpc.getBlockingHelloMethod = getBlockingHelloMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.UNARY) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "BlockingHello")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + example.armeria.grpc.kotlin.Hello.HelloRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + example.armeria.grpc.kotlin.Hello.HelloReply.getDefaultInstance())) + .setSchemaDescriptor(new HelloServiceMethodDescriptorSupplier("BlockingHello")) + .build(); + } + } + } + return getBlockingHelloMethod; + } + + private static volatile io.grpc.MethodDescriptor getLotsOfRepliesMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "LotsOfReplies", + requestType = example.armeria.grpc.kotlin.Hello.HelloRequest.class, + responseType = example.armeria.grpc.kotlin.Hello.HelloReply.class, + methodType = io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING) + public static io.grpc.MethodDescriptor getLotsOfRepliesMethod() { + io.grpc.MethodDescriptor getLotsOfRepliesMethod; + if ((getLotsOfRepliesMethod = HelloServiceGrpc.getLotsOfRepliesMethod) == null) { + synchronized (HelloServiceGrpc.class) { + if ((getLotsOfRepliesMethod = HelloServiceGrpc.getLotsOfRepliesMethod) == null) { + HelloServiceGrpc.getLotsOfRepliesMethod = getLotsOfRepliesMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "LotsOfReplies")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + example.armeria.grpc.kotlin.Hello.HelloRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + example.armeria.grpc.kotlin.Hello.HelloReply.getDefaultInstance())) + .setSchemaDescriptor(new HelloServiceMethodDescriptorSupplier("LotsOfReplies")) + .build(); + } + } + } + return getLotsOfRepliesMethod; + } + + private static volatile io.grpc.MethodDescriptor getLotsOfGreetingsMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "LotsOfGreetings", + requestType = example.armeria.grpc.kotlin.Hello.HelloRequest.class, + responseType = example.armeria.grpc.kotlin.Hello.HelloReply.class, + methodType = io.grpc.MethodDescriptor.MethodType.CLIENT_STREAMING) + public static io.grpc.MethodDescriptor getLotsOfGreetingsMethod() { + io.grpc.MethodDescriptor getLotsOfGreetingsMethod; + if ((getLotsOfGreetingsMethod = HelloServiceGrpc.getLotsOfGreetingsMethod) == null) { + synchronized (HelloServiceGrpc.class) { + if ((getLotsOfGreetingsMethod = HelloServiceGrpc.getLotsOfGreetingsMethod) == null) { + HelloServiceGrpc.getLotsOfGreetingsMethod = getLotsOfGreetingsMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.CLIENT_STREAMING) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "LotsOfGreetings")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + example.armeria.grpc.kotlin.Hello.HelloRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + example.armeria.grpc.kotlin.Hello.HelloReply.getDefaultInstance())) + .setSchemaDescriptor(new HelloServiceMethodDescriptorSupplier("LotsOfGreetings")) + .build(); + } + } + } + return getLotsOfGreetingsMethod; + } + + private static volatile io.grpc.MethodDescriptor getBidiHelloMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "BidiHello", + requestType = example.armeria.grpc.kotlin.Hello.HelloRequest.class, + responseType = example.armeria.grpc.kotlin.Hello.HelloReply.class, + methodType = io.grpc.MethodDescriptor.MethodType.BIDI_STREAMING) + public static io.grpc.MethodDescriptor getBidiHelloMethod() { + io.grpc.MethodDescriptor getBidiHelloMethod; + if ((getBidiHelloMethod = HelloServiceGrpc.getBidiHelloMethod) == null) { + synchronized (HelloServiceGrpc.class) { + if ((getBidiHelloMethod = HelloServiceGrpc.getBidiHelloMethod) == null) { + HelloServiceGrpc.getBidiHelloMethod = getBidiHelloMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.BIDI_STREAMING) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "BidiHello")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + example.armeria.grpc.kotlin.Hello.HelloRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + example.armeria.grpc.kotlin.Hello.HelloReply.getDefaultInstance())) + .setSchemaDescriptor(new HelloServiceMethodDescriptorSupplier("BidiHello")) + .build(); + } + } + } + return getBidiHelloMethod; + } + + /** + * Creates a new async stub that supports all call types for the service + */ + public static HelloServiceStub newStub(io.grpc.Channel channel) { + io.grpc.stub.AbstractStub.StubFactory factory = + new io.grpc.stub.AbstractStub.StubFactory() { + @java.lang.Override + public HelloServiceStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new HelloServiceStub(channel, callOptions); + } + }; + return HelloServiceStub.newStub(factory, channel); + } + + /** + * Creates a new blocking-style stub that supports unary and streaming output calls on the service + */ + public static HelloServiceBlockingStub newBlockingStub( + io.grpc.Channel channel) { + io.grpc.stub.AbstractStub.StubFactory factory = + new io.grpc.stub.AbstractStub.StubFactory() { + @java.lang.Override + public HelloServiceBlockingStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new HelloServiceBlockingStub(channel, callOptions); + } + }; + return HelloServiceBlockingStub.newStub(factory, channel); + } + + /** + * Creates a new ListenableFuture-style stub that supports unary calls on the service + */ + public static HelloServiceFutureStub newFutureStub( + io.grpc.Channel channel) { + io.grpc.stub.AbstractStub.StubFactory factory = + new io.grpc.stub.AbstractStub.StubFactory() { + @java.lang.Override + public HelloServiceFutureStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new HelloServiceFutureStub(channel, callOptions); + } + }; + return HelloServiceFutureStub.newStub(factory, channel); + } + + /** + */ + public static abstract class HelloServiceImplBase implements io.grpc.BindableService { + + /** + */ + public void hello(example.armeria.grpc.kotlin.Hello.HelloRequest request, + io.grpc.stub.StreamObserver responseObserver) { + asyncUnimplementedUnaryCall(getHelloMethod(), responseObserver); + } + + /** + */ + public void lazyHello(example.armeria.grpc.kotlin.Hello.HelloRequest request, + io.grpc.stub.StreamObserver responseObserver) { + asyncUnimplementedUnaryCall(getLazyHelloMethod(), responseObserver); + } + + /** + */ + public void blockingHello(example.armeria.grpc.kotlin.Hello.HelloRequest request, + io.grpc.stub.StreamObserver responseObserver) { + asyncUnimplementedUnaryCall(getBlockingHelloMethod(), responseObserver); + } + + /** + */ + public void lotsOfReplies(example.armeria.grpc.kotlin.Hello.HelloRequest request, + io.grpc.stub.StreamObserver responseObserver) { + asyncUnimplementedUnaryCall(getLotsOfRepliesMethod(), responseObserver); + } + + /** + */ + public io.grpc.stub.StreamObserver lotsOfGreetings( + io.grpc.stub.StreamObserver responseObserver) { + return asyncUnimplementedStreamingCall(getLotsOfGreetingsMethod(), responseObserver); + } + + /** + */ + public io.grpc.stub.StreamObserver bidiHello( + io.grpc.stub.StreamObserver responseObserver) { + return asyncUnimplementedStreamingCall(getBidiHelloMethod(), responseObserver); + } + + @java.lang.Override public final io.grpc.ServerServiceDefinition bindService() { + return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor()) + .addMethod( + getHelloMethod(), + asyncUnaryCall( + new MethodHandlers< + example.armeria.grpc.kotlin.Hello.HelloRequest, + example.armeria.grpc.kotlin.Hello.HelloReply>( + this, METHODID_HELLO))) + .addMethod( + getLazyHelloMethod(), + asyncUnaryCall( + new MethodHandlers< + example.armeria.grpc.kotlin.Hello.HelloRequest, + example.armeria.grpc.kotlin.Hello.HelloReply>( + this, METHODID_LAZY_HELLO))) + .addMethod( + getBlockingHelloMethod(), + asyncUnaryCall( + new MethodHandlers< + example.armeria.grpc.kotlin.Hello.HelloRequest, + example.armeria.grpc.kotlin.Hello.HelloReply>( + this, METHODID_BLOCKING_HELLO))) + .addMethod( + getLotsOfRepliesMethod(), + asyncServerStreamingCall( + new MethodHandlers< + example.armeria.grpc.kotlin.Hello.HelloRequest, + example.armeria.grpc.kotlin.Hello.HelloReply>( + this, METHODID_LOTS_OF_REPLIES))) + .addMethod( + getLotsOfGreetingsMethod(), + asyncClientStreamingCall( + new MethodHandlers< + example.armeria.grpc.kotlin.Hello.HelloRequest, + example.armeria.grpc.kotlin.Hello.HelloReply>( + this, METHODID_LOTS_OF_GREETINGS))) + .addMethod( + getBidiHelloMethod(), + asyncBidiStreamingCall( + new MethodHandlers< + example.armeria.grpc.kotlin.Hello.HelloRequest, + example.armeria.grpc.kotlin.Hello.HelloReply>( + this, METHODID_BIDI_HELLO))) + .build(); + } + } + + /** + */ + public static final class HelloServiceStub extends io.grpc.stub.AbstractAsyncStub { + private HelloServiceStub( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + super(channel, callOptions); + } + + @java.lang.Override + protected HelloServiceStub build( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new HelloServiceStub(channel, callOptions); + } + + /** + */ + public void hello(example.armeria.grpc.kotlin.Hello.HelloRequest request, + io.grpc.stub.StreamObserver responseObserver) { + asyncUnaryCall( + getChannel().newCall(getHelloMethod(), getCallOptions()), request, responseObserver); + } + + /** + */ + public void lazyHello(example.armeria.grpc.kotlin.Hello.HelloRequest request, + io.grpc.stub.StreamObserver responseObserver) { + asyncUnaryCall( + getChannel().newCall(getLazyHelloMethod(), getCallOptions()), request, responseObserver); + } + + /** + */ + public void blockingHello(example.armeria.grpc.kotlin.Hello.HelloRequest request, + io.grpc.stub.StreamObserver responseObserver) { + asyncUnaryCall( + getChannel().newCall(getBlockingHelloMethod(), getCallOptions()), request, responseObserver); + } + + /** + */ + public void lotsOfReplies(example.armeria.grpc.kotlin.Hello.HelloRequest request, + io.grpc.stub.StreamObserver responseObserver) { + asyncServerStreamingCall( + getChannel().newCall(getLotsOfRepliesMethod(), getCallOptions()), request, responseObserver); + } + + /** + */ + public io.grpc.stub.StreamObserver lotsOfGreetings( + io.grpc.stub.StreamObserver responseObserver) { + return asyncClientStreamingCall( + getChannel().newCall(getLotsOfGreetingsMethod(), getCallOptions()), responseObserver); + } + + /** + */ + public io.grpc.stub.StreamObserver bidiHello( + io.grpc.stub.StreamObserver responseObserver) { + return asyncBidiStreamingCall( + getChannel().newCall(getBidiHelloMethod(), getCallOptions()), responseObserver); + } + } + + /** + */ + public static final class HelloServiceBlockingStub extends io.grpc.stub.AbstractBlockingStub { + private HelloServiceBlockingStub( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + super(channel, callOptions); + } + + @java.lang.Override + protected HelloServiceBlockingStub build( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new HelloServiceBlockingStub(channel, callOptions); + } + + /** + */ + public example.armeria.grpc.kotlin.Hello.HelloReply hello(example.armeria.grpc.kotlin.Hello.HelloRequest request) { + return blockingUnaryCall( + getChannel(), getHelloMethod(), getCallOptions(), request); + } + + /** + */ + public example.armeria.grpc.kotlin.Hello.HelloReply lazyHello(example.armeria.grpc.kotlin.Hello.HelloRequest request) { + return blockingUnaryCall( + getChannel(), getLazyHelloMethod(), getCallOptions(), request); + } + + /** + */ + public example.armeria.grpc.kotlin.Hello.HelloReply blockingHello(example.armeria.grpc.kotlin.Hello.HelloRequest request) { + return blockingUnaryCall( + getChannel(), getBlockingHelloMethod(), getCallOptions(), request); + } + + /** + */ + public java.util.Iterator lotsOfReplies( + example.armeria.grpc.kotlin.Hello.HelloRequest request) { + return blockingServerStreamingCall( + getChannel(), getLotsOfRepliesMethod(), getCallOptions(), request); + } + } + + /** + */ + public static final class HelloServiceFutureStub extends io.grpc.stub.AbstractFutureStub { + private HelloServiceFutureStub( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + super(channel, callOptions); + } + + @java.lang.Override + protected HelloServiceFutureStub build( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new HelloServiceFutureStub(channel, callOptions); + } + + /** + */ + public com.google.common.util.concurrent.ListenableFuture hello( + example.armeria.grpc.kotlin.Hello.HelloRequest request) { + return futureUnaryCall( + getChannel().newCall(getHelloMethod(), getCallOptions()), request); + } + + /** + */ + public com.google.common.util.concurrent.ListenableFuture lazyHello( + example.armeria.grpc.kotlin.Hello.HelloRequest request) { + return futureUnaryCall( + getChannel().newCall(getLazyHelloMethod(), getCallOptions()), request); + } + + /** + */ + public com.google.common.util.concurrent.ListenableFuture blockingHello( + example.armeria.grpc.kotlin.Hello.HelloRequest request) { + return futureUnaryCall( + getChannel().newCall(getBlockingHelloMethod(), getCallOptions()), request); + } + } + + private static final int METHODID_HELLO = 0; + private static final int METHODID_LAZY_HELLO = 1; + private static final int METHODID_BLOCKING_HELLO = 2; + private static final int METHODID_LOTS_OF_REPLIES = 3; + private static final int METHODID_LOTS_OF_GREETINGS = 4; + private static final int METHODID_BIDI_HELLO = 5; + + private static final class MethodHandlers implements + io.grpc.stub.ServerCalls.UnaryMethod, + io.grpc.stub.ServerCalls.ServerStreamingMethod, + io.grpc.stub.ServerCalls.ClientStreamingMethod, + io.grpc.stub.ServerCalls.BidiStreamingMethod { + private final HelloServiceImplBase serviceImpl; + private final int methodId; + + MethodHandlers(HelloServiceImplBase serviceImpl, int methodId) { + this.serviceImpl = serviceImpl; + this.methodId = methodId; + } + + @java.lang.Override + @java.lang.SuppressWarnings("unchecked") + public void invoke(Req request, io.grpc.stub.StreamObserver responseObserver) { + switch (methodId) { + case METHODID_HELLO: + serviceImpl.hello((example.armeria.grpc.kotlin.Hello.HelloRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + case METHODID_LAZY_HELLO: + serviceImpl.lazyHello((example.armeria.grpc.kotlin.Hello.HelloRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + case METHODID_BLOCKING_HELLO: + serviceImpl.blockingHello((example.armeria.grpc.kotlin.Hello.HelloRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + case METHODID_LOTS_OF_REPLIES: + serviceImpl.lotsOfReplies((example.armeria.grpc.kotlin.Hello.HelloRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + default: + throw new AssertionError(); + } + } + + @java.lang.Override + @java.lang.SuppressWarnings("unchecked") + public io.grpc.stub.StreamObserver invoke( + io.grpc.stub.StreamObserver responseObserver) { + switch (methodId) { + case METHODID_LOTS_OF_GREETINGS: + return (io.grpc.stub.StreamObserver) serviceImpl.lotsOfGreetings( + (io.grpc.stub.StreamObserver) responseObserver); + case METHODID_BIDI_HELLO: + return (io.grpc.stub.StreamObserver) serviceImpl.bidiHello( + (io.grpc.stub.StreamObserver) responseObserver); + default: + throw new AssertionError(); + } + } + } + + private static abstract class HelloServiceBaseDescriptorSupplier + implements io.grpc.protobuf.ProtoFileDescriptorSupplier, io.grpc.protobuf.ProtoServiceDescriptorSupplier { + HelloServiceBaseDescriptorSupplier() {} + + @java.lang.Override + public com.google.protobuf.Descriptors.FileDescriptor getFileDescriptor() { + return example.armeria.grpc.kotlin.Hello.getDescriptor(); + } + + @java.lang.Override + public com.google.protobuf.Descriptors.ServiceDescriptor getServiceDescriptor() { + return getFileDescriptor().findServiceByName("HelloService"); + } + } + + private static final class HelloServiceFileDescriptorSupplier + extends HelloServiceBaseDescriptorSupplier { + HelloServiceFileDescriptorSupplier() {} + } + + private static final class HelloServiceMethodDescriptorSupplier + extends HelloServiceBaseDescriptorSupplier + implements io.grpc.protobuf.ProtoMethodDescriptorSupplier { + private final String methodName; + + HelloServiceMethodDescriptorSupplier(String methodName) { + this.methodName = methodName; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.MethodDescriptor getMethodDescriptor() { + return getServiceDescriptor().findMethodByName(methodName); + } + } + + private static volatile io.grpc.ServiceDescriptor serviceDescriptor; + + public static io.grpc.ServiceDescriptor getServiceDescriptor() { + io.grpc.ServiceDescriptor result = serviceDescriptor; + if (result == null) { + synchronized (HelloServiceGrpc.class) { + result = serviceDescriptor; + if (result == null) { + serviceDescriptor = result = io.grpc.ServiceDescriptor.newBuilder(SERVICE_NAME) + .setSchemaDescriptor(new HelloServiceFileDescriptorSupplier()) + .addMethod(getHelloMethod()) + .addMethod(getLazyHelloMethod()) + .addMethod(getBlockingHelloMethod()) + .addMethod(getLotsOfRepliesMethod()) + .addMethod(getLotsOfGreetingsMethod()) + .addMethod(getBidiHelloMethod()) + .build(); + } + } + } + return result; + } +} diff --git a/examples/grpc-kotlin/gen-src/main/java/example/armeria/grpc/kotlin/Hello.java b/examples/grpc-kotlin/gen-src/main/java/example/armeria/grpc/kotlin/Hello.java new file mode 100644 index 00000000000..b290746a2b7 --- /dev/null +++ b/examples/grpc-kotlin/gen-src/main/java/example/armeria/grpc/kotlin/Hello.java @@ -0,0 +1,1205 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: hello.proto + +package example.armeria.grpc.kotlin; + +public final class Hello { + private Hello() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistryLite registry) { + } + + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + registerAllExtensions( + (com.google.protobuf.ExtensionRegistryLite) registry); + } + public interface HelloRequestOrBuilder extends + // @@protoc_insertion_point(interface_extends:example.grpc.hello.HelloRequest) + com.google.protobuf.MessageOrBuilder { + + /** + * string name = 1; + * @return The name. + */ + java.lang.String getName(); + /** + * string name = 1; + * @return The bytes for name. + */ + com.google.protobuf.ByteString + getNameBytes(); + } + /** + * Protobuf type {@code example.grpc.hello.HelloRequest} + */ + public static final class HelloRequest extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:example.grpc.hello.HelloRequest) + HelloRequestOrBuilder { + private static final long serialVersionUID = 0L; + // Use HelloRequest.newBuilder() to construct. + private HelloRequest(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + private HelloRequest() { + name_ = ""; + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new HelloRequest(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private HelloRequest( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + java.lang.String s = input.readStringRequireUtf8(); + + name_ = s; + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return example.armeria.grpc.kotlin.Hello.internal_static_example_grpc_hello_HelloRequest_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return example.armeria.grpc.kotlin.Hello.internal_static_example_grpc_hello_HelloRequest_fieldAccessorTable + .ensureFieldAccessorsInitialized( + example.armeria.grpc.kotlin.Hello.HelloRequest.class, example.armeria.grpc.kotlin.Hello.HelloRequest.Builder.class); + } + + public static final int NAME_FIELD_NUMBER = 1; + private volatile java.lang.Object name_; + /** + * string name = 1; + * @return The name. + */ + public java.lang.String getName() { + java.lang.Object ref = name_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + name_ = s; + return s; + } + } + /** + * string name = 1; + * @return The bytes for name. + */ + public com.google.protobuf.ByteString + getNameBytes() { + java.lang.Object ref = name_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!getNameBytes().isEmpty()) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 1, name_); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!getNameBytes().isEmpty()) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, name_); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof example.armeria.grpc.kotlin.Hello.HelloRequest)) { + return super.equals(obj); + } + example.armeria.grpc.kotlin.Hello.HelloRequest other = (example.armeria.grpc.kotlin.Hello.HelloRequest) obj; + + if (!getName() + .equals(other.getName())) return false; + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + NAME_FIELD_NUMBER; + hash = (53 * hash) + getName().hashCode(); + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static example.armeria.grpc.kotlin.Hello.HelloRequest parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static example.armeria.grpc.kotlin.Hello.HelloRequest parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static example.armeria.grpc.kotlin.Hello.HelloRequest parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static example.armeria.grpc.kotlin.Hello.HelloRequest parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static example.armeria.grpc.kotlin.Hello.HelloRequest parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static example.armeria.grpc.kotlin.Hello.HelloRequest parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static example.armeria.grpc.kotlin.Hello.HelloRequest parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static example.armeria.grpc.kotlin.Hello.HelloRequest parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static example.armeria.grpc.kotlin.Hello.HelloRequest parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static example.armeria.grpc.kotlin.Hello.HelloRequest parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static example.armeria.grpc.kotlin.Hello.HelloRequest parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static example.armeria.grpc.kotlin.Hello.HelloRequest parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(example.armeria.grpc.kotlin.Hello.HelloRequest prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code example.grpc.hello.HelloRequest} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder implements + // @@protoc_insertion_point(builder_implements:example.grpc.hello.HelloRequest) + example.armeria.grpc.kotlin.Hello.HelloRequestOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return example.armeria.grpc.kotlin.Hello.internal_static_example_grpc_hello_HelloRequest_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return example.armeria.grpc.kotlin.Hello.internal_static_example_grpc_hello_HelloRequest_fieldAccessorTable + .ensureFieldAccessorsInitialized( + example.armeria.grpc.kotlin.Hello.HelloRequest.class, example.armeria.grpc.kotlin.Hello.HelloRequest.Builder.class); + } + + // Construct using example.armeria.grpc.kotlin.Hello.HelloRequest.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + name_ = ""; + + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return example.armeria.grpc.kotlin.Hello.internal_static_example_grpc_hello_HelloRequest_descriptor; + } + + @java.lang.Override + public example.armeria.grpc.kotlin.Hello.HelloRequest getDefaultInstanceForType() { + return example.armeria.grpc.kotlin.Hello.HelloRequest.getDefaultInstance(); + } + + @java.lang.Override + public example.armeria.grpc.kotlin.Hello.HelloRequest build() { + example.armeria.grpc.kotlin.Hello.HelloRequest result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public example.armeria.grpc.kotlin.Hello.HelloRequest buildPartial() { + example.armeria.grpc.kotlin.Hello.HelloRequest result = new example.armeria.grpc.kotlin.Hello.HelloRequest(this); + result.name_ = name_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof example.armeria.grpc.kotlin.Hello.HelloRequest) { + return mergeFrom((example.armeria.grpc.kotlin.Hello.HelloRequest)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(example.armeria.grpc.kotlin.Hello.HelloRequest other) { + if (other == example.armeria.grpc.kotlin.Hello.HelloRequest.getDefaultInstance()) return this; + if (!other.getName().isEmpty()) { + name_ = other.name_; + onChanged(); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + example.armeria.grpc.kotlin.Hello.HelloRequest parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (example.armeria.grpc.kotlin.Hello.HelloRequest) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + + private java.lang.Object name_ = ""; + /** + * string name = 1; + * @return The name. + */ + public java.lang.String getName() { + java.lang.Object ref = name_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + name_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * string name = 1; + * @return The bytes for name. + */ + public com.google.protobuf.ByteString + getNameBytes() { + java.lang.Object ref = name_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * string name = 1; + * @param value The name to set. + * @return This builder for chaining. + */ + public Builder setName( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + + name_ = value; + onChanged(); + return this; + } + /** + * string name = 1; + * @return This builder for chaining. + */ + public Builder clearName() { + + name_ = getDefaultInstance().getName(); + onChanged(); + return this; + } + /** + * string name = 1; + * @param value The bytes for name to set. + * @return This builder for chaining. + */ + public Builder setNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + checkByteStringIsUtf8(value); + + name_ = value; + onChanged(); + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:example.grpc.hello.HelloRequest) + } + + // @@protoc_insertion_point(class_scope:example.grpc.hello.HelloRequest) + private static final example.armeria.grpc.kotlin.Hello.HelloRequest DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new example.armeria.grpc.kotlin.Hello.HelloRequest(); + } + + public static example.armeria.grpc.kotlin.Hello.HelloRequest getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public HelloRequest parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new HelloRequest(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public example.armeria.grpc.kotlin.Hello.HelloRequest getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + public interface HelloReplyOrBuilder extends + // @@protoc_insertion_point(interface_extends:example.grpc.hello.HelloReply) + com.google.protobuf.MessageOrBuilder { + + /** + * string message = 1; + * @return The message. + */ + java.lang.String getMessage(); + /** + * string message = 1; + * @return The bytes for message. + */ + com.google.protobuf.ByteString + getMessageBytes(); + } + /** + * Protobuf type {@code example.grpc.hello.HelloReply} + */ + public static final class HelloReply extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:example.grpc.hello.HelloReply) + HelloReplyOrBuilder { + private static final long serialVersionUID = 0L; + // Use HelloReply.newBuilder() to construct. + private HelloReply(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + private HelloReply() { + message_ = ""; + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new HelloReply(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private HelloReply( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + java.lang.String s = input.readStringRequireUtf8(); + + message_ = s; + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return example.armeria.grpc.kotlin.Hello.internal_static_example_grpc_hello_HelloReply_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return example.armeria.grpc.kotlin.Hello.internal_static_example_grpc_hello_HelloReply_fieldAccessorTable + .ensureFieldAccessorsInitialized( + example.armeria.grpc.kotlin.Hello.HelloReply.class, example.armeria.grpc.kotlin.Hello.HelloReply.Builder.class); + } + + public static final int MESSAGE_FIELD_NUMBER = 1; + private volatile java.lang.Object message_; + /** + * string message = 1; + * @return The message. + */ + public java.lang.String getMessage() { + java.lang.Object ref = message_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + message_ = s; + return s; + } + } + /** + * string message = 1; + * @return The bytes for message. + */ + public com.google.protobuf.ByteString + getMessageBytes() { + java.lang.Object ref = message_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + message_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!getMessageBytes().isEmpty()) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 1, message_); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!getMessageBytes().isEmpty()) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, message_); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof example.armeria.grpc.kotlin.Hello.HelloReply)) { + return super.equals(obj); + } + example.armeria.grpc.kotlin.Hello.HelloReply other = (example.armeria.grpc.kotlin.Hello.HelloReply) obj; + + if (!getMessage() + .equals(other.getMessage())) return false; + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + MESSAGE_FIELD_NUMBER; + hash = (53 * hash) + getMessage().hashCode(); + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static example.armeria.grpc.kotlin.Hello.HelloReply parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static example.armeria.grpc.kotlin.Hello.HelloReply parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static example.armeria.grpc.kotlin.Hello.HelloReply parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static example.armeria.grpc.kotlin.Hello.HelloReply parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static example.armeria.grpc.kotlin.Hello.HelloReply parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static example.armeria.grpc.kotlin.Hello.HelloReply parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static example.armeria.grpc.kotlin.Hello.HelloReply parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static example.armeria.grpc.kotlin.Hello.HelloReply parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static example.armeria.grpc.kotlin.Hello.HelloReply parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static example.armeria.grpc.kotlin.Hello.HelloReply parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static example.armeria.grpc.kotlin.Hello.HelloReply parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static example.armeria.grpc.kotlin.Hello.HelloReply parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(example.armeria.grpc.kotlin.Hello.HelloReply prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code example.grpc.hello.HelloReply} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder implements + // @@protoc_insertion_point(builder_implements:example.grpc.hello.HelloReply) + example.armeria.grpc.kotlin.Hello.HelloReplyOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return example.armeria.grpc.kotlin.Hello.internal_static_example_grpc_hello_HelloReply_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return example.armeria.grpc.kotlin.Hello.internal_static_example_grpc_hello_HelloReply_fieldAccessorTable + .ensureFieldAccessorsInitialized( + example.armeria.grpc.kotlin.Hello.HelloReply.class, example.armeria.grpc.kotlin.Hello.HelloReply.Builder.class); + } + + // Construct using example.armeria.grpc.kotlin.Hello.HelloReply.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + message_ = ""; + + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return example.armeria.grpc.kotlin.Hello.internal_static_example_grpc_hello_HelloReply_descriptor; + } + + @java.lang.Override + public example.armeria.grpc.kotlin.Hello.HelloReply getDefaultInstanceForType() { + return example.armeria.grpc.kotlin.Hello.HelloReply.getDefaultInstance(); + } + + @java.lang.Override + public example.armeria.grpc.kotlin.Hello.HelloReply build() { + example.armeria.grpc.kotlin.Hello.HelloReply result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public example.armeria.grpc.kotlin.Hello.HelloReply buildPartial() { + example.armeria.grpc.kotlin.Hello.HelloReply result = new example.armeria.grpc.kotlin.Hello.HelloReply(this); + result.message_ = message_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof example.armeria.grpc.kotlin.Hello.HelloReply) { + return mergeFrom((example.armeria.grpc.kotlin.Hello.HelloReply)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(example.armeria.grpc.kotlin.Hello.HelloReply other) { + if (other == example.armeria.grpc.kotlin.Hello.HelloReply.getDefaultInstance()) return this; + if (!other.getMessage().isEmpty()) { + message_ = other.message_; + onChanged(); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + example.armeria.grpc.kotlin.Hello.HelloReply parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (example.armeria.grpc.kotlin.Hello.HelloReply) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + + private java.lang.Object message_ = ""; + /** + * string message = 1; + * @return The message. + */ + public java.lang.String getMessage() { + java.lang.Object ref = message_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + message_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * string message = 1; + * @return The bytes for message. + */ + public com.google.protobuf.ByteString + getMessageBytes() { + java.lang.Object ref = message_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + message_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * string message = 1; + * @param value The message to set. + * @return This builder for chaining. + */ + public Builder setMessage( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + + message_ = value; + onChanged(); + return this; + } + /** + * string message = 1; + * @return This builder for chaining. + */ + public Builder clearMessage() { + + message_ = getDefaultInstance().getMessage(); + onChanged(); + return this; + } + /** + * string message = 1; + * @param value The bytes for message to set. + * @return This builder for chaining. + */ + public Builder setMessageBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + checkByteStringIsUtf8(value); + + message_ = value; + onChanged(); + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:example.grpc.hello.HelloReply) + } + + // @@protoc_insertion_point(class_scope:example.grpc.hello.HelloReply) + private static final example.armeria.grpc.kotlin.Hello.HelloReply DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new example.armeria.grpc.kotlin.Hello.HelloReply(); + } + + public static example.armeria.grpc.kotlin.Hello.HelloReply getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public HelloReply parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new HelloReply(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public example.armeria.grpc.kotlin.Hello.HelloReply getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_example_grpc_hello_HelloRequest_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_example_grpc_hello_HelloRequest_fieldAccessorTable; + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_example_grpc_hello_HelloReply_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_example_grpc_hello_HelloReply_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + java.lang.String[] descriptorData = { + "\n\013hello.proto\022\022example.grpc.hello\"\034\n\014Hel" + + "loRequest\022\014\n\004name\030\001 \001(\t\"\035\n\nHelloReply\022\017\n" + + "\007message\030\001 \001(\t2\206\004\n\014HelloService\022K\n\005Hello" + + "\022 .example.grpc.hello.HelloRequest\032\036.exa" + + "mple.grpc.hello.HelloReply\"\000\022O\n\tLazyHell" + + "o\022 .example.grpc.hello.HelloRequest\032\036.ex" + + "ample.grpc.hello.HelloReply\"\000\022S\n\rBlockin" + + "gHello\022 .example.grpc.hello.HelloRequest" + + "\032\036.example.grpc.hello.HelloReply\"\000\022U\n\rLo" + + "tsOfReplies\022 .example.grpc.hello.HelloRe" + + "quest\032\036.example.grpc.hello.HelloReply\"\0000" + + "\001\022W\n\017LotsOfGreetings\022 .example.grpc.hell" + + "o.HelloRequest\032\036.example.grpc.hello.Hell" + + "oReply\"\000(\001\022S\n\tBidiHello\022 .example.grpc.h" + + "ello.HelloRequest\032\036.example.grpc.hello.H" + + "elloReply\"\000(\0010\001B\035\n\033example.armeria.grpc." + + "kotlinb\006proto3" + }; + descriptor = com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }); + internal_static_example_grpc_hello_HelloRequest_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_example_grpc_hello_HelloRequest_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_example_grpc_hello_HelloRequest_descriptor, + new java.lang.String[] { "Name", }); + internal_static_example_grpc_hello_HelloReply_descriptor = + getDescriptor().getMessageTypes().get(1); + internal_static_example_grpc_hello_HelloReply_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_example_grpc_hello_HelloReply_descriptor, + new java.lang.String[] { "Message", }); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/examples/grpc-kotlin/gen-src/main/kotlinGrpc/example/armeria/grpc/kotlin/HelloGrpcKt.kt b/examples/grpc-kotlin/gen-src/main/kotlinGrpc/example/armeria/grpc/kotlin/HelloGrpcKt.kt new file mode 100644 index 00000000000..ae82647f567 --- /dev/null +++ b/examples/grpc-kotlin/gen-src/main/kotlinGrpc/example/armeria/grpc/kotlin/HelloGrpcKt.kt @@ -0,0 +1,297 @@ +package example.armeria.grpc.kotlin + +import example.armeria.grpc.kotlin.HelloServiceGrpc.getServiceDescriptor +import io.grpc.CallOptions +import io.grpc.CallOptions.DEFAULT +import io.grpc.Channel +import io.grpc.Metadata +import io.grpc.ServerServiceDefinition +import io.grpc.ServerServiceDefinition.builder +import io.grpc.Status +import io.grpc.Status.UNIMPLEMENTED +import io.grpc.StatusException +import io.grpc.kotlin.AbstractCoroutineServerImpl +import io.grpc.kotlin.AbstractCoroutineStub +import io.grpc.kotlin.ClientCalls +import io.grpc.kotlin.ClientCalls.bidiStreamingRpc +import io.grpc.kotlin.ClientCalls.clientStreamingRpc +import io.grpc.kotlin.ClientCalls.serverStreamingRpc +import io.grpc.kotlin.ClientCalls.unaryRpc +import io.grpc.kotlin.ServerCalls +import io.grpc.kotlin.ServerCalls.bidiStreamingServerMethodDefinition +import io.grpc.kotlin.ServerCalls.clientStreamingServerMethodDefinition +import io.grpc.kotlin.ServerCalls.serverStreamingServerMethodDefinition +import io.grpc.kotlin.ServerCalls.unaryServerMethodDefinition +import io.grpc.kotlin.StubFor +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext +import kotlin.jvm.JvmOverloads +import kotlinx.coroutines.flow.Flow + +/** + * Holder for Kotlin coroutine-based client and server APIs for example.grpc.hello.HelloService. + */ +object HelloServiceGrpcKt { + /** + * A stub for issuing RPCs to a(n) example.grpc.hello.HelloService service as suspending + * coroutines. + */ + @StubFor(HelloServiceGrpc::class) + class HelloServiceCoroutineStub @JvmOverloads constructor( + channel: Channel, + callOptions: CallOptions = DEFAULT + ) : AbstractCoroutineStub(channel, callOptions) { + override fun build(channel: Channel, callOptions: CallOptions): HelloServiceCoroutineStub = + HelloServiceCoroutineStub(channel, callOptions) + + /** + * Executes this RPC and returns the response message, suspending until the RPC completes + * with [`Status.OK`][Status]. If the RPC completes with another status, a corresponding + * [StatusException] is thrown. If this coroutine is cancelled, the RPC is also cancelled + * with the corresponding exception as a cause. + * + * @param request The request message to send to the server. + * + * @return The single response from the server. + */ + suspend fun hello(request: Hello.HelloRequest): Hello.HelloReply = unaryRpc( + channel, + HelloServiceGrpc.getHelloMethod(), + request, + callOptions, + Metadata() + ) + /** + * Executes this RPC and returns the response message, suspending until the RPC completes + * with [`Status.OK`][Status]. If the RPC completes with another status, a corresponding + * [StatusException] is thrown. If this coroutine is cancelled, the RPC is also cancelled + * with the corresponding exception as a cause. + * + * @param request The request message to send to the server. + * + * @return The single response from the server. + */ + suspend fun lazyHello(request: Hello.HelloRequest): Hello.HelloReply = unaryRpc( + channel, + HelloServiceGrpc.getLazyHelloMethod(), + request, + callOptions, + Metadata() + ) + /** + * Executes this RPC and returns the response message, suspending until the RPC completes + * with [`Status.OK`][Status]. If the RPC completes with another status, a corresponding + * [StatusException] is thrown. If this coroutine is cancelled, the RPC is also cancelled + * with the corresponding exception as a cause. + * + * @param request The request message to send to the server. + * + * @return The single response from the server. + */ + suspend fun blockingHello(request: Hello.HelloRequest): Hello.HelloReply = unaryRpc( + channel, + HelloServiceGrpc.getBlockingHelloMethod(), + request, + callOptions, + Metadata() + ) + /** + * Returns a [Flow] that, when collected, executes this RPC and emits responses from the + * server as they arrive. That flow finishes normally if the server closes its response with + * [`Status.OK`][Status], and fails by throwing a [StatusException] otherwise. If + * collecting the flow downstream fails exceptionally (including via cancellation), the RPC + * is cancelled with that exception as a cause. + * + * @param request The request message to send to the server. + * + * @return A flow that, when collected, emits the responses from the server. + */ + fun lotsOfReplies(request: Hello.HelloRequest): Flow = serverStreamingRpc( + channel, + HelloServiceGrpc.getLotsOfRepliesMethod(), + request, + callOptions, + Metadata() + ) + /** + * Executes this RPC and returns the response message, suspending until the RPC completes + * with [`Status.OK`][Status]. If the RPC completes with another status, a corresponding + * [StatusException] is thrown. If this coroutine is cancelled, the RPC is also cancelled + * with the corresponding exception as a cause. + * + * This function collects the [Flow] of requests. If the server terminates the RPC + * for any reason before collection of requests is complete, the collection of requests + * will be cancelled. If the collection of requests completes exceptionally for any other + * reason, the RPC will be cancelled for that reason and this method will throw that + * exception. + * + * @param requests A [Flow] of request messages. + * + * @return The single response from the server. + */ + suspend fun lotsOfGreetings(requests: Flow): Hello.HelloReply = + clientStreamingRpc( + channel, + HelloServiceGrpc.getLotsOfGreetingsMethod(), + requests, + callOptions, + Metadata() + ) + /** + * Returns a [Flow] that, when collected, executes this RPC and emits responses from the + * server as they arrive. That flow finishes normally if the server closes its response with + * [`Status.OK`][Status], and fails by throwing a [StatusException] otherwise. If + * collecting the flow downstream fails exceptionally (including via cancellation), the RPC + * is cancelled with that exception as a cause. + * + * The [Flow] of requests is collected once each time the [Flow] of responses is + * collected. If collection of the [Flow] of responses completes normally or + * exceptionally before collection of `requests` completes, the collection of + * `requests` is cancelled. If the collection of `requests` completes + * exceptionally for any other reason, then the collection of the [Flow] of responses + * completes exceptionally for the same reason and the RPC is cancelled with that reason. + * + * @param requests A [Flow] of request messages. + * + * @return A flow that, when collected, emits the responses from the server. + */ + fun bidiHello(requests: Flow): Flow = bidiStreamingRpc( + channel, + HelloServiceGrpc.getBidiHelloMethod(), + requests, + callOptions, + Metadata() + )} + + /** + * Skeletal implementation of the example.grpc.hello.HelloService service based on Kotlin + * coroutines. + */ + abstract class HelloServiceCoroutineImplBase( + coroutineContext: CoroutineContext = EmptyCoroutineContext + ) : AbstractCoroutineServerImpl(coroutineContext) { + /** + * Returns the response to an RPC for example.grpc.hello.HelloService.Hello. + * + * If this method fails with a [StatusException], the RPC will fail with the corresponding + * [Status]. If this method fails with a [java.util.concurrent.CancellationException], the RPC + * will fail + * with status `Status.CANCELLED`. If this method fails for any other reason, the RPC will + * fail with `Status.UNKNOWN` with the exception as a cause. + * + * @param request The request from the client. + */ + open suspend fun hello(request: Hello.HelloRequest): Hello.HelloReply = throw + StatusException(UNIMPLEMENTED.withDescription("Method example.grpc.hello.HelloService.Hello is unimplemented")) + + /** + * Returns the response to an RPC for example.grpc.hello.HelloService.LazyHello. + * + * If this method fails with a [StatusException], the RPC will fail with the corresponding + * [Status]. If this method fails with a [java.util.concurrent.CancellationException], the RPC + * will fail + * with status `Status.CANCELLED`. If this method fails for any other reason, the RPC will + * fail with `Status.UNKNOWN` with the exception as a cause. + * + * @param request The request from the client. + */ + open suspend fun lazyHello(request: Hello.HelloRequest): Hello.HelloReply = throw + StatusException(UNIMPLEMENTED.withDescription("Method example.grpc.hello.HelloService.LazyHello is unimplemented")) + + /** + * Returns the response to an RPC for example.grpc.hello.HelloService.BlockingHello. + * + * If this method fails with a [StatusException], the RPC will fail with the corresponding + * [Status]. If this method fails with a [java.util.concurrent.CancellationException], the RPC + * will fail + * with status `Status.CANCELLED`. If this method fails for any other reason, the RPC will + * fail with `Status.UNKNOWN` with the exception as a cause. + * + * @param request The request from the client. + */ + open suspend fun blockingHello(request: Hello.HelloRequest): Hello.HelloReply = throw + StatusException(UNIMPLEMENTED.withDescription("Method example.grpc.hello.HelloService.BlockingHello is unimplemented")) + + /** + * Returns a [Flow] of responses to an RPC for example.grpc.hello.HelloService.LotsOfReplies. + * + * If creating or collecting the returned flow fails with a [StatusException], the RPC + * will fail with the corresponding [Status]. If it fails with a + * [java.util.concurrent.CancellationException], the RPC will fail with status + * `Status.CANCELLED`. If creating + * or collecting the returned flow fails for any other reason, the RPC will fail with + * `Status.UNKNOWN` with the exception as a cause. + * + * @param request The request from the client. + */ + open fun lotsOfReplies(request: Hello.HelloRequest): Flow = throw + StatusException(UNIMPLEMENTED.withDescription("Method example.grpc.hello.HelloService.LotsOfReplies is unimplemented")) + + /** + * Returns the response to an RPC for example.grpc.hello.HelloService.LotsOfGreetings. + * + * If this method fails with a [StatusException], the RPC will fail with the corresponding + * [Status]. If this method fails with a [java.util.concurrent.CancellationException], the RPC + * will fail + * with status `Status.CANCELLED`. If this method fails for any other reason, the RPC will + * fail with `Status.UNKNOWN` with the exception as a cause. + * + * @param requests A [Flow] of requests from the client. This flow can be + * collected only once and throws [java.lang.IllegalStateException] on attempts to + * collect + * it more than once. + */ + open suspend fun lotsOfGreetings(requests: Flow): Hello.HelloReply = throw + StatusException(UNIMPLEMENTED.withDescription("Method example.grpc.hello.HelloService.LotsOfGreetings is unimplemented")) + + /** + * Returns a [Flow] of responses to an RPC for example.grpc.hello.HelloService.BidiHello. + * + * If creating or collecting the returned flow fails with a [StatusException], the RPC + * will fail with the corresponding [Status]. If it fails with a + * [java.util.concurrent.CancellationException], the RPC will fail with status + * `Status.CANCELLED`. If creating + * or collecting the returned flow fails for any other reason, the RPC will fail with + * `Status.UNKNOWN` with the exception as a cause. + * + * @param requests A [Flow] of requests from the client. This flow can be + * collected only once and throws [java.lang.IllegalStateException] on attempts to + * collect + * it more than once. + */ + open fun bidiHello(requests: Flow): Flow = throw + StatusException(UNIMPLEMENTED.withDescription("Method example.grpc.hello.HelloService.BidiHello is unimplemented")) + + final override fun bindService(): ServerServiceDefinition = builder(getServiceDescriptor()) + .addMethod(unaryServerMethodDefinition( + context = this.context, + descriptor = HelloServiceGrpc.getHelloMethod(), + implementation = ::hello + )) + .addMethod(unaryServerMethodDefinition( + context = this.context, + descriptor = HelloServiceGrpc.getLazyHelloMethod(), + implementation = ::lazyHello + )) + .addMethod(unaryServerMethodDefinition( + context = this.context, + descriptor = HelloServiceGrpc.getBlockingHelloMethod(), + implementation = ::blockingHello + )) + .addMethod(serverStreamingServerMethodDefinition( + context = this.context, + descriptor = HelloServiceGrpc.getLotsOfRepliesMethod(), + implementation = ::lotsOfReplies + )) + .addMethod(clientStreamingServerMethodDefinition( + context = this.context, + descriptor = HelloServiceGrpc.getLotsOfGreetingsMethod(), + implementation = ::lotsOfGreetings + )) + .addMethod(bidiStreamingServerMethodDefinition( + context = this.context, + descriptor = HelloServiceGrpc.getBidiHelloMethod(), + implementation = ::bidiHello + )).build() + } +} diff --git a/examples/grpc-kotlin/gen-src/main/resources/META-INF/armeria/grpc/armeria-main.dsc b/examples/grpc-kotlin/gen-src/main/resources/META-INF/armeria/grpc/armeria-main.dsc new file mode 100644 index 00000000000..0b10cace357 Binary files /dev/null and b/examples/grpc-kotlin/gen-src/main/resources/META-INF/armeria/grpc/armeria-main.dsc differ diff --git a/examples/grpc-kotlin/src/main/kotlin/example/armeria/grpc/kotlin/HelloServiceImpl.kt b/examples/grpc-kotlin/src/main/kotlin/example/armeria/grpc/kotlin/HelloServiceImpl.kt index 2faa5ed6d5e..896fbd2161e 100644 --- a/examples/grpc-kotlin/src/main/kotlin/example/armeria/grpc/kotlin/HelloServiceImpl.kt +++ b/examples/grpc-kotlin/src/main/kotlin/example/armeria/grpc/kotlin/HelloServiceImpl.kt @@ -3,28 +3,41 @@ package example.armeria.grpc.kotlin import com.linecorp.armeria.server.ServiceRequestContext import example.armeria.grpc.kotlin.Hello.HelloReply import example.armeria.grpc.kotlin.Hello.HelloRequest -import io.grpc.stub.StreamObserver -import java.time.Duration -import java.util.concurrent.TimeUnit -import reactor.core.publisher.Flux -import reactor.core.scheduler.Schedulers - -class HelloServiceImpl : HelloServiceGrpc.HelloServiceImplBase() { +import example.armeria.grpc.kotlin.HelloServiceImpl.Companion.withArmeriaBlockingContext +import example.armeria.grpc.kotlin.HelloServiceImpl.Companion.withArmeriaContext +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.asCoroutineDispatcher +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.toList +import kotlinx.coroutines.withContext + +/** + * Note that if you want to access a current [ServiceRequestContext] in [HelloServiceImpl], + * you should initialize [HelloServiceImpl] with [Dispatchers.Unconfined] and wrap your rpc methods with + * [withArmeriaContext] or [withArmeriaBlockingContext]. + */ +class HelloServiceImpl : HelloServiceGrpcKt.HelloServiceCoroutineImplBase(Dispatchers.Unconfined) { /** * Sends a [HelloReply] immediately when receiving a request. */ - override fun hello(request: HelloRequest, responseObserver: StreamObserver) { - responseObserver.onNext(buildReply(toMessage(request.name))) - responseObserver.onCompleted() + override suspend fun hello(request: HelloRequest): HelloReply = withArmeriaContext { + // Make sure that current thread is request context aware + ServiceRequestContext.current() + buildReply(toMessage(request.name)) } - override fun lazyHello(request: HelloRequest, responseObserver: StreamObserver) { - // You can use the event loop for scheduling a task. - ServiceRequestContext.current().contextAwareEventLoop().schedule({ - responseObserver.onNext(buildReply(toMessage(request.name))) - responseObserver.onCompleted() - }, 3, TimeUnit.SECONDS) + override suspend fun lazyHello(request: HelloRequest): HelloReply = withArmeriaContext { + delay(3000L) + ServiceRequestContext.current() + buildReply(toMessage(request.name)) } /** @@ -32,24 +45,14 @@ class HelloServiceImpl : HelloServiceGrpc.HelloServiceImplBase() { * * @see [Blocking service implementation](https://line.github.io/armeria/server-grpc.html#blocking-service-implementation) */ - override fun blockingHello(request: HelloRequest, responseObserver: StreamObserver) { - // Unlike upstream gRPC-Java, Armeria does not run service logic in a separate thread pool by default. - // Therefore, this method will run in the event loop, which means that you can suffer the performance - // degradation if you call a blocking API in this method. In this case, you have the following options: - // - // 1. Call a blocking API in the blockingTaskExecutor provided by Armeria. - // 2. Set `GrpcServiceBuilder.useBlockingTaskExecutor(true)` when building your GrpcService. - // 3. Call a blocking API in the separate thread pool you manage. - // - // In this example, we chose the option 1: - ServiceRequestContext.current().blockingTaskExecutor().submit { - try { // Simulate a blocking API call. - Thread.sleep(3000) - } catch (ignored: Exception) { // Do nothing. - } - responseObserver.onNext(buildReply(toMessage(request.name))) - responseObserver.onCompleted() + override suspend fun blockingHello(request: HelloRequest): HelloReply = withArmeriaBlockingContext { + try { // Simulate a blocking API call. + Thread.sleep(3000) + } catch (ignored: Exception) { // Do nothing. } + // Make sure that current thread is request context aware + ServiceRequestContext.current() + buildReply(toMessage(request.name)) } /** @@ -57,75 +60,55 @@ class HelloServiceImpl : HelloServiceGrpc.HelloServiceImplBase() { * * @see lazyHello(HelloRequest, StreamObserver) */ - override fun lotsOfReplies(request: HelloRequest, responseObserver: StreamObserver) { + override fun lotsOfReplies(request: HelloRequest): Flow { // You can also write this code without Reactor like 'lazyHello' example. - Flux.interval(Duration.ofSeconds(1)) - .take(5) - .map { "Hello, ${request.name}! (sequence: ${it + 1})" } - // You can make your Flux/Mono publish the signals in the RequestContext-aware executor. - .publishOn(Schedulers.fromExecutor(ServiceRequestContext.current().contextAwareExecutor())) - .subscribe({ - // Confirm this callback is being executed on the RequestContext-aware executor. - ServiceRequestContext.current() - responseObserver.onNext(buildReply(it)) - }, - { - // Confirm this callback is being executed on the RequestContext-aware executor. - ServiceRequestContext.current() - responseObserver.onError(it) - }, - { - // Confirm this callback is being executed on the RequestContext-aware executor. - ServiceRequestContext.current() - responseObserver.onCompleted() - }) + return flow { + for (i in 1..5) { + // Check context between delay and emit + ServiceRequestContext.current() + delay(1000) + ServiceRequestContext.current() + emit(buildReply("Hello, ${request.name}! (sequence: $i)")) // emit next value + ServiceRequestContext.current() + } + }.flowOn(armeriaDispatcher()) } /** * Sends a [HelloReply] when a request has been completed with multiple [HelloRequest]s. */ - override fun lotsOfGreetings(responseObserver: StreamObserver): StreamObserver { - return object : StreamObserver { - val names = arrayListOf() - - override fun onNext(value: HelloRequest) { - names.add(value.name) - } - - override fun onError(t: Throwable) { - responseObserver.onError(t) - } - - override fun onCompleted() { - responseObserver.onNext(buildReply(toMessage(names.joinToString()))) - responseObserver.onCompleted() - } - } + override suspend fun lotsOfGreetings(requests: Flow): HelloReply = withArmeriaContext { + val names = mutableListOf() + requests.map { it.name }.toList(names) + // Make sure that current thread is request context aware + ServiceRequestContext.current() + buildReply(toMessage(names.joinToString())) } /** * Sends a [HelloReply] when each [HelloRequest] is received. The response will be completed * when the request is completed. */ - override fun bidiHello(responseObserver: StreamObserver): StreamObserver { - return object : StreamObserver { - override fun onNext(value: HelloRequest) { // Respond to every request received. - responseObserver.onNext(buildReply(toMessage(value.name))) + override fun bidiHello(requests: Flow): Flow = + flow { + requests.collect { request -> + ServiceRequestContext.current() + emit(buildReply(toMessage(request.name))) } + }.flowOn(armeriaDispatcher()) - override fun onError(t: Throwable) { - responseObserver.onError(t) - } + companion object { + fun armeriaDispatcher(): CoroutineDispatcher = + ServiceRequestContext.current().contextAwareExecutor().asCoroutineDispatcher() - override fun onCompleted() { - responseObserver.onCompleted() - } - } - } + suspend fun withArmeriaContext(block: suspend CoroutineScope.() -> T): T = + withContext(armeriaDispatcher(), block) - companion object { + suspend fun withArmeriaBlockingContext(block: suspend CoroutineScope.() -> T): T = + withContext(ServiceRequestContext.current().blockingTaskExecutor().asCoroutineDispatcher(), block) - private fun buildReply(message: String): HelloReply = HelloReply.newBuilder().setMessage(message).build() + private fun buildReply(message: String): HelloReply = + HelloReply.newBuilder().setMessage(message).build() private fun toMessage(message: String): String = "Hello, $message!" } diff --git a/examples/grpc-kotlin/src/test/kotlin/example/armeria/grpc/kotlin/HelloServiceTest.kt b/examples/grpc-kotlin/src/test/kotlin/example/armeria/grpc/kotlin/HelloServiceTest.kt index b87fc82de80..43b66989969 100644 --- a/examples/grpc-kotlin/src/test/kotlin/example/armeria/grpc/kotlin/HelloServiceTest.kt +++ b/examples/grpc-kotlin/src/test/kotlin/example/armeria/grpc/kotlin/HelloServiceTest.kt @@ -1,22 +1,16 @@ package example.armeria.grpc.kotlin import com.google.common.base.Stopwatch -import com.google.common.util.concurrent.FutureCallback -import com.google.common.util.concurrent.Futures -import com.google.common.util.concurrent.MoreExecutors import com.linecorp.armeria.client.Clients import com.linecorp.armeria.server.Server import example.armeria.grpc.kotlin.Hello.HelloReply import example.armeria.grpc.kotlin.Hello.HelloRequest -import example.armeria.grpc.kotlin.HelloServiceGrpc.HelloServiceBlockingStub -import example.armeria.grpc.kotlin.HelloServiceGrpc.HelloServiceFutureStub -import example.armeria.grpc.kotlin.HelloServiceGrpc.HelloServiceStub -import io.grpc.stub.StreamObserver -import java.util.concurrent.LinkedBlockingQueue +import example.armeria.grpc.kotlin.HelloServiceGrpcKt.HelloServiceCoroutineStub import java.util.concurrent.TimeUnit -import java.util.concurrent.atomic.AtomicBoolean +import kotlinx.coroutines.flow.asFlow +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.runBlocking import org.assertj.core.api.Assertions.assertThat -import org.awaitility.Awaitility.await import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.Test @@ -25,156 +19,94 @@ class HelloServiceTest { @Test fun reply() { - val helloService = Clients.newClient(uri(), HelloServiceBlockingStub::class.java) - assertThat(helloService.hello(HelloRequest.newBuilder().setName("Armeria").build()).message) - .isEqualTo("Hello, Armeria!") + runBlocking { + val helloService = Clients.newClient(uri(), HelloServiceCoroutineStub::class.java) + assertThat(helloService.hello(HelloRequest.newBuilder().setName("Armeria").build()).message) + .isEqualTo("Hello, Armeria!") + } } - // Should never reach here. @Test fun replyWithDelay() { - val helloService = Clients.newClient(uri(), HelloServiceFutureStub::class.java) - val future = helloService.lazyHello(HelloRequest.newBuilder().setName("Armeria").build()) - val completed = AtomicBoolean() - Futures.addCallback(future, object : FutureCallback { - override fun onSuccess(result: HelloReply?) { - assertThat(result?.message).isEqualTo("Hello, Armeria!") - completed.set(true) - } - - override fun onFailure(t: Throwable) { // Should never reach here. - throw Error(t) - } - }, MoreExecutors.directExecutor()) - - await().untilTrue(completed) + runBlocking { + val helloService = Clients.newClient(uri(), HelloServiceCoroutineStub::class.java) + val reply: HelloReply = helloService.lazyHello(HelloRequest.newBuilder().setName("Armeria").build()) + assertThat(reply.message).isEqualTo("Hello, Armeria!") + } } @Test fun replyFromServerSideBlockingCall() { - val helloService = Clients.newClient(uri(), HelloServiceBlockingStub::class.java) - val watch = Stopwatch.createStarted() - assertThat(helloService.blockingHello(HelloRequest.newBuilder().setName("Armeria").build()).message) - .isEqualTo("Hello, Armeria!") - assertThat(watch.elapsed(TimeUnit.SECONDS)).isGreaterThanOrEqualTo(3) + runBlocking { + val helloService = Clients.newClient(uri(), HelloServiceCoroutineStub::class.java) + val watch = Stopwatch.createStarted() + assertThat(helloService.blockingHello(HelloRequest.newBuilder().setName("Armeria").build()).message) + .isEqualTo("Hello, Armeria!") + assertThat(watch.elapsed(TimeUnit.SECONDS)).isGreaterThanOrEqualTo(3) + } } // Should never reach here. @Test fun lotsOfReplies() { - val completed = AtomicBoolean() - helloService.lotsOfReplies( - HelloRequest.newBuilder().setName("Armeria").build(), - object : StreamObserver { - private var sequence = 0 - override fun onNext(value: HelloReply) { - assertThat(value.message).isEqualTo("Hello, Armeria! (sequence: ${++sequence})") - } - - override fun onError(t: Throwable) { // Should never reach here. - throw Error(t) + runBlocking { + var sequence = 0 + helloService.lotsOfReplies(HelloRequest.newBuilder().setName("Armeria").build()) + .collect { + assertThat(it.message).isEqualTo("Hello, Armeria! (sequence: ${++sequence})") } - - override fun onCompleted() { - assertThat(sequence).isEqualTo(5) - completed.set(true) - } - }) - await().untilTrue(completed) + assertThat(sequence).isEqualTo(5) + } } @Test fun blockForLotsOfReplies() { - val replies = LinkedBlockingQueue() - val completed = AtomicBoolean() - helloService.lotsOfReplies( - HelloRequest.newBuilder().setName("Armeria").build(), - object : StreamObserver { - override fun onNext(value: HelloReply) { - replies.offer(value) - } - - override fun onError(t: Throwable) { // Should never reach here. - throw Error(t) - } - - override fun onCompleted() { - completed.set(true) - } - }) - var sequence = 0 - while (completed.get().not() or replies.isNotEmpty()) { - val value = replies.poll(100, TimeUnit.MILLISECONDS) ?: continue - assertThat(value.message).isEqualTo("Hello, Armeria! (sequence: ${++sequence})") + runBlocking { + val replies = ArrayList() + helloService.lotsOfReplies(HelloRequest.newBuilder().setName("Armeria").build()) + .collect { replies.add(it) } + for ((sequence, reply) in replies.withIndex()) { + assertThat(reply.message).isEqualTo("Hello, Armeria! (sequence: ${sequence + 1})") + } } - assertThat(sequence).isEqualTo(5) } @Test fun sendLotsOfGreetings() { - val names = listOf("Armeria", "Grpc", "Streaming") - val completed = AtomicBoolean() - val request = helloService.lotsOfGreetings(object : StreamObserver { - private var received = false - override fun onNext(value: HelloReply) { - assertThat(received).isFalse() - received = true - assertThat(value.message).isEqualTo("Hello, ${names.joinToString()}!") - } + runBlocking { + val names = listOf("Armeria", "Grpc", "Streaming") + val requests = names.map { HelloRequest.newBuilder().setName(it).build() } - override fun onError(t: Throwable) { // Should never reach here. - throw Error(t) - } - - override fun onCompleted() { - assertThat(received).isTrue() - completed.set(true) - } - }) - for (name in names) { - request.onNext(HelloRequest.newBuilder().setName(name).build()) + val reply: HelloReply = helloService.lotsOfGreetings(requests.asFlow()) + assertThat(reply.message).isEqualTo("Hello, ${names.joinToString()}!") } - request.onCompleted() - await().untilTrue(completed) } @Test fun bidirectionalHello() { - val names = listOf("Armeria", "Grpc", "Streaming") - val completed = AtomicBoolean() - val request = helloService.bidiHello(object : StreamObserver { - private var received = 0 - override fun onNext(value: HelloReply) { - assertThat(value.message).isEqualTo("Hello, ${names[received++]}!") - } - - override fun onError(t: Throwable) { // Should never reach here. - throw Error(t) - } - - override fun onCompleted() { - assertThat(received).isEqualTo(names.size) - completed.set(true) + runBlocking { + val names = listOf("Armeria", "Grpc", "Streaming") + val requests = names.map { HelloRequest.newBuilder().setName(it).build() } + val request = helloService.bidiHello(requests.asFlow()) + + var received = 0 + request.collect { + assertThat(it.message).isEqualTo("Hello, ${names[received++]}!") } - }) - for (name in names) { - request.onNext(HelloRequest.newBuilder().setName(name).build()) } - request.onCompleted() - await().untilTrue(completed) } companion object { private lateinit var server: Server - private lateinit var helloService: HelloServiceStub + private lateinit var helloService: HelloServiceCoroutineStub @BeforeAll @JvmStatic fun beforeClass() { server = Main.newServer(0, 0) server.start().join() - helloService = Clients.newClient(uri(), HelloServiceStub::class.java) + helloService = Clients.newClient(uri(), HelloServiceCoroutineStub::class.java) } @AfterAll diff --git a/gradle/scripts/lib/java-rpc-proto.gradle b/gradle/scripts/lib/java-rpc-proto.gradle index 3c7e7e3c2ed..2c6a3fc5534 100644 --- a/gradle/scripts/lib/java-rpc-proto.gradle +++ b/gradle/scripts/lib/java-rpc-proto.gradle @@ -36,6 +36,11 @@ configure(projectsWithFlags('java')) { artifact = "com.salesforce.servicelibs:rxgrpc:${managedVersions['com.salesforce.servicelibs:reactor-grpc']}" } } + if (project.ext.hasFlag('kotlin-grpc')) { + kotlinGrpc { + artifact = "io.grpc:protoc-gen-grpc-kotlin:${managedVersions['io.grpc:protoc-gen-grpc-kotlin']}" + } + } } generateProtoTasks { all()*.plugins { @@ -48,6 +53,9 @@ configure(projectsWithFlags('java')) { if (project.ext.hasFlag('rxgrpc') && managedVersions.containsKey('com.salesforce.servicelibs:rxgrpc')) { rxgrpc {} } + if (project.ext.hasFlag('kotlin-grpc') && managedVersions.containsKey('io.grpc:grpc-kotlin-stub')) { + kotlinGrpc {} + } } all().each { task -> task.generateDescriptorSet = true diff --git a/grpc/src/main/java/com/linecorp/armeria/internal/client/grpc/GrpcClientFactory.java b/grpc/src/main/java/com/linecorp/armeria/internal/client/grpc/GrpcClientFactory.java index e55da92f984..ba4c3b447cb 100644 --- a/grpc/src/main/java/com/linecorp/armeria/internal/client/grpc/GrpcClientFactory.java +++ b/grpc/src/main/java/com/linecorp/armeria/internal/client/grpc/GrpcClientFactory.java @@ -17,6 +17,7 @@ import static com.google.common.collect.ImmutableSet.toImmutableSet; +import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -25,6 +26,8 @@ import java.util.Set; import java.util.function.Consumer; +import javax.annotation.Nullable; + import org.curioswitch.common.protobuf.json.MessageMarshaller; import com.google.common.collect.ImmutableList; @@ -84,18 +87,19 @@ public Object newClient(ClientBuilderParams params) { final ClientOptions options = params.options(); final SerializationFormat serializationFormat = scheme.serializationFormat(); - final Class stubClass = clientType.getEnclosingClass(); - if (stubClass == null) { + final Class enclosingClass = clientType.getEnclosingClass(); + if (enclosingClass == null) { throw newUnknownClientTypeException(clientType); } final HttpClient httpClient = newHttpClient(params); - final Method stubFactoryMethod = findStubFactoryMethod(clientType, stubClass); + // TODO(ikhoon): Support gjson with Kotlin coroutine stub once + // https://github.com/grpc/grpc-kotlin/pull/63 is released final MessageMarshaller jsonMarshaller = GrpcSerializationFormats.isJson(serializationFormat) ? GrpcJsonUtil.jsonMarshaller( - stubMethods(stubClass), + stubMethods(enclosingClass), options.get(GrpcClientOptions.JSON_MARSHALLER_CUSTOMIZER)) : null; final ArmeriaChannel channel = new ArmeriaChannel( @@ -106,17 +110,27 @@ public Object newClient(ClientBuilderParams params) { serializationFormat, jsonMarshaller); + final Method stubFactoryMethod = findStubFactoryMethod(clientType, enclosingClass); try { // Verified stubFactoryMethod.getReturnType() == clientType in findStubFactoryMethod(). - return stubFactoryMethod.invoke(null, channel); - } catch (IllegalAccessException | InvocationTargetException e) { + if (stubFactoryMethod != null) { + return stubFactoryMethod.invoke(null, channel); + } else { + final Constructor stubConstructor = findStubConstructor(clientType); + if (stubConstructor == null) { + throw newUnknownClientTypeException(clientType); + } + return stubConstructor.newInstance(channel); + } + } catch (IllegalAccessException | InvocationTargetException | InstantiationException e) { throw new IllegalStateException("Could not create a gRPC stub through reflection.", e); } } - private static Method findStubFactoryMethod(Class clientType, Class stubClass) { + @Nullable + private static Method findStubFactoryMethod(Class clientType, Class enclosingClass) { Method newStubMethod = null; - for (Method method : stubClass.getDeclaredMethods()) { + for (Method method : enclosingClass.getDeclaredMethods()) { final int methodModifiers = method.getModifiers(); if (!(Modifier.isPublic(methodModifiers) && Modifier.isStatic(methodModifiers))) { // Must be public and static. @@ -143,11 +157,23 @@ private static Method findStubFactoryMethod(Class clientType, Class st newStubMethod = method; break; } + return newStubMethod; + } - if (newStubMethod == null) { - throw newUnknownClientTypeException(clientType); + @Nullable + private static Constructor findStubConstructor(Class clientType) { + if (!clientType.getName().endsWith("CoroutineStub")) { + return null; } - return newStubMethod; + + for (Constructor constructor: clientType.getConstructors()) { + final Class[] methodParameterTypes = constructor.getParameterTypes(); + if (methodParameterTypes.length == 1 && methodParameterTypes[0] == Channel.class) { + // Must have a single `Channel` parameter. + return constructor; + } + } + return null; } private static IllegalArgumentException newUnknownClientTypeException(Class clientType) {