diff --git a/extensions/resteasy-reactive/quarkus-jaxrs-client/deployment/src/main/java/io/quarkus/resteasy/reactive/client/deployment/JaxrsClientProcessor.java b/extensions/resteasy-reactive/quarkus-jaxrs-client/deployment/src/main/java/io/quarkus/resteasy/reactive/client/deployment/JaxrsClientProcessor.java index e555cbaacccd9..56b161214d2c8 100644 --- a/extensions/resteasy-reactive/quarkus-jaxrs-client/deployment/src/main/java/io/quarkus/resteasy/reactive/client/deployment/JaxrsClientProcessor.java +++ b/extensions/resteasy-reactive/quarkus-jaxrs-client/deployment/src/main/java/io/quarkus/resteasy/reactive/client/deployment/JaxrsClientProcessor.java @@ -8,6 +8,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.function.Function; import javax.ws.rs.RuntimeType; @@ -73,7 +74,7 @@ void setupClientProxies(ResteasyReactiveClientRecorder recorder, List messageBodyReaderBuildItems, List messageBodyWriterBuildItems, BeanArchiveIndexBuildItem beanArchiveIndexBuildItem, - ResourceScanningResultBuildItem resourceScanningResultBuildItem, + Optional resourceScanningResultBuildItem, ResteasyReactiveConfig config, RecorderContext recorderContext, BuildProducer generatedClassBuildItemBuildProducer, @@ -85,12 +86,12 @@ void setupClientProxies(ResteasyReactiveClientRecorder recorder, messageBodyWriterBuildItems, beanContainerBuildItem, applicationResultBuildItem, serialisers, RuntimeType.CLIENT); - if (resourceScanningResultBuildItem == null - || resourceScanningResultBuildItem.getResult().getPathInterfaces().isEmpty()) { + if (!resourceScanningResultBuildItem.isPresent() + || resourceScanningResultBuildItem.get().getResult().getPathInterfaces().isEmpty()) { recorder.setupClientProxies(new HashMap<>()); return; } - ResourceScanningResult result = resourceScanningResultBuildItem.getResult(); + ResourceScanningResult result = resourceScanningResultBuildItem.get().getResult(); AdditionalReaders additionalReaders = new AdditionalReaders(); AdditionalWriters additionalWriters = new AdditionalWriters(); diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ResteasyReactiveCommonProcessor.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ResteasyReactiveCommonProcessor.java index fb09cb98e8986..e01db0be5ed87 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ResteasyReactiveCommonProcessor.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ResteasyReactiveCommonProcessor.java @@ -52,12 +52,12 @@ public class ResteasyReactiveCommonProcessor { @BuildStep void setUpDenyAllJaxRs(CombinedIndexBuildItem index, JaxRsSecurityConfig config, - ResourceScanningResultBuildItem resteasyDeployment, + Optional resteasyDeployment, BuildProducer additionalSecuredClasses) { - if (config.denyJaxRs) { + if (config.denyJaxRs && resteasyDeployment.isPresent()) { final List classes = new ArrayList<>(); - Set resourceClasses = resteasyDeployment.getResult().getScannedResourcePaths().keySet(); + Set resourceClasses = resteasyDeployment.get().getResult().getScannedResourcePaths().keySet(); for (DotName className : resourceClasses) { ClassInfo classInfo = index.getIndex().getClassByName(className); if (!SecurityTransformerUtils.hasSecurityAnnotation(classInfo)) { diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveCDIProcessor.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveCDIProcessor.java index 10333d55536c6..ba5865a8355eb 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveCDIProcessor.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveCDIProcessor.java @@ -1,11 +1,17 @@ package io.quarkus.resteasy.reactive.server.deployment; +import java.lang.reflect.Modifier; +import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.Optional; import javax.ws.rs.BeanParam; +import org.jboss.jandex.ClassInfo; import org.jboss.jandex.DotName; import org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames; +import org.jboss.resteasy.reactive.common.processor.scanning.ResourceScanningResult; import org.jboss.resteasy.reactive.server.injection.ContextProducers; import io.quarkus.arc.deployment.AdditionalBeanBuildItem; @@ -15,6 +21,7 @@ import io.quarkus.arc.processor.DotNames; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.resteasy.reactive.common.deployment.ResourceScanningResultBuildItem; import io.quarkus.resteasy.reactive.server.runtime.QuarkusContextProducers; import io.quarkus.resteasy.reactive.spi.DynamicFeatureBuildItem; import io.quarkus.resteasy.reactive.spi.JaxrsFeatureBuildItem; @@ -43,6 +50,38 @@ void beanDefiningAnnotations(BuildProducer bean BuiltinScope.SINGLETON.getName())); } + // when an interface is annotated with @Path and there is only one implementation of it that is not annotated with @Path, + // we need to make this class a bean. See https://github.com/quarkusio/quarkus/issues/15028 + @BuildStep + void pathInterfaceImpls(Optional resourceScanningResultBuildItem, + BuildProducer additionalBeanBuildItemBuildProducer) { + if (!resourceScanningResultBuildItem.isPresent()) { + return; + } + ResourceScanningResult resourceScanningResult = resourceScanningResultBuildItem.get().getResult(); + Map pathInterfaces = resourceScanningResult.getPathInterfaces(); + List impls = new ArrayList<>(); + for (Map.Entry i : pathInterfaces.entrySet()) { + List candidateBeans = new ArrayList<>(1); + for (ClassInfo clazz : resourceScanningResult.getIndex().getAllKnownImplementors(i.getKey())) { + if (!Modifier.isAbstract(clazz.flags())) { + if ((clazz.enclosingClass() == null || Modifier.isStatic(clazz.flags())) && + clazz.enclosingMethod() == null) { + candidateBeans.add(clazz); + } + } + } + if (candidateBeans.size() == 1) { + impls.add(candidateBeans.get(0).name().toString()); + } + } + if (!impls.isEmpty()) { + additionalBeanBuildItemBuildProducer + .produce(AdditionalBeanBuildItem.builder().setUnremovable().addBeanClasses(impls.toArray(new String[0])) + .build()); + } + } + @BuildStep void additionalBeans(List additionalDynamicFeatures, List featureBuildItems, diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/InterfaceResource.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/InterfaceResource.java new file mode 100644 index 0000000000000..c1871daa1da30 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/InterfaceResource.java @@ -0,0 +1,14 @@ +package io.quarkus.resteasy.reactive.server.test.simple; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +@Path("iface") +public interface InterfaceResource { + + @GET + @Produces(MediaType.TEXT_PLAIN) + String hello(); +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/InterfaceResourceImpl.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/InterfaceResourceImpl.java new file mode 100644 index 0000000000000..e7943d12c2f94 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/InterfaceResourceImpl.java @@ -0,0 +1,14 @@ +package io.quarkus.resteasy.reactive.server.test.simple; + +import javax.inject.Inject; + +public class InterfaceResourceImpl implements InterfaceResource { + + @Inject + HelloService helloService; + + @Override + public String hello() { + return helloService.sayHello(); + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/SimpleQuarkusRestTestCase.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/SimpleQuarkusRestTestCase.java index 9df63331690a5..0af962e3c270f 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/SimpleQuarkusRestTestCase.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/SimpleQuarkusRestTestCase.java @@ -44,7 +44,7 @@ public JavaArchive get() { ParameterWithFromString.class, BeanParamSubClass.class, FieldInjectedSubClassResource.class, BeanParamSuperClass.class, IllegalClassExceptionMapper.class, MyParameterProvider.class, MyParameterConverter.class, MyParameter.class, - NewParamsRestResource.class); + NewParamsRestResource.class, InterfaceResource.class, InterfaceResourceImpl.class); } }); @@ -413,4 +413,10 @@ public void bigDecimal() { RestAssured.get("/simple/bigDecimal/1.0") .then().statusCode(200).body(Matchers.equalTo("1.0")); } + + @Test + public void testInterfaceResource() { + RestAssured.get("/iface") + .then().statusCode(200).body(Matchers.equalTo("Hello")); + } } diff --git a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/scanning/ResourceScanningResult.java b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/scanning/ResourceScanningResult.java index ccdb28231fe64..f2c01c69d356f 100644 --- a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/scanning/ResourceScanningResult.java +++ b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/scanning/ResourceScanningResult.java @@ -5,10 +5,12 @@ import java.util.Set; import org.jboss.jandex.ClassInfo; import org.jboss.jandex.DotName; +import org.jboss.jandex.IndexView; import org.jboss.jandex.MethodInfo; public final class ResourceScanningResult { + private final IndexView index; final Map scannedResources; final Map scannedResourcePaths; final Map possibleSubResources; @@ -18,10 +20,12 @@ public final class ResourceScanningResult { final Map httpAnnotationToMethod; final List classLevelExceptionMappers; - public ResourceScanningResult(Map scannedResources, Map scannedResourcePaths, + public ResourceScanningResult(IndexView index, Map scannedResources, + Map scannedResourcePaths, Map possibleSubResources, Map pathInterfaces, Map resourcesThatNeedCustomProducer, Set beanParams, Map httpAnnotationToMethod, List classLevelExceptionMappers) { + this.index = index; this.scannedResources = scannedResources; this.scannedResourcePaths = scannedResourcePaths; this.possibleSubResources = possibleSubResources; @@ -32,6 +36,10 @@ public ResourceScanningResult(Map scannedResources, Map getScannedResources() { return scannedResources; } diff --git a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/scanning/ResteasyReactiveScanner.java b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/scanning/ResteasyReactiveScanner.java index c37f18504f718..7b213a3d6590d 100644 --- a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/scanning/ResteasyReactiveScanner.java +++ b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/scanning/ResteasyReactiveScanner.java @@ -186,7 +186,7 @@ public static ResourceScanningResult scanResources( } httpAnnotationToMethod.put(httpMethodInstance.target().asClass().name(), httpMethodInstance.value().asString()); } - return new ResourceScanningResult(scannedResources, + return new ResourceScanningResult(index, scannedResources, scannedResourcePaths, possibleSubResources, pathInterfaces, resourcesThatNeedCustomProducer, beanParams, httpAnnotationToMethod, methodExceptionMappers); }