I was trying to play around with protobuf and gRPC in java while following the basic tutorial provided, Although I always tend to create my own scenario and build everything from the ground up. I learn better this way than to copy-paste tutorial code and run it.
I have created the following project (https://github.com/IngoHenkel/protobuf_test) that contains a service class with two services, one synchronous service that simply returns a server info, and an asynchronous service that simulates a longrunning task returning data in a stream.
...
service DeepThoughtServices {
rpc WhoAreYou (google.protobuf.Empty) returns (WhoAmI) {}
rpc RequestIntegerTokens(CalculateIntegerTokens) returns (stream IntegerToken) {}
}
...
The first service (WhoAreYou) works perfectly fine, but for the other service, I always get a NotImplementedException on the client. Strangely, the method itsself gets called on the server perfectly fine (which you can see by the logs), but the moment I try to call the "onNext" method to return data, I get the exception:
java.lang.IllegalStateException: Stream was terminated by error, no further calls are allowed
at com.google.common.base.Preconditions.checkState(Preconditions.java:502)
at io.grpc.stub.ServerCalls$ServerCallStreamObserverImpl.onNext(ServerCalls.java:374)
at de.squirrelsquad.tutorials.protobuf.protos.service.DeepThoughtServices.requestIntegerTokens(DeepThoughtServices.java:56)
at de.squirrelsquad.tutorials.protobuf.protos.service.DeepThoughtServicesGrpc$MethodHandlers.invoke(DeepThoughtServicesGrpc.java:271)
at io.grpc.stub.ServerCalls$UnaryServerCallHandler$UnaryServerCallListener.onHalfClose(ServerCalls.java:182)
at io.grpc.internal.ServerCallImpl$ServerStreamListenerImpl.halfClosed(ServerCallImpl.java:340)
at io.grpc.internal.ServerImpl$JumpToApplicationThreadServerStreamListener$1HalfClosed.runInContext(ServerImpl.java:866)
at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:37)
at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:133)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.base/java.lang.Thread.run(Thread.java:833)
. I first thought that it may have to do with me trying to dispatch the "work" into a background thread and sending the response from there, but even though I now simply send the responses directly within the service method, I still get the error.
Both client and server share the same code base, you can run it by executing mvn package and then run:
java -jar target/grpc-test-0.0.1-SNAPSHOT-jar-with-dependencies.jar -s 8980
to start the server and
ava -jar target/grpc-test-0.0.1-SNAPSHOT-jar-with-dependencies.jar -c localhost:8980
for the client, which will call both methods and then return.
Has anybody got an Idea whats wrong here?
Looking at the beginning of DeepThoughtServices.requestIntegerTokens()
we see:
public void requestIntegerTokens(CalculateIntegerTokens request, StreamObserver<IntegerToken> responseObserver) {
// TODO Auto-generated method stub
logger.info("Request integer tokens called");
super.requestIntegerTokens(request, responseObserver);
responseObserver.onNext(IntegerToken.newBuilder().setValue(1).build());
The last line there is the one that throws the IllegalStateException
.
The problem is the super.requestIntegerTokens()
. DeepThoughtServicesImplBase
has default implementations for each method that fail the RPC with UNIMPLEMENTED. This allows adding new methods to the service in the .proto
without breaking existing code.
To fix your problem, simply remove the call to super
:
public void requestIntegerTokens(CalculateIntegerTokens request, StreamObserver<IntegerToken> responseObserver) {
// TODO Auto-generated method stub
logger.info("Request integer tokens called");
// DON'T DELEGATE TO SUPER!
//super.requestIntegerTokens(request, responseObserver);
responseObserver.onNext(IntegerToken.newBuilder().setValue(1).build());