From 54ee32272cd5e387bcc0c58511608c6ad476f98c Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Wed, 1 Sep 2021 17:35:41 +0300 Subject: [PATCH] Replace runtime reflection usage in rest-links with build time metadata capturing --- .../reactive/links/deployment/DotNames.java | 15 ++++ .../deployment/LinksContainerFactory.java | 4 +- .../links/deployment/LinksMethodScanner.java | 59 +++++++++++++ .../links/deployment/LinksProcessor.java | 13 ++- .../reactive/links/RestLinksHandler.java | 82 +++++++++++++++++++ .../links/RestLinksResponseFilter.java | 76 ----------------- 6 files changed, 163 insertions(+), 86 deletions(-) create mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/DotNames.java create mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/LinksMethodScanner.java create mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/RestLinksHandler.java delete mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/RestLinksResponseFilter.java diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/DotNames.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/DotNames.java new file mode 100644 index 0000000000000..4a493e9584a46 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/DotNames.java @@ -0,0 +1,15 @@ +package io.quarkus.resteasy.reactive.links.deployment; + +import org.jboss.jandex.DotName; + +import io.quarkus.resteasy.reactive.links.InjectRestLinks; +import io.quarkus.resteasy.reactive.links.RestLink; + +final class DotNames { + + static final DotName INJECT_REST_LINKS_ANNOTATION = DotName.createSimple(InjectRestLinks.class.getName()); + static final DotName REST_LINK_ANNOTATION = DotName.createSimple(RestLink.class.getName()); + + private DotNames() { + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/LinksContainerFactory.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/LinksContainerFactory.java index 0a28fd7382997..6e9142867e568 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/LinksContainerFactory.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/LinksContainerFactory.java @@ -24,8 +24,6 @@ final class LinksContainerFactory { - private static final DotName REST_LINK_ANNOTATION = DotName.createSimple(RestLink.class.getName()); - private final IndexView index; LinksContainerFactory(IndexView index) { @@ -41,7 +39,7 @@ LinksContainer getLinksContainer(List resourceClasses) { for (ResourceClass resourceClass : resourceClasses) { for (ResourceMethod resourceMethod : resourceClass.getMethods()) { MethodInfo resourceMethodInfo = getResourceMethodInfo(resourceClass, resourceMethod); - AnnotationInstance restLinkAnnotation = resourceMethodInfo.annotation(REST_LINK_ANNOTATION); + AnnotationInstance restLinkAnnotation = resourceMethodInfo.annotation(DotNames.REST_LINK_ANNOTATION); if (restLinkAnnotation != null) { LinkInfo linkInfo = getLinkInfo(resourceClass, resourceMethod, resourceMethodInfo, restLinkAnnotation); diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/LinksMethodScanner.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/LinksMethodScanner.java new file mode 100644 index 0000000000000..59d54071640ad --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/LinksMethodScanner.java @@ -0,0 +1,59 @@ +package io.quarkus.resteasy.reactive.links.deployment; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.jboss.jandex.AnnotationInstance; +import org.jboss.jandex.AnnotationValue; +import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.MethodInfo; +import org.jboss.resteasy.reactive.server.model.FixedHandlerChainCustomizer; +import org.jboss.resteasy.reactive.server.model.HandlerChainCustomizer; +import org.jboss.resteasy.reactive.server.processor.scanning.MethodScanner; + +import io.quarkus.resteasy.reactive.links.RestLinkType; +import io.quarkus.resteasy.reactive.links.RestLinksHandler; + +public class LinksMethodScanner implements MethodScanner { + + @Override + public List scan(MethodInfo method, ClassInfo actualEndpointClass, + Map methodContext) { + AnnotationInstance injectRestLinksInstance = getInjectRestLinksAnnotation(method, actualEndpointClass); + if (injectRestLinksInstance == null) { + return Collections.emptyList(); + } + + RestLinkType restLinkType = RestLinkType.TYPE; + AnnotationValue injectRestLinksValue = injectRestLinksInstance.value(); + if (injectRestLinksValue != null) { + restLinkType = RestLinkType.valueOf(injectRestLinksValue.asEnum()); + } + + AnnotationInstance restLinkInstance = method.annotation(DotNames.REST_LINK_ANNOTATION); + String entityType = null; + if (restLinkInstance != null) { + AnnotationValue restInstanceValue = restLinkInstance.value("entityType"); + if (restInstanceValue != null) { + entityType = restInstanceValue.asClass().name().toString(); + } + } + + RestLinksHandler handler = new RestLinksHandler(); + handler.setRestLinkData(new RestLinksHandler.RestLinkData(restLinkType, entityType)); + return Collections.singletonList(new FixedHandlerChainCustomizer(handler, + HandlerChainCustomizer.Phase.AFTER_RESPONSE_CREATED)); + } + + private AnnotationInstance getInjectRestLinksAnnotation(MethodInfo method, ClassInfo actualEndpointClass) { + AnnotationInstance annotationInstance = method.annotation(DotNames.INJECT_REST_LINKS_ANNOTATION); + if (annotationInstance == null) { + annotationInstance = method.declaringClass().classAnnotation(DotNames.INJECT_REST_LINKS_ANNOTATION); + if ((annotationInstance == null) && !actualEndpointClass.equals(method.declaringClass())) { + annotationInstance = actualEndpointClass.classAnnotation(DotNames.INJECT_REST_LINKS_ANNOTATION); + } + } + return annotationInstance; + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/LinksProcessor.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/LinksProcessor.java index c3733000d26f1..380e9086b5598 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/LinksProcessor.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/LinksProcessor.java @@ -22,7 +22,6 @@ import io.quarkus.deployment.builditem.GeneratedClassBuildItem; import io.quarkus.gizmo.ClassOutput; import io.quarkus.resteasy.reactive.common.deployment.JaxRsResourceIndexBuildItem; -import io.quarkus.resteasy.reactive.links.RestLinksResponseFilter; import io.quarkus.resteasy.reactive.links.runtime.GetterAccessorsContainer; import io.quarkus.resteasy.reactive.links.runtime.GetterAccessorsContainerRecorder; import io.quarkus.resteasy.reactive.links.runtime.LinkInfo; @@ -30,7 +29,7 @@ import io.quarkus.resteasy.reactive.links.runtime.LinksProviderRecorder; import io.quarkus.resteasy.reactive.links.runtime.RestLinksProviderProducer; import io.quarkus.resteasy.reactive.server.deployment.ResteasyReactiveDeploymentInfoBuildItem; -import io.quarkus.resteasy.reactive.spi.CustomContainerResponseFilterBuildItem; +import io.quarkus.resteasy.reactive.server.spi.MethodScannerBuildItem; import io.quarkus.runtime.RuntimeValue; final class LinksProcessor { @@ -42,6 +41,11 @@ void feature(BuildProducer feature) { feature.produce(new FeatureBuildItem(Feature.RESTEASY_REACTIVE_LINKS)); } + @BuildStep + MethodScannerBuildItem linksSupport() { + return new MethodScannerBuildItem(new LinksMethodScanner()); + } + @BuildStep @Record(STATIC_INIT) void initializeLinksProvider(JaxRsResourceIndexBuildItem indexBuildItem, @@ -68,11 +72,6 @@ AdditionalBeanBuildItem registerRestLinksProviderProducer() { return AdditionalBeanBuildItem.unremovableOf(RestLinksProviderProducer.class); } - @BuildStep - CustomContainerResponseFilterBuildItem registerRestLinksResponseFilter() { - return new CustomContainerResponseFilterBuildItem(RestLinksResponseFilter.class.getName()); - } - private LinksContainer getLinksContainer(IndexView index, ResteasyReactiveDeploymentInfoBuildItem deploymentInfoBuildItem) { LinksContainerFactory linksContainerFactory = new LinksContainerFactory(index); diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/RestLinksHandler.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/RestLinksHandler.java new file mode 100644 index 0000000000000..10c2e24571c8e --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/RestLinksHandler.java @@ -0,0 +1,82 @@ +package io.quarkus.resteasy.reactive.links; + +import java.util.Collection; + +import javax.ws.rs.core.Link; +import javax.ws.rs.core.Response; + +import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext; +import org.jboss.resteasy.reactive.server.spi.ServerRestHandler; + +import io.quarkus.arc.Arc; + +public class RestLinksHandler implements ServerRestHandler { + + private RestLinkData restLinkData; + + public RestLinkData getRestLinkData() { + return restLinkData; + } + + public void setRestLinkData(RestLinkData restLinkData) { + this.restLinkData = restLinkData; + } + + @Override + public void handle(ResteasyReactiveRequestContext context) { + Response response = context.getResponse().get(); + for (Link link : getLinks(response)) { + response.getHeaders().add("Link", link); + } + } + + private Collection getLinks(Response response) { + if ((restLinkData.getRestLinkType() == RestLinkType.INSTANCE) && response.hasEntity()) { + return getTestLinksProvider().getInstanceLinks(response.getEntity()); + } + return getTestLinksProvider() + .getTypeLinks(restLinkData.getEntityType() != null ? entityTypeClass() : response.getEntity().getClass()); + } + + private Class entityTypeClass() { + try { + return Thread.currentThread().getContextClassLoader().loadClass(restLinkData.getEntityType()); + } catch (ClassNotFoundException e) { + throw new IllegalStateException("Unable load class '" + restLinkData.getEntityType() + "'", e); + } + } + + private RestLinksProvider getTestLinksProvider() { + return Arc.container().instance(RestLinksProvider.class).get(); + } + + public static class RestLinkData { + + public RestLinkData(RestLinkType restLinkType, String entityType) { + this.restLinkType = restLinkType; + this.entityType = entityType; + } + + public RestLinkData() { + } + + private RestLinkType restLinkType; + private String entityType; + + public RestLinkType getRestLinkType() { + return restLinkType; + } + + public void setRestLinkType(RestLinkType restLinkType) { + this.restLinkType = restLinkType; + } + + public String getEntityType() { + return entityType; + } + + public void setEntityType(String entityType) { + this.entityType = entityType; + } + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/RestLinksResponseFilter.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/RestLinksResponseFilter.java deleted file mode 100644 index 09b275d0d7a15..0000000000000 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/RestLinksResponseFilter.java +++ /dev/null @@ -1,76 +0,0 @@ -package io.quarkus.resteasy.reactive.links; - -import java.lang.annotation.Annotation; -import java.util.Collection; -import java.util.Collections; - -import javax.ws.rs.container.ContainerResponseContext; -import javax.ws.rs.container.ResourceInfo; -import javax.ws.rs.core.Link; - -import org.jboss.resteasy.reactive.server.ServerResponseFilter; -import org.jboss.resteasy.reactive.server.spi.ResteasyReactiveResourceInfo; - -public class RestLinksResponseFilter { - - private final RestLinksProvider linksProvider; - - public RestLinksResponseFilter(RestLinksProvider linksProvider) { - this.linksProvider = linksProvider; - } - - @ServerResponseFilter - public void filter(ResourceInfo resourceInfo, ContainerResponseContext responseContext) { - if (!(resourceInfo instanceof ResteasyReactiveResourceInfo)) { - return; - } - for (Link link : getLinks((ResteasyReactiveResourceInfo) resourceInfo, responseContext)) { - responseContext.getHeaders().add("Link", link); - } - } - - private Collection getLinks(ResteasyReactiveResourceInfo resourceInfo, - ContainerResponseContext responseContext) { - InjectRestLinks injectRestLinksAnnotation = getInjectRestLinksAnnotation(resourceInfo); - if (injectRestLinksAnnotation == null) { - return Collections.emptyList(); - } - - if (injectRestLinksAnnotation.value() == RestLinkType.INSTANCE && responseContext.hasEntity()) { - return linksProvider.getInstanceLinks(responseContext.getEntity()); - } - - return linksProvider.getTypeLinks(getEntityType(resourceInfo, responseContext)); - } - - private InjectRestLinks getInjectRestLinksAnnotation(ResteasyReactiveResourceInfo resourceInfo) { - if (resourceInfo.getMethodAnnotationNames().contains(InjectRestLinks.class.getName())) { - for (Annotation annotation : resourceInfo.getAnnotations()) { - if (annotation instanceof InjectRestLinks) { - return (InjectRestLinks) annotation; - } - } - } - if (resourceInfo.getClassAnnotationNames().contains(InjectRestLinks.class.getName())) { - for (Annotation annotation : resourceInfo.getClassAnnotations()) { - if (annotation instanceof InjectRestLinks) { - return (InjectRestLinks) annotation; - } - } - } - return null; - } - - private Class getEntityType(ResteasyReactiveResourceInfo resourceInfo, - ContainerResponseContext responseContext) { - for (Annotation annotation : resourceInfo.getAnnotations()) { - if (annotation instanceof RestLink) { - Class entityType = ((RestLink) annotation).entityType(); - if (entityType != Object.class) { - return entityType; - } - } - } - return responseContext.getEntityClass(); - } -}