Skip to content

Commit

Permalink
Merge pull request #23177 from Ladicek/arc-initializer-method-validation
Browse files Browse the repository at this point in the history
Add initializer method validation to ArC
  • Loading branch information
mkouba authored Jan 25, 2022
2 parents e75b76f + 124a552 commit 8c1dbd2
Show file tree
Hide file tree
Showing 7 changed files with 196 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,22 @@ public static Set<AnnotationInstance> 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<AnnotationInstance> getParameterAnnotations(BeanDeployment beanDeployment, MethodInfo method) {
Set<AnnotationInstance> annotations = new HashSet<>();
for (AnnotationInstance annotation : beanDeployment.getAnnotations(method)) {
if (Kind.METHOD_PARAMETER == annotation.target().kind()) {
annotations.add(annotation);
}
}
return annotations;
}

/**
*
* @param beanDeployment
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<ScopeInfo, Function<MethodCreator, ResultHandle>> getCustomContexts() {
return customContexts;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,49 @@ static List<Injection> forBean(AnnotationTarget beanTarget, BeanInfo declaringBe
if (Kind.CLASS.equals(beanTarget.kind())) {
List<Injection> injections = new ArrayList<>();
forClassBean(beanTarget.asClass(), beanTarget.asClass(), beanDeployment, injections, transformer, false);

Set<AnnotationTarget> injectConstructors = injections.stream().filter(Injection::isConstructor)
.map(Injection::getTarget).collect(Collectors.toSet());
if (injectConstructors.size() > 1) {
throw new DefinitionException(
"Multiple @Inject constructors found on " + beanTarget.asClass().name() + ":\n"
+ injectConstructors.stream().map(Object::toString).collect(Collectors.joining("\n")));
}

Set<MethodInfo> 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()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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) {
}
}
}
Original file line number Diff line number Diff line change
@@ -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 {
}
}
Original file line number Diff line number Diff line change
@@ -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) {
}
}
}
Original file line number Diff line number Diff line change
@@ -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 {
}
}

0 comments on commit 8c1dbd2

Please sign in to comment.