Skip to content

Commit

Permalink
Correct how we process injection point transformers for SR CP.
Browse files Browse the repository at this point in the history
Correct gRPC injection point transformer to expect method AnnotationTarget instead of method param.
  • Loading branch information
manovotn committed Feb 21, 2024
1 parent 6c7c173 commit d34eb5a
Show file tree
Hide file tree
Showing 5 changed files with 221 additions and 87 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.ConfigProvider;
import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationTarget;
import org.jboss.jandex.AnnotationTarget.Kind;
import org.jboss.jandex.AnnotationValue;
import org.jboss.jandex.ClassInfo;
Expand Down Expand Up @@ -345,16 +346,22 @@ public void transform(TransformationContext ctx) {
AnnotationInstance clientAnnotation = Annotations.find(ctx.getQualifiers(), GrpcDotNames.GRPC_CLIENT);
if (clientAnnotation != null && clientAnnotation.value() == null) {
String clientName = null;
AnnotationTarget annotationTarget = ctx.getTarget();
if (ctx.getTarget().kind() == Kind.FIELD) {
clientName = clientAnnotation.target().asField().name();
} else if (ctx.getTarget().kind() == Kind.METHOD_PARAMETER) {
} else if (ctx.getTarget().kind() == Kind.METHOD
&& clientAnnotation.target().kind().equals(Kind.METHOD_PARAMETER)) {
MethodParameterInfo param = clientAnnotation.target().asMethodParameter();
annotationTarget = param;
// We don't need to check if parameter names are recorded - that's validated elsewhere
clientName = param.method().parameterName(param.position());
}
if (clientName != null) {
ctx.transform().remove(GrpcDotNames::isGrpcClient)
.add(GrpcDotNames.GRPC_CLIENT, AnnotationValue.createStringValue("value", clientName)).done();
.add(AnnotationInstance.builder(GrpcDotNames.GRPC_CLIENT)
.value(clientName)
.buildWithTarget(annotationTarget))
.done();
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
Expand All @@ -22,6 +21,7 @@
import org.jboss.jandex.AnnotationTarget;
import org.jboss.jandex.AnnotationValue;
import org.jboss.jandex.DotName;
import org.jboss.jandex.MethodParameterInfo;
import org.jboss.jandex.Type;

import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
Expand Down Expand Up @@ -52,6 +52,8 @@
*/
class SmallRyeContextPropagationProcessor {

private static final String NAME_DELIMITER = "/";

@BuildStep
void registerBean(BuildProducer<AdditionalBeanBuildItem> additionalBeans) {
additionalBeans
Expand Down Expand Up @@ -130,41 +132,69 @@ public void transform(TransformationContext transformationContext) {
&& !ann.name().equals(io.quarkus.arc.processor.DotNames.DEFAULT))) {
return;
}
// create a unique name based on the injection point
String mpConfigIpName;
AnnotationTarget target = transformationContext.getTarget();
final String nameDelimiter = "/";
switch (target.kind()) {
case FIELD:
mpConfigIpName = target.asField().declaringClass().name().toString()
+ nameDelimiter
if (target.kind().equals(AnnotationTarget.Kind.FIELD)) {
AnnotationInstance meConfigInstance = Annotations.find(transformationContext.getAllAnnotations(),
DotNames.MANAGED_EXECUTOR_CONFIG);
AnnotationInstance tcConfigInstance = Annotations.find(transformationContext.getAllAnnotations(),
DotNames.THREAD_CONTEXT_CONFIG);

if (meConfigInstance != null || tcConfigInstance != null) {
// create a unique name based on the injection point
String mpConfigIpName = target.asField().declaringClass().name().toString()
+ NAME_DELIMITER
+ target.asField().name();
break;
case METHOD_PARAMETER:
mpConfigIpName = target.asMethodParameter().method().declaringClass().name().toString()
+ nameDelimiter
+ target.asMethodParameter().method().name()
+ nameDelimiter
+ (target.asMethodParameter().position() + 1);
break;
// any other value is unexpected and we skip that
default:
return;
}
AnnotationInstance meConfigInstance = Annotations.find(transformationContext.getAllAnnotations(),
DotNames.MANAGED_EXECUTOR_CONFIG);
AnnotationInstance tcConfigInstance = Annotations.find(transformationContext.getAllAnnotations(),
DotNames.THREAD_CONTEXT_CONFIG);

if (meConfigInstance != null || tcConfigInstance != null) {
// add @NamedInstance with the generated name
transformationContext.transform()
.add(DotNames.NAMED_INSTANCE, AnnotationValue.createStringValue("value", mpConfigIpName)).done();
// add @NamedInstance with the generated name
transformationContext.transform()
.add(DotNames.NAMED_INSTANCE, AnnotationValue.createStringValue("value", mpConfigIpName))
.done();
}
} else if (target.kind().equals(AnnotationTarget.Kind.METHOD)) {
// If it's method, we can have multiple parameters that we might need to configure and
// each injection point needs its own unique @NamedInstance.
// Finally, we register these annotation instance with the transformer. Note that when creating
// each annotation instance, we have to use AnnotationTarget of the _method parameter_
Collection<AnnotationInstance> annotationsToAdd = new ArrayList<>();
createRequiredAnnotationInstances(Annotations.getAnnotations(AnnotationTarget.Kind.METHOD_PARAMETER,
DotNames.MANAGED_EXECUTOR_CONFIG, transformationContext.getAllAnnotations()),
transformationContext.getQualifiers(), annotationsToAdd);
createRequiredAnnotationInstances(Annotations.getAnnotations(AnnotationTarget.Kind.METHOD_PARAMETER,
DotNames.THREAD_CONTEXT_CONFIG, transformationContext.getAllAnnotations()),
transformationContext.getQualifiers(), annotationsToAdd);
transformationContext.transform().addAll(annotationsToAdd).done();
}

}
});
}

private void createRequiredAnnotationInstances(Collection<AnnotationInstance> configAnnotationInstances,
Collection<AnnotationInstance> knownQualifiers,
Collection<AnnotationInstance> instancesToAdd) {
for (AnnotationInstance annotationInstance : configAnnotationInstances) {
if (annotationInstance.target().kind().equals(AnnotationTarget.Kind.METHOD_PARAMETER)) {
MethodParameterInfo methodParameterInfo = annotationInstance.target().asMethodParameter();
// skip if the method param injection point has custom qualifiers on it (including @NamedInstance)
if (methodParameterInfo.annotations().stream()
.anyMatch(ann -> knownQualifiers.contains(ann)
&& !ann.name().equals(io.quarkus.arc.processor.DotNames.ANY)
&& !ann.name().equals(io.quarkus.arc.processor.DotNames.DEFAULT))) {
continue;
}
String mpConfigIpName = methodParameterInfo.method().declaringClass().name().toString()
+ NAME_DELIMITER
+ methodParameterInfo.method().name()
+ NAME_DELIMITER
+ (methodParameterInfo.position() + 1);
// create a new AnnotationInstance with annotation target set to the respective _method parameter_
instancesToAdd.add(AnnotationInstance.builder(DotNames.NAMED_INSTANCE)
.value(mpConfigIpName)
.buildWithTarget(methodParameterInfo));
}
}
}

@BuildStep
@Record(ExecutionTime.RUNTIME_INIT)
void createSynthBeansForConfiguredInjectionPoints(BuildProducer<SyntheticBeanBuildItem> syntheticBeanBuildItemBuildProducer,
Expand All @@ -174,54 +204,111 @@ void createSynthBeansForConfiguredInjectionPoints(BuildProducer<SyntheticBeanBui
Map<String, ThreadConfig> threadContextMap = new HashMap<>();
Set<String> unconfiguredContextIPs = new HashSet<>();
for (InjectionPointInfo ipInfo : bdFinishedBuildItem.getInjectionPoints()) {
AnnotationInstance namedAnnotation = ipInfo.getRequiredQualifier(DotNames.NAMED_INSTANCE);
// only look for IP with @NamedInstance on it because the IP transformation made sure it's there
if (namedAnnotation == null) {
continue;
}
// furthermore, we only look for any IP that doesn't have other custom qualifier
if (ipInfo.getRequiredQualifiers().stream()
.anyMatch(ann -> !ann.name().equals(DotNames.NAMED_INSTANCE)
&& !ann.name().equals(io.quarkus.arc.processor.DotNames.ANY)
&& !ann.name().equals(io.quarkus.arc.processor.DotNames.DEFAULT))) {
continue;
}
if (AnnotationTarget.Kind.FIELD.equals(ipInfo.getTarget().kind())) {
AnnotationInstance namedAnnotation = ipInfo.getRequiredQualifier(DotNames.NAMED_INSTANCE);
// only look for IP with @NamedInstance on it because the IP transformation made sure it's there
if (namedAnnotation == null) {
continue;
}
// furthermore, we only look for any IP that doesn't have other custom qualifier
if (ipInfo.getRequiredQualifiers().stream()
.anyMatch(ann -> !ann.name().equals(DotNames.NAMED_INSTANCE)
&& !ann.name().equals(io.quarkus.arc.processor.DotNames.ANY)
&& !ann.name().equals(io.quarkus.arc.processor.DotNames.DEFAULT))) {
continue;
}
AnnotationInstance meConfigInstance = Annotations.find(ipInfo.getTarget().asField().annotations(),
DotNames.MANAGED_EXECUTOR_CONFIG);
AnnotationInstance tcConfigInstance = Annotations.find(ipInfo.getTarget().asField().annotations(),
DotNames.THREAD_CONTEXT_CONFIG);

AnnotationInstance meConfigInstance = Annotations.find(extractAnnotations(ipInfo.getTarget()),
DotNames.MANAGED_EXECUTOR_CONFIG);
AnnotationInstance tcConfigInstance = Annotations.find(extractAnnotations(ipInfo.getTarget()),
DotNames.THREAD_CONTEXT_CONFIG);
// get the name from @NamedInstance qualifier
String nameValue = namedAnnotation.value().asString();

// get the name from @NamedInstance qualifier
String nameValue = namedAnnotation.value().asString();
if (meConfigInstance == null && tcConfigInstance == null) {
// injection point with @NamedInstance on it but no configuration
if (ipInfo.getType().name().equals(DotNames.MANAGED_EXECUTOR)) {
unconfiguredExecutorIPs.add(nameValue);
} else {
unconfiguredContextIPs.add(nameValue);
}
continue;
}
// we are looking for injection points with @ManagedExecutorConfig/@ThreadContextConfig
if (meConfigInstance != null || tcConfigInstance != null) {
if (meConfigInstance != null) {
// parse ME config annotation and store in a map
executorMap.putIfAbsent(nameValue,
new ExecutorConfig(meConfigInstance.value("cleared"),
meConfigInstance.value("propagated"),
meConfigInstance.value("maxAsync"),
meConfigInstance.value("maxQueued")));

if (meConfigInstance == null && tcConfigInstance == null) {
// injection point with @NamedInstance on it but no configuration
if (ipInfo.getType().name().equals(DotNames.MANAGED_EXECUTOR)) {
unconfiguredExecutorIPs.add(nameValue);
} else {
unconfiguredContextIPs.add(nameValue);
} else if (tcConfigInstance != null) {
// parse TC config annotation
threadContextMap.putIfAbsent(nameValue,
new ThreadConfig(tcConfigInstance.value("cleared"),
tcConfigInstance.value("propagated"),
tcConfigInstance.value("unchanged")));
}
}
continue;
}
// we are looking for injection points with @ManagedExecutorConfig/@ThreadContextConfig
if (meConfigInstance != null || tcConfigInstance != null) {
if (meConfigInstance != null) {
// parse ME config annotation and store in a map
executorMap.putIfAbsent(nameValue,
new ExecutorConfig(meConfigInstance.value("cleared"),
meConfigInstance.value("propagated"),
meConfigInstance.value("maxAsync"),
meConfigInstance.value("maxQueued")));

} else if (tcConfigInstance != null) {
// parse TC config annotation
threadContextMap.putIfAbsent(nameValue,
new ThreadConfig(tcConfigInstance.value("cleared"),
tcConfigInstance.value("propagated"),
tcConfigInstance.value("unchanged")));
} else if (AnnotationTarget.Kind.METHOD.equals(ipInfo.getTarget().kind())) {
// for a method, we need to process each parameter as a separate injection point
for (AnnotationInstance annotationInstance : ipInfo.getRequiredQualifiers()) {
// just METHOD_PARAMETER and filter to only @NamedInstance
if (annotationInstance.target() == null
|| !AnnotationTarget.Kind.METHOD_PARAMETER.equals(annotationInstance.target().kind())
|| !annotationInstance.name().equals(DotNames.NAMED_INSTANCE)) {
continue;
}
MethodParameterInfo methodParameterInfo = annotationInstance.target().asMethodParameter();
// there should be no other custom qualifiers in this injection point
// furthermore, we only look for any IP that doesn't have other custom qualifier
if (methodParameterInfo.annotations().stream()
.anyMatch(ann -> ipInfo.getRequiredQualifiers().contains(ann)
&& !ann.name().equals(DotNames.NAMED_INSTANCE)
&& !ann.name().equals(io.quarkus.arc.processor.DotNames.ANY)
&& !ann.name().equals(io.quarkus.arc.processor.DotNames.DEFAULT))) {
continue;
}
AnnotationInstance meConfigInstance = Annotations.find(methodParameterInfo.annotations(),
DotNames.MANAGED_EXECUTOR_CONFIG);
AnnotationInstance tcConfigInstance = Annotations.find(methodParameterInfo.annotations(),
DotNames.THREAD_CONTEXT_CONFIG);

// get the name from @NamedInstance qualifier
String nameValue = annotationInstance.value().asString();

if (meConfigInstance == null && tcConfigInstance == null) {
// injection point with @NamedInstance on it but no configuration
if (ipInfo.getType().name().equals(DotNames.MANAGED_EXECUTOR)) {
unconfiguredExecutorIPs.add(nameValue);
} else {
unconfiguredContextIPs.add(nameValue);
}
continue;
}
// we are looking for injection points with @ManagedExecutorConfig/@ThreadContextConfig
if (meConfigInstance != null || tcConfigInstance != null) {
if (meConfigInstance != null) {
// parse ME config annotation and store in a map
executorMap.putIfAbsent(nameValue,
new ExecutorConfig(meConfigInstance.value("cleared"),
meConfigInstance.value("propagated"),
meConfigInstance.value("maxAsync"),
meConfigInstance.value("maxQueued")));

} else if (tcConfigInstance != null) {
// parse TC config annotation
threadContextMap.putIfAbsent(nameValue,
new ThreadConfig(tcConfigInstance.value("cleared"),
tcConfigInstance.value("propagated"),
tcConfigInstance.value("unchanged")));
}
}
}
}

}
// check all unconfigured IPs, if we also found same name and configured ones, then drop these from the set
unconfiguredExecutorIPs.removeAll(unconfiguredExecutorIPs.stream()
Expand Down Expand Up @@ -297,18 +384,6 @@ void createSynthBeansForConfiguredInjectionPoints(BuildProducer<SyntheticBeanBui
}
}

private Collection<AnnotationInstance> extractAnnotations(AnnotationTarget target) {
switch (target.kind()) {
case FIELD:
return target.asField().annotations();
case METHOD_PARAMETER:
return target.asMethodParameter().method().annotations();
// any other value is unexpected and we skip that
default:
return Collections.EMPTY_SET;
}
}

class ExecutorConfig {

String[] cleared;
Expand Down
Loading

0 comments on commit d34eb5a

Please sign in to comment.