/**
* 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.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import io.grpc.Channel;
import io.grpc.stub.StreamObserver;
import org.springframework.util.ReflectionUtils;
/**
* GrpcUtils helpers are working with dynamic methods via Spring reflection
* utilities
*/
public final class GrpcUtils {
private GrpcUtils() {
}
public static Object constructGrpcAsyncStub(String packageName, String serviceName, Channel channel) {
return constructGrpcStubClass(packageName, serviceName, GrpcConstants.GRPC_SERVICE_ASYNC_STUB_METHOD, channel);
}
public static Object constructGrpcBlockingStub(String packageName, String serviceName, Channel channel) {
return constructGrpcStubClass(packageName, serviceName, GrpcConstants.GRPC_SERVICE_SYNC_STUB_METHOD, channel);
}
/**
* Get gRPC stub class instance depends on the invocation style
* newBlockingStub - for sync style
* newStub - for async style
* newFutureStub - for ListenableFuture-style (not implemented yet)
*/
@SuppressWarnings({"rawtypes"})
private static Object constructGrpcStubClass(String packageName, String serviceName, String stubMethod, Channel channel) {
Class[] paramChannel = new Class[1];
paramChannel[0] = Channel.class;
Object grpcBlockingStub = null;
String serviceClassName = packageName + "." + serviceName + GrpcConstants.GRPC_SERVICE_CLASS_POSTFIX;
try {
Class grpcServiceClass = Class.forName(serviceClassName);
Method grpcBlockingMethod = ReflectionUtils.findMethod(grpcServiceClass, stubMethod, paramChannel);
if (grpcBlockingMethod == null) {
throw new IllegalArgumentException("gRPC service method not found: " + serviceClassName + "." + GrpcConstants.GRPC_SERVICE_SYNC_STUB_METHOD);
}
grpcBlockingStub = ReflectionUtils.invokeMethod(grpcBlockingMethod, grpcServiceClass, channel);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("gRPC service class not found: " + serviceClassName);
}
return grpcBlockingStub;
}
@SuppressWarnings({"rawtypes", "unchecked"})
public static void invokeAsyncMethod(Object asyncStubClass, String invokeMethod, Object request, StreamObserver responseObserver) {
Class[] paramMethod = null;
Method method = ReflectionUtils.findMethod(asyncStubClass.getClass(), invokeMethod, paramMethod);
if (method == null) {
throw new IllegalArgumentException("gRPC service method not found: " + asyncStubClass.getClass().getName() + "." + invokeMethod);
}
if (method.getReturnType().equals(StreamObserver.class)) {
StreamObserver<Object> requestObserver = (StreamObserver<Object>)ReflectionUtils.invokeMethod(method, asyncStubClass, responseObserver);
if (request instanceof List) {
List<Object> requestList = (List<Object>)request;
requestList.forEach((requestItem) -> {
requestObserver.onNext(requestItem);
});
} else {
requestObserver.onNext(request);
}
requestObserver.onCompleted();
} else {
ReflectionUtils.invokeMethod(method, asyncStubClass, request, responseObserver);
}
}
@SuppressWarnings({"rawtypes", "unchecked"})
public static Object invokeSyncMethod(Object blockingStubClass, String invokeMethod, Object request) {
Class[] paramMethod = null;
Method method = ReflectionUtils.findMethod(blockingStubClass.getClass(), invokeMethod, paramMethod);
if (method == null) {
throw new IllegalArgumentException("gRPC service method not found: " + blockingStubClass.getClass().getName() + "." + invokeMethod);
}
if (method.getReturnType().equals(Iterator.class)) {
Iterator<Object> responseObjects = (Iterator<Object>)ReflectionUtils.invokeMethod(method, blockingStubClass, request);
List<Object> objectList = new ArrayList<Object>();
while (responseObjects.hasNext()) {
objectList.add(responseObjects.next());
}
return objectList;
} else {
return ReflectionUtils.invokeMethod(method, blockingStubClass, request);
}
}
/**
* 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
*/
public static String convertMethod2CamelCase(final String method) {
StringBuilder sb = new StringBuilder(method.length());
sb.append(method.substring(0, 1).toLowerCase());
Boolean afterUnderscore = false;
for (int i = 1; i < method.length(); i++) {
if (method.charAt(i) == '_') {
afterUnderscore = true;
} else {
sb.append(afterUnderscore ? Character.toUpperCase(method.charAt(i)) : method.charAt(i));
afterUnderscore = false;
}
}
return sb.toString();
}
}