in extensions/grpc/deployment/src/main/java/org/apache/camel/quarkus/component/grpc/deployment/GrpcProcessor.java [106:233]
void createBindableServiceBeans(
BuildProducer<GeneratedBeanBuildItem> generatedBean,
BuildProducer<ReflectiveClassBuildItem> reflectiveClass,
CombinedIndexBuildItem combinedIndexBuildItem,
CamelGrpcServiceExcludesBuildItem camelGrpcServiceExcludes) {
IndexView index = combinedIndexBuildItem.getIndex();
Collection<ClassInfo> bindableServiceImpls = index.getAllKnownImplementors(BINDABLE_SERVICE_DOT_NAME)
.stream()
.filter(camelGrpcServiceExcludes.serviceExcludesFilter())
.collect(Collectors.toSet());
// Generate implementation classes from any abstract gRPC BindableService implementations included in the application archive
// Override the various sync and async methods so that requests can be intercepted and delegated to Camel routing
// This mimics similar logic in DefaultBindableServiceFactory that uses Javassist ProxyFactory & MethodHandler
for (ClassInfo service : bindableServiceImpls) {
String superClassName = service.name().toString();
String generatedClassName = superClassName + "QuarkusMethodHandler";
if (!Modifier.isAbstract(service.flags())) {
logDebugMessage("Ignoring BindableService %s as it is not an interface or abstract class", superClassName);
continue;
}
if (service.name().withoutPackagePrefix().startsWith("Mutiny")) {
/* The generate-code goal of quarkus-maven-plugin generates also Mutiny service that we do not use
* Not skipping it here results in randomly registering the Mutiny one or the right one.
* In case the Mutiny service one is registered, the client throws something like
* io.grpc.StatusRuntimeException: UNIMPLEMENTED */
logDebugMessage("Ignoring BindableService %s as it a Mutiny service", superClassName);
continue;
}
Optional<String> asyncServiceInterface = service.interfaceNames()
.stream()
.map(DotName::toString)
.filter(className -> className.endsWith("AsyncService"))
.findFirst();
if (asyncServiceInterface.isEmpty()) {
logDebugMessage("Ignoring BindableService %s as it does not implement AsyncService", superClassName);
continue;
}
// Register the service classes for reflection
reflectiveClass
.produce(ReflectiveClassBuildItem.builder(superClassName).methods().build());
reflectiveClass.produce(
ReflectiveClassBuildItem.builder(service.enclosingClass().toString()).methods().build());
reflectiveClass.produce(ReflectiveClassBuildItem.builder(generatedClassName).methods().build());
logDebugMessage("Generating CamelQuarkusBindableService %s extending %s", generatedClassName, superClassName);
try (ClassCreator classCreator = ClassCreator.builder()
.classOutput(new GeneratedBeanGizmoAdaptor(generatedBean))
.className(generatedClassName)
.superClass(superClassName)
.interfaces(CamelQuarkusBindableService.class)
.build()) {
classCreator.addAnnotation(Dependent.class);
FieldCreator serverMethodHandler = classCreator
.getFieldCreator("methodHandler", GrpcMethodHandler.class.getName())
.setModifiers(Modifier.PRIVATE);
// Create constructor
try (MethodCreator initMethod = classCreator.getMethodCreator("<init>", void.class)) {
initMethod.setModifiers(Modifier.PUBLIC);
initMethod.invokeSpecialMethod(MethodDescriptor.ofMethod(superClassName, "<init>", void.class),
initMethod.getThis());
initMethod.returnValue(null);
}
// Create setMethodHandler override
try (MethodCreator setMethodHandlerMethod = classCreator.getMethodCreator("setMethodHandler", void.class,
GrpcMethodHandler.class)) {
setMethodHandlerMethod.setModifiers(Modifier.PUBLIC);
ResultHandle self = setMethodHandlerMethod.getThis();
ResultHandle methodHandlerInstance = setMethodHandlerMethod.getMethodParam(0);
setMethodHandlerMethod.writeInstanceField(serverMethodHandler.getFieldDescriptor(), self,
methodHandlerInstance);
setMethodHandlerMethod.returnValue(null);
}
// Override service methods that the gRPC component is interested in
// E.g methods with one or two parameters where one is of type StreamObserver
ClassInfo asyncServiceClassInfo = index.getClassByName(asyncServiceInterface.get());
List<MethodInfo> methods = asyncServiceClassInfo.methods();
for (MethodInfo method : methods) {
if (isCandidateServiceMethod(method)) {
String[] params = method.parameters()
.stream()
.map(MethodParameterInfo::type)
.map(Type::name)
.map(DotName::toString)
.toArray(String[]::new);
ClassInfo classInfo = index
.getClassByName(DotName.createSimple(GrpcMethodHandler.class.getName()));
String returnType = method.returnType().name().toString();
try (MethodCreator methodCreator = classCreator.getMethodCreator(method.name(), returnType, params)) {
logDebugMessage("Creating service implementation for method %s in %s", method.name(),
generatedClassName);
method.exceptions()
.stream()
.map(type -> type.name().toString())
.forEach(methodCreator::addException);
if (method.parameters().size() == 1) {
ResultHandle returnValue = generateGrpcDelegateMethod(classInfo, serverMethodHandler,
methodCreator,
method, "handleForConsumerStrategy");
methodCreator.returnValue(returnValue);
} else if (method.parameters().size() == 2) {
generateGrpcDelegateMethod(classInfo, serverMethodHandler, methodCreator, method,
"handle");
methodCreator.returnValue(null);
}
}
}
}
}
}
}