Skip to content

Commit

Permalink
Merge pull request #38841 from manovotn/issue38825
Browse files Browse the repository at this point in the history
Correct how we process injection point transformers for SR CP
  • Loading branch information
manovotn authored Feb 21, 2024
2 parents 266f49b + d34eb5a commit 79e4355
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 79e4355

Please sign in to comment.