From 614573218f513d74171943ecd7a09d02299efacd Mon Sep 17 00:00:00 2001 From: Roberto Cortez Date: Fri, 21 Jul 2023 13:29:28 +0100 Subject: [PATCH] Veto REST Resources from CDI when constructors from Resources do not support CDI --- .../resteasy/common/spi/ResteasyDotNames.java | 6 ++ .../ResteasyServerCommonProcessor.java | 61 +++++++++++++++++-- 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/extensions/resteasy-classic/resteasy-common/spi/src/main/java/io/quarkus/resteasy/common/spi/ResteasyDotNames.java b/extensions/resteasy-classic/resteasy-common/spi/src/main/java/io/quarkus/resteasy/common/spi/ResteasyDotNames.java index 8d0fdaca441cf..db2be9b0b5a1e 100644 --- a/extensions/resteasy-classic/resteasy-common/spi/src/main/java/io/quarkus/resteasy/common/spi/ResteasyDotNames.java +++ b/extensions/resteasy-classic/resteasy-common/spi/src/main/java/io/quarkus/resteasy/common/spi/ResteasyDotNames.java @@ -30,6 +30,12 @@ public final class ResteasyDotNames { public static final DotName PATH = DotName.createSimple("jakarta.ws.rs.Path"); public static final DotName DYNAMIC_FEATURE = DotName.createSimple("jakarta.ws.rs.container.DynamicFeature"); public static final DotName CONTEXT = DotName.createSimple("jakarta.ws.rs.core.Context"); + public static final DotName PATH_PARAM = DotName.createSimple("jakarta.ws.rs.PathParam"); + public static final DotName QUERY_PARAM = DotName.createSimple("jakarta.ws.rs.QueryParam"); + public static final DotName HEADER_PARAM = DotName.createSimple("jakarta.ws.rs.HeaderParam"); + public static final DotName FORM_PARAM = DotName.createSimple("jakarta.ws.rs.FormParam"); + public static final DotName MATRIX_PARAM = DotName.createSimple("jakarta.ws.rs.MatrixParam"); + public static final DotName COOKIE_PARAM = DotName.createSimple("jakarta.ws.rs.CookieParam"); public static final DotName RESTEASY_QUERY_PARAM = DotName .createSimple("org.jboss.resteasy.annotations.jaxrs.QueryParam"); public static final DotName RESTEASY_FORM_PARAM = DotName diff --git a/extensions/resteasy-classic/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyServerCommonProcessor.java b/extensions/resteasy-classic/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyServerCommonProcessor.java index 55b74364195db..c270f20592375 100644 --- a/extensions/resteasy-classic/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyServerCommonProcessor.java +++ b/extensions/resteasy-classic/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyServerCommonProcessor.java @@ -1,5 +1,12 @@ package io.quarkus.resteasy.server.common.deployment; +import static io.quarkus.resteasy.common.spi.ResteasyDotNames.CONTEXT; +import static io.quarkus.resteasy.common.spi.ResteasyDotNames.COOKIE_PARAM; +import static io.quarkus.resteasy.common.spi.ResteasyDotNames.FORM_PARAM; +import static io.quarkus.resteasy.common.spi.ResteasyDotNames.HEADER_PARAM; +import static io.quarkus.resteasy.common.spi.ResteasyDotNames.MATRIX_PARAM; +import static io.quarkus.resteasy.common.spi.ResteasyDotNames.PATH_PARAM; +import static io.quarkus.resteasy.common.spi.ResteasyDotNames.QUERY_PARAM; import static io.quarkus.runtime.annotations.ConfigPhase.BUILD_TIME; import java.lang.reflect.InvocationTargetException; @@ -422,6 +429,15 @@ public void build( resteasyServerConfig.produce(new ResteasyServerConfigBuildItem(rootPath, path, resteasyInitParameters)); + Set restConstructorAnnotations = Set.of( + CONTEXT, + PATH_PARAM, + QUERY_PARAM, + HEADER_PARAM, + FORM_PARAM, + MATRIX_PARAM, + COOKIE_PARAM); + Set autoInjectAnnotationNames = autoInjectAnnotations.stream().flatMap(a -> a.getAnnotationNames().stream()) .collect(Collectors.toSet()); annotationsTransformer.produce(new AnnotationsTransformerBuildItem(new AnnotationsTransformer() { @@ -436,10 +452,34 @@ public void transform(TransformationContext context) { ClassInfo clazz = context.getTarget().asClass(); if (clazz.declaredAnnotation(ResteasyDotNames.PATH) != null) { // Root resources - no need to add scope, @Path is a bean defining annotation + Transformation transformation = context.transform(); if (clazz.declaredAnnotation(DotNames.TYPED) == null) { // Add @Typed(MyResource.class) - context.transform().add(createTypedAnnotationInstance(clazz)).done(); + transformation.add(createTypedAnnotationInstance(clazz)); + } + + // If we find a constructor with REST annotations it should be created by RESTEasy and not CDI, so we Veto + // https://issues.redhat.com/browse/RESTEASY-1538 + // https://issues.redhat.com/browse/RESTEASY-2183 + // https://github.com/jakartaee/rest/issues/633 + // https://github.com/jakartaee/rest/issues/938 + boolean restConstructorFound = false; + outer: for (MethodInfo constructor : clazz.constructors()) { + for (MethodParameterInfo parameter : constructor.parameters()) { + for (DotName paramAnnotation : restConstructorAnnotations) { + if (parameter.hasAnnotation(paramAnnotation)) { + restConstructorFound = true; + break outer; + } + } + } } + + if (restConstructorFound) { + transformation.add(DotNames.VETOED); + } + + transformation.done(); return; } if (scopes.isScopeIn(context.getAnnotations())) { @@ -472,6 +512,19 @@ public void transform(TransformationContext context) { // Add @Typed(MySubresource.class) transformation.add(createTypedAnnotationInstance(clazz)); } + // Force constructors with arguments to also include @Inject, because sub-resources can be managed by the user + if (!clazz.hasNoArgsConstructor()) { + boolean hasInject = false; + for (final MethodInfo constructor : clazz.constructors()) { + if (constructor.hasAnnotation(DotNames.INJECT)) { + hasInject = true; + break; + } + } + if (!hasInject) { + transformation.add(DotNames.VETOED); + } + } transformation.done(); } } @@ -645,7 +698,7 @@ private static void registerProviders(ResteasyDeployment deployment, ClassInfo classInfo = index.getClassByName(DotName.createSimple(providerToRegister)); boolean includeFields = false; if (classInfo != null) { - includeFields = classInfo.annotationsMap().containsKey(ResteasyDotNames.CONTEXT); + includeFields = classInfo.annotationsMap().containsKey(CONTEXT); } reflectiveClass.produce(new ReflectiveClassBuildItem(false, includeFields, providerToRegister)); } @@ -769,7 +822,7 @@ private static void checkParameterNames(IndexView index, private static void registerContextProxyDefinitions(IndexView index, BuildProducer proxyDefinition) { // @Context uses proxies for interface injection - for (AnnotationInstance annotation : index.getAnnotations(ResteasyDotNames.CONTEXT)) { + for (AnnotationInstance annotation : index.getAnnotations(CONTEXT)) { Type annotatedType = null; if (annotation.target().kind() == AnnotationTarget.Kind.METHOD) { MethodInfo method = annotation.target().asMethod(); @@ -870,7 +923,7 @@ private static void scanMethods(DotName annotationType, for (short i = 0; i < method.parametersCount(); i++) { Type parameterType = method.parameterType(i); - if (!hasAnnotation(method, i, ResteasyDotNames.CONTEXT)) { + if (!hasAnnotation(method, i, CONTEXT)) { reflectiveHierarchy.produce(new ReflectiveHierarchyBuildItem.Builder() .type(parameterType) .index(index)