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

Refactor to use new SignatureBuilder API by Gizmo in REST Data Panache #30554

Merged
merged 1 commit into from
Jan 24, 2023
Merged
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
@@ -1,7 +1,9 @@
package io.quarkus.rest.data.panache.deployment.methods;

import static io.quarkus.gizmo.MethodDescriptor.ofMethod;
import static io.quarkus.rest.data.panache.deployment.utils.SignatureMethodCreator.ofType;
import static io.quarkus.rest.data.panache.deployment.utils.SignatureMethodCreator.param;
import static io.quarkus.rest.data.panache.deployment.utils.SignatureMethodCreator.responseType;
import static io.quarkus.rest.data.panache.deployment.utils.SignatureMethodCreator.uniType;

import javax.validation.Valid;
import javax.ws.rs.core.Response;
@@ -102,9 +104,8 @@ public AddMethodImplementor(Capabilities capabilities) {
protected void implementInternal(ClassCreator classCreator, ResourceMetadata resourceMetadata,
ResourceProperties resourceProperties, FieldDescriptor resourceField) {
MethodCreator methodCreator = SignatureMethodCreator.getMethodCreator(METHOD_NAME, classCreator,
isNotReactivePanache() ? ofType(Response.class) : ofType(Uni.class, resourceMetadata.getEntityType()),
resourceMetadata.getEntityType(), UriInfo.class);
methodCreator.setParameterNames(new String[] { "entity", "uriInfo" });
isNotReactivePanache() ? responseType() : uniType(resourceMetadata.getEntityType()),
param("entity", resourceMetadata.getEntityType()), param("uriInfo", UriInfo.class));

// Add method annotations
addPathAnnotation(methodCreator, resourceProperties.getPath(RESOURCE_METHOD_NAME));
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package io.quarkus.rest.data.panache.deployment.methods;

import static io.quarkus.gizmo.MethodDescriptor.ofMethod;
import static io.quarkus.rest.data.panache.deployment.utils.SignatureMethodCreator.ofType;
import static io.quarkus.rest.data.panache.deployment.utils.SignatureMethodCreator.responseType;
import static io.quarkus.rest.data.panache.deployment.utils.SignatureMethodCreator.uniType;

import javax.ws.rs.core.Response;

@@ -74,7 +75,7 @@ protected void implementInternal(ClassCreator classCreator, ResourceMetadata res
ResourceProperties resourceProperties, FieldDescriptor resourceField) {
// Method parameters: sort strings, page index, page size, uri info
MethodCreator methodCreator = SignatureMethodCreator.getMethodCreator(RESOURCE_METHOD_NAME, classCreator,
isNotReactivePanache() ? ofType(Response.class) : ofType(Uni.class, Long.class));
isNotReactivePanache() ? responseType() : uniType(Long.class));

// Add method annotations
addGetAnnotation(methodCreator);
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package io.quarkus.rest.data.panache.deployment.methods;

import static io.quarkus.gizmo.MethodDescriptor.ofMethod;
import static io.quarkus.rest.data.panache.deployment.utils.SignatureMethodCreator.ofType;
import static io.quarkus.rest.data.panache.deployment.utils.SignatureMethodCreator.param;
import static io.quarkus.rest.data.panache.deployment.utils.SignatureMethodCreator.responseType;
import static io.quarkus.rest.data.panache.deployment.utils.SignatureMethodCreator.uniType;

import javax.ws.rs.core.Response;

@@ -82,9 +84,8 @@ public DeleteMethodImplementor(Capabilities capabilities) {
protected void implementInternal(ClassCreator classCreator, ResourceMetadata resourceMetadata,
ResourceProperties resourceProperties, FieldDescriptor resourceField) {
MethodCreator methodCreator = SignatureMethodCreator.getMethodCreator(METHOD_NAME, classCreator,
isNotReactivePanache() ? ofType(Response.class) : ofType(Uni.class, resourceMetadata.getEntityType()),
resourceMetadata.getIdType());
methodCreator.setParameterNames(new String[] { "id" });
isNotReactivePanache() ? responseType() : uniType(resourceMetadata.getEntityType()),
param("id", resourceMetadata.getIdType()));

// Add method annotations
addPathAnnotation(methodCreator, appendToPath(resourceProperties.getPath(RESOURCE_METHOD_NAME), "{id}"));
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package io.quarkus.rest.data.panache.deployment.methods;

import static io.quarkus.gizmo.MethodDescriptor.ofMethod;
import static io.quarkus.rest.data.panache.deployment.utils.SignatureMethodCreator.ofType;
import static io.quarkus.rest.data.panache.deployment.utils.SignatureMethodCreator.param;
import static io.quarkus.rest.data.panache.deployment.utils.SignatureMethodCreator.responseType;
import static io.quarkus.rest.data.panache.deployment.utils.SignatureMethodCreator.uniType;

import javax.ws.rs.core.Response;

@@ -84,9 +86,8 @@ public GetMethodImplementor(Capabilities capabilities) {
protected void implementInternal(ClassCreator classCreator, ResourceMetadata resourceMetadata,
ResourceProperties resourceProperties, FieldDescriptor resourceField) {
MethodCreator methodCreator = SignatureMethodCreator.getMethodCreator(METHOD_NAME, classCreator,
isNotReactivePanache() ? ofType(Response.class) : ofType(Uni.class, resourceMetadata.getEntityType()),
resourceMetadata.getIdType());
methodCreator.setParameterNames(new String[] { "id" });
isNotReactivePanache() ? responseType() : uniType(resourceMetadata.getEntityType()),
param("id", resourceMetadata.getIdType()));

// Add method annotations
addPathAnnotation(methodCreator, appendToPath(resourceProperties.getPath(RESOURCE_METHOD_NAME), "{id}"));
Original file line number Diff line number Diff line change
@@ -9,10 +9,12 @@
import static io.quarkus.arc.processor.DotNames.STRING;
import static io.quarkus.gizmo.MethodDescriptor.ofConstructor;
import static io.quarkus.gizmo.MethodDescriptor.ofMethod;
import static io.quarkus.gizmo.Type.intType;
import static io.quarkus.rest.data.panache.deployment.utils.PaginationImplementor.DEFAULT_PAGE_INDEX;
import static io.quarkus.rest.data.panache.deployment.utils.PaginationImplementor.DEFAULT_PAGE_SIZE;
import static io.quarkus.rest.data.panache.deployment.utils.SignatureMethodCreator.ofType;
import static io.quarkus.rest.data.panache.deployment.utils.SignatureMethodCreator.param;
import static io.quarkus.rest.data.panache.deployment.utils.SignatureMethodCreator.responseType;
import static io.quarkus.rest.data.panache.deployment.utils.SignatureMethodCreator.uniType;

import java.util.ArrayList;
import java.util.Collection;
@@ -173,14 +175,14 @@ private void implementPaged(ClassCreator classCreator, ResourceMetadata resource
Collection<SignatureMethodCreator.Parameter> compatibleFieldsForQuery = getFieldsToQuery(resourceMetadata);
List<SignatureMethodCreator.Parameter> parameters = new ArrayList<>();
parameters.add(param("sort", List.class));
parameters.add(param("page", int.class));
parameters.add(param("size", int.class));
parameters.add(param("page", int.class, intType()));
parameters.add(param("size", int.class, intType()));
parameters.add(param("uriInfo", UriInfo.class));
parameters.add(param("namedQuery", String.class));
parameters.addAll(compatibleFieldsForQuery);
MethodCreator methodCreator = SignatureMethodCreator.getMethodCreator(getMethodName(), classCreator,
isNotReactivePanache() ? ofType(Response.class) : ofType(Uni.class, resourceMetadata.getEntityType()),
parameters);
isNotReactivePanache() ? responseType() : uniType(resourceMetadata.getEntityType()),
parameters.toArray(new SignatureMethodCreator.Parameter[0]));

// Add method annotations
addGetAnnotation(methodCreator);
@@ -265,8 +267,8 @@ private void implementNotPaged(ClassCreator classCreator, ResourceMetadata resou
parameters.add(param("namedQuery", String.class));
parameters.addAll(compatibleFieldsForQuery);
MethodCreator methodCreator = SignatureMethodCreator.getMethodCreator(getMethodName(), classCreator,
isNotReactivePanache() ? ofType(Response.class) : ofType(Uni.class, resourceMetadata.getEntityType()),
parameters);
isNotReactivePanache() ? responseType() : uniType(resourceMetadata.getEntityType()),
parameters.toArray(new SignatureMethodCreator.Parameter[0]));

// Add method annotations
addGetAnnotation(methodCreator);
Original file line number Diff line number Diff line change
@@ -27,6 +27,7 @@
import io.quarkus.gizmo.ClassCreator;
import io.quarkus.gizmo.FieldDescriptor;
import io.quarkus.gizmo.TryBlock;
import io.quarkus.gizmo.Type;
import io.quarkus.rest.data.panache.RestDataPanacheException;
import io.quarkus.rest.data.panache.deployment.ResourceMetadata;
import io.quarkus.rest.data.panache.deployment.properties.ResourceProperties;
@@ -235,6 +236,19 @@ protected boolean isNotReactivePanache() {
return !capabilities.isPresent(Capability.HIBERNATE_REACTIVE);
}

public static Type toType(Object object) {
if (object instanceof Type) {
return (Type) object;
} else if (object instanceof String) {
return Type.classType((String) object);
} else if (object instanceof Class) {
return Type.classType((Class<?>) object);
}

throw new IllegalArgumentException("Unsupported object of type " + object.getClass()
+ ". Supported types are Type, String and Class");
}

private static Class<?> toClass(String className) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
try {
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package io.quarkus.rest.data.panache.deployment.methods;

import static io.quarkus.gizmo.MethodDescriptor.ofMethod;
import static io.quarkus.rest.data.panache.deployment.utils.SignatureMethodCreator.ofType;
import static io.quarkus.rest.data.panache.deployment.utils.SignatureMethodCreator.param;
import static io.quarkus.rest.data.panache.deployment.utils.SignatureMethodCreator.responseType;
import static io.quarkus.rest.data.panache.deployment.utils.SignatureMethodCreator.uniType;

import java.lang.annotation.Annotation;
import java.util.function.Supplier;
@@ -134,9 +136,10 @@ public UpdateMethodImplementor(Capabilities capabilities) {
protected void implementInternal(ClassCreator classCreator, ResourceMetadata resourceMetadata,
ResourceProperties resourceProperties, FieldDescriptor resourceField) {
MethodCreator methodCreator = SignatureMethodCreator.getMethodCreator(METHOD_NAME, classCreator,
isNotReactivePanache() ? ofType(Response.class) : ofType(Uni.class, resourceMetadata.getEntityType()),
resourceMetadata.getIdType(), resourceMetadata.getEntityType(), UriInfo.class);
methodCreator.setParameterNames(new String[] { "id", "entity", "uriInfo" });
isNotReactivePanache() ? responseType() : uniType(resourceMetadata.getEntityType()),
param("id", resourceMetadata.getIdType()),
param("entity", resourceMetadata.getEntityType()),
param("uriInfo", UriInfo.class));

// Add method annotations
addPathAnnotation(methodCreator, appendToPath(resourceProperties.getPath(RESOURCE_UPDATE_METHOD_NAME), "{id}"));
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
package io.quarkus.rest.data.panache.deployment.utils;

import static io.quarkus.gizmo.Type.classType;
import static io.quarkus.gizmo.Type.parameterizedType;
import static io.quarkus.rest.data.panache.deployment.methods.StandardMethodImplementor.toType;

import java.util.ArrayList;
import java.util.List;

import javax.ws.rs.core.Response;

import io.quarkus.gizmo.ClassCreator;
import io.quarkus.gizmo.DescriptorUtils;
import io.quarkus.gizmo.MethodCreator;
import io.quarkus.gizmo.SignatureBuilder;
import io.quarkus.gizmo.Type;
import io.smallrye.mutiny.Uni;

public final class SignatureMethodCreator {

private static final Type RESPONSE_TYPE = Type.classType(Response.class);

/**
* Creates a method using a signature (which allows declaring parameterized types like lists).
*
@@ -20,94 +30,71 @@ public final class SignatureMethodCreator {
* version of the method you want to see the signature.
*/
public static MethodCreator getMethodCreator(String methodName, ClassCreator classCreator, ReturnType returnType,
List<Parameter> parameters) {
List<Object> paramTypes = new ArrayList<>();
Parameter... parameters) {
List<Type> paramTypes = new ArrayList<>();
List<String> paramNames = new ArrayList<>();
List<Object> paramClasses = new ArrayList<>();
for (Parameter param : parameters) {
paramNames.add(param.name);
paramTypes.add(param.type);
paramClasses.add(param.clazz);
}
MethodCreator methodCreator = getMethodCreator(methodName, classCreator, returnType, paramTypes.toArray(new Object[0]));
methodCreator.setParameterNames(paramNames.toArray(new String[0]));
return methodCreator;
}

/**
* Creates a method using a signature (which allows declaring parameterized types like lists).
*
* For example, for the method: "List<String> list(List<String> sort, int size, int other, String uri)"
* It will use the following signature the generated the method:
* "(Ljava/util/List<Ljava/lang/String;>;IILjava/lang/String;)Ljava/util/List<Ljava/lang/String;>;".
*
* One useful utility to verify the method signatures is using "javap -v Test.class" where the Test java class is a compiled
* version of the method you want to see the signature.
*/
public static MethodCreator getMethodCreator(String methodName, ClassCreator classCreator, ReturnType returnType,
Object... parameters) {
MethodCreator methodCreator = classCreator.getMethodCreator(methodName, returnType.type, parameters);

StringBuilder signatureBuilder = new StringBuilder();
// Params first within parenthesis. If method is: "void method(Integer a)", it should return "(Ljava/lang/Integer;)":
signatureBuilder.append("(");
parametersToSignature(signatureBuilder, parameters);

signatureBuilder.append(")");

// Then, result type. If method is: "List<String> method(Integer a)",
// it should return "Ljava/util/List<Ljava/lang/String;>;"
signatureBuilder.append(stringToSignature(returnType.type.getName()));
if (returnType.parameterTypes.length > 0) {
signatureBuilder.append("<");
parametersToSignature(signatureBuilder, returnType.parameterTypes);
signatureBuilder.append(">");
}

signatureBuilder.append(";");

methodCreator.setSignature(signatureBuilder.toString());
MethodCreator methodCreator = classCreator.getMethodCreator(methodName, returnType.classType,
paramClasses.toArray(new Object[0]));
SignatureBuilder.MethodSignatureBuilder signatureBuilder = SignatureBuilder.forMethod()
.setReturnType(returnType.type);
paramTypes.forEach(signatureBuilder::addParameterType);
methodCreator.setSignature(signatureBuilder.build());
methodCreator.setParameterNames(paramNames.toArray(new String[0]));

return methodCreator;
}

private static void parametersToSignature(StringBuilder signatureBuilder, Object[] parameters) {
for (Object parameter : parameters) {
if (parameter instanceof Class) {
signatureBuilder.append(DescriptorUtils.classToStringRepresentation((Class<?>) parameter));
} else if (parameter instanceof String) {
signatureBuilder.append(stringToSignature((String) parameter) + ";");
}
}
}

private static String stringToSignature(String className) {
return "L" + className.replace('.', '/');
}

public static ReturnType ofType(Class<?> type, Object... parameterTypes) {
ReturnType returnType = new ReturnType();
returnType.type = type;
returnType.parameterTypes = parameterTypes;
return returnType;
public static Parameter param(String name, Object type) {
return param(name, type, toType(type));
}

public static Parameter param(String name, Object type) {
public static Parameter param(String name, Object clazz, Type type) {
Parameter parameter = new Parameter();
parameter.name = name;
parameter.clazz = clazz;
parameter.type = type;

return parameter;
}

public static class Parameter {
private String name;
private Object type;
private Type type;
private Object clazz;

public String getName() {
return name;
}
}

public static class ReturnType {
private Class<?> type;
private Object[] parameterTypes;
private Class<?> classType;
private Type type;
}

public static ReturnType responseType() {
ReturnType returnType = new ReturnType();
returnType.classType = Response.class;
returnType.type = RESPONSE_TYPE;
return returnType;
}

public static ReturnType uniType(Object... arguments) {
ReturnType returnType = new ReturnType();
Type[] typeArguments = new Type[arguments.length];
for (int index = 0; index < arguments.length; index++) {
typeArguments[index] = toType(arguments[index]);
}

returnType.classType = Uni.class;
returnType.type = parameterizedType(classType(Uni.class), typeArguments);
return returnType;
}
}