From 0a80d0eaee0f0ee17ffc32e5b987915b5d45d14c Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Wed, 12 Aug 2020 16:41:45 +0200 Subject: [PATCH] Introduce AutoAddScopeBuildItem to remove boilerplate necessary when... ...annotation transformers are used to add a scope annotation to a class - also fixes #11340 --- .../arc/deployment/AutoAddScopeBuildItem.java | 243 ++++++++++++++++++ .../arc/deployment/AutoAddScopeProcessor.java | 129 ++++++++++ extensions/jsonb/deployment/pom.xml | 9 +- .../jsonb/deployment/JsonbProcessor.java | 13 +- .../io/quarkus/jsonb/deployment/Alpha.java | 22 ++ .../jsonb/deployment/AlphaAdapter.java | 22 ++ .../io/quarkus/jsonb/deployment/Bravo.java | 16 ++ .../jsonb/deployment/JsonbAdapterTest.java | 38 +++ .../deployment/SchedulerProcessor.java | 31 +-- .../web/deployment/VertxWebProcessor.java | 36 +-- .../vertx/deployment/VertxProcessor.java | 30 +-- 11 files changed, 506 insertions(+), 83 deletions(-) create mode 100644 extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/AutoAddScopeBuildItem.java create mode 100644 extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/AutoAddScopeProcessor.java create mode 100644 extensions/jsonb/deployment/src/test/java/io/quarkus/jsonb/deployment/Alpha.java create mode 100644 extensions/jsonb/deployment/src/test/java/io/quarkus/jsonb/deployment/AlphaAdapter.java create mode 100644 extensions/jsonb/deployment/src/test/java/io/quarkus/jsonb/deployment/Bravo.java create mode 100644 extensions/jsonb/deployment/src/test/java/io/quarkus/jsonb/deployment/JsonbAdapterTest.java diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/AutoAddScopeBuildItem.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/AutoAddScopeBuildItem.java new file mode 100644 index 0000000000000..3991aade39ec1 --- /dev/null +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/AutoAddScopeBuildItem.java @@ -0,0 +1,243 @@ +package io.quarkus.arc.deployment; + +import java.util.Collection; + +import org.jboss.jandex.AnnotationInstance; +import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.DotName; +import org.jboss.jandex.IndexView; + +import io.quarkus.arc.processor.Annotations; +import io.quarkus.arc.processor.BuiltinScope; +import io.quarkus.arc.processor.DotNames; +import io.quarkus.builder.item.MultiBuildItem; + +/** + * This build item can be used to turn a class that is not annotated with a CDI scope annotation into a bean, i.e. the default + * scope annotation is added automatically if conditions are met. + */ +public final class AutoAddScopeBuildItem extends MultiBuildItem { + + public static Builder builder() { + return new Builder(); + } + + private final MatchPredicate matchPredicate; + private final boolean containerServicesRequired; + private final DotName defaultScope; + private final boolean unremovable; + private final String reason; + + private AutoAddScopeBuildItem(MatchPredicate matchPredicate, boolean containerServicesRequired, + DotName defaultScope, boolean unremovable, String reason) { + this.matchPredicate = matchPredicate; + this.containerServicesRequired = containerServicesRequired; + this.defaultScope = defaultScope; + this.unremovable = unremovable; + this.reason = reason; + } + + public boolean isContainerServicesRequired() { + return containerServicesRequired; + } + + public DotName getDefaultScope() { + return defaultScope; + } + + public boolean isUnremovable() { + return unremovable; + } + + public String getReason() { + return reason != null ? ": " + reason : ""; + } + + public boolean test(ClassInfo clazz, Collection annotations, IndexView index) { + return matchPredicate.test(clazz, annotations, index); + } + + public interface MatchPredicate { + + /** + * @param clazz + * @param annotations + * @param index + * @return {@code true} if the input arguments match the predicate, + * {@code false} otherwise + */ + boolean test(ClassInfo clazz, Collection annotations, IndexView index); + + default MatchPredicate and(MatchPredicate other) { + return new MatchPredicate() { + @Override + public boolean test(ClassInfo clazz, Collection annotations, IndexView index) { + return test(clazz, annotations, index) && other.test(clazz, annotations, index); + } + }; + } + + } + + public static class Builder { + + private MatchPredicate matchPredicate; + private boolean requiresContainerServices; + private DotName defaultScope; + private boolean unremovable; + private String reason; + + private Builder() { + this.defaultScope = BuiltinScope.DEPENDENT.getName(); + this.unremovable = false; + this.requiresContainerServices = false; + } + + /** + * At least one injection point or lifecycle callback must be declared in the class hierarchy. Otherwise, the scope + * annotation is not added. + *

+ * Note that the detection algorithm is just the best effort. Some inheritance rules defined by the spec are not + * followed, e.g. per spec an initializer method is only inherited if not overriden. This method merely scans the + * annotations. + * + * @return self + */ + public Builder requiresContainerServices() { + this.requiresContainerServices = true; + return this; + } + + /** + * The bean will be unremovable. + * + * @see ArcConfig#removeUnusedBeans + * @return self + */ + public Builder unremovable() { + this.unremovable = true; + return this; + } + + /** + * Set a custom predicate. + * + * @param predicate + * @return self + */ + public Builder match(MatchPredicate predicate) { + this.matchPredicate = predicate; + return this; + } + + /** + * The class must be annotated with the given annotation. Otherwise, the scope annotation is not added. + *

+ * The final predicate is a short-circuiting logical AND of the previous predicate (if any) and this condition. + * + * @param annotationName + * @return self + */ + public Builder isAnnotatedWith(DotName annotationName) { + return and((clazz, annotations, index) -> Annotations.contains(annotations, annotationName)); + } + + /** + * The class or any of its element must be annotated with the given annotation. Otherwise, the scope annotation is not + * added. + *

+ * The final predicate is a short-circuiting logical AND of the previous predicate (if any) and this condition. + * + * + * @param annotationNames + * @return self + */ + public Builder containsAnnotations(DotName... annotationNames) { + return and((clazz, annotations, index) -> { + for (DotName annotation : annotationNames) { + if (clazz.annotations().containsKey(annotation)) { + return true; + } + } + return false; + }); + } + + /** + * The class must directly or indirectly implement the given interface. + *

+ * The final predicate is a short-circuiting logical AND of the previous predicate (if any) and this condition. + * + * @param interfaceName + * @param index + * @return self + */ + public Builder implementsInterface(DotName interfaceName) { + return and((clazz, annotations, index) -> { + if (clazz.interfaceNames().contains(interfaceName)) { + return true; + } + DotName superName = clazz.superName(); + while (superName != null && !superName.equals(DotNames.OBJECT)) { + ClassInfo superClass = index.getClassByName(superName); + if (superClass != null) { + if (superClass.interfaceNames().contains(interfaceName)) { + return true; + } + superName = superClass.superName(); + } + } + return false; + }); + } + + /** + * The scope annotation added to the class. + * + * @param scopeAnnotationName + * @return self + */ + public Builder defaultScope(DotName scopeAnnotationName) { + this.defaultScope = scopeAnnotationName; + return this; + } + + /** + * The scope annotation added to the class. + * + * @param scope + * @return + */ + public Builder defaultScope(BuiltinScope scope) { + return defaultScope(scope.getName()); + } + + /** + * Specify an optional reason description that is used in log messages. + * + * @param reason + * @return the reason why the scope annotation was added + */ + public Builder reason(String reason) { + this.reason = reason; + return this; + } + + private Builder and(MatchPredicate other) { + if (matchPredicate == null) { + matchPredicate = other; + } else { + matchPredicate = matchPredicate.and(other); + } + return this; + } + + public AutoAddScopeBuildItem build() { + if (matchPredicate == null) { + throw new IllegalStateException("A matching predicate must be set!"); + } + return new AutoAddScopeBuildItem(matchPredicate, requiresContainerServices, defaultScope, unremovable, reason); + } + } + +} diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/AutoAddScopeProcessor.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/AutoAddScopeProcessor.java new file mode 100644 index 0000000000000..ad84d64573756 --- /dev/null +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/AutoAddScopeProcessor.java @@ -0,0 +1,129 @@ +package io.quarkus.arc.deployment; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import org.jboss.jandex.AnnotationTarget.Kind; +import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.DotName; +import org.jboss.jandex.IndexView; +import org.jboss.logging.Logger; + +import io.quarkus.arc.processor.AnnotationsTransformer; +import io.quarkus.arc.processor.BeanInfo; +import io.quarkus.arc.processor.DotNames; +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; + +public class AutoAddScopeProcessor { + + private static final Logger LOGGER = Logger.getLogger(AutoAddScopeProcessor.class); + + @BuildStep + void annotationTransformer(List autoScopes, CustomScopeAnnotationsBuildItem scopes, + List autoInjectAnnotations, + BuildProducer annotationsTransformers, + BuildProducer unremovableBeans, + BeanArchiveIndexBuildItem beanArchiveIndex) throws Exception { + if (autoScopes.isEmpty()) { + return; + } + Set containerAnnotationNames = autoInjectAnnotations.stream().flatMap(a -> a.getAnnotationNames().stream()) + .collect(Collectors.toSet()); + containerAnnotationNames.add(DotNames.POST_CONSTRUCT); + containerAnnotationNames.add(DotNames.PRE_DESTROY); + containerAnnotationNames.add(DotNames.INJECT); + + Set unremovables = new HashSet<>(); + + annotationsTransformers.produce(new AnnotationsTransformerBuildItem(new AnnotationsTransformer() { + + @Override + public boolean appliesTo(Kind kind) { + return kind == Kind.CLASS; + } + + @Override + public void transform(TransformationContext context) { + if (scopes.isScopeIn(context.getAnnotations())) { + // Skip classes annotated with a scope + return; + } + ClassInfo clazz = context.getTarget().asClass(); + Boolean requiresContainerServices = null; + + for (AutoAddScopeBuildItem autoScope : autoScopes) { + if (autoScope.isContainerServicesRequired()) { + if (requiresContainerServices == null) { + requiresContainerServices = requiresContainerServices(clazz, containerAnnotationNames, + beanArchiveIndex.getIndex()); + } + if (!requiresContainerServices) { + // Skip - no injection point detected + continue; + } + } + if (autoScope.test(clazz, context.getAnnotations(), beanArchiveIndex.getIndex())) { + context.transform().add(autoScope.getDefaultScope()).done(); + if (autoScope.isUnremovable()) { + unremovables.add(clazz.name()); + } + LOGGER.debugf("Automatically added scope %s to class %s" + autoScope.getReason(), + autoScope.getDefaultScope(), clazz, autoScope.getReason()); + break; + } + } + } + })); + + if (!unremovables.isEmpty()) { + unremovableBeans.produce(new UnremovableBeanBuildItem(new Predicate() { + + @Override + public boolean test(BeanInfo bean) { + return bean.isClassBean() && unremovables.contains(bean.getBeanClass()); + } + })); + } + } + + private boolean requiresContainerServices(ClassInfo clazz, Set containerAnnotationNames, IndexView index) { + // Note that transformed methods/fields are not taken into account + if (hasContainerAnnotation(clazz, containerAnnotationNames)) { + return true; + } + if (index != null) { + DotName superName = clazz.superName(); + while (superName != null && !superName.equals(DotNames.OBJECT)) { + ClassInfo superClass = index.getClassByName(superName); + if (superClass != null) { + if (hasContainerAnnotation(clazz, containerAnnotationNames)) { + return true; + } + superName = superClass.superName(); + } + } + } + return false; + } + + private boolean hasContainerAnnotation(ClassInfo clazz, Set containerAnnotationNames) { + if (clazz.annotations().isEmpty() || containerAnnotationNames.isEmpty()) { + return false; + } + return containsAny(clazz, containerAnnotationNames); + } + + private boolean containsAny(ClassInfo clazz, Set annotationNames) { + for (DotName annotation : clazz.annotations().keySet()) { + if (annotationNames.contains(annotation)) { + return true; + } + } + return false; + } + +} diff --git a/extensions/jsonb/deployment/pom.xml b/extensions/jsonb/deployment/pom.xml index 52a02e4004f80..90b75ddcef11f 100644 --- a/extensions/jsonb/deployment/pom.xml +++ b/extensions/jsonb/deployment/pom.xml @@ -1,7 +1,7 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> quarkus-jsonb-parent io.quarkus @@ -34,6 +34,11 @@ io.quarkus quarkus-jsonb + + io.quarkus + quarkus-junit5-internal + test + diff --git a/extensions/jsonb/deployment/src/main/java/io/quarkus/jsonb/deployment/JsonbProcessor.java b/extensions/jsonb/deployment/src/main/java/io/quarkus/jsonb/deployment/JsonbProcessor.java index 6c184a9be95b1..8cd22da76d0f1 100755 --- a/extensions/jsonb/deployment/src/main/java/io/quarkus/jsonb/deployment/JsonbProcessor.java +++ b/extensions/jsonb/deployment/src/main/java/io/quarkus/jsonb/deployment/JsonbProcessor.java @@ -25,11 +25,12 @@ import org.jboss.jandex.IndexView; import io.quarkus.arc.deployment.AdditionalBeanBuildItem; -import io.quarkus.arc.deployment.BeanArchiveIndexBuildItem; +import io.quarkus.arc.deployment.AutoAddScopeBuildItem; import io.quarkus.arc.deployment.GeneratedBeanBuildItem; import io.quarkus.arc.deployment.GeneratedBeanGizmoAdaptor; import io.quarkus.arc.deployment.UnremovableBeanBuildItem; import io.quarkus.arc.processor.BeanInfo; +import io.quarkus.arc.processor.BuiltinScope; import io.quarkus.deployment.Capability; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; @@ -103,8 +104,14 @@ private void registerInstance(BuildProducer reflective } @BuildStep - void unremovableJsonbAdapters(BuildProducer unremovableBeans, - BeanArchiveIndexBuildItem beanArchiveIndex) { + void processJsonbAdapters(BuildProducer unremovableBeans, + BuildProducer autoScopes) { + + // An adapter with an injection point but no scope is @Singleton + autoScopes.produce(AutoAddScopeBuildItem.builder().implementsInterface(JSONB_ADAPTER_NAME).requiresContainerServices() + .defaultScope(BuiltinScope.SINGLETON).build()); + + // Make all adapters unremovable unremovableBeans.produce(new UnremovableBeanBuildItem(new Predicate() { @Override diff --git a/extensions/jsonb/deployment/src/test/java/io/quarkus/jsonb/deployment/Alpha.java b/extensions/jsonb/deployment/src/test/java/io/quarkus/jsonb/deployment/Alpha.java new file mode 100644 index 0000000000000..b33b2c99dd67e --- /dev/null +++ b/extensions/jsonb/deployment/src/test/java/io/quarkus/jsonb/deployment/Alpha.java @@ -0,0 +1,22 @@ +package io.quarkus.jsonb.deployment; + +import javax.json.bind.annotation.JsonbTypeAdapter; + +@JsonbTypeAdapter(AlphaAdapter.class) +public class Alpha { + + private String name; + + public Alpha(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/extensions/jsonb/deployment/src/test/java/io/quarkus/jsonb/deployment/AlphaAdapter.java b/extensions/jsonb/deployment/src/test/java/io/quarkus/jsonb/deployment/AlphaAdapter.java new file mode 100644 index 0000000000000..f76518f977e29 --- /dev/null +++ b/extensions/jsonb/deployment/src/test/java/io/quarkus/jsonb/deployment/AlphaAdapter.java @@ -0,0 +1,22 @@ +package io.quarkus.jsonb.deployment; + +import javax.inject.Inject; +import javax.json.bind.adapter.JsonbAdapter; + +// scope annotation is added automatically +public class AlphaAdapter implements JsonbAdapter { + + @Inject + Bravo bravo; + + @Override + public String adaptToJson(Alpha obj) throws Exception { + return bravo.getVal(obj); + } + + @Override + public Alpha adaptFromJson(String obj) throws Exception { + return new Alpha(bravo.getVal(obj)); + } + +} diff --git a/extensions/jsonb/deployment/src/test/java/io/quarkus/jsonb/deployment/Bravo.java b/extensions/jsonb/deployment/src/test/java/io/quarkus/jsonb/deployment/Bravo.java new file mode 100644 index 0000000000000..57b7f47278d24 --- /dev/null +++ b/extensions/jsonb/deployment/src/test/java/io/quarkus/jsonb/deployment/Bravo.java @@ -0,0 +1,16 @@ +package io.quarkus.jsonb.deployment; + +import javax.enterprise.context.ApplicationScoped; + +@ApplicationScoped +public class Bravo { + + String getVal(Alpha alpha) { + return getVal(alpha.getName()); + } + + String getVal(String name) { + return name + "_bravo"; + } + +} diff --git a/extensions/jsonb/deployment/src/test/java/io/quarkus/jsonb/deployment/JsonbAdapterTest.java b/extensions/jsonb/deployment/src/test/java/io/quarkus/jsonb/deployment/JsonbAdapterTest.java new file mode 100644 index 0000000000000..90e03d07fd21f --- /dev/null +++ b/extensions/jsonb/deployment/src/test/java/io/quarkus/jsonb/deployment/JsonbAdapterTest.java @@ -0,0 +1,38 @@ +package io.quarkus.jsonb.deployment; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +import javax.enterprise.inject.Instance; +import javax.inject.Inject; +import javax.json.bind.Jsonb; +import javax.json.bind.adapter.JsonbAdapter; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; + +public class JsonbAdapterTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addClasses(Alpha.class, AlphaAdapter.class, Bravo.class)); + + @Inject + Jsonb jsonb; + + @Inject + Instance> adapters; + + @Test + public void testAdapterInjection() { + assertFalse(adapters.isUnsatisfied()); + assertEquals("\"foo_bravo\"", jsonb.toJson(new Alpha("foo"))); + assertEquals("bar_bravo", jsonb.fromJson("\"bar\"", Alpha.class).getName()); + } + +} diff --git a/extensions/scheduler/deployment/src/main/java/io/quarkus/scheduler/deployment/SchedulerProcessor.java b/extensions/scheduler/deployment/src/main/java/io/quarkus/scheduler/deployment/SchedulerProcessor.java index c65429994d486..1d6f0f8584181 100644 --- a/extensions/scheduler/deployment/src/main/java/io/quarkus/scheduler/deployment/SchedulerProcessor.java +++ b/extensions/scheduler/deployment/src/main/java/io/quarkus/scheduler/deployment/SchedulerProcessor.java @@ -10,8 +10,6 @@ import java.util.List; import java.util.Map; -import javax.inject.Singleton; - import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.AnnotationValue; import org.jboss.jandex.ClassInfo; @@ -30,17 +28,15 @@ import io.quarkus.arc.InjectableBean; import io.quarkus.arc.InstanceHandle; import io.quarkus.arc.deployment.AdditionalBeanBuildItem; -import io.quarkus.arc.deployment.AnnotationsTransformerBuildItem; +import io.quarkus.arc.deployment.AutoAddScopeBuildItem; import io.quarkus.arc.deployment.BeanArchiveIndexBuildItem; import io.quarkus.arc.deployment.BeanRegistrationPhaseBuildItem; -import io.quarkus.arc.deployment.CustomScopeAnnotationsBuildItem; import io.quarkus.arc.deployment.SyntheticBeanBuildItem; import io.quarkus.arc.deployment.UnremovableBeanBuildItem; import io.quarkus.arc.deployment.UnremovableBeanBuildItem.BeanClassAnnotationExclusion; import io.quarkus.arc.deployment.ValidationPhaseBuildItem; import io.quarkus.arc.deployment.ValidationPhaseBuildItem.ValidationErrorBuildItem; import io.quarkus.arc.processor.AnnotationStore; -import io.quarkus.arc.processor.AnnotationsTransformer; import io.quarkus.arc.processor.BeanInfo; import io.quarkus.arc.processor.BuildExtension; import io.quarkus.arc.processor.BuiltinScope; @@ -95,27 +91,10 @@ void beans(Capabilities capabilities, BuildProducer add } @BuildStep - AnnotationsTransformerBuildItem annotationTransformer(CustomScopeAnnotationsBuildItem scopes) { - return new AnnotationsTransformerBuildItem(new AnnotationsTransformer() { - - @Override - public boolean appliesTo(org.jboss.jandex.AnnotationTarget.Kind kind) { - return kind == org.jboss.jandex.AnnotationTarget.Kind.CLASS; - } - - @Override - public void transform(TransformationContext context) { - ClassInfo target = context.getTarget().asClass(); - if (!scopes.isScopeIn(context.getAnnotations()) - && (target.annotations().containsKey(SCHEDULED_NAME) - || target.annotations().containsKey(SCHEDULES_NAME))) { - // Class with no scope annotation but with @Scheduled method - LOGGER.debugf("Found scheduled business methods on a class %s with no scope defined - adding @Singleton", - context.getTarget()); - context.transform().add(Singleton.class).done(); - } - } - }); + AutoAddScopeBuildItem autoAddScope() { + return AutoAddScopeBuildItem.builder().containsAnnotations(SCHEDULED_NAME, SCHEDULES_NAME) + .defaultScope(BuiltinScope.SINGLETON) + .reason("Found scheduled business methods").build(); } @BuildStep diff --git a/extensions/vertx-web/deployment/src/main/java/io/quarkus/vertx/web/deployment/VertxWebProcessor.java b/extensions/vertx-web/deployment/src/main/java/io/quarkus/vertx/web/deployment/VertxWebProcessor.java index 404c6040a210e..494df05431c50 100644 --- a/extensions/vertx-web/deployment/src/main/java/io/quarkus/vertx/web/deployment/VertxWebProcessor.java +++ b/extensions/vertx-web/deployment/src/main/java/io/quarkus/vertx/web/deployment/VertxWebProcessor.java @@ -23,7 +23,6 @@ import javax.enterprise.context.ContextNotActiveException; import javax.enterprise.context.spi.Contextual; -import javax.inject.Singleton; import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.AnnotationValue; @@ -39,9 +38,8 @@ import io.quarkus.arc.ArcContainer; import io.quarkus.arc.InjectableBean; import io.quarkus.arc.InjectableContext; -import io.quarkus.arc.deployment.AnnotationsTransformerBuildItem; +import io.quarkus.arc.deployment.AutoAddScopeBuildItem; import io.quarkus.arc.deployment.BeanArchiveIndexBuildItem; -import io.quarkus.arc.deployment.CustomScopeAnnotationsBuildItem; import io.quarkus.arc.deployment.TransformedAnnotationsBuildItem; import io.quarkus.arc.deployment.UnremovableBeanBuildItem; import io.quarkus.arc.deployment.ValidationPhaseBuildItem; @@ -49,7 +47,6 @@ import io.quarkus.arc.impl.CreationalContextImpl; import io.quarkus.arc.processor.AnnotationStore; import io.quarkus.arc.processor.Annotations; -import io.quarkus.arc.processor.AnnotationsTransformer; import io.quarkus.arc.processor.BeanInfo; import io.quarkus.arc.processor.BuildExtension; import io.quarkus.arc.processor.BuiltinScope; @@ -332,30 +329,13 @@ void addAdditionalRoutes( } @BuildStep - AnnotationsTransformerBuildItem annotationTransformer(CustomScopeAnnotationsBuildItem scopes) { - return new AnnotationsTransformerBuildItem(new AnnotationsTransformer() { - - @Override - public boolean appliesTo(org.jboss.jandex.AnnotationTarget.Kind kind) { - return kind == org.jboss.jandex.AnnotationTarget.Kind.CLASS; - } - - @Override - public void transform(TransformationContext context) { - if (!scopes.isScopeIn(context.getAnnotations())) { - // Class with no scope annotation but with a method annotated with @Route, @RouteFilter - ClassInfo target = context.getTarget().asClass(); - if (target.annotations().containsKey(io.quarkus.vertx.web.deployment.DotNames.ROUTE) - || target.annotations().containsKey(io.quarkus.vertx.web.deployment.DotNames.ROUTES) - || target.annotations().containsKey(io.quarkus.vertx.web.deployment.DotNames.ROUTE_FILTER)) { - LOGGER.debugf( - "Found route handler business methods on a class %s with no scope annotation - adding @Singleton", - context.getTarget()); - context.transform().add(Singleton.class).done(); - } - } - } - }); + AutoAddScopeBuildItem autoAddScope() { + return AutoAddScopeBuildItem.builder() + .containsAnnotations(io.quarkus.vertx.web.deployment.DotNames.ROUTE, + io.quarkus.vertx.web.deployment.DotNames.ROUTES, + io.quarkus.vertx.web.deployment.DotNames.ROUTE_FILTER) + .defaultScope(BuiltinScope.SINGLETON) + .reason("Found route handler business methods").build(); } private void validateRouteFilterMethod(BeanInfo bean, MethodInfo method) { diff --git a/extensions/vertx/deployment/src/main/java/io/quarkus/vertx/deployment/VertxProcessor.java b/extensions/vertx/deployment/src/main/java/io/quarkus/vertx/deployment/VertxProcessor.java index 6d602b3f0cefe..7b4e4f0f4cd1f 100644 --- a/extensions/vertx/deployment/src/main/java/io/quarkus/vertx/deployment/VertxProcessor.java +++ b/extensions/vertx/deployment/src/main/java/io/quarkus/vertx/deployment/VertxProcessor.java @@ -7,7 +7,6 @@ import java.util.Map; import javax.inject.Inject; -import javax.inject.Singleton; import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.ClassInfo; @@ -17,16 +16,15 @@ import org.jboss.logging.Logger; import io.quarkus.arc.deployment.AdditionalBeanBuildItem; -import io.quarkus.arc.deployment.AnnotationsTransformerBuildItem; +import io.quarkus.arc.deployment.AutoAddScopeBuildItem; import io.quarkus.arc.deployment.BeanRegistrationPhaseBuildItem; import io.quarkus.arc.deployment.BeanRegistrationPhaseBuildItem.BeanConfiguratorBuildItem; -import io.quarkus.arc.deployment.CustomScopeAnnotationsBuildItem; import io.quarkus.arc.deployment.UnremovableBeanBuildItem; import io.quarkus.arc.deployment.UnremovableBeanBuildItem.BeanClassAnnotationExclusion; import io.quarkus.arc.processor.AnnotationStore; -import io.quarkus.arc.processor.AnnotationsTransformer; import io.quarkus.arc.processor.BeanInfo; import io.quarkus.arc.processor.BuildExtension; +import io.quarkus.arc.processor.BuiltinScope; import io.quarkus.deployment.Capability; import io.quarkus.deployment.Feature; import io.quarkus.deployment.GeneratedClassGizmoAdaptor; @@ -133,26 +131,10 @@ void collectEventConsumers( } @BuildStep - AnnotationsTransformerBuildItem annotationTransformer(CustomScopeAnnotationsBuildItem scopes) { - return new AnnotationsTransformerBuildItem(new AnnotationsTransformer() { - - @Override - public boolean appliesTo(org.jboss.jandex.AnnotationTarget.Kind kind) { - return kind == org.jboss.jandex.AnnotationTarget.Kind.CLASS; - } - - @Override - public void transform(TransformationContext context) { - if (!scopes.isScopeIn(context.getAnnotations()) - && context.getTarget().asClass().annotations().containsKey(CONSUME_EVENT)) { - // Class with no built-in scope annotation but with a method annotated with @ConsumeMessage - LOGGER.debugf( - "Found event consumer business methods on a class %s with no scope annotation - adding @Singleton", - context.getTarget()); - context.transform().add(Singleton.class).done(); - } - } - }); + AutoAddScopeBuildItem autoAddScope() { + // Add @Singleton to a class with no scope annotation but with a method annotated with @ConsumeEvent + return AutoAddScopeBuildItem.builder().containsAnnotations(CONSUME_EVENT).defaultScope(BuiltinScope.SINGLETON) + .reason("Found event consumer business methods").build(); } @BuildStep