Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace runtime reflection usage in rest-links with build time metadata capturing #19839

Merged
merged 1 commit into from
Sep 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.