Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Correct how we process injection point transformers for SR CP #38841

Merged
merged 1 commit into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading