diff --git a/junit5/src/main/java/org/jboss/weld/junit5/auto/ClassScanning.java b/junit5/src/main/java/org/jboss/weld/junit5/auto/ClassScanning.java index 815019f6..4e38768c 100644 --- a/junit5/src/main/java/org/jboss/weld/junit5/auto/ClassScanning.java +++ b/junit5/src/main/java/org/jboss/weld/junit5/auto/ClassScanning.java @@ -25,21 +25,18 @@ import org.junit.jupiter.api.Test; import org.junit.platform.commons.support.AnnotationSupport; import org.junit.platform.commons.support.HierarchyTraversalMode; -import org.junit.platform.commons.util.AnnotationUtils; import org.junit.platform.commons.util.CollectionUtils; import org.junit.platform.commons.util.Preconditions; -import javax.enterprise.context.ApplicationScoped; -import javax.enterprise.context.ConversationScoped; +import javax.decorator.Decorator; import javax.enterprise.context.Dependent; import javax.enterprise.context.NormalScope; -import javax.enterprise.context.RequestScoped; -import javax.enterprise.context.SessionScoped; import javax.enterprise.inject.Produces; import javax.enterprise.inject.Stereotype; import javax.enterprise.inject.spi.Extension; import javax.inject.Inject; import javax.inject.Qualifier; +import javax.interceptor.Interceptor; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Executable; @@ -107,7 +104,7 @@ static void scanForRequiredBeanClass(Class testClass, Weld weld, boolean expl findFirstAnnotatedConstructor(currClass, Inject.class) .map(Stream::of).orElseGet(Stream::empty) - .flatMap(cons -> getExecutableParameterTypes(cons, weld, explicitInjection).stream()) + .flatMap(cons -> getExecutableParameterTypes(cons, explicitInjection).stream()) .forEach(cls -> addClassesToProcess(classesToProcess, cls)); findAnnotatedFields(currClass, Produces.class).stream() @@ -115,31 +112,36 @@ static void scanForRequiredBeanClass(Class testClass, Weld weld, boolean expl .forEach(cls -> addClassesToProcess(classesToProcess, cls)); AnnotationSupport.findAnnotatedMethods(currClass, Produces.class, HierarchyTraversalMode.BOTTOM_UP).stream() - .map(Method::getReturnType) + .flatMap(method -> + Stream.concat( + getExecutableParameterTypes(method, explicitInjection).stream(), + Stream.of(method.getReturnType()) + ) + ) .forEach(cls -> addClassesToProcess(classesToProcess, cls)); AnnotationSupport.findAnnotatedMethods(currClass, Test.class, HierarchyTraversalMode.BOTTOM_UP).stream() - .flatMap(method -> getExecutableParameterTypes(method, weld, explicitInjection).stream()) + .flatMap(method -> getExecutableParameterTypes(method, explicitInjection).stream()) .forEach(cls -> addClassesToProcess(classesToProcess, cls)); AnnotationSupport.findAnnotatedMethods(currClass, RepeatedTest.class, HierarchyTraversalMode.BOTTOM_UP).stream() - .flatMap(method -> getExecutableParameterTypes(method, weld, explicitInjection).stream()) + .flatMap(method -> getExecutableParameterTypes(method, explicitInjection).stream()) .forEach(cls -> addClassesToProcess(classesToProcess, cls)); AnnotationSupport.findAnnotatedMethods(currClass, BeforeAll.class, HierarchyTraversalMode.BOTTOM_UP).stream() - .flatMap(method -> getExecutableParameterTypes(method, weld, explicitInjection).stream()) + .flatMap(method -> getExecutableParameterTypes(method, explicitInjection).stream()) .forEach(cls -> addClassesToProcess(classesToProcess, cls)); AnnotationSupport.findAnnotatedMethods(currClass, BeforeEach.class, HierarchyTraversalMode.BOTTOM_UP).stream() - .flatMap(method -> getExecutableParameterTypes(method, weld, explicitInjection).stream()) + .flatMap(method -> getExecutableParameterTypes(method, explicitInjection).stream()) .forEach(cls -> addClassesToProcess(classesToProcess, cls)); AnnotationSupport.findAnnotatedMethods(currClass, AfterEach.class, HierarchyTraversalMode.BOTTOM_UP).stream() - .flatMap(method -> getExecutableParameterTypes(method, weld, explicitInjection).stream()) + .flatMap(method -> getExecutableParameterTypes(method, explicitInjection).stream()) .forEach(cls -> addClassesToProcess(classesToProcess, cls)); AnnotationSupport.findAnnotatedMethods(currClass, AfterAll.class, HierarchyTraversalMode.BOTTOM_UP).stream() - .flatMap(method -> getExecutableParameterTypes(method, weld, explicitInjection).stream()) + .flatMap(method -> getExecutableParameterTypes(method, explicitInjection).stream()) .forEach(cls -> addClassesToProcess(classesToProcess, cls)); AnnotationSupport.findRepeatableAnnotations(currClass, AddPackages.class) @@ -151,7 +153,10 @@ static void scanForRequiredBeanClass(Class testClass, Weld weld, boolean expl AnnotationSupport.findRepeatableAnnotations(currClass, AddBeanClasses.class).stream() .flatMap(ann -> stream(ann.value())) .distinct() - .forEach(weld::addBeanClass); + .forEach(it -> { + classesToProcess.add(it); + weld.addBeanClass(it); + }); AnnotationSupport.findRepeatableAnnotations(currClass, AddExtensions.class).stream() .flatMap(ann -> stream(ann.value())) @@ -163,6 +168,7 @@ static void scanForRequiredBeanClass(Class testClass, Weld weld, boolean expl .flatMap(ann -> stream(ann.value())) .distinct() .forEach(interceptor -> { + classesToProcess.add(interceptor); weld.addInterceptor(interceptor); weld.addBeanClass(interceptor); }); @@ -171,6 +177,7 @@ static void scanForRequiredBeanClass(Class testClass, Weld weld, boolean expl .flatMap(ann -> stream(ann.value())) .distinct() .forEach(decorator -> { + classesToProcess.add(decorator); weld.addDecorator(decorator); weld.addBeanClass(decorator); }); @@ -224,24 +231,20 @@ private static void addClassesToProcess(Collection> classesToProcess, T } - private static List> getExecutableParameterTypes(Executable executable, Weld weld, boolean explicitInjection) { + private static List> getExecutableParameterTypes(Executable executable, boolean explicitInjection) { List> types = new ArrayList<>(); if (explicitInjection) { Annotation[][] paramAnns = executable.getParameterAnnotations(); Class[] paramTypes = executable.getParameterTypes(); - for (int c = 0; c < paramAnns.length; ++c) { - if (stream(paramAnns[c]).anyMatch(ann -> isAnnotated(ann.annotationType(), Qualifier.class) || isAnnotated(ann.annotationType(), NormalScope.class))) { - weld.addBeanClass(paramTypes[c]); + for (int c = 0; c < paramTypes.length; ++c) { + if (stream(paramAnns[c]).anyMatch(ClassScanning::isBeanParameterAnnotation)) { types.add(paramTypes[c]); } } } else { - for (Class paramType : executable.getParameterTypes()) { - weld.addBeanClass(paramType); - types.add(paramType); - } + types.addAll(asList(executable.getParameterTypes())); } return types; @@ -255,14 +258,14 @@ private static Extension createExtension(Class clazz) { } } + private static boolean isBeanParameterAnnotation(Annotation ann) { + return isAnnotated(ann.annotationType(), Qualifier.class); + } + private static boolean hasBeanDefiningAnnotation(Class clazz) { - return - AnnotationUtils.isAnnotated(clazz, ApplicationScoped.class) || - AnnotationUtils.isAnnotated(clazz, SessionScoped.class) || - AnnotationUtils.isAnnotated(clazz, ConversationScoped.class) || - AnnotationUtils.isAnnotated(clazz, RequestScoped.class) || - AnnotationUtils.isAnnotated(clazz, Dependent.class) || - AnnotationUtils.isAnnotated(clazz, Stereotype.class); + return isAnnotated(clazz, NormalScope.class) || isAnnotated(clazz, Dependent.class) || + isAnnotated(clazz, Interceptor.class) || isAnnotated(clazz, Decorator.class) || + isAnnotated(clazz, Stereotype.class); } private static List findAllFieldsInHierarchy(Class clazz) { diff --git a/junit5/src/test/java/org/jboss/weld/junit5/auto/ProducerMethodParametersScanningTest.java b/junit5/src/test/java/org/jboss/weld/junit5/auto/ProducerMethodParametersScanningTest.java new file mode 100644 index 00000000..2dd5fbc6 --- /dev/null +++ b/junit5/src/test/java/org/jboss/weld/junit5/auto/ProducerMethodParametersScanningTest.java @@ -0,0 +1,38 @@ +package org.jboss.weld.junit5.auto; + +import org.jboss.weld.junit5.auto.beans.Engine; +import org.jboss.weld.junit5.auto.beans.V6; +import org.junit.jupiter.api.Test; + +import javax.enterprise.context.Dependent; +import javax.enterprise.inject.Produces; +import javax.inject.Named; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@EnableAutoWeld +public class ProducerMethodParametersScanningTest { + + @Produces + @Named("custom") + @Dependent + Engine getEngine(V6 v6) { + return new Engine() { + @Override + public int getThrottle() { + return v6.getThrottle(); + } + + @Override + public void setThrottle(int value) { + v6.setThrottle(value); + } + }; + } + + @Test + public void test(@Named("custom") Engine engine) { + assertNotNull(engine); + } + +} diff --git a/junit5/src/test/java/org/jboss/weld/junit5/auto/ScannedClassesAreNotForcedBeansTest.java b/junit5/src/test/java/org/jboss/weld/junit5/auto/ScannedClassesAreNotForcedBeansTest.java new file mode 100644 index 00000000..f9ab248c --- /dev/null +++ b/junit5/src/test/java/org/jboss/weld/junit5/auto/ScannedClassesAreNotForcedBeansTest.java @@ -0,0 +1,39 @@ +package org.jboss.weld.junit5.auto; + + +import org.jboss.weld.junit5.auto.beans.InjectedV8; +import org.jboss.weld.junit5.auto.beans.V8; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import javax.enterprise.inject.Produces; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + + +@EnableAutoWeld +@AddBeanClasses(InjectedV8.class) +public class ScannedClassesAreNotForcedBeansTest { + + /** + * V8 is *not* a "bean" class, in that it has no bean defining annotation. To satisfy + * a dependency on it, a producer method or a reference in an @AddBeanClasses annotation + * is required. + *

+ * This test ensures that as V8 is discovered via class scanning it is not automatically + * added as a bean class. If it was added that way, the bean class and producer method would + * create an ambiguous injection case for V8. + * + * NOTE: This case only tests for classes found as non-parameters (e.g. injected fields) + */ + + @Produces + private V8 engine = new V8(); + + @Test + @DisplayName("Test that V8 is not ambiguous to do incorrectly being identified as a bean class") + void test(V8 engine) { + assertNotNull(engine); + } + +} diff --git a/junit5/src/test/java/org/jboss/weld/junit5/auto/ScannedParameterClassesAreNotForcedBeansTest.java b/junit5/src/test/java/org/jboss/weld/junit5/auto/ScannedParameterClassesAreNotForcedBeansTest.java new file mode 100644 index 00000000..834ca6eb --- /dev/null +++ b/junit5/src/test/java/org/jboss/weld/junit5/auto/ScannedParameterClassesAreNotForcedBeansTest.java @@ -0,0 +1,40 @@ +package org.jboss.weld.junit5.auto; + + +import org.jboss.weld.junit5.auto.beans.ConstructedV8; +import org.jboss.weld.junit5.auto.beans.V8; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import javax.enterprise.inject.Produces; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + + +@EnableAutoWeld +@AddBeanClasses(ConstructedV8.class) +public class ScannedParameterClassesAreNotForcedBeansTest { + + /** + * V8 is *not* a "bean" class, in that it has no bean defining annotation. To satisfy + * a dependency on it, a producer method or a reference in an @AddBeanClasses annotation + * is required. + *

+ * This test ensures that as V8 is discovered via class scanning it is not automatically + * added as a bean class. If it was added that way, the bean class and producer method would + * create an ambiguous injection case for V8. + * + * NOTE: This case only tests for classes found from parameters (e.g. constructor injection + * parameters) + */ + + @Produces + private V8 engine = new V8(); + + @Test + @DisplayName("Test that V8 is not ambiguous to do incorrectly being identified as a bean class from parameter") + void test(V8 engine) { + assertNotNull(engine); + } + +} diff --git a/junit5/src/test/java/org/jboss/weld/junit5/auto/beans/ConstructedV8.java b/junit5/src/test/java/org/jboss/weld/junit5/auto/beans/ConstructedV8.java new file mode 100644 index 00000000..a9984357 --- /dev/null +++ b/junit5/src/test/java/org/jboss/weld/junit5/auto/beans/ConstructedV8.java @@ -0,0 +1,16 @@ +package org.jboss.weld.junit5.auto.beans; + +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; + +@ApplicationScoped +public class ConstructedV8 { + + private V8 engine; + + @Inject + public ConstructedV8(V8 engine) { + this.engine = engine; + } + +} diff --git a/junit5/src/test/java/org/jboss/weld/junit5/auto/beans/InjectedV8.java b/junit5/src/test/java/org/jboss/weld/junit5/auto/beans/InjectedV8.java new file mode 100644 index 00000000..6bf4c189 --- /dev/null +++ b/junit5/src/test/java/org/jboss/weld/junit5/auto/beans/InjectedV8.java @@ -0,0 +1,12 @@ +package org.jboss.weld.junit5.auto.beans; + +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; + +@ApplicationScoped +public class InjectedV8 { + + @Inject + private V8 engine; + +}