Skip to content

Commit

Permalink
Merge pull request quarkusio#19839 from geoand/rest-links-handler
Browse files Browse the repository at this point in the history
Replace runtime reflection usage in rest-links with build time metadata capturing
  • Loading branch information
stuartwdouglas authored Sep 2, 2021
2 parents 94ae125 + 54ee322 commit 5a9b3cc
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 86 deletions.
Original file line number Diff line number Diff line change
@@ -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() {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -41,7 +39,7 @@ LinksContainer getLinksContainer(List<ResourceClass> 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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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<HandlerChainCustomizer> scan(MethodInfo method, ClassInfo actualEndpointClass,
Map<String, Object> 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,14 @@
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;
import io.quarkus.resteasy.reactive.links.runtime.LinksContainer;
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 {
Expand All @@ -42,6 +41,11 @@ void feature(BuildProducer<FeatureBuildItem> 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,
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Link> 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;
}
}
}

This file was deleted.

0 comments on commit 5a9b3cc

Please sign in to comment.