diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java index a9671d9d4213d..dad5f355c0d2d 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java @@ -900,7 +900,7 @@ private List findBeans(Collection beanDefiningAnnotations, Li } } - if (annotationStore.hasAnnotation(beanClass, DotNames.VETOED)) { + if (isVetoed(beanClass)) { // Skip vetoed bean classes continue; } @@ -1094,6 +1094,20 @@ private List findBeans(Collection beanDefiningAnnotations, Li return beans; } + private boolean isVetoed(ClassInfo beanClass) { + if (annotationStore.hasAnnotation(beanClass, DotNames.VETOED)) { + return true; + } + + // using immutable index, because we expect that if class is discovered, + // the whole package is indexed (otherwise we'd get a lot of warnings that + // package-info.class couldn't be loaded during on-demand indexing) + String packageName = beanClass.name().packagePrefix(); + org.jboss.jandex.ClassInfo packageClass = beanArchiveImmutableIndex.getClassByName( + DotName.createSimple(packageName + ".package-info")); + return packageClass != null && annotationStore.hasAnnotation(packageClass, DotNames.VETOED); + } + private boolean isExcluded(ClassInfo beanClass) { if (!excludeTypes.isEmpty()) { for (Predicate exclude : excludeTypes) { @@ -1250,7 +1264,7 @@ private List findInterceptors(List injectio } List interceptors = new ArrayList<>(); for (ClassInfo interceptorClass : interceptorClasses.values()) { - if (annotationStore.hasAnnotation(interceptorClass, DotNames.VETOED) || isExcluded(interceptorClass)) { + if (isVetoed(interceptorClass) || isExcluded(interceptorClass)) { // Skip vetoed interceptors continue; } @@ -1277,7 +1291,7 @@ private List findDecorators(List injectionPoi } List decorators = new ArrayList<>(); for (ClassInfo decoratorClass : decoratorClasses.values()) { - if (annotationStore.hasAnnotation(decoratorClass, DotNames.VETOED) || isExcluded(decoratorClass)) { + if (isVetoed(decoratorClass) || isExcluded(decoratorClass)) { // Skip vetoed decorators continue; } diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java index cf983ca736aee..108e9dc8ea88f 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java @@ -868,16 +868,19 @@ protected void implementDestroy(BeanInfo bean, ClassCreator beanCreator, Provide if (i == disposedParamPosition) { referenceHandles[i] = destroy.getMethodParam(0); } else { + InjectionPointInfo injectionPoint = injectionPointsIterator.next(); ResultHandle childCtxHandle = destroy.invokeStaticMethod(MethodDescriptors.CREATIONAL_CTX_CHILD_CONTEXTUAL, declaringProviderHandle, ctxHandle); ResultHandle providerSupplierHandle = destroy .readInstanceField(FieldDescriptor.of(beanCreator.getClassName(), - injectionPointToProviderField.get(injectionPointsIterator.next()), + injectionPointToProviderField.get(injectionPoint), Supplier.class.getName()), destroy.getThis()); ResultHandle providerHandle = destroy.invokeInterfaceMethod(MethodDescriptors.SUPPLIER_GET, providerSupplierHandle); - ResultHandle referenceHandle = destroy.invokeInterfaceMethod(MethodDescriptors.INJECTABLE_REF_PROVIDER_GET, - providerHandle, childCtxHandle); + AssignableResultHandle referenceHandle = destroy.createVariable(Object.class); + destroy.assign(referenceHandle, destroy.invokeInterfaceMethod(MethodDescriptors.INJECTABLE_REF_PROVIDER_GET, + providerHandle, childCtxHandle)); + checkPrimitiveInjection(destroy, injectionPoint, referenceHandle); referenceHandles[i] = referenceHandle; } } @@ -998,8 +1001,10 @@ private void newProviderHandles(BeanInfo bean, ClassCreator beanCreator, MethodC providerSupplierHandle); ResultHandle childCtx = createMethod.invokeStaticMethod(MethodDescriptors.CREATIONAL_CTX_CHILD_CONTEXTUAL, providerHandle, createMethod.getMethodParam(0)); - ResultHandle referenceHandle = createMethod.invokeInterfaceMethod(MethodDescriptors.INJECTABLE_REF_PROVIDER_GET, - providerHandle, childCtx); + AssignableResultHandle referenceHandle = createMethod.createVariable(Object.class); + createMethod.assign(referenceHandle, createMethod + .invokeInterfaceMethod(MethodDescriptors.INJECTABLE_REF_PROVIDER_GET, providerHandle, childCtx)); + checkPrimitiveInjection(createMethod, injectionPoint, referenceHandle); injectableParamHandles.add(referenceHandle); if (injectionPoint.isDependentTransientReference()) { transientReferences.add(new TransientReference(providerHandle, referenceHandle, childCtx)); @@ -1033,6 +1038,24 @@ private void newProviderHandles(BeanInfo bean, ClassCreator beanCreator, MethodC } } + static void checkPrimitiveInjection(BytecodeCreator bytecode, InjectionPointInfo injectionPoint, + AssignableResultHandle referenceHandle) { + if (injectionPoint.getType().kind() == Type.Kind.PRIMITIVE) { + Type type = null; + if (injectionPoint.getResolvedBean().isProducerField()) { + type = injectionPoint.getResolvedBean().getTarget().get().asField().type(); + } else if (injectionPoint.getResolvedBean().isProducerMethod()) { + type = injectionPoint.getResolvedBean().getTarget().get().asMethod().returnType(); + } + + if (type != null && Types.isPrimitiveWrapperType(type)) { + BytecodeCreator isNull = bytecode.ifNull(referenceHandle).trueBranch(); + isNull.assign(referenceHandle, + Types.loadPrimitiveDefault(injectionPoint.getType().asPrimitiveType().primitive(), isNull)); + } + } + } + private ResultHandle newInstanceHandle(BeanInfo bean, ClassCreator beanCreator, BytecodeCreator creator, MethodCreator createMethod, String providerTypeName, String baseName, List providerHandles, ReflectionRegistration registration, @@ -1234,8 +1257,10 @@ void implementCreateForProducerMethod(ClassOutput classOutput, ClassCreator bean providerSupplierHandle); ResultHandle childCtxHandle = create.invokeStaticMethod(MethodDescriptors.CREATIONAL_CTX_CHILD_CONTEXTUAL, providerHandle, create.getMethodParam(0)); - ResultHandle referenceHandle = create.invokeInterfaceMethod(MethodDescriptors.INJECTABLE_REF_PROVIDER_GET, - providerHandle, childCtxHandle); + AssignableResultHandle referenceHandle = create.createVariable(Object.class); + create.assign(referenceHandle, create.invokeInterfaceMethod(MethodDescriptors.INJECTABLE_REF_PROVIDER_GET, + providerHandle, childCtxHandle)); + checkPrimitiveInjection(create, injectionPoint, referenceHandle); referenceHandles[paramIdx++] = referenceHandle; // We need to destroy dependent beans for @TransientReference injection points if (injectionPoint.isDependentTransientReference()) { @@ -1501,8 +1526,10 @@ void implementCreateForClassBean(ClassOutput classOutput, ClassCreator beanCreat MethodDescriptors.SUPPLIER_GET, providerSupplierHandle); ResultHandle childCtxHandle = tryBlock.invokeStaticMethod(MethodDescriptors.CREATIONAL_CTX_CHILD_CONTEXTUAL, providerHandle, tryBlock.getMethodParam(0)); - ResultHandle referenceHandle = tryBlock.invokeInterfaceMethod(MethodDescriptors.INJECTABLE_REF_PROVIDER_GET, - providerHandle, childCtxHandle); + AssignableResultHandle referenceHandle = tryBlock.createVariable(Object.class); + tryBlock.assign(referenceHandle, tryBlock.invokeInterfaceMethod(MethodDescriptors.INJECTABLE_REF_PROVIDER_GET, + providerHandle, childCtxHandle)); + checkPrimitiveInjection(tryBlock, injectionPoint, referenceHandle); FieldInfo injectedField = fieldInjection.target.asField(); // only use reflection fallback if we are not performing transformation @@ -1541,8 +1568,10 @@ void implementCreateForClassBean(ClassOutput classOutput, ClassCreator beanCreat providerSupplierHandle); ResultHandle childCtxHandle = create.invokeStaticMethod(MethodDescriptors.CREATIONAL_CTX_CHILD_CONTEXTUAL, providerHandle, create.getMethodParam(0)); - ResultHandle referenceHandle = create.invokeInterfaceMethod(MethodDescriptors.INJECTABLE_REF_PROVIDER_GET, - providerHandle, childCtxHandle); + AssignableResultHandle referenceHandle = create.createVariable(Object.class); + create.assign(referenceHandle, create.invokeInterfaceMethod(MethodDescriptors.INJECTABLE_REF_PROVIDER_GET, + providerHandle, childCtxHandle)); + checkPrimitiveInjection(create, injectionPoint, referenceHandle); referenceHandles[paramIdx++] = referenceHandle; // We need to destroy dependent beans for @TransientReference injection points if (injectionPoint.isDependentTransientReference()) { diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/InjectionPointInfo.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/InjectionPointInfo.java index 33e80fd13f665..78466bf6b7ac6 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/InjectionPointInfo.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/InjectionPointInfo.java @@ -14,9 +14,12 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Predicate; +import javax.enterprise.inject.spi.DefinitionException; + 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; import org.jboss.jandex.DotName; import org.jboss.jandex.FieldInfo; @@ -31,12 +34,28 @@ */ public class InjectionPointInfo { + private static boolean isNamedWithoutValue(AnnotationInstance annotation) { + if (annotation.name().equals(DotNames.NAMED)) { + AnnotationValue name = annotation.value(); + return name == null || name.asString().isEmpty(); + } + return false; + } + static InjectionPointInfo fromField(FieldInfo field, ClassInfo beanClass, BeanDeployment beanDeployment, InjectionPointModifier transformer) { Set qualifiers = new HashSet<>(); Collection annotations = beanDeployment.getAnnotations(field); for (AnnotationInstance annotation : annotations) { - beanDeployment.extractQualifiers(annotation).forEach(qualifiers::add); + for (AnnotationInstance annotationInstance : beanDeployment.extractQualifiers(annotation)) { + // if the qualifier is `@Named` without value, replace it with `@Named(fieldName) + if (isNamedWithoutValue(annotationInstance)) { + annotationInstance = AnnotationInstance.builder(annotationInstance.name()) + .value(field.name()) + .buildWithTarget(annotationInstance.target()); + } + qualifiers.add(annotationInstance); + } } Type type = resolveType(field.type(), beanClass, field.declaringClass(), beanDeployment); return new InjectionPointInfo(type, @@ -70,7 +89,12 @@ static List fromMethod(MethodInfo method, ClassInfo beanClas } Set paramQualifiers = new HashSet<>(); for (AnnotationInstance paramAnnotation : paramAnnotations) { - beanDeployment.extractQualifiers(paramAnnotation).forEach(paramQualifiers::add); + for (AnnotationInstance annotationInstance : beanDeployment.extractQualifiers(paramAnnotation)) { + if (isNamedWithoutValue(annotationInstance)) { + throw new DefinitionException("@Named without value may not be used on method parameter: " + method); + } + paramQualifiers.add(annotationInstance); + } } Type type = resolveType(paramType, beanClass, method.declaringClass(), beanDeployment); injectionPoints.add(new InjectionPointInfo(type, @@ -259,7 +283,7 @@ public String toString() { } private static Type resolveType(Type type, ClassInfo beanClass, ClassInfo declaringClass, BeanDeployment beanDeployment) { - if (type.kind() == org.jboss.jandex.Type.Kind.CLASS) { + if (type.kind() == Type.Kind.PRIMITIVE || type.kind() == Type.Kind.CLASS) { return type; } Map> resolvedTypeVariables = Types.resolvedTypeVariables(beanClass, beanDeployment); diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ObserverGenerator.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ObserverGenerator.java index 144ce5fee6200..5db8a410af2fe 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ObserverGenerator.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ObserverGenerator.java @@ -431,15 +431,18 @@ protected void implementNotify(ObserverInfo observer, ClassCreator observerCreat referenceHandles[i] = notify.invokeInterfaceMethod(MethodDescriptors.EVENT_CONTEXT_GET_METADATA, notify.getMethodParam(0)); } else { + InjectionPointInfo injectionPoint = injectionPointsIterator.next(); ResultHandle childCtxHandle = notify.invokeStaticMethod(MethodDescriptors.CREATIONAL_CTX_CHILD, ctxHandle); ResultHandle providerSupplierHandle = notify .readInstanceField(FieldDescriptor.of(observerCreator.getClassName(), - injectionPointToProviderField.get(injectionPointsIterator.next()), + injectionPointToProviderField.get(injectionPoint), Supplier.class.getName()), notify.getThis()); ResultHandle providerHandle = notify.invokeInterfaceMethod(MethodDescriptors.SUPPLIER_GET, providerSupplierHandle); - ResultHandle referenceHandle = notify.invokeInterfaceMethod(MethodDescriptors.INJECTABLE_REF_PROVIDER_GET, - providerHandle, childCtxHandle); + AssignableResultHandle referenceHandle = notify.createVariable(Object.class); + notify.assign(referenceHandle, notify.invokeInterfaceMethod(MethodDescriptors.INJECTABLE_REF_PROVIDER_GET, + providerHandle, childCtxHandle)); + BeanGenerator.checkPrimitiveInjection(notify, injectionPoint, referenceHandle); referenceHandles[i] = referenceHandle; } } diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Types.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Types.java index f6a97250338d7..54f0af4547bdb 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Types.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Types.java @@ -12,6 +12,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.function.BiConsumer; @@ -55,18 +56,25 @@ public final class Types { private static final Type OBJECT_TYPE = Type.create(DotNames.OBJECT, Kind.CLASS); - private static final Set PRIMITIVE_CLASS_NAMES = new HashSet<>(); - - static { - PRIMITIVE_CLASS_NAMES.add("byte"); - PRIMITIVE_CLASS_NAMES.add("char"); - PRIMITIVE_CLASS_NAMES.add("double"); - PRIMITIVE_CLASS_NAMES.add("float"); - PRIMITIVE_CLASS_NAMES.add("int"); - PRIMITIVE_CLASS_NAMES.add("long"); - PRIMITIVE_CLASS_NAMES.add("short"); - PRIMITIVE_CLASS_NAMES.add("boolean"); - } + private static final Set PRIMITIVE_CLASS_NAMES = Set.of( + "boolean", + "byte", + "short", + "int", + "long", + "float", + "double", + "char"); + + private static final Set PRIMITIVE_WRAPPERS = Set.of( + DotNames.BOOLEAN, + DotNames.BYTE, + DotNames.SHORT, + DotNames.INTEGER, + DotNames.LONG, + DotNames.FLOAT, + DotNames.DOUBLE, + DotNames.CHARACTER); // we ban these interfaces because they are new to Java 12 and are used by java.lang.String which // means that they cannot be included in bytecode if we want to have application built with Java 12+ but targeting Java 8 - 11 @@ -744,6 +752,50 @@ static boolean isPrimitiveClassName(String className) { return PRIMITIVE_CLASS_NAMES.contains(className); } + static boolean isPrimitiveWrapperType(Type type) { + if (type.kind() == Kind.CLASS) { + return PRIMITIVE_WRAPPERS.contains(type.name()); + } + return false; + } + + /** + * Emits a bytecode instruction to load the default value of given {@code primitive} type + * into given {@code bytecode} creator and returns the {@link ResultHandle} of the loaded + * default value. The default primitive value is {@code 0} for integral types, {@code 0.0} + * for floating point types, {@code false} for {@code boolean} and the null character for + * {@code char}. + *

+ * Can also be used to load a default primitive value of the corresponding wrapper type, + * because Gizmo will box automatically. + * + * @param primitive primitive type, must not be {@code null} + * @param bytecode bytecode creator that will receive the load instruction, must not be {@code null} + * @return a result handle of the loaded default primitive value + */ + static ResultHandle loadPrimitiveDefault(Primitive primitive, BytecodeCreator bytecode) { + switch (Objects.requireNonNull(primitive)) { + case BOOLEAN: + return bytecode.load(false); + case BYTE: + return bytecode.load((byte) 0); + case SHORT: + return bytecode.load((short) 0); + case INT: + return bytecode.load(0); + case LONG: + return bytecode.load(0L); + case FLOAT: + return bytecode.load(0.0F); + case DOUBLE: + return bytecode.load(0.0); + case CHAR: + return bytecode.load((char) 0); + default: + throw new IllegalArgumentException("Unknown primitive type: " + primitive); + } + } + static boolean containsTypeVariable(Type type) { if (type.kind() == Kind.TYPE_VARIABLE) { return true; diff --git a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/BeanManagerImpl.java b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/BeanManagerImpl.java index 059c86e3235c8..35f088e70524e 100644 --- a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/BeanManagerImpl.java +++ b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/BeanManagerImpl.java @@ -57,6 +57,9 @@ public Object getReference(Bean bean, Type beanType, CreationalContext ctx Objects.requireNonNull(bean, "Bean is null"); Objects.requireNonNull(beanType, "Bean type is null"); Objects.requireNonNull(ctx, "CreationalContext is null"); + if (!BeanTypeAssignabilityRules.instance().matches(beanType, bean.getTypes())) { + throw new IllegalArgumentException("Type " + beanType + " is not a bean type of " + bean); + } if (bean instanceof InjectableBean && ctx instanceof CreationalContextImpl) { return ArcContainerImpl.instance().beanInstanceHandle((InjectableBean) bean, (CreationalContextImpl) ctx).get(); } diff --git a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/BeanTypeAssignabilityRules.java b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/BeanTypeAssignabilityRules.java index cd5a2e51085e1..eb59f305a9905 100644 --- a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/BeanTypeAssignabilityRules.java +++ b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/BeanTypeAssignabilityRules.java @@ -42,6 +42,9 @@ protected boolean matches(Type requiredType, Type beanType) { } private boolean matchesNoBoxing(Type requiredType, Type beanType) { + if (Types.isArray(requiredType) && Types.isArray(beanType)) { + return matchesNoBoxing(Types.getArrayComponentType(requiredType), Types.getArrayComponentType(beanType)); + } if (requiredType instanceof Class) { if (beanType instanceof Class) { return matches((Class) requiredType, (Class) beanType); diff --git a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/Types.java b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/Types.java index e5e65649c2fd1..18567ee1d3783 100644 --- a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/Types.java +++ b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/Types.java @@ -54,14 +54,46 @@ static Class boxedClass(Class type) { } } + /** + * Determines whether the given type is an actual type. A type is considered actual if it is a raw type, + * a parameterized type or an array type. + * + * @param type the given type + * @return true if and only if the given type is an actual type + */ static boolean isActualType(Type type) { return (type instanceof Class) || (type instanceof ParameterizedType) || (type instanceof GenericArrayType); } + /** + * Determines whether the given type is an array type. + * + * @param type the given type + * @return true if the given type is a subclass of java.lang.Class or implements GenericArrayType + */ static boolean isArray(Type type) { return (type instanceof GenericArrayType) || (type instanceof Class && ((Class) type).isArray()); } + /** + * Determines the component type for a given array type. + * + * @param type the given array type + * @return the component type of a given array type + */ + public static Type getArrayComponentType(Type type) { + if (type instanceof GenericArrayType) { + return GenericArrayType.class.cast(type).getGenericComponentType(); + } + if (type instanceof Class) { + Class clazz = (Class) type; + if (clazz.isArray()) { + return clazz.getComponentType(); + } + } + throw new IllegalArgumentException("Not an array type " + type); + } + /** * Determines whether the given array only contains unbounded type variables or Object.class. * diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/ArcTestContainer.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/ArcTestContainer.java index fda3d072e7158..0df1daa6ada5e 100644 --- a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/ArcTestContainer.java +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/ArcTestContainer.java @@ -12,7 +12,9 @@ import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Predicate; @@ -467,12 +469,22 @@ public Enumeration getResources(String name) throws IOException { private Index index(Iterable> classes) throws IOException { Indexer indexer = new Indexer(); + Set packages = new HashSet<>(); for (Class clazz : classes) { + packages.add(clazz.getPackageName()); try (InputStream stream = ArcTestContainer.class.getClassLoader() .getResourceAsStream(clazz.getName().replace('.', '/') + ".class")) { indexer.index(stream); } } + for (String pkg : packages) { + try (InputStream stream = ArcTestContainer.class.getClassLoader() + .getResourceAsStream(pkg.replace('.', '/') + "/package-info.class")) { + if (stream != null) { + indexer.index(stream); + } + } + } return indexer.complete(); } diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/beanmanager/BeanManagerTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/beanmanager/BeanManagerTest.java index 72729d1d1ab53..76bbbb4b145cd 100644 --- a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/beanmanager/BeanManagerTest.java +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/beanmanager/BeanManagerTest.java @@ -9,6 +9,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.lang.annotation.Annotation; @@ -109,6 +110,9 @@ public void testGetReference() { @SuppressWarnings("unchecked") Bean legacyBean = (Bean) legacyBeans.iterator().next(); CreationalContext ctx = beanManager.createCreationalContext(legacyBean); + assertThrows(IllegalArgumentException.class, () -> { + beanManager.getReference(legacyBean, String.class, ctx); + }); Legacy legacy = (Legacy) beanManager.getReference(legacyBean, Legacy.class, ctx); assertNotNull(legacy.getBeanManager()); ctx.release(); diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/name/InvalidNamedInjectionPointTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/name/InvalidNamedInjectionPointTest.java new file mode 100644 index 0000000000000..de39e4dd722a2 --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/name/InvalidNamedInjectionPointTest.java @@ -0,0 +1,43 @@ +package io.quarkus.arc.test.name; + +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import javax.enterprise.context.Dependent; +import javax.enterprise.inject.spi.DefinitionException; +import javax.inject.Inject; +import javax.inject.Named; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.test.ArcTestContainer; + +public class InvalidNamedInjectionPointTest { + + @RegisterExtension + public ArcTestContainer container = ArcTestContainer.builder() + .beanClasses(Foo.class, Consumer.class) + .shouldFail() + .build(); + + @Test + public void test() { + assertNotNull(container.getFailure()); + assertInstanceOf(DefinitionException.class, container.getFailure()); + assertTrue(container.getFailure().getMessage().contains("@Named without value may not be used on method parameter")); + } + + @Named("foo") + @Dependent + static class Foo { + } + + @Dependent + static class Consumer { + @Inject + Consumer(@Named Foo foo) { + } + } +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/name/NameResolutionTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/name/NameResolutionTest.java index f6097778775ab..0e4ddfac45ae1 100644 --- a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/name/NameResolutionTest.java +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/name/NameResolutionTest.java @@ -6,6 +6,7 @@ import javax.enterprise.context.Dependent; import javax.enterprise.inject.Produces; import javax.enterprise.inject.literal.NamedLiteral; +import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; @@ -18,7 +19,7 @@ public class NameResolutionTest { @RegisterExtension - public ArcTestContainer container = new ArcTestContainer(Bravo.class, Alpha.class); + public ArcTestContainer container = new ArcTestContainer(Bravo.class, Alpha.class, Consumer.class); @Test public void testBeanNames() { @@ -29,6 +30,10 @@ public void testBeanNames() { assertEquals(1, Arc.container().beanManager().getBeans("bongo").size()); // Test that for defaulted name the @Named qualifier is replaced the defaulted value assertEquals("bing", Arc.container().instance(String.class, NamedLiteral.of("producedBing")).get()); + + Consumer consumer = Arc.container().instance(Consumer.class).get(); + assertEquals("bing", consumer.producedBing); + assertEquals(12345, consumer.bongo); } @Named("A") @@ -53,4 +58,14 @@ Integer getBongo() { } + @Dependent + static class Consumer { + @Inject + @Named + String producedBing; + + @Inject + @Named + Integer bongo; + } } diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/producer/primitive/PrimitiveWrapperProducerTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/producer/primitive/PrimitiveWrapperProducerTest.java new file mode 100644 index 0000000000000..4987f93f79d0c --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/producer/primitive/PrimitiveWrapperProducerTest.java @@ -0,0 +1,211 @@ +package io.quarkus.arc.test.producer.primitive; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import javax.enterprise.context.Dependent; +import javax.enterprise.event.Observes; +import javax.enterprise.inject.Disposes; +import javax.enterprise.inject.Produces; +import javax.inject.Inject; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.InstanceHandle; +import io.quarkus.arc.test.ArcTestContainer; + +public class PrimitiveWrapperProducerTest { + + @RegisterExtension + public ArcTestContainer container = new ArcTestContainer(Producers.class, Injection.class, Observer.class, + ProducerDisposer.class); + + @Test + public void testPrimitiveWrapperNullProducers() { + // field injection, constructor injetion, initializer method + Injection bean = Arc.container().instance(Injection.class).get(); + assertEquals(false, bean.bool); + assertEquals((byte) 0, bean.b); + assertEquals((short) 0, bean.s); + assertEquals(0, bean.i); + assertEquals(0L, bean.l); + assertEquals(0.0F, bean.f); + assertEquals(0.0, bean.d); + assertEquals((char) 0, bean.c); + + // observer method + Arc.container().beanManager().getEvent().fire("foo"); + assertEquals(false, Observer.bool); + assertEquals((byte) 0, Observer.b); + assertEquals((short) 0, Observer.s); + assertEquals(0, Observer.i); + assertEquals(0L, Observer.l); + assertEquals(0.0F, Observer.f); + assertEquals(0.0, Observer.d); + assertEquals((char) 0, Observer.c); + + // producer method + InstanceHandle handle = Arc.container().instance(MyPojo.class); + assertNotNull(handle.get()); + assertEquals(false, ProducerDisposer.producer_bool); + assertEquals((byte) 0, ProducerDisposer.producer_b); + assertEquals((short) 0, ProducerDisposer.producer_s); + assertEquals(0, ProducerDisposer.producer_i); + assertEquals(0L, ProducerDisposer.producer_l); + assertEquals(0.0F, ProducerDisposer.producer_f); + assertEquals(0.0, ProducerDisposer.producer_d); + assertEquals((char) 0, ProducerDisposer.producer_c); + + // disposer method + handle.destroy(); + assertEquals(false, ProducerDisposer.disposer_bool); + assertEquals((byte) 0, ProducerDisposer.disposer_b); + assertEquals((short) 0, ProducerDisposer.disposer_s); + assertEquals(0, ProducerDisposer.disposer_i); + assertEquals(0L, ProducerDisposer.disposer_l); + assertEquals(0.0F, ProducerDisposer.disposer_f); + assertEquals(0.0, ProducerDisposer.disposer_d); + assertEquals((char) 0, ProducerDisposer.disposer_c); + } + + @Dependent + static class Producers { + @Produces + Boolean bool() { + return null; + } + + @Produces + Byte b = null; + + @Produces + Short s() { + return null; + } + + @Produces + Integer i = null; + + @Produces + Long l() { + return null; + } + + @Produces + Float f = null; + + @Produces + Double d() { + return null; + } + + @Produces + Character c = null; + } + + @Dependent + static class Injection { + @Inject + boolean bool = true; + @Inject + byte b = 1; + @Inject + short s = 1; + int i = 1; + long l = 1L; + float f = 1.0F; + double d = 1.0; + char c = 'a'; + + @Inject + Injection(int i, long l) { + this.i = i; + this.l = l; + } + + @Inject + void doubleParamInit(float f, double d) { + this.f = f; + this.d = d; + } + + @Inject + void singleParamInit(char c) { + this.c = c; + } + } + + @Dependent + static class Observer { + static boolean bool = true; + static byte b = 1; + static short s = 1; + static int i = 1; + static long l = 1L; + static float f = 1.0F; + static double d = 1.0; + static char c = 'a'; + + void observe(@Observes String event, boolean bool, byte b, short s, int i, long l, float f, double d, char c) { + Observer.bool = bool; + Observer.b = b; + Observer.s = s; + Observer.i = i; + Observer.l = l; + Observer.f = f; + Observer.d = d; + Observer.c = c; + } + } + + static class MyPojo { + } + + @Dependent + static class ProducerDisposer { + static boolean producer_bool = true; + static byte producer_b = 1; + static short producer_s = 1; + static int producer_i = 1; + static long producer_l = 1L; + static float producer_f = 1.0F; + static double producer_d = 1.0; + static char producer_c = 'a'; + + static boolean disposer_bool = true; + static byte disposer_b = 1; + static short disposer_s = 1; + static int disposer_i = 1; + static long disposer_l = 1L; + static float disposer_f = 1.0F; + static double disposer_d = 1.0; + static char disposer_c = 'a'; + + @Produces + @Dependent + MyPojo produce(boolean bool, byte b, short s, int i, long l, float f, double d, char c) { + ProducerDisposer.producer_bool = bool; + ProducerDisposer.producer_b = b; + ProducerDisposer.producer_s = s; + ProducerDisposer.producer_i = i; + ProducerDisposer.producer_l = l; + ProducerDisposer.producer_f = f; + ProducerDisposer.producer_d = d; + ProducerDisposer.producer_c = c; + return new MyPojo(); + } + + void dispose(@Disposes MyPojo ignored, boolean bool, byte b, short s, int i, long l, float f, double d, char c) { + ProducerDisposer.disposer_bool = bool; + ProducerDisposer.disposer_b = b; + ProducerDisposer.disposer_s = s; + ProducerDisposer.disposer_i = i; + ProducerDisposer.disposer_l = l; + ProducerDisposer.disposer_f = f; + ProducerDisposer.disposer_d = d; + ProducerDisposer.disposer_c = c; + } + } +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/resolution/RuntimeResolutionTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/resolution/RuntimeResolutionTest.java index b0833540378ec..94995ad1f743a 100644 --- a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/resolution/RuntimeResolutionTest.java +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/resolution/RuntimeResolutionTest.java @@ -7,6 +7,7 @@ import java.util.AbstractList; import java.util.List; +import javax.enterprise.inject.Produces; import javax.enterprise.util.TypeLiteral; import javax.inject.Singleton; @@ -21,7 +22,7 @@ public class RuntimeResolutionTest { @RegisterExtension - public ArcTestContainer container = new ArcTestContainer(MyList.class); + public ArcTestContainer container = new ArcTestContainer(MyList.class, ArrayProducer.class); @SuppressWarnings("serial") @Test @@ -32,6 +33,11 @@ public void testResolution() throws IOException { }); assertTrue(list.isAvailable()); assertEquals(Integer.valueOf(7), list.get().get(1)); + + InstanceHandle array = arc.instance(MyList[].class); + assertTrue(array.isAvailable()); + assertEquals(1, array.get().length); + assertEquals(Integer.valueOf(7), array.get()[0].get(1)); } @Singleton @@ -49,4 +55,12 @@ public int size() { } + @Singleton + static class ArrayProducer { + @Produces + @Singleton + MyList[] produce() { + return new MyList[] { new MyList() }; + } + } } diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/vetoed/VetoedTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/vetoed/VetoedTest.java index 319dca8810975..71cc95a8f0369 100644 --- a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/vetoed/VetoedTest.java +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/vetoed/VetoedTest.java @@ -20,11 +20,13 @@ import io.quarkus.arc.Arc; import io.quarkus.arc.ArcContainer; import io.quarkus.arc.test.ArcTestContainer; +import io.quarkus.arc.test.vetoed.subpkg.PackageVetoed; public class VetoedTest { @RegisterExtension - public ArcTestContainer container = new ArcTestContainer(Seven.class, One.class, VetoedInterceptor.class, Logging.class); + public ArcTestContainer container = new ArcTestContainer(Seven.class, One.class, VetoedInterceptor.class, Logging.class, + PackageVetoed.class); @Test public void testVetoed() { @@ -36,6 +38,9 @@ public void testVetoed() { assertEquals(Integer.valueOf(7), Integer.valueOf(arc.instance(Seven.class).get().size())); // Interceptor is vetoed assertFalse(VetoedInterceptor.INTERCEPTED.get()); + + // vetoed by package annotation + assertFalse(arc.instance(PackageVetoed.class).isAvailable()); } @Logging diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/vetoed/subpkg/PackageVetoed.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/vetoed/subpkg/PackageVetoed.java new file mode 100644 index 0000000000000..ba5fad7a05341 --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/vetoed/subpkg/PackageVetoed.java @@ -0,0 +1,7 @@ +package io.quarkus.arc.test.vetoed.subpkg; + +import javax.enterprise.context.Dependent; + +@Dependent +public class PackageVetoed { +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/vetoed/subpkg/package-info.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/vetoed/subpkg/package-info.java new file mode 100644 index 0000000000000..d71de85769d4d --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/vetoed/subpkg/package-info.java @@ -0,0 +1,4 @@ +@Vetoed +package io.quarkus.arc.test.vetoed.subpkg; + +import javax.enterprise.inject.Vetoed; \ No newline at end of file