mirror of https://github.com/grpc/grpc-java.git
stub: disable exception if onCancelHandler set
This restrains a cancellation Exception when an onCancelHandler is set in ServerCallStreamObserverImpl. Signed-off-by: Venil Noronha <veniln@vmware.com>
This commit is contained in:
parent
3dab7aed2f
commit
3f4c3f0406
|
@ -39,12 +39,15 @@ public abstract class ServerCallStreamObserver<V> extends CallStreamObserver<V>
|
||||||
public abstract boolean isCancelled();
|
public abstract boolean isCancelled();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a {@link Runnable} that will be called if the calls {@link #isCancelled()} state
|
* Set a {@link Runnable} that will be called if the calls {@link #isCancelled()} state
|
||||||
* changes from {@code false} to {@code true}. It is guaranteed that execution of the
|
* changes from {@code false} to {@code true}. It is guaranteed that execution of the
|
||||||
* {@link Runnable} are serialized with calls to the 'inbound' {@link StreamObserver}.
|
* {@link Runnable} are serialized with calls to the 'inbound' {@link StreamObserver}.
|
||||||
*
|
*
|
||||||
* <p>Note that the handler may be called some time after {@link #isCancelled} has transitioned to
|
* <p>Note that the handler may be called some time after {@link #isCancelled()} has transitioned
|
||||||
* {@code true} as other callbacks may still be executing in the 'inbound' observer.
|
* to {@code true} as other callbacks may still be executing in the 'inbound' observer.
|
||||||
|
*
|
||||||
|
* <p>Setting the onCancelHandler will suppress the on-cancel exception thrown by
|
||||||
|
* {@link #onNext()}.
|
||||||
*
|
*
|
||||||
* @param onCancelHandler to call when client has cancelled the call.
|
* @param onCancelHandler to call when client has cancelled the call.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -332,7 +332,10 @@ public final class ServerCalls {
|
||||||
@Override
|
@Override
|
||||||
public void onNext(RespT response) {
|
public void onNext(RespT response) {
|
||||||
if (cancelled) {
|
if (cancelled) {
|
||||||
throw Status.CANCELLED.withDescription("call already cancelled").asRuntimeException();
|
if (onCancelHandler == null) {
|
||||||
|
throw Status.CANCELLED.withDescription("call already cancelled").asRuntimeException();
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (!sentHeaders) {
|
if (!sentHeaders) {
|
||||||
call.sendHeaders(new Metadata());
|
call.sendHeaders(new Metadata());
|
||||||
|
@ -353,7 +356,9 @@ public final class ServerCalls {
|
||||||
@Override
|
@Override
|
||||||
public void onCompleted() {
|
public void onCompleted() {
|
||||||
if (cancelled) {
|
if (cancelled) {
|
||||||
throw Status.CANCELLED.withDescription("call already cancelled").asRuntimeException();
|
if (onCancelHandler == null) {
|
||||||
|
throw Status.CANCELLED.withDescription("call already cancelled").asRuntimeException();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
call.close(Status.OK, new Metadata());
|
call.close(Status.OK, new Metadata());
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ import io.grpc.ServerCallHandler;
|
||||||
import io.grpc.ServerServiceDefinition;
|
import io.grpc.ServerServiceDefinition;
|
||||||
import io.grpc.ServiceDescriptor;
|
import io.grpc.ServiceDescriptor;
|
||||||
import io.grpc.Status;
|
import io.grpc.Status;
|
||||||
|
import io.grpc.StatusRuntimeException;
|
||||||
import io.grpc.inprocess.InProcessChannelBuilder;
|
import io.grpc.inprocess.InProcessChannelBuilder;
|
||||||
import io.grpc.inprocess.InProcessServerBuilder;
|
import io.grpc.inprocess.InProcessServerBuilder;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
|
@ -134,6 +135,74 @@ public class ServerCallsTest {
|
||||||
assertThat(serverCall.requestCalls).containsExactly(1, 1).inOrder();
|
assertThat(serverCall.requestCalls).containsExactly(1, 1).inOrder();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void noCancellationExceptionIfOnCancelHandlerSet() throws Exception {
|
||||||
|
final AtomicBoolean onCancelCalled = new AtomicBoolean();
|
||||||
|
final AtomicReference<ServerCallStreamObserver<Integer>> callObserver =
|
||||||
|
new AtomicReference<ServerCallStreamObserver<Integer>>();
|
||||||
|
ServerCallHandler<Integer, Integer> callHandler =
|
||||||
|
ServerCalls.asyncBidiStreamingCall(
|
||||||
|
new ServerCalls.BidiStreamingMethod<Integer, Integer>() {
|
||||||
|
@Override
|
||||||
|
public StreamObserver<Integer> invoke(StreamObserver<Integer> responseObserver) {
|
||||||
|
ServerCallStreamObserver<Integer> serverCallObserver =
|
||||||
|
(ServerCallStreamObserver<Integer>) responseObserver;
|
||||||
|
callObserver.set(serverCallObserver);
|
||||||
|
serverCallObserver.setOnCancelHandler(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
onCancelCalled.set(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return new ServerCalls.NoopStreamObserver<Integer>();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ServerCall.Listener<Integer> callListener =
|
||||||
|
callHandler.startCall(serverCall, new Metadata());
|
||||||
|
callListener.onReady();
|
||||||
|
callListener.onCancel();
|
||||||
|
assertTrue(onCancelCalled.get());
|
||||||
|
serverCall.isCancelled = true;
|
||||||
|
assertTrue(callObserver.get().isCancelled());
|
||||||
|
callObserver.get().onNext(null);
|
||||||
|
callObserver.get().onCompleted();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void expectCancellationExceptionIfOnCancelHandlerNotSet() throws Exception {
|
||||||
|
final AtomicReference<ServerCallStreamObserver<Integer>> callObserver =
|
||||||
|
new AtomicReference<ServerCallStreamObserver<Integer>>();
|
||||||
|
ServerCallHandler<Integer, Integer> callHandler =
|
||||||
|
ServerCalls.asyncBidiStreamingCall(
|
||||||
|
new ServerCalls.BidiStreamingMethod<Integer, Integer>() {
|
||||||
|
@Override
|
||||||
|
public StreamObserver<Integer> invoke(StreamObserver<Integer> responseObserver) {
|
||||||
|
ServerCallStreamObserver<Integer> serverCallObserver =
|
||||||
|
(ServerCallStreamObserver<Integer>) responseObserver;
|
||||||
|
callObserver.set(serverCallObserver);
|
||||||
|
return new ServerCalls.NoopStreamObserver<Integer>();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ServerCall.Listener<Integer> callListener =
|
||||||
|
callHandler.startCall(serverCall, new Metadata());
|
||||||
|
callListener.onReady();
|
||||||
|
callListener.onCancel();
|
||||||
|
serverCall.isCancelled = true;
|
||||||
|
assertTrue(callObserver.get().isCancelled());
|
||||||
|
try {
|
||||||
|
callObserver.get().onNext(null);
|
||||||
|
fail("Expected cancellation exception when onCallHandler not set");
|
||||||
|
} catch (StatusRuntimeException expected) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
callObserver.get().onCompleted();
|
||||||
|
fail("Expected cancellation exception when onCallHandler not set");
|
||||||
|
} catch (StatusRuntimeException expected) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void cannotSetOnCancelHandlerAfterServiceInvocation() throws Exception {
|
public void cannotSetOnCancelHandlerAfterServiceInvocation() throws Exception {
|
||||||
final AtomicReference<ServerCallStreamObserver<Integer>> callObserver =
|
final AtomicReference<ServerCallStreamObserver<Integer>> callObserver =
|
||||||
|
|
Loading…
Reference in New Issue