/**
* 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.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import com.google.common.base.Stopwatch;
import io.grpc.BindableService;
import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.stub.StreamObserver;
import org.apache.camel.Exchange;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.support.SynchronizationAdapter;
import org.apache.camel.test.AvailablePortFinder;
import org.apache.camel.test.junit4.CamelTestSupport;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class GrpcProducerSyncTest extends CamelTestSupport {
private static final Logger LOG = LoggerFactory.getLogger(GrpcProducerSyncTest.class);
private static final int GRPC_TEST_PORT = AvailablePortFinder.getNextAvailable();
private static final int GRPC_TEST_PING_ID = 1;
private static final int GRPC_TEST_PONG_ID01 = 1;
private static final int GRPC_TEST_PONG_ID02 = 2;
private static final int MULTIPLE_RUN_TEST_COUNT = 100;
private static final String GRPC_TEST_PING_VALUE = "PING";
private static final String GRPC_TEST_PONG_VALUE = "PONG";
private static Server grpcServer;
@BeforeClass
public static void startGrpcServer() throws Exception {
grpcServer = ServerBuilder.forPort(GRPC_TEST_PORT).addService(new PingPongImpl()).build().start();
LOG.info("gRPC server started on port " + GRPC_TEST_PORT);
}
@AfterClass
public static void stopGrpcServer() throws IOException {
if (grpcServer != null) {
grpcServer.shutdown();
LOG.info("gRPC server stoped");
}
}
@Test
public void testPingSyncSyncMethodInvocation() throws Exception {
LOG.info("gRPC PingSyncSync method test start");
// Testing simple sync method invoke with host and port parameters
PingRequest pingRequest = PingRequest.newBuilder().setPingName(GRPC_TEST_PING_VALUE).setPingId(GRPC_TEST_PING_ID).build();
Object pongResponse = template.requestBody("direct:grpc-sync-sync", pingRequest);
assertNotNull(pongResponse);
assertTrue(pongResponse instanceof PongResponse);
assertEquals(((PongResponse)pongResponse).getPongId(), GRPC_TEST_PING_ID);
assertEquals(((PongResponse)pongResponse).getPongName(), GRPC_TEST_PING_VALUE + GRPC_TEST_PONG_VALUE);
// Testing simple sync method invoke with target instead of host and
// port parameters
pongResponse = template.requestBody("direct:grpc-sync-target", pingRequest);
assertNotNull(pongResponse);
assertTrue(pongResponse instanceof PongResponse);
assertEquals(((PongResponse)pongResponse).getPongId(), GRPC_TEST_PING_ID);
// Testing simple sync method with name described in .proto file instead
// of generated class
pongResponse = template.requestBody("direct:grpc-sync-proto-method-name", pingRequest);
assertNotNull(pongResponse);
assertTrue(pongResponse instanceof PongResponse);
assertEquals(((PongResponse)pongResponse).getPongId(), GRPC_TEST_PING_ID);
}
@Test
public void testPingSyncSyncMultipleInvocation() throws Exception {
final Stopwatch stopwatch = Stopwatch.createStarted();
// Multiple sync methods call for average performance estimation
for (int id = 0; id < MULTIPLE_RUN_TEST_COUNT; id++) {
PingRequest pingRequest = PingRequest.newBuilder().setPingName(GRPC_TEST_PING_VALUE + id).setPingId(id).build();
Object pongResponse = template.requestBody("direct:grpc-sync-sync", pingRequest);
assertEquals(((PongResponse)pongResponse).getPongId(), id);
}
LOG.info("Multiple sync invocation time {} milliseconds, everage operations/sec {} ", stopwatch.stop().elapsed(TimeUnit.MILLISECONDS),
Math.round(1000 * MULTIPLE_RUN_TEST_COUNT / stopwatch.elapsed(TimeUnit.MILLISECONDS)));
}
@Test
@SuppressWarnings("unchecked")
public void testPingSyncAsyncMethodInvocation() throws Exception {
LOG.info("gRPC PingSyncAsync method test start");
// Testing simple method with sync request and asyc response in synchronous invocation style
PingRequest pingRequest = PingRequest.newBuilder().setPingName(GRPC_TEST_PING_VALUE).setPingId(GRPC_TEST_PING_ID).build();
Object pongResponse = template.requestBody("direct:grpc-sync-async", pingRequest);
assertNotNull(pongResponse);
assertTrue(pongResponse instanceof List<?>);
assertEquals(((List<PongResponse>)pongResponse).get(0).getPongId(), GRPC_TEST_PONG_ID01);
assertEquals(((List<PongResponse>)pongResponse).get(1).getPongId(), GRPC_TEST_PONG_ID02);
assertEquals(((List<PongResponse>)pongResponse).get(0).getPongName(), GRPC_TEST_PING_VALUE + GRPC_TEST_PONG_VALUE);
}
@Override
protected RouteBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
@Override
public void configure() {
from("direct:grpc-sync-sync").to("grpc://org.apache.camel.component.grpc.PingPong?method=pingSyncSync&host=localhost&port=" + GRPC_TEST_PORT + "&synchronous=true");
from("direct:grpc-sync-target")
.to("grpc://org.apache.camel.component.grpc.PingPong?method=pingSyncSync&target=dns:///localhost:" + GRPC_TEST_PORT + "&synchronous=true");
from("direct:grpc-sync-proto-method-name")
.to("grpc://org.apache.camel.component.grpc.PingPong?method=PingSyncSync&host=localhost&port=" + GRPC_TEST_PORT + "&synchronous=true");
from("direct:grpc-sync-async").to("grpc://org.apache.camel.component.grpc.PingPong?method=pingSyncAsync&host=localhost&port=" + GRPC_TEST_PORT + "&synchronous=true");
}
};
}
/**
* Test gRPC PingPong server implementation
*/
static class PingPongImpl extends PingPongGrpc.PingPongImplBase {
@Override
public void pingSyncSync(PingRequest request, StreamObserver<PongResponse> responseObserver) {
LOG.info("gRPC server received data from PingPong service PingId={} PingName={}", request.getPingId(), request.getPingName());
PongResponse response = PongResponse.newBuilder().setPongName(request.getPingName() + GRPC_TEST_PONG_VALUE).setPongId(request.getPingId()).build();
responseObserver.onNext(response);
responseObserver.onCompleted();
}
@Override
public void pingSyncAsync(PingRequest request, StreamObserver<PongResponse> responseObserver) {
LOG.info("gRPC server received data from PingAsyncResponse service PingId={} PingName={}", request.getPingId(), request.getPingName());
PongResponse response01 = PongResponse.newBuilder().setPongName(request.getPingName() + GRPC_TEST_PONG_VALUE).setPongId(GRPC_TEST_PONG_ID01).build();
PongResponse response02 = PongResponse.newBuilder().setPongName(request.getPingName() + GRPC_TEST_PONG_VALUE).setPongId(GRPC_TEST_PONG_ID02).build();
responseObserver.onNext(response01);
responseObserver.onNext(response02);
responseObserver.onCompleted();
}
}
}