diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcProcessor.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcProcessor.java index 42931a5fc49b5..21dfd9f5144c3 100644 --- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcProcessor.java +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcProcessor.java @@ -31,12 +31,15 @@ import org.jboss.jandex.AnnotationTarget.Kind; import org.jboss.jandex.AnnotationValue; import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.ClassType; import org.jboss.jandex.DotName; import org.jboss.jandex.FieldInfo; import org.jboss.jandex.IndexView; import org.jboss.jandex.MethodInfo; import org.jboss.jandex.MethodParameterInfo; +import org.jboss.jandex.ParameterizedType; import org.jboss.jandex.Type; +import org.jboss.jandex.WildcardType; import org.jboss.logging.Logger; import io.quarkus.arc.ArcContainer; @@ -923,11 +926,18 @@ public void registerField(FieldInfo fieldInfo) { : elementType, qualifiers)); + // by default, we keep the type of the injection point + // we only need to inspect and change this in case of param. type with a wildcard + Type syntheticBeanType = injectionPoint.getRequiredType(); + if (syntheticBeanType.kind() == Type.Kind.PARAMETERIZED_TYPE) { + ParameterizedType paramType = syntheticBeanType.asParameterizedType(); + syntheticBeanType = removeWildcardFromParamType(paramType.name(), paramType.arguments(), paramType.owner()); + } BeanConfigurator configurator = beanRegistrationPhase.getContext() .configure(List.class) .forceApplicationClass() .scope(BuiltinScope.DEPENDENT.getInfo()) - .addType(injectionPoint.getRequiredType()); + .addType(syntheticBeanType); injectionPoint.getRequiredQualifiers().forEach(configurator::addQualifier); @@ -996,6 +1006,33 @@ public boolean test(BeanInfo bean) { } } + private ParameterizedType removeWildcardFromParamType(DotName name, List typeArgs, Type owner) { + Type[] typeArray = new Type[typeArgs.size()]; + for (int i = 0; i < typeArgs.size(); i++) { + Type t = typeArgs.get(i); + if (t.kind() == Type.Kind.WILDCARD_TYPE) { + // detect wildcard and replace with upper/lower bound or Object type + WildcardType wildcard = t.asWildcardType(); + if (wildcard.extendsBound() != null && !wildcard.extendsBound().equals(ClassType.OBJECT_TYPE)) { + typeArray[i] = wildcard.extendsBound(); + } else if (wildcard.superBound() != null && !wildcard.superBound().equals(ClassType.OBJECT_TYPE)) { + typeArray[i] = wildcard.superBound(); + } else { + typeArray[i] = ClassType.OBJECT_TYPE; + } + } else if (t.kind() == Type.Kind.PARAMETERIZED_TYPE) { + // recursively search parameterized types + ParameterizedType parameterizedArg = t.asParameterizedType(); + typeArray[i] = removeWildcardFromParamType(parameterizedArg.name(), parameterizedArg.arguments(), + parameterizedArg.owner()); + } else { + // all else stays the same + typeArray[i] = t; + } + } + return ParameterizedType.create(name, typeArray, owner); + } + private boolean isListAllInjectionPoint(InjectionPointInfo injectionPoint) { return DotNames.LIST.equals(injectionPoint.getRequiredType().name()) && Annotations.contains(injectionPoint.getRequiredQualifiers(), DotNames.ALL); diff --git a/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/lookup/ListInjectionTest.java b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/lookup/ListInjectionTest.java index 1858d9d334fcf..9d20d2b3903e7 100644 --- a/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/lookup/ListInjectionTest.java +++ b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/lookup/ListInjectionTest.java @@ -23,6 +23,7 @@ import io.quarkus.arc.Arc; import io.quarkus.arc.InstanceHandle; import io.quarkus.arc.Priority; +import io.quarkus.arc.Unremovable; import io.quarkus.test.QuarkusUnitTest; public class ListInjectionTest { @@ -31,7 +32,8 @@ public class ListInjectionTest { static final QuarkusUnitTest config = new QuarkusUnitTest() .withApplicationRoot((jar) -> jar .addClasses(Foo.class, ServiceAlpha.class, ServiceBravo.class, ServiceCharlie.class, Service.class, - Counter.class, Converter.class, ConverterAlpha.class, ConverterBravo.class, MyQualifier.class)); + Counter.class, Converter.class, ConverterAlpha.class, ConverterBravo.class, MyQualifier.class, + Some.class, SomeInt.class, SomeStringA.class, SomeStringB.class, BeanA.class, BeanB.class)); @Inject Foo foo; @@ -89,6 +91,18 @@ public void testListAll() { assertEquals(Object.class, bravo.getInjectionPoint().get().getType()); } + @Test + public void testWildcardInBeanType() { + BeanA beanA = Arc.container().instance(BeanA.class).get(); + assertEquals(3, beanA.somes.size()); + assertEquals(2, beanA.somesExtends.size()); + assertEquals(2, beanA.somesSuper.size()); + BeanB beanB = Arc.container().instance(BeanB.class).get(); + assertEquals(3, beanB.somes.size()); + assertEquals(2, beanB.somesExtends.size()); + assertEquals(2, beanB.somesSuper.size()); + } + @Singleton static class Foo { @@ -217,4 +231,61 @@ void destroy() { } + @Singleton + @Unremovable + public static class BeanA { + + final List> somes; + + @Inject + @All + List> somesExtends; + + @Inject + @All + List> somesSuper; + + public BeanA(@All List> somes) { + this.somes = somes; + } + + } + + @Singleton + @Unremovable + // this bean is, in its functionality, copy of BeanA but it was required to reproduce the problem + // see https://github.com/quarkusio/quarkus/issues/32080 for details + public static class BeanB { + + final List> somes; + + @Inject + @All + List> somesExtends; + + @Inject + @All + List> somesSuper; + + public BeanB(@All List> somes) { + this.somes = somes; + } + + } + + public interface Some { + } + + @Singleton + public static class SomeStringA implements Some { + } + + @Singleton + public static class SomeStringB implements Some { + } + + @Singleton + public static class SomeInt implements Some { + } + }