davsclaus commented on code in PR #29112:
URL: https://github.com/apache/camel/pull/29112#discussion_r4457607673


##########
components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcProducerToRouteControlledStreamObserver.java:
##########
@@ -30,15 +33,48 @@ public GrpcProducerToRouteControlledStreamObserver(Endpoint 
endpoint) {
     public void process(Exchange exchange) throws Exception {
         StreamObserver<Object> observer = exchange.getProperty(
                 GrpcConstants.GRPC_RESPONSE_OBSERVER, StreamObserver.class);
+        if (observer == null) {
+            throw new IllegalArgumentException(
+                    "The exchange has no " + 
GrpcConstants.GRPC_RESPONSE_OBSERVER + " property: it did not come from a"
+                                               + " gRPC consumer with 
routeControlledStreamObserver=true (with"
+                                               + " 
inheritExchangePropertiesForReplies=true for the replies of a gRPC producer)");
+        }
         String eventType = exchange.getMessage().getHeader(
                 GrpcConstants.GRPC_EVENT_TYPE_HEADER, String.class);
+        if (eventType == null) {
+            // nothing is sent: the event to send to the client is unknown
+            throw new IllegalArgumentException(
+                    "The " + GrpcConstants.GRPC_EVENT_TYPE_HEADER + " header 
must be set to "
+                                               + 
GrpcConstants.GRPC_EVENT_TYPE_ON_NEXT + ", "
+                                               + 
GrpcConstants.GRPC_EVENT_TYPE_ON_COMPLETED + " or "
+                                               + 
GrpcConstants.GRPC_EVENT_TYPE_ON_ERROR
+                                               + " to send a reply with 
toRouteControlledStreamObserver");
+        }
         switch (eventType) {
-            case GrpcConstants.GRPC_EVENT_TYPE_ON_NEXT -> 
observer.onNext(exchange.getMessage().getBody());
+            case GrpcConstants.GRPC_EVENT_TYPE_ON_NEXT -> onNext(exchange, 
observer);
             case GrpcConstants.GRPC_EVENT_TYPE_ON_COMPLETED -> 
observer.onCompleted();
             case GrpcConstants.GRPC_EVENT_TYPE_ON_ERROR -> 
observer.onError((Throwable) exchange.getMessage().getBody());
             default -> {
                 // NO-OP
             }
         }
     }
+
+    /**
+     * Sends the body to the client: each element of a List body, or the body 
itself, converted to the response type of
+     * the method that the consumer set as exchange property. When a message 
cannot be converted, nothing is sent, the
+     * call ends with an error and the exchange fails, as the client would 
otherwise decode the bytes of another message
+     * type (for example the unchanged request) as the response.
+     */
+    private void onNext(Exchange exchange, StreamObserver<Object> observer) {
+        Class<?> responseType = 
exchange.getProperty(GrpcConstants.GRPC_RESPONSE_TYPE, Class.class);
+        List<Object> responses;
+        try {
+            responses = GrpcMethodHandler.toResponses(exchange, 
exchange.getMessage().getBody(), responseType);
+        } catch (IllegalArgumentException e) {
+            
observer.onError(GrpcMethodHandler.toStatusException((GrpcEndpoint) 
getEndpoint(), e));

Review Comment:
   Thanks, fixed in 538574afd4fe. The consumer now shares an `AtomicBoolean` 
between the exchanges of one call, in the `grpcResponseObserverFailed` exchange 
property (one per unary call, one per `DELEGATION` request observer; copied to 
reply exchanges like the observer). Once the producer ended the call with 
`onError`, it ignores later `onNext`/`onCompleted`/`onError` events (debug 
log), so the forwarded `onCompleted` no longer fails. On a redelivery the 
conversion runs again and throws the same `IllegalArgumentException`, but no 
second `onError` is sent.
   
   `delegationUnchangedRequestIsNotSentAsResponse` now also checks the server 
side with an event notifier: the `onNext` exchange fails with the 
`IllegalArgumentException` naming both types, the `onCompleted` exchange 
completes. New `redeliveryFailsWithTheReplyTypeError` 
(`maximumRedeliveries(1)`) checks the client gets `INTERNAL` naming both types 
and the exchange ends with the `IllegalArgumentException`. Without the fix both 
fail with `IllegalStateException: call already closed`.
   
   _Claude Code on behalf of Claus Ibsen (davsclaus)_



##########
components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcProducerToRouteControlledStreamObserver.java:
##########
@@ -30,15 +33,48 @@ public GrpcProducerToRouteControlledStreamObserver(Endpoint 
endpoint) {
     public void process(Exchange exchange) throws Exception {
         StreamObserver<Object> observer = exchange.getProperty(
                 GrpcConstants.GRPC_RESPONSE_OBSERVER, StreamObserver.class);
+        if (observer == null) {
+            throw new IllegalArgumentException(
+                    "The exchange has no " + 
GrpcConstants.GRPC_RESPONSE_OBSERVER + " property: it did not come from a"
+                                               + " gRPC consumer with 
routeControlledStreamObserver=true (with"
+                                               + " 
inheritExchangePropertiesForReplies=true for the replies of a gRPC producer)");
+        }
         String eventType = exchange.getMessage().getHeader(
                 GrpcConstants.GRPC_EVENT_TYPE_HEADER, String.class);
+        if (eventType == null) {
+            // nothing is sent: the event to send to the client is unknown
+            throw new IllegalArgumentException(
+                    "The " + GrpcConstants.GRPC_EVENT_TYPE_HEADER + " header 
must be set to "
+                                               + 
GrpcConstants.GRPC_EVENT_TYPE_ON_NEXT + ", "
+                                               + 
GrpcConstants.GRPC_EVENT_TYPE_ON_COMPLETED + " or "
+                                               + 
GrpcConstants.GRPC_EVENT_TYPE_ON_ERROR
+                                               + " to send a reply with 
toRouteControlledStreamObserver");
+        }
         switch (eventType) {
-            case GrpcConstants.GRPC_EVENT_TYPE_ON_NEXT -> 
observer.onNext(exchange.getMessage().getBody());
+            case GrpcConstants.GRPC_EVENT_TYPE_ON_NEXT -> onNext(exchange, 
observer);
             case GrpcConstants.GRPC_EVENT_TYPE_ON_COMPLETED -> 
observer.onCompleted();
             case GrpcConstants.GRPC_EVENT_TYPE_ON_ERROR -> 
observer.onError((Throwable) exchange.getMessage().getBody());
             default -> {
                 // NO-OP
             }
         }
     }
+
+    /**
+     * Sends the body to the client: each element of a List body, or the body 
itself, converted to the response type of
+     * the method that the consumer set as exchange property. When a message 
cannot be converted, nothing is sent, the
+     * call ends with an error and the exchange fails, as the client would 
otherwise decode the bytes of another message
+     * type (for example the unchanged request) as the response.
+     */
+    private void onNext(Exchange exchange, StreamObserver<Object> observer) {
+        Class<?> responseType = 
exchange.getProperty(GrpcConstants.GRPC_RESPONSE_TYPE, Class.class);
+        List<Object> responses;
+        try {
+            responses = GrpcMethodHandler.toResponses(exchange, 
exchange.getMessage().getBody(), responseType);
+        } catch (IllegalArgumentException e) {
+            
observer.onError(GrpcMethodHandler.toStatusException((GrpcEndpoint) 
getEndpoint(), e));

Review Comment:
   Thanks, done as you suggested in 538574afd4fe. The consumer puts its 
`muteException` in the `grpcMuteException` exchange property, next to 
`grpcResponseType`, and the producer uses it (muted when the property is 
missing). The producer endpoint option is not read any more, and the doc now 
points to the consumer option. `GrpcMethodHandler.toStatusException` got an 
overload that takes the boolean, so both paths share the code.
   
   The tests now set `muteException=false` on the consumer, not on the reply 
endpoint: `unchangedRequestIsNotSentAsResponse`, 
`delegationUnchangedRequestIsNotSentAsResponse` and 
`redeliveryFailsWithTheReplyTypeError` check the client gets the real 
description; they failed with `Exchange processing failed` before.
   
   _Claude Code on behalf of Claus Ibsen (davsclaus)_



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to