Skip to content

Commit

Permalink
Merge pull request #21532 from mkouba/arc-lookup-priority
Browse files Browse the repository at this point in the history
Sort beans injected via Instance and List<> with io.quarkus.arc.All
  • Loading branch information
mkouba authored Nov 19, 2021
2 parents 2c1be38 + 642efef commit 56ed1f3
Show file tree
Hide file tree
Showing 25 changed files with 489 additions and 280 deletions.
9 changes: 5 additions & 4 deletions docs/src/main/asciidoc/cdi-reference.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -623,10 +623,9 @@ NOTE: Properties set at runtime have absolutely no effect on the bean resolution
In CDI, an alternative bean may be selected either globally for an application by means of `@Priority`, or for a bean archive using a `beans.xml` descriptor.
Quarkus has a simplified bean discovery and the content of `beans.xml` is ignored.

The disadvantage of `@Priority` is that it has `@Target({ TYPE, PARAMETER })` and so it cannot be used for producer methods and fields.
To address this problem and to simplify the code Quarkus provides the `io.quarkus.arc.AlternativePriority` annotation.
It's basically a shortcut for `@Alternative` plus `@Priority`.
Additionally, it can be used for producers.
The disadvantage of `@javax.annotation.Priority` is that it has `@Target({ TYPE, PARAMETER })` and so it cannot be used for producer methods and fields.
This problem should be fixed in Common Annotations 2.1.
Users are encouraged to use `@io.quarkus.arc.Priority` instead, until Quarkus upgrades to this version of `jakarta.annotation-api`.

However, it is also possible to select alternatives for an application using the unified configuration.
The `quarkus.arc.selected-alternatives` property accepts a list of string values that are used to match alternative beans.
Expand Down Expand Up @@ -931,6 +930,8 @@ public class Processor {
<1> The injected instance is an _immutable list_ of the contextual references of the _disambiguated_ beans.
<2> For this injection point the required type is `Service` and no additional qualifiers are declared.

TIP: By default, the list of beans is sorted by priority as defined by `io.quarkus.arc.InjectableBean#getPriority()`. Higher priority goes first. In general, the `@javax.annotation.Priority` and `@io.quarkus.arc.Priority` annotations can be used to assign the priority to a class bean, producer method or producer field.

If an injection point declares no other qualifier than `@All` then `@Any` is used, i.e. the behavior is equivalent to `@Inject @Any Instance<Service>`.

You can also inject a list of bean instances wrapped in `io.quarkus.arc.InstanceHandle`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import io.quarkus.arc.All;
import io.quarkus.arc.InstanceHandle;
import io.quarkus.arc.Priority;
import io.quarkus.test.QuarkusUnitTest;

public class ListInjectionTest {
Expand All @@ -41,6 +42,8 @@ public void testInjection() {
// The list is immutable
assertThatExceptionOfType(UnsupportedOperationException.class)
.isThrownBy(() -> foo.services.add(new ServiceAlpha()));
// ServiceBravo has higher priority
assertEquals("bravo", foo.services.get(0).ping());
for (Service service : foo.services) {
Optional<InjectionPoint> ip = service.getInjectionPoint();
if (ip.isPresent()) {
Expand Down Expand Up @@ -124,6 +127,7 @@ public String ping() {
}
}

@Priority(5) // this impl should go first
@Dependent
static class ServiceBravo implements Service {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ public void done() {
.scope(scope)
.types(types)
.qualifiers(qualifiers)
.alternativePriority(alternativePriority)
.alternative(alternative)
.priority(priority)
.name(name)
.creator(creatorConsumer)
.destroyer(destroyerConsumer)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public abstract class BeanConfiguratorBase<B extends BeanConfiguratorBase<B, T>,
protected final Set<Type> types;
protected final Set<AnnotationInstance> qualifiers;
protected ScopeInfo scope;
protected Integer alternativePriority;
protected boolean alternative;
protected String name;
protected Consumer<MethodCreator> creatorConsumer;
protected Consumer<MethodCreator> destroyerConsumer;
Expand All @@ -42,6 +42,7 @@ public abstract class BeanConfiguratorBase<B extends BeanConfiguratorBase<B, T>,
protected Type providerType;
protected boolean forceApplicationClass;
protected String targetPackageName;
protected Integer priority;

protected BeanConfiguratorBase(DotName implClazz) {
this.implClazz = implClazz;
Expand All @@ -68,9 +69,8 @@ public B read(BeanConfiguratorBase<?, ?> base) {
forceApplicationClass = base.forceApplicationClass;
targetPackageName = base.targetPackageName;
scope(base.scope);
if (base.alternativePriority != null) {
alternativePriority(base.alternativePriority);
}
alternative = base.alternative;
priority = base.priority;
name(base.name);
creator(base.creatorConsumer);
destroyer(base.destroyerConsumer);
Expand Down Expand Up @@ -191,8 +191,14 @@ public B targetPackageName(String name) {
return self();
}

public B alternativePriority(int priority) {
this.alternativePriority = priority;
public B alternativePriority(int value) {
this.alternative = true;
this.priority = value;
return self();
}

public B priority(int value) {
this.priority = value;
return self();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,9 +234,10 @@ Collection<Resource> generateSyntheticBean(BeanInfo bean) {
if (qualifiers != null) {
implementGetQualifiers(bean, beanCreator, qualifiers.getFieldDescriptor());
}
if (bean.isAlternative()) {
implementGetAlternativePriority(bean, beanCreator);
}

implementIsAlternative(bean, beanCreator);
implementGetPriority(bean, beanCreator);

if (stereotypes != null) {
implementGetStereotypes(bean, beanCreator, stereotypes.getFieldDescriptor());
}
Expand Down Expand Up @@ -325,9 +326,10 @@ Collection<Resource> generateClassBean(BeanInfo bean, ClassInfo beanClass) {
if (qualifiers != null) {
implementGetQualifiers(bean, beanCreator, qualifiers.getFieldDescriptor());
}
if (bean.isAlternative()) {
implementGetAlternativePriority(bean, beanCreator);
}

implementIsAlternative(bean, beanCreator);
implementGetPriority(bean, beanCreator);

if (stereotypes != null) {
implementGetStereotypes(bean, beanCreator, stereotypes.getFieldDescriptor());
}
Expand Down Expand Up @@ -426,9 +428,10 @@ Collection<Resource> generateProducerMethodBean(BeanInfo bean, MethodInfo produc
if (qualifiers != null) {
implementGetQualifiers(bean, beanCreator, qualifiers.getFieldDescriptor());
}
if (bean.isAlternative()) {
implementGetAlternativePriority(bean, beanCreator);
}

implementIsAlternative(bean, beanCreator);
implementGetPriority(bean, beanCreator);

implementGetDeclaringBean(beanCreator);
if (stereotypes != null) {
implementGetStereotypes(bean, beanCreator, stereotypes.getFieldDescriptor());
Expand Down Expand Up @@ -513,9 +516,10 @@ Collection<Resource> generateProducerFieldBean(BeanInfo bean, FieldInfo producer
if (qualifiers != null) {
implementGetQualifiers(bean, beanCreator, qualifiers.getFieldDescriptor());
}
if (bean.isAlternative()) {
implementGetAlternativePriority(bean, beanCreator);
}

implementIsAlternative(bean, beanCreator);
implementGetPriority(bean, beanCreator);

implementGetDeclaringBean(beanCreator);
if (stereotypes != null) {
implementGetStereotypes(bean, beanCreator, stereotypes.getFieldDescriptor());
Expand Down Expand Up @@ -1722,12 +1726,22 @@ protected void implementGetDeclaringBean(ClassCreator beanCreator) {
MethodDescriptors.SUPPLIER_GET, declaringProviderSupplierHandle));
}

protected void implementGetAlternativePriority(BeanInfo bean, ClassCreator beanCreator) {
MethodCreator getAlternativePriority = beanCreator.getMethodCreator("getAlternativePriority", Integer.class)
.setModifiers(ACC_PUBLIC);
getAlternativePriority
.returnValue(getAlternativePriority.newInstance(MethodDescriptor.ofConstructor(Integer.class, int.class),
getAlternativePriority.load(bean.getAlternativePriority())));
protected void implementIsAlternative(BeanInfo bean, ClassCreator beanCreator) {
if (bean.isAlternative()) {
MethodCreator isAlternative = beanCreator.getMethodCreator("isAlternative", boolean.class)
.setModifiers(ACC_PUBLIC);
isAlternative
.returnValue(isAlternative.load(true));
}
}

protected void implementGetPriority(BeanInfo bean, ClassCreator beanCreator) {
if (bean.getPriority() != null) {
MethodCreator getPriority = beanCreator.getMethodCreator("getPriority", int.class)
.setModifiers(ACC_PUBLIC);
getPriority
.returnValue(getPriority.load(bean.getPriority()));
}
}

protected void implementIsDefaultBean(BeanInfo bean, ClassCreator beanCreator) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ public class BeanInfo implements InjectionTargetInfo {

private final Map<InterceptionType, InterceptionInfo> lifecycleInterceptors;

private final Integer alternativePriority;
private final boolean alternative;
private final Integer priority;

private final List<StereotypeInfo> stereotypes;

Expand All @@ -88,22 +89,20 @@ public class BeanInfo implements InjectionTargetInfo {

BeanInfo(AnnotationTarget target, BeanDeployment beanDeployment, ScopeInfo scope, Set<Type> types,
Set<AnnotationInstance> qualifiers, List<Injection> injections, BeanInfo declaringBean, DisposerInfo disposer,
Integer alternativePriority,
List<StereotypeInfo> stereotypes, String name, boolean isDefaultBean, String targetPackageName) {
boolean alternative, List<StereotypeInfo> stereotypes, String name, boolean isDefaultBean, String targetPackageName,
Integer priority) {
this(null, null, target, beanDeployment, scope, types, qualifiers, injections, declaringBean, disposer,
alternativePriority,
stereotypes, name, isDefaultBean, null, null,
Collections.emptyMap(), true, false, targetPackageName);
alternative, stereotypes, name, isDefaultBean, null, null, Collections.emptyMap(), true, false,
targetPackageName, priority);
}

BeanInfo(ClassInfo implClazz, Type providerType, AnnotationTarget target, BeanDeployment beanDeployment, ScopeInfo scope,
Set<Type> types,
Set<AnnotationInstance> qualifiers,
List<Injection> injections, BeanInfo declaringBean, DisposerInfo disposer, Integer alternativePriority,
List<StereotypeInfo> stereotypes,
String name, boolean isDefaultBean, Consumer<MethodCreator> creatorConsumer,
Consumer<MethodCreator> destroyerConsumer,
Map<String, Object> params, boolean isRemovable, boolean forceApplicationClass, String targetPackageName) {
Set<Type> types, Set<AnnotationInstance> qualifiers, List<Injection> injections, BeanInfo declaringBean,
DisposerInfo disposer, boolean alternative,
List<StereotypeInfo> stereotypes, String name, boolean isDefaultBean, Consumer<MethodCreator> creatorConsumer,
Consumer<MethodCreator> destroyerConsumer, Map<String, Object> params, boolean isRemovable,
boolean forceApplicationClass, String targetPackageName, Integer priority) {

this.target = Optional.ofNullable(target);
if (implClazz == null && target != null) {
implClazz = initImplClazz(target, beanDeployment);
Expand All @@ -129,7 +128,8 @@ public class BeanInfo implements InjectionTargetInfo {
this.injections = injections;
this.declaringBean = declaringBean;
this.disposer = disposer;
this.alternativePriority = alternativePriority;
this.alternative = alternative;
this.priority = priority;
this.stereotypes = stereotypes;
this.name = name;
this.defaultBean = isDefaultBean;
Expand Down Expand Up @@ -418,11 +418,15 @@ public DisposerInfo getDisposer() {
}

public boolean isAlternative() {
return alternativePriority != null;
return alternative;
}

public Integer getAlternativePriority() {
return alternativePriority;
return alternative ? priority : null;
}

public Integer getPriority() {
return priority;
}

public List<StereotypeInfo> getStereotypes() {
Expand Down Expand Up @@ -829,7 +833,7 @@ static class Builder {

private DisposerInfo disposer;

private Integer alternativePriority;
private boolean alternative;

private List<StereotypeInfo> stereotypes;

Expand All @@ -849,6 +853,8 @@ static class Builder {

private String targetPackageName;

private Integer priority;

Builder() {
injections = Collections.emptyList();
stereotypes = Collections.emptyList();
Expand Down Expand Up @@ -905,7 +911,16 @@ Builder disposer(DisposerInfo disposer) {
}

Builder alternativePriority(Integer alternativePriority) {
this.alternativePriority = alternativePriority;
return alternative(true).priority(priority);
}

Builder alternative(boolean value) {
this.alternative = value;
return this;
}

Builder priority(Integer value) {
this.priority = value;
return this;
}

Expand Down Expand Up @@ -951,8 +966,8 @@ Builder targetPackageName(String name) {

BeanInfo build() {
return new BeanInfo(implClazz, providerType, target, beanDeployment, scope, types, qualifiers, injections,
declaringBean, disposer, alternativePriority, stereotypes, name, isDefaultBean, creatorConsumer,
destroyerConsumer, params, removable, forceApplicationClass, targetPackageName);
declaringBean, disposer, alternative, stereotypes, name, isDefaultBean, creatorConsumer,
destroyerConsumer, params, removable, forceApplicationClass, targetPackageName, priority);
}

public Builder forceApplicationClass(boolean forceApplicationClass) {
Expand Down
Loading

0 comments on commit 56ed1f3

Please sign in to comment.