You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by nf...@apache.org on 2017/06/16 15:27:04 UTC

[1/2] camel git commit: CAMEL-11236: added gRPC streaming producer strategy

Repository: camel
Updated Branches:
  refs/heads/master b78aea78e -> 04191e541


CAMEL-11236: added gRPC streaming producer strategy


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/0def0634
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/0def0634
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/0def0634

Branch: refs/heads/master
Commit: 0def06345b6e0740e8d9e3824832ea7490dc3a84
Parents: b78aea7
Author: Nicola Ferraro <ni...@gmail.com>
Authored: Mon Jun 12 12:20:01 2017 +0200
Committer: Nicola Ferraro <ni...@gmail.com>
Committed: Fri Jun 16 17:19:44 2017 +0200

----------------------------------------------------------------------
 .../src/main/docs/grpc-component.adoc           |  28 ++-
 .../camel/component/grpc/GrpcConfiguration.java |  47 ++++-
 .../component/grpc/GrpcConsumerStrategy.java    |  34 ++++
 .../grpc/GrpcProcessingStrategies.java          |  38 ----
 .../camel/component/grpc/GrpcProducer.java      |  45 +++--
 .../component/grpc/GrpcProducerStrategy.java    |  34 ++++
 .../apache/camel/component/grpc/GrpcUtils.java  |  19 +-
 .../grpc/client/GrpcExchangeForwarder.java      |  35 ++++
 .../client/GrpcExchangeForwarderFactory.java    |  40 +++++
 .../grpc/client/GrpcRPCExchangeForwarder.java   |  65 +++++++
 .../GrpcResponseRouterStreamObserver.java       |  92 ++++++++++
 .../client/GrpcStreamingExchangeForwarder.java  | 116 +++++++++++++
 .../grpc/server/GrpcMethodHandler.java          |  10 +-
 .../grpc/GrpcConsumerAggregationTest.java       |   4 +-
 .../grpc/GrpcConsumerConcurrentTest.java        |   4 +-
 .../grpc/GrpcConsumerPropagationTest.java       |   4 +-
 .../grpc/GrpcProducerStreamingTest.java         | 174 +++++++++++++++++++
 17 files changed, 711 insertions(+), 78 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/0def0634/components/camel-grpc/src/main/docs/grpc-component.adoc
----------------------------------------------------------------------
diff --git a/components/camel-grpc/src/main/docs/grpc-component.adoc b/components/camel-grpc/src/main/docs/grpc-component.adoc
index a74c6df..65130f2 100644
--- a/components/camel-grpc/src/main/docs/grpc-component.adoc
+++ b/components/camel-grpc/src/main/docs/grpc-component.adoc
@@ -47,20 +47,22 @@ with the following path and query parameters:
 | **service** | *Required* Fully qualified service name from the protocol buffer descriptor file (package dot service definition name) |  | String
 |=======================================================================
 
-#### Query Parameters (12 parameters):
+#### Query Parameters (14 parameters):
 
 [width="100%",cols="2,5,^1,2",options="header"]
 |=======================================================================
 | Name | Description | Default | Type
+| **forwardOnCompleted** (common) | Determines if onCompleted events should be pushed to the Camel route. | false | boolean
+| **forwardOnError** (common) | Determines if onError events should be pushed to the Camel route. Exceptions will be set as message body. | false | boolean
 | **host** (common) | The gRPC server host name |  | String
 | **port** (common) | The gRPC server port |  | int
 | **bridgeErrorHandler** (consumer) | Allows for bridging the consumer to the Camel routing Error Handler which mean any exceptions occurred while the consumer is trying to pickup incoming messages or the likes will now be processed as a message and handled by the routing Error Handler. By default the consumer will use the org.apache.camel.spi.ExceptionHandler to deal with exceptions that will be logged at WARN or ERROR level and ignored. | false | boolean
-| **forwardOnCompleted** (consumer) | Determines if onCompleted events should be pushed to the Camel route. | false | boolean
-| **forwardOnError** (consumer) | Determines if onError events should be pushed to the Camel route. Exceptions will be set as message body. | false | boolean
-| **processingStrategy** (consumer) | This option specifies the top-level strategy for processing service requests and responses in streaming mode. If an aggregation strategy is selected all requests will be accumulated in the list then transferred to the flow and the accumulated responses will be sent to the sender. If a propagation strategy is selected request is sent to the stream and the response will be immediately sent back to the sender. |  | GrpcProcessing Strategies
+| **consumerStrategy** (consumer) | This option specifies the top-level strategy for processing service requests and responses in streaming mode. If an aggregation strategy is selected all requests will be accumulated in the list then transferred to the flow and the accumulated responses will be sent to the sender. If a propagation strategy is selected request is sent to the stream and the response will be immediately sent back to the sender. |  | GrpcConsumerStrategy
 | **exceptionHandler** (consumer) | To let the consumer use a custom ExceptionHandler. Notice if the option bridgeErrorHandler is enabled then this options is not in use. By default the consumer will deal with exceptions that will be logged at WARN or ERROR level and ignored. |  | ExceptionHandler
 | **exchangePattern** (consumer) | Sets the exchange pattern when the consumer creates an exchange. |  | ExchangePattern
 | **method** (producer) | gRPC method name |  | String
+| **producerStrategy** (producer) | The mode used to communicate with a remote gRPC server. In RPC mode a single exchange is translated to a remote call. In STREAMING mode all exchanges will be sent within the same request (input and output of the recipient gRPC service must be of type 'stream'). |  | GrpcProducerStrategy
+| **streamRepliesTo** (producer) | When using STREAMING client mode it indicates the endpoint where responses should be forwarded. |  | String
 | **target** (producer) | The channel target name as alternative to host and port parameters |  | String
 | **usePlainText** (producer) | The plain text connection to the server flag | true | Boolean
 | **synchronous** (advanced) | Sets whether synchronous processing should be strictly used or Camel is allowed to use asynchronous processing (if supported). | false | boolean
@@ -125,14 +127,28 @@ from("direct:grpc-async")
 .to("grpc://org.apache.camel.component.grpc.PingPong?method=pingAsyncResponse&target=dns:///hostname:8000");
 -------------------------------------------------------------------------------
 
-gRPC service consumer with propagation processing strategy
+gRPC service consumer with propagation consumer strategy
 
 [source,java]
 -------------------------------------------------------------------------------
-from("grpc://org.apache.camel.component.grpc.PingPong??processingStrategy=PROPAGATION&host=localhost&port=1000")
+from("grpc://org.apache.camel.component.grpc.PingPong?consumerStrategy=PROPAGATION&host=localhost&port=1000")
 .to("direct:grpc-service");
 -------------------------------------------------------------------------------
 
+gRPC service producer with streaming producer strategy (requires a service that uses "stream" mode as input and output)
+
+[source,java]
+-------------------------------------------------------------------------------
+from("direct:grpc-request-stream")
+.to("grpc://org.apache.camel.component.grpc.PingPong?method=PingAsyncAsync&producerStrategy=STREAMING&streamRepliesTo=direct:grpc-response-stream&target=dns:///hostname:8000");
+
+from("direct:grpc-response-stream")
+.log("Response received: ${body}");
+-------------------------------------------------------------------------------
+
+
+### Configuration
+
 It's it is recommended to use Maven Protocol Buffers Plugin which calls Protocol Buffer Compiler (protoc) tool to generate Java source files from .proto (protocol buffer definition) files for the custom project. This plugin will generate procedures request and response classes, their builders and gRPC procedures stubs classes as well.
 
 Following steps are required:

http://git-wip-us.apache.org/repos/asf/camel/blob/0def0634/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcConfiguration.java
----------------------------------------------------------------------
diff --git a/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcConfiguration.java b/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcConfiguration.java
index 123de61..f37d0cf 100644
--- a/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcConfiguration.java
+++ b/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcConfiguration.java
@@ -42,14 +42,21 @@ public class GrpcConfiguration {
     
     @UriParam(label = "producer", defaultValue = "true")
     private Boolean usePlainText = true;
-    
+
+    @UriParam(label = "producer")
+    private GrpcProducerStrategy producerStrategy = GrpcProducerStrategy.RPC;
+
+    @UriParam(label = "producer")
+    private String streamRepliesTo;
+
+
     @UriParam(label = "consumer")
-    private GrpcProcessingStrategies processingStrategy = GrpcProcessingStrategies.PROPAGATION;
+    private GrpcConsumerStrategy consumerStrategy = GrpcConsumerStrategy.PROPAGATION;
     
-    @UriParam(label = "consumer", defaultValue = "false")
+    @UriParam(defaultValue = "false")
     private boolean forwardOnCompleted;
 
-    @UriParam(label = "consumer", defaultValue = "false")
+    @UriParam(defaultValue = "false")
     private boolean forwardOnError;
 
     private String serviceName;
@@ -130,12 +137,12 @@ public class GrpcConfiguration {
      * a propagation strategy is selected, request is sent to the stream, and the
      * response will be immediately sent back to the sender.
      */
-    public GrpcProcessingStrategies getProcessingStrategy() {
-        return processingStrategy;
+    public GrpcConsumerStrategy getConsumerStrategy() {
+        return consumerStrategy;
     }
 
-    public void setProcessingStrategy(GrpcProcessingStrategies processingStrategy) {
-        this.processingStrategy = processingStrategy;
+    public void setConsumerStrategy(GrpcConsumerStrategy consumerStrategy) {
+        this.consumerStrategy = consumerStrategy;
     }
 
     /**
@@ -182,4 +189,28 @@ public class GrpcConfiguration {
     protected void setServicePackage(String servicePackage) {
         this.servicePackage = servicePackage;
     }
+
+    public GrpcProducerStrategy getProducerStrategy() {
+        return producerStrategy;
+    }
+
+    /**
+     * The mode used to communicate with a remote gRPC server.
+     * In RPC mode a single exchange is translated to a remote call.
+     * In STREAMING mode all exchanges will be sent within the same request (input and output of the recipient gRPC service must be of type 'stream').
+     */
+    public void setProducerStrategy(GrpcProducerStrategy producerStrategy) {
+        this.producerStrategy = producerStrategy;
+    }
+
+    public String getStreamRepliesTo() {
+        return streamRepliesTo;
+    }
+
+    /**
+     * When using STREAMING client mode, it indicates the endpoint where responses should be forwarded.
+     */
+    public void setStreamRepliesTo(String streamRepliesTo) {
+        this.streamRepliesTo = streamRepliesTo;
+    }
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/0def0634/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcConsumerStrategy.java
----------------------------------------------------------------------
diff --git a/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcConsumerStrategy.java b/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcConsumerStrategy.java
new file mode 100644
index 0000000..8b25f3d
--- /dev/null
+++ b/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcConsumerStrategy.java
@@ -0,0 +1,34 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.grpc;
+
+/*
+ * Available values for the consumer processing strategy.
+ */
+public enum GrpcConsumerStrategy {
+
+    /**
+     * Collect all streaming elements in a single request and process them as a list in the route.
+     */
+    AGGREGATION,
+
+    /**
+     * Process each streaming element of a request independently.
+     */
+    PROPAGATION;
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/0def0634/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcProcessingStrategies.java
----------------------------------------------------------------------
diff --git a/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcProcessingStrategies.java b/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcProcessingStrategies.java
deleted file mode 100644
index 69766bf..0000000
--- a/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcProcessingStrategies.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.camel.component.grpc;
-
-/*
- * Available values for the request and response processing strategies
- */
-public enum GrpcProcessingStrategies {
-    
-    AGGREGATION("AGGREGATION"),
-    PROPAGATION("PROPAGATION");
-
-    private final String strategy;
-
-    GrpcProcessingStrategies(final String strategy) {
-        this.strategy = strategy;
-    }
-
-    @Override
-    public String toString() {
-        return strategy;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/camel/blob/0def0634/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcProducer.java
----------------------------------------------------------------------
diff --git a/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcProducer.java b/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcProducer.java
index 2a2030a..d05ad34 100644
--- a/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcProducer.java
+++ b/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcProducer.java
@@ -18,11 +18,15 @@ package org.apache.camel.component.grpc;
 
 import io.grpc.ManagedChannel;
 import io.grpc.netty.NettyChannelBuilder;
+import io.grpc.stub.StreamObserver;
+
 import org.apache.camel.AsyncCallback;
 import org.apache.camel.AsyncProcessor;
 import org.apache.camel.Exchange;
-import org.apache.camel.Message;
+import org.apache.camel.component.grpc.client.GrpcExchangeForwarder;
+import org.apache.camel.component.grpc.client.GrpcExchangeForwarderFactory;
 import org.apache.camel.component.grpc.client.GrpcResponseAggregationStreamObserver;
+import org.apache.camel.component.grpc.client.GrpcResponseRouterStreamObserver;
 import org.apache.camel.impl.DefaultProducer;
 import org.apache.camel.util.ObjectHelper;
 import org.slf4j.Logger;
@@ -38,32 +42,36 @@ public class GrpcProducer extends DefaultProducer implements AsyncProcessor {
     protected final GrpcEndpoint endpoint;
     private ManagedChannel channel;
     private Object grpcStub;
+    private GrpcExchangeForwarder forwarder;
+    private StreamObserver<Object> globalResponseObserver;
 
     public GrpcProducer(GrpcEndpoint endpoint, GrpcConfiguration configuration) {
         super(endpoint);
         this.endpoint = endpoint;
         this.configuration = configuration;
+
+        if (configuration.getProducerStrategy() == GrpcProducerStrategy.STREAMING) {
+            if (endpoint.isSynchronous()) {
+                throw new IllegalStateException("Cannot use synchronous processing in streaming mode");
+            } else if (configuration.getStreamRepliesTo() == null) {
+                throw new IllegalStateException("The streamReplyTo property is mandatory when using the STREAMING mode");
+            }
+        }
     }
 
     @Override
     public boolean process(Exchange exchange, AsyncCallback callback) {
-        Message message = exchange.getIn();
-
-        try {
-            GrpcUtils.invokeAsyncMethod(grpcStub, configuration.getMethod(), message.getBody(), new GrpcResponseAggregationStreamObserver(exchange, callback));
-        } catch (Exception e) {
-            exchange.setException(e);
-            callback.done(true);
-            return true;
+        StreamObserver<Object> streamObserver = this.globalResponseObserver;
+        if (globalResponseObserver == null) {
+            streamObserver = new GrpcResponseAggregationStreamObserver(exchange, callback);
         }
-        return false;
+
+        return forwarder.forward(exchange, streamObserver, callback);
     }
 
     @Override
     public void process(Exchange exchange) throws Exception {
-        Message message = exchange.getIn();
-        Object outBody = GrpcUtils.invokeSyncMethod(grpcStub, configuration.getMethod(), message.getBody());
-        exchange.getOut().setBody(outBody);
+        forwarder.forward(exchange);
     }
 
     @Override
@@ -78,16 +86,25 @@ public class GrpcProducer extends DefaultProducer implements AsyncProcessor {
                 LOG.debug("Getting asynchronous method stub from channel");
                 grpcStub = GrpcUtils.constructGrpcAsyncStub(configuration.getServicePackage(), configuration.getServiceName(), channel, endpoint.getCamelContext());
             }
+            forwarder = GrpcExchangeForwarderFactory.createExchangeForwarder(configuration, grpcStub);
+
+            if (configuration.getStreamRepliesTo() != null) {
+                this.globalResponseObserver = new GrpcResponseRouterStreamObserver(configuration, getEndpoint());
+            }
         }
     }
 
     @Override
     protected void doStop() throws Exception {
         if (channel != null) {
+            forwarder.shutdown();
+            forwarder = null;
+
             LOG.debug("Terminating channel to the remote gRPC server");
             channel.shutdown().shutdownNow();
             channel = null;
             grpcStub = null;
+            globalResponseObserver = null;
         }
         super.doStop();
     }
@@ -99,7 +116,7 @@ public class GrpcProducer extends DefaultProducer implements AsyncProcessor {
             channelBuilder = NettyChannelBuilder.forAddress(configuration.getHost(), configuration.getPort());
         } else if (!ObjectHelper.isEmpty(configuration.getTarget())) {
             LOG.info("Creating channel to the remote gRPC server " + configuration.getTarget());
-            channelBuilder =  NettyChannelBuilder.forTarget(configuration.getTarget());
+            channelBuilder = NettyChannelBuilder.forTarget(configuration.getTarget());
         } else {
             throw new IllegalArgumentException("No connection properties (host, port or target) specified");
         }

http://git-wip-us.apache.org/repos/asf/camel/blob/0def0634/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcProducerStrategy.java
----------------------------------------------------------------------
diff --git a/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcProducerStrategy.java b/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcProducerStrategy.java
new file mode 100644
index 0000000..e94c4b7
--- /dev/null
+++ b/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcProducerStrategy.java
@@ -0,0 +1,34 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.grpc;
+
+/*
+ * Available values for the producer processing strategy.
+ */
+public enum GrpcProducerStrategy {
+
+    /**
+     * Transform each exchange into a RPC.
+     */
+    RPC,
+
+    /**
+     * Forward each exchange using a shared gRPC request.
+     */
+    STREAMING;
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/0def0634/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcUtils.java
----------------------------------------------------------------------
diff --git a/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcUtils.java b/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcUtils.java
index 00cf25b..448c106 100644
--- a/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcUtils.java
+++ b/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcUtils.java
@@ -70,7 +70,7 @@ public final class GrpcUtils {
         }
         return grpcBlockingStub;
     }
-    
+
     @SuppressWarnings("rawtypes")
     public static Class constructGrpcImplBaseClass(String packageName, String serviceName, final CamelContext context) {
         Class grpcServerImpl;
@@ -110,6 +110,21 @@ public final class GrpcUtils {
     }
 
     @SuppressWarnings({"rawtypes", "unchecked"})
+    public static StreamObserver<Object> invokeAsyncMethodStreaming(Object asyncStubClass, String invokeMethod, StreamObserver<?> responseObserver) {
+        Class[] paramMethod = null;
+        Method method = ReflectionHelper.findMethod(asyncStubClass.getClass(), invokeMethod, paramMethod);
+        if (method == null) {
+            throw new IllegalArgumentException("gRPC service method not found: " + asyncStubClass.getClass().getName() + "." + invokeMethod);
+        }
+        if (!StreamObserver.class.isAssignableFrom(method.getReturnType())) {
+            throw new IllegalArgumentException("gRPC service method does not declare an input of type stream (cannot be used in streaming mode): "
+                    + asyncStubClass.getClass().getName() + "." + invokeMethod);
+        }
+
+        return  (StreamObserver<Object>) ObjectHelper.invokeMethod(method, asyncStubClass, responseObserver);
+    }
+
+    @SuppressWarnings({"rawtypes", "unchecked"})
     public static Object invokeSyncMethod(Object blockingStubClass, String invokeMethod, Object request) {
         Class[] paramMethod = null;
 
@@ -132,7 +147,7 @@ public final class GrpcUtils {
     /**
      * Migrated MixedLower function from the gRPC converting plugin source code
      * (https://github.com/grpc/grpc-java/blob/master/compiler/src/java_plugin/cpp/java_generator.cpp)
-     * 
+     *
      * - decapitalize the first letter
      * - remove embedded underscores & capitalize the following letter
      */

http://git-wip-us.apache.org/repos/asf/camel/blob/0def0634/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/client/GrpcExchangeForwarder.java
----------------------------------------------------------------------
diff --git a/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/client/GrpcExchangeForwarder.java b/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/client/GrpcExchangeForwarder.java
new file mode 100644
index 0000000..c487bd6
--- /dev/null
+++ b/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/client/GrpcExchangeForwarder.java
@@ -0,0 +1,35 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.grpc.client;
+
+import io.grpc.stub.StreamObserver;
+
+import org.apache.camel.AsyncCallback;
+import org.apache.camel.Exchange;
+
+/**
+ * A forwarder is responsible to forward exchanges to a remote gRPC server.
+ */
+public interface GrpcExchangeForwarder {
+
+    boolean forward(Exchange exchange, StreamObserver<Object> responseObserver, AsyncCallback callback);
+
+    void forward(Exchange exchange);
+
+    void shutdown();
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/0def0634/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/client/GrpcExchangeForwarderFactory.java
----------------------------------------------------------------------
diff --git a/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/client/GrpcExchangeForwarderFactory.java b/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/client/GrpcExchangeForwarderFactory.java
new file mode 100644
index 0000000..f4dc9d6
--- /dev/null
+++ b/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/client/GrpcExchangeForwarderFactory.java
@@ -0,0 +1,40 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.grpc.client;
+
+import org.apache.camel.component.grpc.GrpcConfiguration;
+import org.apache.camel.component.grpc.GrpcProducerStrategy;
+
+/**
+ * Creates the correct forwarder according to the configuration.
+ */
+public final class GrpcExchangeForwarderFactory {
+
+    private GrpcExchangeForwarderFactory() {
+    }
+
+    public static GrpcExchangeForwarder createExchangeForwarder(GrpcConfiguration configuration, Object grpcStub) {
+        if (configuration.getProducerStrategy() == GrpcProducerStrategy.RPC) {
+            return new GrpcRPCExchangeForwarder(configuration, grpcStub);
+        } else if (configuration.getProducerStrategy() == GrpcProducerStrategy.STREAMING) {
+            return new GrpcStreamingExchangeForwarder(configuration, grpcStub);
+        } else {
+            throw new IllegalStateException("Unsupported producer strategy: " + configuration.getProducerStrategy());
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/0def0634/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/client/GrpcRPCExchangeForwarder.java
----------------------------------------------------------------------
diff --git a/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/client/GrpcRPCExchangeForwarder.java b/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/client/GrpcRPCExchangeForwarder.java
new file mode 100644
index 0000000..0ff9c6c
--- /dev/null
+++ b/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/client/GrpcRPCExchangeForwarder.java
@@ -0,0 +1,65 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.grpc.client;
+
+import io.grpc.stub.StreamObserver;
+
+import org.apache.camel.AsyncCallback;
+import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+import org.apache.camel.component.grpc.GrpcConfiguration;
+import org.apache.camel.component.grpc.GrpcUtils;
+
+/**
+ * An exchange forwarder that creates a RPC request for each camel Exchange.
+ */
+class GrpcRPCExchangeForwarder implements GrpcExchangeForwarder {
+
+    private final GrpcConfiguration configuration;
+
+    private final Object grpcStub;
+
+    public GrpcRPCExchangeForwarder(GrpcConfiguration configuration, Object grpcStub) {
+        this.configuration = configuration;
+        this.grpcStub = grpcStub;
+
+    }
+
+    @Override
+    public boolean forward(Exchange exchange, StreamObserver<Object> responseObserver, AsyncCallback callback) {
+        Message message = exchange.getIn();
+        try {
+            GrpcUtils.invokeAsyncMethod(grpcStub, configuration.getMethod(), message.getBody(), responseObserver);
+        } catch (Exception e) {
+            exchange.setException(e);
+            callback.done(true);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void forward(Exchange exchange) {
+        Message message = exchange.getIn();
+        Object outBody = GrpcUtils.invokeSyncMethod(grpcStub, configuration.getMethod(), message.getBody());
+        exchange.getOut().setBody(outBody);
+    }
+
+    @Override
+    public void shutdown() {
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/0def0634/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/client/GrpcResponseRouterStreamObserver.java
----------------------------------------------------------------------
diff --git a/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/client/GrpcResponseRouterStreamObserver.java b/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/client/GrpcResponseRouterStreamObserver.java
new file mode 100644
index 0000000..e430362
--- /dev/null
+++ b/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/client/GrpcResponseRouterStreamObserver.java
@@ -0,0 +1,92 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.grpc.client;
+
+import io.grpc.stub.StreamObserver;
+
+import org.apache.camel.AsyncCallback;
+import org.apache.camel.AsyncProcessor;
+import org.apache.camel.AsyncProducerCallback;
+import org.apache.camel.Endpoint;
+import org.apache.camel.Exchange;
+import org.apache.camel.ExchangePattern;
+import org.apache.camel.Producer;
+import org.apache.camel.component.grpc.GrpcConfiguration;
+import org.apache.camel.component.grpc.GrpcConstants;
+import org.apache.camel.impl.EmptyProducerCache;
+import org.apache.camel.impl.ProducerCache;
+import org.apache.camel.util.CamelContextHelper;
+
+/**
+ * A stream observer that routes all responses to another endpoint.
+ */
+public class GrpcResponseRouterStreamObserver implements StreamObserver<Object> {
+
+    private final Endpoint sourceEndpoint;
+    private final GrpcConfiguration configuration;
+    private final Endpoint endpoint;
+    private final ProducerCache producerCache;
+
+    public GrpcResponseRouterStreamObserver(GrpcConfiguration configuration, Endpoint sourceEndpoint) {
+        this.configuration = configuration;
+        this.sourceEndpoint = sourceEndpoint;
+        this.endpoint = CamelContextHelper.getMandatoryEndpoint(sourceEndpoint.getCamelContext(), configuration.getStreamRepliesTo());
+        this.producerCache = new EmptyProducerCache(this, sourceEndpoint.getCamelContext());
+    }
+
+    @Override
+    public void onNext(Object o) {
+        Exchange exchange = sourceEndpoint.createExchange();
+        exchange.getIn().setHeader(GrpcConstants.GRPC_EVENT_TYPE_HEADER, GrpcConstants.GRPC_EVENT_TYPE_ON_NEXT);
+        exchange.getIn().setBody(o);
+        doSend(exchange, done -> {
+        });
+
+    }
+
+    @Override
+    public void onError(Throwable throwable) {
+        if (configuration.isForwardOnError()) {
+            Exchange exchange = sourceEndpoint.createExchange();
+            exchange.getIn().setHeader(GrpcConstants.GRPC_EVENT_TYPE_HEADER, GrpcConstants.GRPC_EVENT_TYPE_ON_ERROR);
+            exchange.getIn().setBody(throwable);
+            doSend(exchange, done -> {
+            });
+        }
+    }
+
+    @Override
+    public void onCompleted() {
+        if (configuration.isForwardOnCompleted()) {
+            Exchange exchange = sourceEndpoint.createExchange();
+            exchange.getIn().setHeader(GrpcConstants.GRPC_EVENT_TYPE_HEADER, GrpcConstants.GRPC_EVENT_TYPE_ON_COMPLETED);
+            doSend(exchange, done -> {
+            });
+        }
+    }
+
+
+    private void doSend(Exchange ex, AsyncCallback callback) {
+        producerCache.doInAsyncProducer(endpoint, ex, ExchangePattern.InOnly, callback, new AsyncProducerCallback() {
+            @Override
+            public boolean doInAsyncProducer(Producer producer, AsyncProcessor asyncProducer, Exchange exchange2, ExchangePattern exchangePattern, AsyncCallback callback2) {
+                return asyncProducer.process(exchange2, callback2);
+            }
+        });
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/0def0634/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/client/GrpcStreamingExchangeForwarder.java
----------------------------------------------------------------------
diff --git a/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/client/GrpcStreamingExchangeForwarder.java b/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/client/GrpcStreamingExchangeForwarder.java
new file mode 100644
index 0000000..3746c22
--- /dev/null
+++ b/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/client/GrpcStreamingExchangeForwarder.java
@@ -0,0 +1,116 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.grpc.client;
+
+import io.grpc.stub.StreamObserver;
+
+import org.apache.camel.AsyncCallback;
+import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+import org.apache.camel.component.grpc.GrpcConfiguration;
+import org.apache.camel.component.grpc.GrpcUtils;
+
+/**
+ * An exchange forwarder that forwards each Camel exchange in the same request channel.
+ */
+class GrpcStreamingExchangeForwarder implements GrpcExchangeForwarder {
+
+    private final GrpcConfiguration configuration;
+
+    private final Object grpcStub;
+
+    private volatile StreamObserver<Object> currentStream;
+
+    private volatile StreamObserver<Object> currentResponseObserver;
+
+    public GrpcStreamingExchangeForwarder(GrpcConfiguration configuration, Object grpcStub) {
+        this.configuration = configuration;
+        this.grpcStub = grpcStub;
+    }
+
+    @Override
+    public boolean forward(Exchange exchange, StreamObserver<Object> responseObserver, AsyncCallback callback) {
+        Message message = exchange.getIn();
+        checkAndRecreateStreamObserver(responseObserver).onNext(message.getBody());
+        callback.done(true);
+        return true;
+    }
+
+    @Override
+    public void forward(Exchange exchange) {
+        throw new UnsupportedOperationException("Synchronous call is not supported in streaming mode");
+    }
+
+    @Override
+    public void shutdown() {
+        checkAndRecreateStreamObserver(this.currentResponseObserver).onCompleted();
+        doCloseStream();
+    }
+
+    private StreamObserver<Object> checkAndRecreateStreamObserver(StreamObserver<Object> responseObserver) {
+        StreamObserver<Object> curStream = this.currentStream;
+        if (curStream == null) {
+            synchronized (this) {
+                if (this.currentStream == null) {
+                    this.currentResponseObserver = responseObserver;
+                    this.currentStream = doCreateStream(responseObserver);
+                }
+
+                curStream = this.currentStream;
+            }
+        }
+
+        StreamObserver<Object> curResponseObserver = this.currentResponseObserver;
+        if (curResponseObserver != null && !curResponseObserver.equals(responseObserver)) {
+            throw new IllegalArgumentException("This forwarder must always use the same response observer");
+        }
+        return curStream;
+    }
+
+    private void doCloseStream() {
+        synchronized (this) {
+            this.currentStream = null;
+            this.currentResponseObserver = null;
+        }
+    }
+
+    private StreamObserver<Object> doCreateStream(StreamObserver<Object> streamObserver) {
+
+        return GrpcUtils.invokeAsyncMethodStreaming(grpcStub, configuration.getMethod(), new StreamObserver<Object>() {
+
+            @Override
+            public void onNext(Object o) {
+                streamObserver.onNext(o);
+
+            }
+
+            @Override
+            public void onError(Throwable throwable) {
+                doCloseStream();
+                streamObserver.onError(throwable);
+            }
+
+            @Override
+            public void onCompleted() {
+                doCloseStream();
+                streamObserver.onCompleted();
+            }
+
+        });
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/0def0634/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/server/GrpcMethodHandler.java
----------------------------------------------------------------------
diff --git a/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/server/GrpcMethodHandler.java b/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/server/GrpcMethodHandler.java
index 5e41932..5007846 100644
--- a/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/server/GrpcMethodHandler.java
+++ b/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/server/GrpcMethodHandler.java
@@ -22,12 +22,14 @@ import java.util.List;
 import java.util.Map;
 
 import io.grpc.stub.StreamObserver;
+
 import javassist.util.proxy.MethodHandler;
+
 import org.apache.camel.Exchange;
 import org.apache.camel.component.grpc.GrpcConstants;
 import org.apache.camel.component.grpc.GrpcConsumer;
+import org.apache.camel.component.grpc.GrpcConsumerStrategy;
 import org.apache.camel.component.grpc.GrpcEndpoint;
-import org.apache.camel.component.grpc.GrpcProcessingStrategies;
 
 /**
  * gRPC server method invocation handler
@@ -79,12 +81,12 @@ public class GrpcMethodHandler implements MethodHandler {
             final StreamObserver<Object> responseObserver = (StreamObserver<Object>)args[0];
             StreamObserver<Object> requestObserver = null;
             
-            if (consumer.getConfiguration().getProcessingStrategy() == GrpcProcessingStrategies.AGGREGATION) {
+            if (consumer.getConfiguration().getConsumerStrategy() == GrpcConsumerStrategy.AGGREGATION) {
                 requestObserver = new GrpcRequestAggregationStreamObserver(endpoint, consumer, responseObserver, grcpHeaders);
-            } else if (consumer.getConfiguration().getProcessingStrategy() == GrpcProcessingStrategies.PROPAGATION) {
+            } else if (consumer.getConfiguration().getConsumerStrategy() == GrpcConsumerStrategy.PROPAGATION) {
                 requestObserver = new GrpcRequestPropagationStreamObserver(endpoint, consumer, responseObserver, grcpHeaders);
             } else {
-                throw new IllegalArgumentException("gRPC processing strategy not implemented " + consumer.getConfiguration().getProcessingStrategy());
+                throw new IllegalArgumentException("gRPC processing strategy not implemented " + consumer.getConfiguration().getConsumerStrategy());
             }
             
             return requestObserver;

http://git-wip-us.apache.org/repos/asf/camel/blob/0def0634/components/camel-grpc/src/test/java/org/apache/camel/component/grpc/GrpcConsumerAggregationTest.java
----------------------------------------------------------------------
diff --git a/components/camel-grpc/src/test/java/org/apache/camel/component/grpc/GrpcConsumerAggregationTest.java b/components/camel-grpc/src/test/java/org/apache/camel/component/grpc/GrpcConsumerAggregationTest.java
index 587bc9b..be96c02 100644
--- a/components/camel-grpc/src/test/java/org/apache/camel/component/grpc/GrpcConsumerAggregationTest.java
+++ b/components/camel-grpc/src/test/java/org/apache/camel/component/grpc/GrpcConsumerAggregationTest.java
@@ -166,10 +166,10 @@ public class GrpcConsumerAggregationTest extends CamelTestSupport {
         return new RouteBuilder() {
             @Override
             public void configure() {
-                from("grpc://org.apache.camel.component.grpc.PingPong?synchronous=true&processingStrategy=AGGREGATION&host=localhost&port=" + GRPC_SYNC_REQUEST_TEST_PORT)
+                from("grpc://org.apache.camel.component.grpc.PingPong?synchronous=true&consumerStrategy=AGGREGATION&host=localhost&port=" + GRPC_SYNC_REQUEST_TEST_PORT)
                     .bean(new GrpcMessageBuilder(), "buildPongResponse");
                 
-                from("grpc://org.apache.camel.component.grpc.PingPong?synchronous=true&processingStrategy=AGGREGATION&host=localhost&port=" + GRPC_ASYNC_REQUEST_TEST_PORT)
+                from("grpc://org.apache.camel.component.grpc.PingPong?synchronous=true&consumerStrategy=AGGREGATION&host=localhost&port=" + GRPC_ASYNC_REQUEST_TEST_PORT)
                     .bean(new GrpcMessageBuilder(), "buildAsyncPongResponse");
             }
         };

http://git-wip-us.apache.org/repos/asf/camel/blob/0def0634/components/camel-grpc/src/test/java/org/apache/camel/component/grpc/GrpcConsumerConcurrentTest.java
----------------------------------------------------------------------
diff --git a/components/camel-grpc/src/test/java/org/apache/camel/component/grpc/GrpcConsumerConcurrentTest.java b/components/camel-grpc/src/test/java/org/apache/camel/component/grpc/GrpcConsumerConcurrentTest.java
index c341e51..8cddcc2 100644
--- a/components/camel-grpc/src/test/java/org/apache/camel/component/grpc/GrpcConsumerConcurrentTest.java
+++ b/components/camel-grpc/src/test/java/org/apache/camel/component/grpc/GrpcConsumerConcurrentTest.java
@@ -136,10 +136,10 @@ public class GrpcConsumerConcurrentTest extends CamelTestSupport {
         return new RouteBuilder() {
             @Override
             public void configure() {
-                from("grpc://org.apache.camel.component.grpc.PingPong?synchronous=true&processingStrategy=AGGREGATION&host=localhost&port=" + GRPC_ASYNC_REQUEST_TEST_PORT)
+                from("grpc://org.apache.camel.component.grpc.PingPong?synchronous=true&consumerStrategy=AGGREGATION&host=localhost&port=" + GRPC_ASYNC_REQUEST_TEST_PORT)
                     .bean(new GrpcMessageBuilder(), "buildAsyncPongResponse");
                 
-                from("grpc://org.apache.camel.component.grpc.PingPong?synchronous=true&processingStrategy=AGGREGATION&host=localhost&port=" + GRPC_HEADERS_TEST_PORT)
+                from("grpc://org.apache.camel.component.grpc.PingPong?synchronous=true&consumerStrategy=AGGREGATION&host=localhost&port=" + GRPC_HEADERS_TEST_PORT)
                     .process(new HeaderExchangeProcessor());
             }
         };

http://git-wip-us.apache.org/repos/asf/camel/blob/0def0634/components/camel-grpc/src/test/java/org/apache/camel/component/grpc/GrpcConsumerPropagationTest.java
----------------------------------------------------------------------
diff --git a/components/camel-grpc/src/test/java/org/apache/camel/component/grpc/GrpcConsumerPropagationTest.java b/components/camel-grpc/src/test/java/org/apache/camel/component/grpc/GrpcConsumerPropagationTest.java
index e7cb8c7..9d5fc5f 100644
--- a/components/camel-grpc/src/test/java/org/apache/camel/component/grpc/GrpcConsumerPropagationTest.java
+++ b/components/camel-grpc/src/test/java/org/apache/camel/component/grpc/GrpcConsumerPropagationTest.java
@@ -108,11 +108,11 @@ public class GrpcConsumerPropagationTest extends CamelTestSupport {
             @Override
             public void configure() {
                 
-                from("grpc://org.apache.camel.component.grpc.PingPong?processingStrategy=PROPAGATION&host=localhost&port=" + GRPC_ASYNC_NEXT_REQUEST_TEST_PORT)
+                from("grpc://org.apache.camel.component.grpc.PingPong?consumerStrategy=PROPAGATION&host=localhost&port=" + GRPC_ASYNC_NEXT_REQUEST_TEST_PORT)
                     .to("mock:async-on-next-propagation")
                     .bean(new GrpcMessageBuilder(), "buildAsyncPongResponse");
                 
-                from("grpc://org.apache.camel.component.grpc.PingPong?processingStrategy=PROPAGATION&forwardOnCompleted=true&host=localhost&port=" + GRPC_ASYNC_COMPLETED_REQUEST_TEST_PORT)
+                from("grpc://org.apache.camel.component.grpc.PingPong?consumerStrategy=PROPAGATION&forwardOnCompleted=true&host=localhost&port=" + GRPC_ASYNC_COMPLETED_REQUEST_TEST_PORT)
                     .to("mock:async-on-completed-propagation")
                     .bean(new GrpcMessageBuilder(), "buildAsyncPongResponse");
             }

http://git-wip-us.apache.org/repos/asf/camel/blob/0def0634/components/camel-grpc/src/test/java/org/apache/camel/component/grpc/GrpcProducerStreamingTest.java
----------------------------------------------------------------------
diff --git a/components/camel-grpc/src/test/java/org/apache/camel/component/grpc/GrpcProducerStreamingTest.java b/components/camel-grpc/src/test/java/org/apache/camel/component/grpc/GrpcProducerStreamingTest.java
new file mode 100644
index 0000000..3ed3848
--- /dev/null
+++ b/components/camel-grpc/src/test/java/org/apache/camel/component/grpc/GrpcProducerStreamingTest.java
@@ -0,0 +1,174 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.grpc;
+
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
+
+import io.grpc.Server;
+import io.grpc.ServerBuilder;
+import io.grpc.stub.StreamObserver;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.AvailablePortFinder;
+import org.apache.camel.test.junit4.CamelTestSupport;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class GrpcProducerStreamingTest extends CamelTestSupport {
+    private static final Logger LOG = LoggerFactory.getLogger(GrpcProducerStreamingTest.class);
+
+    private static final int GRPC_TEST_PORT = AvailablePortFinder.getNextAvailable();
+
+    private static Server grpcServer;
+    private static PingPongImpl pingPongServer;
+
+    @Before
+    public void startGrpcServer() throws Exception {
+        pingPongServer = new PingPongImpl();
+        grpcServer = ServerBuilder.forPort(GRPC_TEST_PORT).addService(pingPongServer).build().start();
+        LOG.info("gRPC server started on port " + GRPC_TEST_PORT);
+    }
+
+    @After
+    public void stopGrpcServer() throws IOException {
+        if (grpcServer != null) {
+            grpcServer.shutdown();
+            LOG.info("gRPC server stopped");
+            pingPongServer = null;
+        }
+    }
+
+    @Test
+    public void testPingAsyncAsync() throws Exception {
+        int messageCount = 10;
+        for (int i = 1; i <= messageCount; i++) {
+            template.sendBody("direct:grpc-stream-async-async-route", PingRequest.newBuilder().setPingName(String.valueOf(i)).build());
+        }
+
+        MockEndpoint replies = getMockEndpoint("mock:grpc-replies");
+        replies.expectedMessageCount(messageCount);
+        replies.assertIsSatisfied();
+
+        context().stop();
+
+        assertNotNull(pingPongServer.getLastStreamRequests());
+        assertListSize(pingPongServer.getLastStreamRequests(), 1);
+        assertListSize(pingPongServer.getLastStreamRequests().get(0), messageCount);
+    }
+
+    @Test
+    public void testPingAsyncAsyncRecovery() throws Exception {
+        int messageGroupCount = 5;
+        for (int i = 1; i <= messageGroupCount; i++) {
+            template.sendBody("direct:grpc-stream-async-async-route", PingRequest.newBuilder().setPingName(String.valueOf(i)).build());
+        }
+
+        template.sendBody("direct:grpc-stream-async-async-route", PingRequest.newBuilder().setPingName(String.valueOf("error")).build());
+
+
+
+        MockEndpoint replies = getMockEndpoint("mock:grpc-replies");
+        replies.expectedMessageCount(messageGroupCount);
+        replies.assertIsSatisfied();
+
+        Thread.sleep(200);
+
+        for (int i = messageGroupCount + 1; i <= 2 * messageGroupCount; i++) {
+            template.sendBody("direct:grpc-stream-async-async-route", PingRequest.newBuilder().setPingName(String.valueOf(i)).build());
+        }
+
+        replies.reset();
+        replies.expectedMessageCount(messageGroupCount);
+        replies.assertIsSatisfied();
+
+        context().stop();
+
+        assertNotNull(pingPongServer.getLastStreamRequests());
+        assertListSize(pingPongServer.getLastStreamRequests(), 2);
+        assertListSize(pingPongServer.getLastStreamRequests().get(0), messageGroupCount + 1);
+        assertListSize(pingPongServer.getLastStreamRequests().get(1), messageGroupCount);
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:grpc-stream-async-async-route")
+                        .to("grpc://org.apache.camel.component.grpc.PingPong?producerStrategy=STREAMING&streamRepliesTo=direct:grpc-replies&method=pingAsyncAsync&host=localhost&port="
+                                + GRPC_TEST_PORT);
+
+                from("direct:grpc-replies")
+                        .to("mock:grpc-replies");
+            }
+        };
+    }
+
+    /**
+     * Test gRPC PingPong server implementation
+     */
+    static class PingPongImpl extends PingPongGrpc.PingPongImplBase {
+
+        private List<List<PingRequest>> streamRequests = new LinkedList<>();
+
+        @Override
+        public StreamObserver<PingRequest> pingAsyncAsync(StreamObserver<PongResponse> responseObserver) {
+
+            @SuppressWarnings({"unchecked", "rawtypes"})
+            StreamObserver<PingRequest> requestObserver = new StreamObserver<PingRequest>() {
+
+                private List<PingRequest> streamRequests = new LinkedList<>();
+
+                @Override
+                public void onNext(PingRequest request) {
+                    streamRequests.add(request);
+                    if ("error".equals(request.getPingName())) {
+                        PingPongImpl.this.streamRequests.add(streamRequests);
+                        responseObserver.onError(new RuntimeException("Requested error"));
+                    } else {
+                        PongResponse response = PongResponse.newBuilder().setPongName("Hello " + request.getPingName()).build();
+                        responseObserver.onNext(response);
+                    }
+                }
+
+                @Override
+                public void onError(Throwable t) {
+                    PingPongImpl.this.streamRequests.add(streamRequests);
+                    LOG.info("Error in pingAsyncAsync() " + t.getMessage());
+                }
+
+                @Override
+                public void onCompleted() {
+                    PingPongImpl.this.streamRequests.add(streamRequests);
+                    responseObserver.onCompleted();
+                }
+            };
+            return requestObserver;
+        }
+
+        public List<List<PingRequest>> getLastStreamRequests() {
+            return streamRequests;
+        }
+
+    }
+}
\ No newline at end of file


[2/2] camel git commit: CAMEL-11236: renaming gRPC RPC mode to SIMPLE

Posted by nf...@apache.org.
CAMEL-11236: renaming gRPC RPC mode to SIMPLE


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/04191e54
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/04191e54
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/04191e54

Branch: refs/heads/master
Commit: 04191e541839052fd036f04444d22494186e7292
Parents: 0def063
Author: Nicola Ferraro <ni...@gmail.com>
Authored: Fri Jun 16 17:26:43 2017 +0200
Committer: Nicola Ferraro <ni...@gmail.com>
Committed: Fri Jun 16 17:26:43 2017 +0200

----------------------------------------------------------------------
 .../src/main/docs/grpc-component.adoc           |  4 +-
 .../camel/component/grpc/GrpcConfiguration.java |  8 +--
 .../component/grpc/GrpcProducerStrategy.java    |  2 +-
 .../client/GrpcExchangeForwarderFactory.java    |  4 +-
 .../grpc/client/GrpcRPCExchangeForwarder.java   | 65 --------------------
 .../client/GrpcSimpleExchangeForwarder.java     | 65 ++++++++++++++++++++
 6 files changed, 74 insertions(+), 74 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/04191e54/components/camel-grpc/src/main/docs/grpc-component.adoc
----------------------------------------------------------------------
diff --git a/components/camel-grpc/src/main/docs/grpc-component.adoc b/components/camel-grpc/src/main/docs/grpc-component.adoc
index 65130f2..95820de 100644
--- a/components/camel-grpc/src/main/docs/grpc-component.adoc
+++ b/components/camel-grpc/src/main/docs/grpc-component.adoc
@@ -57,11 +57,11 @@ with the following path and query parameters:
 | **host** (common) | The gRPC server host name |  | String
 | **port** (common) | The gRPC server port |  | int
 | **bridgeErrorHandler** (consumer) | Allows for bridging the consumer to the Camel routing Error Handler which mean any exceptions occurred while the consumer is trying to pickup incoming messages or the likes will now be processed as a message and handled by the routing Error Handler. By default the consumer will use the org.apache.camel.spi.ExceptionHandler to deal with exceptions that will be logged at WARN or ERROR level and ignored. | false | boolean
-| **consumerStrategy** (consumer) | This option specifies the top-level strategy for processing service requests and responses in streaming mode. If an aggregation strategy is selected all requests will be accumulated in the list then transferred to the flow and the accumulated responses will be sent to the sender. If a propagation strategy is selected request is sent to the stream and the response will be immediately sent back to the sender. |  | GrpcConsumerStrategy
+| **consumerStrategy** (consumer) | This option specifies the top-level strategy for processing service requests and responses in streaming mode. If an aggregation strategy is selected all requests will be accumulated in the list then transferred to the flow and the accumulated responses will be sent to the sender. If a propagation strategy is selected request is sent to the stream and the response will be immediately sent back to the sender. | PROPAGATION | GrpcConsumerStrategy
 | **exceptionHandler** (consumer) | To let the consumer use a custom ExceptionHandler. Notice if the option bridgeErrorHandler is enabled then this options is not in use. By default the consumer will deal with exceptions that will be logged at WARN or ERROR level and ignored. |  | ExceptionHandler
 | **exchangePattern** (consumer) | Sets the exchange pattern when the consumer creates an exchange. |  | ExchangePattern
 | **method** (producer) | gRPC method name |  | String
-| **producerStrategy** (producer) | The mode used to communicate with a remote gRPC server. In RPC mode a single exchange is translated to a remote call. In STREAMING mode all exchanges will be sent within the same request (input and output of the recipient gRPC service must be of type 'stream'). |  | GrpcProducerStrategy
+| **producerStrategy** (producer) | The mode used to communicate with a remote gRPC server. In SIMPLE mode a single exchange is translated into a remote procedure call. In STREAMING mode all exchanges will be sent within the same request (input and output of the recipient gRPC service must be of type 'stream'). | SIMPLE | GrpcProducerStrategy
 | **streamRepliesTo** (producer) | When using STREAMING client mode it indicates the endpoint where responses should be forwarded. |  | String
 | **target** (producer) | The channel target name as alternative to host and port parameters |  | String
 | **usePlainText** (producer) | The plain text connection to the server flag | true | Boolean

http://git-wip-us.apache.org/repos/asf/camel/blob/04191e54/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcConfiguration.java
----------------------------------------------------------------------
diff --git a/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcConfiguration.java b/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcConfiguration.java
index f37d0cf..3a708d8 100644
--- a/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcConfiguration.java
+++ b/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcConfiguration.java
@@ -43,14 +43,14 @@ public class GrpcConfiguration {
     @UriParam(label = "producer", defaultValue = "true")
     private Boolean usePlainText = true;
 
-    @UriParam(label = "producer")
-    private GrpcProducerStrategy producerStrategy = GrpcProducerStrategy.RPC;
+    @UriParam(label = "producer", defaultValue = "SIMPLE")
+    private GrpcProducerStrategy producerStrategy = GrpcProducerStrategy.SIMPLE;
 
     @UriParam(label = "producer")
     private String streamRepliesTo;
 
 
-    @UriParam(label = "consumer")
+    @UriParam(label = "consumer", defaultValue = "PROPAGATION")
     private GrpcConsumerStrategy consumerStrategy = GrpcConsumerStrategy.PROPAGATION;
     
     @UriParam(defaultValue = "false")
@@ -196,7 +196,7 @@ public class GrpcConfiguration {
 
     /**
      * The mode used to communicate with a remote gRPC server.
-     * In RPC mode a single exchange is translated to a remote call.
+     * In SIMPLE mode a single exchange is translated into a remote procedure call.
      * In STREAMING mode all exchanges will be sent within the same request (input and output of the recipient gRPC service must be of type 'stream').
      */
     public void setProducerStrategy(GrpcProducerStrategy producerStrategy) {

http://git-wip-us.apache.org/repos/asf/camel/blob/04191e54/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcProducerStrategy.java
----------------------------------------------------------------------
diff --git a/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcProducerStrategy.java b/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcProducerStrategy.java
index e94c4b7..f0cbeb4 100644
--- a/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcProducerStrategy.java
+++ b/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/GrpcProducerStrategy.java
@@ -24,7 +24,7 @@ public enum GrpcProducerStrategy {
     /**
      * Transform each exchange into a RPC.
      */
-    RPC,
+    SIMPLE,
 
     /**
      * Forward each exchange using a shared gRPC request.

http://git-wip-us.apache.org/repos/asf/camel/blob/04191e54/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/client/GrpcExchangeForwarderFactory.java
----------------------------------------------------------------------
diff --git a/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/client/GrpcExchangeForwarderFactory.java b/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/client/GrpcExchangeForwarderFactory.java
index f4dc9d6..2b92d43 100644
--- a/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/client/GrpcExchangeForwarderFactory.java
+++ b/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/client/GrpcExchangeForwarderFactory.java
@@ -28,8 +28,8 @@ public final class GrpcExchangeForwarderFactory {
     }
 
     public static GrpcExchangeForwarder createExchangeForwarder(GrpcConfiguration configuration, Object grpcStub) {
-        if (configuration.getProducerStrategy() == GrpcProducerStrategy.RPC) {
-            return new GrpcRPCExchangeForwarder(configuration, grpcStub);
+        if (configuration.getProducerStrategy() == GrpcProducerStrategy.SIMPLE) {
+            return new GrpcSimpleExchangeForwarder(configuration, grpcStub);
         } else if (configuration.getProducerStrategy() == GrpcProducerStrategy.STREAMING) {
             return new GrpcStreamingExchangeForwarder(configuration, grpcStub);
         } else {

http://git-wip-us.apache.org/repos/asf/camel/blob/04191e54/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/client/GrpcRPCExchangeForwarder.java
----------------------------------------------------------------------
diff --git a/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/client/GrpcRPCExchangeForwarder.java b/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/client/GrpcRPCExchangeForwarder.java
deleted file mode 100644
index 0ff9c6c..0000000
--- a/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/client/GrpcRPCExchangeForwarder.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.camel.component.grpc.client;
-
-import io.grpc.stub.StreamObserver;
-
-import org.apache.camel.AsyncCallback;
-import org.apache.camel.Exchange;
-import org.apache.camel.Message;
-import org.apache.camel.component.grpc.GrpcConfiguration;
-import org.apache.camel.component.grpc.GrpcUtils;
-
-/**
- * An exchange forwarder that creates a RPC request for each camel Exchange.
- */
-class GrpcRPCExchangeForwarder implements GrpcExchangeForwarder {
-
-    private final GrpcConfiguration configuration;
-
-    private final Object grpcStub;
-
-    public GrpcRPCExchangeForwarder(GrpcConfiguration configuration, Object grpcStub) {
-        this.configuration = configuration;
-        this.grpcStub = grpcStub;
-
-    }
-
-    @Override
-    public boolean forward(Exchange exchange, StreamObserver<Object> responseObserver, AsyncCallback callback) {
-        Message message = exchange.getIn();
-        try {
-            GrpcUtils.invokeAsyncMethod(grpcStub, configuration.getMethod(), message.getBody(), responseObserver);
-        } catch (Exception e) {
-            exchange.setException(e);
-            callback.done(true);
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    public void forward(Exchange exchange) {
-        Message message = exchange.getIn();
-        Object outBody = GrpcUtils.invokeSyncMethod(grpcStub, configuration.getMethod(), message.getBody());
-        exchange.getOut().setBody(outBody);
-    }
-
-    @Override
-    public void shutdown() {
-    }
-}

http://git-wip-us.apache.org/repos/asf/camel/blob/04191e54/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/client/GrpcSimpleExchangeForwarder.java
----------------------------------------------------------------------
diff --git a/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/client/GrpcSimpleExchangeForwarder.java b/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/client/GrpcSimpleExchangeForwarder.java
new file mode 100644
index 0000000..4a497b5
--- /dev/null
+++ b/components/camel-grpc/src/main/java/org/apache/camel/component/grpc/client/GrpcSimpleExchangeForwarder.java
@@ -0,0 +1,65 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.grpc.client;
+
+import io.grpc.stub.StreamObserver;
+
+import org.apache.camel.AsyncCallback;
+import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+import org.apache.camel.component.grpc.GrpcConfiguration;
+import org.apache.camel.component.grpc.GrpcUtils;
+
+/**
+ * An exchange forwarder that creates a RPC request for each camel Exchange.
+ */
+class GrpcSimpleExchangeForwarder implements GrpcExchangeForwarder {
+
+    private final GrpcConfiguration configuration;
+
+    private final Object grpcStub;
+
+    public GrpcSimpleExchangeForwarder(GrpcConfiguration configuration, Object grpcStub) {
+        this.configuration = configuration;
+        this.grpcStub = grpcStub;
+
+    }
+
+    @Override
+    public boolean forward(Exchange exchange, StreamObserver<Object> responseObserver, AsyncCallback callback) {
+        Message message = exchange.getIn();
+        try {
+            GrpcUtils.invokeAsyncMethod(grpcStub, configuration.getMethod(), message.getBody(), responseObserver);
+        } catch (Exception e) {
+            exchange.setException(e);
+            callback.done(true);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void forward(Exchange exchange) {
+        Message message = exchange.getIn();
+        Object outBody = GrpcUtils.invokeSyncMethod(grpcStub, configuration.getMethod(), message.getBody());
+        exchange.getOut().setBody(outBody);
+    }
+
+    @Override
+    public void shutdown() {
+    }
+}