diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Annotations.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Annotations.java index 66d43bf7538d4..d52ed97e0f1d0 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Annotations.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Annotations.java @@ -105,6 +105,22 @@ public static Set getAnnotations(Kind kind, DotName name, Co return ret; } + /** + * + * @param beanDeployment + * @param method + * @return collection of annotations present on all parameters of given method + */ + public static Set getParameterAnnotations(BeanDeployment beanDeployment, MethodInfo method) { + Set annotations = new HashSet<>(); + for (AnnotationInstance annotation : beanDeployment.getAnnotations(method)) { + if (Kind.METHOD_PARAMETER == annotation.target().kind()) { + annotations.add(annotation); + } + } + return annotations; + } + /** * * @param beanDeployment 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 786837e2c0b61..593178f895023 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 @@ -613,6 +613,10 @@ public AnnotationInstance getAnnotation(AnnotationTarget target, DotName name) { return annotationStore.getAnnotation(target, name); } + public boolean hasAnnotation(AnnotationTarget target, DotName name) { + return annotationStore.hasAnnotation(target, name); + } + Map> getCustomContexts() { return customContexts; } diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Injection.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Injection.java index 58867caadfaa9..dd3c68336b164 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Injection.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Injection.java @@ -40,6 +40,7 @@ static List forBean(AnnotationTarget beanTarget, BeanInfo declaringBe if (Kind.CLASS.equals(beanTarget.kind())) { List injections = new ArrayList<>(); forClassBean(beanTarget.asClass(), beanTarget.asClass(), beanDeployment, injections, transformer, false); + Set injectConstructors = injections.stream().filter(Injection::isConstructor) .map(Injection::getTarget).collect(Collectors.toSet()); if (injectConstructors.size() > 1) { @@ -47,6 +48,41 @@ static List forBean(AnnotationTarget beanTarget, BeanInfo declaringBe "Multiple @Inject constructors found on " + beanTarget.asClass().name() + ":\n" + injectConstructors.stream().map(Object::toString).collect(Collectors.joining("\n"))); } + + Set initializerMethods = injections.stream() + .filter(it -> it.isMethod() && !it.isConstructor()) + .map(Injection::getTarget) + .map(AnnotationTarget::asMethod) + .collect(Collectors.toSet()); + for (MethodInfo initializerMethod : initializerMethods) { + if (beanDeployment.hasAnnotation(initializerMethod, DotNames.PRODUCES)) { + throw new DefinitionException("Initializer method must not be marked @Produces " + + "(alternatively, producer method must not be marked @Inject): " + + beanTarget.asClass() + "." + initializerMethod.name()); + } + + if (Annotations.contains(Annotations.getParameterAnnotations(beanDeployment, initializerMethod), + DotNames.DISPOSES)) { + throw new DefinitionException("Initializer method must not have a parameter marked @Disposes " + + "(alternatively, disposer method must not be marked @Inject): " + + beanTarget.asClass() + "." + initializerMethod.name()); + } + + if (Annotations.contains(Annotations.getParameterAnnotations(beanDeployment, initializerMethod), + DotNames.OBSERVES)) { + throw new DefinitionException("Initializer method must not have a parameter marked @Observes " + + "(alternatively, observer method must not be marked @Inject): " + + beanTarget.asClass() + "." + initializerMethod.name()); + } + + if (Annotations.contains(Annotations.getParameterAnnotations(beanDeployment, initializerMethod), + DotNames.OBSERVES_ASYNC)) { + throw new DefinitionException("Initializer method must not have a parameter marked @ObservesAsync " + + "(alternatively, async observer method must not be marked @Inject): " + + beanTarget.asClass() + "." + initializerMethod.name()); + } + } + return injections; } else if (Kind.METHOD.equals(beanTarget.kind())) { if (beanTarget.asMethod().parameters().isEmpty()) { diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/validation/InitializerMethodMarkedAsyncObserverTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/validation/InitializerMethodMarkedAsyncObserverTest.java new file mode 100644 index 0000000000000..b73ebc907d69f --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/validation/InitializerMethodMarkedAsyncObserverTest.java @@ -0,0 +1,31 @@ +package io.quarkus.arc.test.validation; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import io.quarkus.arc.test.ArcTestContainer; +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.event.ObservesAsync; +import javax.enterprise.inject.spi.DefinitionException; +import javax.inject.Inject; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +public class InitializerMethodMarkedAsyncObserverTest { + @RegisterExtension + public ArcTestContainer container = ArcTestContainer.builder().beanClasses(MyBean.class).shouldFail().build(); + + @Test + public void testFailure() { + Throwable error = container.getFailure(); + assertNotNull(error); + assertTrue(error instanceof DefinitionException); + } + + @ApplicationScoped + static class MyBean { + @Inject + void observeAsync(@ObservesAsync Object ignored) { + } + } +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/validation/InitializerMethodMarkedDisposerTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/validation/InitializerMethodMarkedDisposerTest.java new file mode 100644 index 0000000000000..0094546f3dc12 --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/validation/InitializerMethodMarkedDisposerTest.java @@ -0,0 +1,41 @@ +package io.quarkus.arc.test.validation; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import io.quarkus.arc.test.ArcTestContainer; +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.inject.Disposes; +import javax.enterprise.inject.Produces; +import javax.enterprise.inject.spi.DefinitionException; +import javax.inject.Inject; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +public class InitializerMethodMarkedDisposerTest { + @RegisterExtension + public ArcTestContainer container = ArcTestContainer.builder().beanClasses(Producers.class).shouldFail().build(); + + @Test + public void testFailure() { + Throwable error = container.getFailure(); + assertNotNull(error); + assertTrue(error instanceof DefinitionException); + } + + @ApplicationScoped + static class Producers { + @Produces + @ApplicationScoped + MyBean produce() { + return new MyBean(); + } + + @Inject + void dispose(@Disposes MyBean bean) { + } + } + + static class MyBean { + } +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/validation/InitializerMethodMarkedObserverTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/validation/InitializerMethodMarkedObserverTest.java new file mode 100644 index 0000000000000..7b24ff4c97114 --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/validation/InitializerMethodMarkedObserverTest.java @@ -0,0 +1,31 @@ +package io.quarkus.arc.test.validation; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import io.quarkus.arc.test.ArcTestContainer; +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.event.Observes; +import javax.enterprise.inject.spi.DefinitionException; +import javax.inject.Inject; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +public class InitializerMethodMarkedObserverTest { + @RegisterExtension + public ArcTestContainer container = ArcTestContainer.builder().beanClasses(MyBean.class).shouldFail().build(); + + @Test + public void testFailure() { + Throwable error = container.getFailure(); + assertNotNull(error); + assertTrue(error instanceof DefinitionException); + } + + @ApplicationScoped + static class MyBean { + @Inject + void observe(@Observes Object ignored) { + } + } +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/validation/InitializerMethodMarkedProducerTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/validation/InitializerMethodMarkedProducerTest.java new file mode 100644 index 0000000000000..188393f30ab87 --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/validation/InitializerMethodMarkedProducerTest.java @@ -0,0 +1,37 @@ +package io.quarkus.arc.test.validation; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import io.quarkus.arc.test.ArcTestContainer; +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.inject.Produces; +import javax.enterprise.inject.spi.DefinitionException; +import javax.inject.Inject; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +public class InitializerMethodMarkedProducerTest { + @RegisterExtension + public ArcTestContainer container = ArcTestContainer.builder().beanClasses(Producers.class).shouldFail().build(); + + @Test + public void testFailure() { + Throwable error = container.getFailure(); + assertNotNull(error); + assertTrue(error instanceof DefinitionException); + } + + @ApplicationScoped + static class Producers { + @Inject + @Produces + @ApplicationScoped + MyBean produce() { + return new MyBean(); + } + } + + static class MyBean { + } +}