diff --git a/interop-testing/build.gradle b/interop-testing/build.gradle index 0b867c4e28..1d6c1f42d4 100644 --- a/interop-testing/build.gradle +++ b/interop-testing/build.gradle @@ -3,16 +3,6 @@ apply plugin: 'application' description = "gRPC: Integration Testing" startScripts.enabled = false -// Add dependency on the protobuf plugin -buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath libraries.protobuf_plugin - } -} - dependencies { compile project(':grpc-auth'), project(':grpc-core'), @@ -21,6 +11,7 @@ dependencies { project(':grpc-protobuf'), project(':grpc-stub'), project(':grpc-testing'), + project(':grpc-testing-proto'), libraries.junit, libraries.mockito, libraries.netty_tcnative, @@ -84,13 +75,3 @@ applicationDistribution.into("bin") { from(stresstest_client) fileMode = 0755 } - -configureProtoCompilation() - -// Let intellij projects refer to generated code -idea { - module { - sourceDirs += file("${projectDir}/src/generated/main/java"); - sourceDirs += file("${projectDir}/src/generated/main/grpc"); - } -} diff --git a/settings.gradle b/settings.gradle index d9ea440620..ba0e64090d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -10,6 +10,7 @@ include ":grpc-protobuf-nano" include ":grpc-netty" include ":grpc-grpclb" include ":grpc-testing" +include ":grpc-testing-proto" include ":grpc-interop-testing" include ":grpc-all" include ":grpc-benchmarks" @@ -27,6 +28,7 @@ project(':grpc-protobuf-nano').projectDir = "$rootDir/protobuf-nano" as File project(':grpc-netty').projectDir = "$rootDir/netty" as File project(':grpc-grpclb').projectDir = "$rootDir/grpclb" as File project(':grpc-testing').projectDir = "$rootDir/testing" as File +project(':grpc-testing-proto').projectDir = "$rootDir/testing-proto" as File project(':grpc-interop-testing').projectDir = "$rootDir/interop-testing" as File project(':grpc-all').projectDir = "$rootDir/all" as File project(':grpc-benchmarks').projectDir = "$rootDir/benchmarks" as File diff --git a/testing-proto/build.gradle b/testing-proto/build.gradle new file mode 100644 index 0000000000..f5fc71ecec --- /dev/null +++ b/testing-proto/build.gradle @@ -0,0 +1,26 @@ +description = "gRPC: Testing Protos" + +// Add dependency on the protobuf plugin +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath libraries.protobuf_plugin + } +} + +dependencies { + compile project(':grpc-protobuf'), + project(':grpc-stub') +} + +configureProtoCompilation() + +// Let intellij projects refer to generated code +idea { + module { + sourceDirs += file("${projectDir}/src/generated/main/java") + sourceDirs += file("${projectDir}/src/generated/main/grpc") + } +} diff --git a/interop-testing/src/generated/main/grpc/io/grpc/testing/integration/MetricsServiceGrpc.java b/testing-proto/src/generated/main/grpc/io/grpc/testing/integration/MetricsServiceGrpc.java similarity index 100% rename from interop-testing/src/generated/main/grpc/io/grpc/testing/integration/MetricsServiceGrpc.java rename to testing-proto/src/generated/main/grpc/io/grpc/testing/integration/MetricsServiceGrpc.java diff --git a/interop-testing/src/generated/main/grpc/io/grpc/testing/integration/ReconnectServiceGrpc.java b/testing-proto/src/generated/main/grpc/io/grpc/testing/integration/ReconnectServiceGrpc.java similarity index 100% rename from interop-testing/src/generated/main/grpc/io/grpc/testing/integration/ReconnectServiceGrpc.java rename to testing-proto/src/generated/main/grpc/io/grpc/testing/integration/ReconnectServiceGrpc.java diff --git a/interop-testing/src/generated/main/grpc/io/grpc/testing/integration/TestServiceGrpc.java b/testing-proto/src/generated/main/grpc/io/grpc/testing/integration/TestServiceGrpc.java similarity index 100% rename from interop-testing/src/generated/main/grpc/io/grpc/testing/integration/TestServiceGrpc.java rename to testing-proto/src/generated/main/grpc/io/grpc/testing/integration/TestServiceGrpc.java diff --git a/interop-testing/src/generated/main/grpc/io/grpc/testing/integration/UnimplementedServiceGrpc.java b/testing-proto/src/generated/main/grpc/io/grpc/testing/integration/UnimplementedServiceGrpc.java similarity index 100% rename from interop-testing/src/generated/main/grpc/io/grpc/testing/integration/UnimplementedServiceGrpc.java rename to testing-proto/src/generated/main/grpc/io/grpc/testing/integration/UnimplementedServiceGrpc.java diff --git a/interop-testing/src/generated/main/java/com/google/protobuf/EmptyProtos.java b/testing-proto/src/generated/main/java/com/google/protobuf/EmptyProtos.java similarity index 100% rename from interop-testing/src/generated/main/java/com/google/protobuf/EmptyProtos.java rename to testing-proto/src/generated/main/java/com/google/protobuf/EmptyProtos.java diff --git a/interop-testing/src/generated/main/java/io/grpc/testing/integration/Messages.java b/testing-proto/src/generated/main/java/io/grpc/testing/integration/Messages.java similarity index 100% rename from interop-testing/src/generated/main/java/io/grpc/testing/integration/Messages.java rename to testing-proto/src/generated/main/java/io/grpc/testing/integration/Messages.java diff --git a/interop-testing/src/generated/main/java/io/grpc/testing/integration/Metrics.java b/testing-proto/src/generated/main/java/io/grpc/testing/integration/Metrics.java similarity index 100% rename from interop-testing/src/generated/main/java/io/grpc/testing/integration/Metrics.java rename to testing-proto/src/generated/main/java/io/grpc/testing/integration/Metrics.java diff --git a/interop-testing/src/generated/main/java/io/grpc/testing/integration/Test.java b/testing-proto/src/generated/main/java/io/grpc/testing/integration/Test.java similarity index 100% rename from interop-testing/src/generated/main/java/io/grpc/testing/integration/Test.java rename to testing-proto/src/generated/main/java/io/grpc/testing/integration/Test.java diff --git a/interop-testing/src/main/proto/io/grpc/testing/integration/empty.proto b/testing-proto/src/main/proto/io/grpc/testing/integration/empty.proto similarity index 99% rename from interop-testing/src/main/proto/io/grpc/testing/integration/empty.proto rename to testing-proto/src/main/proto/io/grpc/testing/integration/empty.proto index 8f71229b7f..af5591b665 100644 --- a/interop-testing/src/main/proto/io/grpc/testing/integration/empty.proto +++ b/testing-proto/src/main/proto/io/grpc/testing/integration/empty.proto @@ -1,4 +1,3 @@ - // Copyright 2015, Google Inc. // All rights reserved. // @@ -43,4 +42,4 @@ option java_outer_classname = "EmptyProtos"; // rpc Bar (grpc.testing.Empty) returns (grpc.testing.Empty) { }; // }; // -message Empty {} \ No newline at end of file +message Empty {} diff --git a/interop-testing/src/main/proto/io/grpc/testing/integration/messages.proto b/testing-proto/src/main/proto/io/grpc/testing/integration/messages.proto similarity index 99% rename from interop-testing/src/main/proto/io/grpc/testing/integration/messages.proto rename to testing-proto/src/main/proto/io/grpc/testing/integration/messages.proto index f18a632944..5110719e82 100644 --- a/interop-testing/src/main/proto/io/grpc/testing/integration/messages.proto +++ b/testing-proto/src/main/proto/io/grpc/testing/integration/messages.proto @@ -1,4 +1,3 @@ - // Copyright 2015, Google Inc. // All rights reserved. // @@ -176,4 +175,4 @@ message ReconnectParams { message ReconnectInfo { bool passed = 1; repeated int32 backoff_ms = 2; -} \ No newline at end of file +} diff --git a/interop-testing/src/main/proto/io/grpc/testing/integration/metrics.proto b/testing-proto/src/main/proto/io/grpc/testing/integration/metrics.proto similarity index 100% rename from interop-testing/src/main/proto/io/grpc/testing/integration/metrics.proto rename to testing-proto/src/main/proto/io/grpc/testing/integration/metrics.proto diff --git a/interop-testing/src/main/proto/io/grpc/testing/integration/test.proto b/testing-proto/src/main/proto/io/grpc/testing/integration/test.proto similarity index 99% rename from interop-testing/src/main/proto/io/grpc/testing/integration/test.proto rename to testing-proto/src/main/proto/io/grpc/testing/integration/test.proto index 23f8e8483a..e4a41b4c9e 100644 --- a/interop-testing/src/main/proto/io/grpc/testing/integration/test.proto +++ b/testing-proto/src/main/proto/io/grpc/testing/integration/test.proto @@ -1,4 +1,3 @@ - // Copyright 2015, Google Inc. // All rights reserved. // @@ -80,7 +79,7 @@ service TestService { // that case. service UnimplementedService { // A call that no server should implement - rpc UnimplementedCall(grpc.testing.Empty) returns(grpc.testing.Empty); + rpc UnimplementedCall(grpc.testing.Empty) returns(grpc.testing.Empty); } // A service used to control reconnect server. diff --git a/testing/build.gradle b/testing/build.gradle index 80707895e0..2ebc78fa26 100644 --- a/testing/build.gradle +++ b/testing/build.gradle @@ -1,8 +1,11 @@ description = "gRPC: Testing" + dependencies { compile project(':grpc-core'), project(':grpc-stub'), libraries.junit, libraries.mockito, libraries.truth + + testCompile project(':grpc-testing-proto') } diff --git a/testing/src/main/java/io/grpc/testing/GrpcServerRule.java b/testing/src/main/java/io/grpc/testing/GrpcServerRule.java new file mode 100644 index 0000000000..8f8ccfc37a --- /dev/null +++ b/testing/src/main/java/io/grpc/testing/GrpcServerRule.java @@ -0,0 +1,157 @@ +/* + * Copyright 2016, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.grpc.testing; + +import io.grpc.BindableService; +import io.grpc.ExperimentalApi; +import io.grpc.ManagedChannel; +import io.grpc.Server; +import io.grpc.ServerServiceDefinition; +import io.grpc.inprocess.InProcessChannelBuilder; +import io.grpc.inprocess.InProcessServerBuilder; +import io.grpc.stub.AbstractStub; +import io.grpc.util.MutableHandlerRegistry; + +import org.junit.rules.ExternalResource; +import org.junit.rules.TestRule; + +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +/** + * {@code GrpcServerRule} is a JUnit {@link TestRule} that starts an in-process gRPC service with + * a {@link MutableHandlerRegistry} for adding services. It is particularly useful for mocking out + * external gRPC-based services and asserting that the expected requests were made. + * + *

An {@link AbstractStub} can be created against this service by using the + * {@link ManagedChannel} provided by {@link GrpcServerRule#getChannel()}. + */ +@ExperimentalApi("https://github.com/grpc/grpc-java/issues/2488") +public class GrpcServerRule extends ExternalResource { + + private ManagedChannel channel; + private Server server; + private String serverName; + private MutableHandlerRegistry serviceRegistry; + private boolean useDirectExecutor; + + /** + * Returns {@code this} configured to use a direct executor for the {@link ManagedChannel} and + * {@link Server}. + */ + public final GrpcServerRule directExecutor() { + useDirectExecutor = true; + return this; + } + + /** + * Returns a {@link ManagedChannel} connected to this service. + */ + public final ManagedChannel getChannel() { + return channel; + } + + /** + * Returns the underlying gRPC {@link Server} for this service. + */ + public final Server getServer() { + return server; + } + + /** + * Returns the randomly generated server name for this service. + */ + public final String getServerName() { + return serverName; + } + + /** + * Returns the service registry for this service. The registry is used to add service instances + * (e.g. {@link BindableService} or {@link ServerServiceDefinition} to the server. + */ + public final MutableHandlerRegistry getServiceRegistry() { + return serviceRegistry; + } + + /** + * After the test has completed, clean up the channel and server. + */ + @Override + protected void after() { + serverName = null; + serviceRegistry = null; + + channel.shutdown(); + server.shutdown(); + + try { + channel.awaitTermination(1, TimeUnit.MINUTES); + server.awaitTermination(1, TimeUnit.MINUTES); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException(e); + } finally { + channel.shutdownNow(); + channel = null; + + server.shutdownNow(); + server = null; + } + } + + /** + * Before the test has started, create the server and channel. + */ + @Override + protected void before() throws Throwable { + serverName = UUID.randomUUID().toString(); + + serviceRegistry = new MutableHandlerRegistry(); + + InProcessServerBuilder serverBuilder = InProcessServerBuilder.forName(serverName) + .fallbackHandlerRegistry(serviceRegistry); + + if (useDirectExecutor) { + serverBuilder.directExecutor(); + } + + server = serverBuilder.build().start(); + + InProcessChannelBuilder channelBuilder = InProcessChannelBuilder.forName(serverName); + + if (useDirectExecutor) { + channelBuilder.directExecutor(); + } + + channel = channelBuilder.build(); + } +} diff --git a/testing/src/test/java/io/grpc/testing/GrpcServerRuleTest.java b/testing/src/test/java/io/grpc/testing/GrpcServerRuleTest.java new file mode 100644 index 0000000000..89171bd07d --- /dev/null +++ b/testing/src/test/java/io/grpc/testing/GrpcServerRuleTest.java @@ -0,0 +1,254 @@ +/* + * Copyright 2016, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.grpc.testing; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.protobuf.ByteString; +import com.google.protobuf.EmptyProtos; + +import io.grpc.ManagedChannel; +import io.grpc.Server; +import io.grpc.stub.StreamObserver; +import io.grpc.testing.integration.Messages; +import io.grpc.testing.integration.TestServiceGrpc; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runners.model.Statement; + +import java.util.Collection; +import java.util.UUID; +import java.util.concurrent.ConcurrentLinkedQueue; + +public class GrpcServerRuleTest { + + public static class WithoutDirectExecutor { + + @Rule + public final GrpcServerRule grpcServerRule = new GrpcServerRule(); + + @Test + public void serverAndChannelAreStarted() { + assertThat(grpcServerRule.getServer().isShutdown()).isFalse(); + assertThat(grpcServerRule.getServer().isTerminated()).isFalse(); + + assertThat(grpcServerRule.getChannel().isShutdown()).isFalse(); + assertThat(grpcServerRule.getChannel().isTerminated()).isFalse(); + + assertThat(grpcServerRule.getServerName()).isNotNull(); + assertThat(grpcServerRule.getServiceRegistry()).isNotNull(); + } + + @Test + public void serverAllowsServicesToBeAddedViaServiceRegistry() { + TestServiceImpl testService = new TestServiceImpl(); + + grpcServerRule.getServiceRegistry().addService(testService); + + TestServiceGrpc.TestServiceBlockingStub stub = + TestServiceGrpc.newBlockingStub(grpcServerRule.getChannel()); + + Messages.SimpleRequest request1 = Messages.SimpleRequest.newBuilder() + .setPayload(Messages.Payload.newBuilder() + .setBody(ByteString.copyFromUtf8(UUID.randomUUID().toString()))) + .build(); + + Messages.SimpleRequest request2 = Messages.SimpleRequest.newBuilder() + .setPayload(Messages.Payload.newBuilder() + .setBody(ByteString.copyFromUtf8(UUID.randomUUID().toString()))) + .build(); + + stub.unaryCall(request1); + stub.unaryCall(request2); + + assertThat(testService.unaryCallRequests) + .containsExactly(request1, request2); + } + + @Test + public void serviceIsNotRunOnSameThreadAsTest() { + TestServiceImpl testService = new TestServiceImpl(); + + grpcServerRule.getServiceRegistry().addService(testService); + + TestServiceGrpc.TestServiceBlockingStub stub = + TestServiceGrpc.newBlockingStub(grpcServerRule.getChannel()); + + // Make a garbage request first due to https://github.com/grpc/grpc-java/issues/2444. + stub.emptyCall(EmptyProtos.Empty.newBuilder().build()); + stub.emptyCall(EmptyProtos.Empty.newBuilder().build()); + + assertThat(testService.lastEmptyCallRequestThread).isNotEqualTo(Thread.currentThread()); + } + } + + public static class WithDirectExecutor { + + @Rule + public final GrpcServerRule grpcServerRule = new GrpcServerRule().directExecutor(); + + @Test + public void serverAndChannelAreStarted() { + assertThat(grpcServerRule.getServer().isShutdown()).isFalse(); + assertThat(grpcServerRule.getServer().isTerminated()).isFalse(); + + assertThat(grpcServerRule.getChannel().isShutdown()).isFalse(); + assertThat(grpcServerRule.getChannel().isTerminated()).isFalse(); + + assertThat(grpcServerRule.getServerName()).isNotNull(); + assertThat(grpcServerRule.getServiceRegistry()).isNotNull(); + } + + @Test + public void serverAllowsServicesToBeAddedViaServiceRegistry() { + TestServiceImpl testService = new TestServiceImpl(); + + grpcServerRule.getServiceRegistry().addService(testService); + + TestServiceGrpc.TestServiceBlockingStub stub = + TestServiceGrpc.newBlockingStub(grpcServerRule.getChannel()); + + Messages.SimpleRequest request1 = Messages.SimpleRequest.newBuilder() + .setPayload(Messages.Payload.newBuilder() + .setBody(ByteString.copyFromUtf8(UUID.randomUUID().toString()))) + .build(); + + Messages.SimpleRequest request2 = Messages.SimpleRequest.newBuilder() + .setPayload(Messages.Payload.newBuilder() + .setBody(ByteString.copyFromUtf8(UUID.randomUUID().toString()))) + .build(); + + stub.unaryCall(request1); + stub.unaryCall(request2); + + assertThat(testService.unaryCallRequests) + .containsExactly(request1, request2); + } + + @Test + public void serviceIsRunOnSameThreadAsTest() { + TestServiceImpl testService = new TestServiceImpl(); + + grpcServerRule.getServiceRegistry().addService(testService); + + TestServiceGrpc.TestServiceBlockingStub stub = + TestServiceGrpc.newBlockingStub(grpcServerRule.getChannel()); + + // Make a garbage request first due to https://github.com/grpc/grpc-java/issues/2444. + stub.emptyCall(EmptyProtos.Empty.newBuilder().build()); + stub.emptyCall(EmptyProtos.Empty.newBuilder().build()); + + assertThat(testService.lastEmptyCallRequestThread).isEqualTo(Thread.currentThread()); + } + } + + public static class ResourceCleanup { + + @Test + public void serverAndChannelAreShutdownAfterRule() throws Throwable { + GrpcServerRule grpcServerRule = new GrpcServerRule(); + + // Before the rule has been executed, all of its resources should be null. + assertThat(grpcServerRule.getChannel()).isNull(); + assertThat(grpcServerRule.getServer()).isNull(); + assertThat(grpcServerRule.getServerName()).isNull(); + assertThat(grpcServerRule.getServiceRegistry()).isNull(); + + // The TestStatement stores the channel and server instances so that we can inspect them after + // the rule cleans up. + TestStatement statement = new TestStatement(grpcServerRule); + + grpcServerRule.apply(statement, null).evaluate(); + + // Ensure that the stored channel and server instances were shut down. + assertThat(statement.channel.isShutdown()).isTrue(); + assertThat(statement.server.isShutdown()).isTrue(); + + // All references to the resources that we created should be set to null. + assertThat(grpcServerRule.getChannel()).isNull(); + assertThat(grpcServerRule.getServer()).isNull(); + assertThat(grpcServerRule.getServerName()).isNull(); + assertThat(grpcServerRule.getServiceRegistry()).isNull(); + } + + private static class TestStatement extends Statement { + + private final GrpcServerRule grpcServerRule; + + private ManagedChannel channel; + private Server server; + + private TestStatement(GrpcServerRule grpcServerRule) { + this.grpcServerRule = grpcServerRule; + } + + @Override + public void evaluate() throws Throwable { + channel = grpcServerRule.getChannel(); + server = grpcServerRule.getServer(); + } + } + } + + private static class TestServiceImpl extends TestServiceGrpc.TestServiceImplBase { + + private final Collection unaryCallRequests = + new ConcurrentLinkedQueue(); + + private volatile Thread lastEmptyCallRequestThread; + + @Override + public void emptyCall( + EmptyProtos.Empty request, + StreamObserver responseObserver) { + + lastEmptyCallRequestThread = Thread.currentThread(); + + responseObserver.onNext(EmptyProtos.Empty.newBuilder().build()); + + responseObserver.onCompleted(); + } + + @Override + public void unaryCall( + Messages.SimpleRequest request, + StreamObserver responseObserver) { + + unaryCallRequests.add(request); + + responseObserver.onNext(Messages.SimpleResponse.newBuilder().build()); + + responseObserver.onCompleted(); + } + } +}