-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Provide correct generic type and annotations in ParamConverterProvider
These changes will provide the correct generic type and array of annotations when the JAX-RS annotation is present on a field or a method of a Bean Param class. The solution is similar to what was being done for the parameters of a REST Client method: it will load the metadata (generic type and annotations from fields and methods) of a BeanParam class using reflection only if there is a ParamConverterProvider present. Fix #32765
- Loading branch information
Showing
14 changed files
with
496 additions
and
160 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
267 changes: 166 additions & 101 deletions
267
...c/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java
Large diffs are not rendered by default.
Oops, something went wrong.
52 changes: 52 additions & 0 deletions
52
...n/java/io/quarkus/jaxrs/client/reactive/runtime/ParameterDescriptorFromClassSupplier.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package io.quarkus.jaxrs.client.reactive.runtime; | ||
|
||
import java.lang.annotation.Annotation; | ||
import java.lang.reflect.Field; | ||
import java.lang.reflect.Method; | ||
import java.lang.reflect.Type; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.function.Supplier; | ||
|
||
public class ParameterDescriptorFromClassSupplier | ||
implements Supplier<Map<String, ParameterDescriptorFromClassSupplier.ParameterDescriptor>> { | ||
|
||
private final Class clazz; | ||
private volatile Map<String, ParameterDescriptor> value; | ||
|
||
public ParameterDescriptorFromClassSupplier(Class clazz) { | ||
this.clazz = clazz; | ||
} | ||
|
||
@Override | ||
public Map<String, ParameterDescriptor> get() { | ||
if (value == null) { | ||
value = new HashMap<>(); | ||
Class currentClass = clazz; | ||
while (currentClass != null && currentClass != Object.class) { | ||
for (Field field : currentClass.getDeclaredFields()) { | ||
ParameterDescriptor descriptor = new ParameterDescriptor(); | ||
descriptor.annotations = field.getAnnotations(); | ||
descriptor.genericType = field.getGenericType(); | ||
value.put(field.getName(), descriptor); | ||
} | ||
|
||
for (Method method : currentClass.getDeclaredMethods()) { | ||
ParameterDescriptor descriptor = new ParameterDescriptor(); | ||
descriptor.annotations = method.getAnnotations(); | ||
descriptor.genericType = method.getGenericReturnType(); | ||
value.put(method.getName(), descriptor); | ||
} | ||
|
||
currentClass = currentClass.getSuperclass(); | ||
} | ||
} | ||
|
||
return value; | ||
} | ||
|
||
public static class ParameterDescriptor { | ||
public Annotation[] annotations; | ||
public Type genericType; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
175 changes: 175 additions & 0 deletions
175
...a/io/quarkus/rest/client/reactive/converter/ParamConverterReadAnnotationFromBeanTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
package io.quarkus.rest.client.reactive.converter; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.assertj.core.api.Assertions.fail; | ||
import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
||
import java.lang.annotation.Annotation; | ||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
import java.lang.reflect.Type; | ||
import java.net.URI; | ||
import java.util.Arrays; | ||
|
||
import jakarta.ws.rs.BeanParam; | ||
import jakarta.ws.rs.CookieParam; | ||
import jakarta.ws.rs.GET; | ||
import jakarta.ws.rs.HeaderParam; | ||
import jakarta.ws.rs.Path; | ||
import jakarta.ws.rs.PathParam; | ||
import jakarta.ws.rs.QueryParam; | ||
import jakarta.ws.rs.ext.ParamConverterProvider; | ||
|
||
import org.eclipse.microprofile.rest.client.RestClientBuilder; | ||
import org.eclipse.microprofile.rest.client.annotation.RegisterProvider; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.extension.RegisterExtension; | ||
|
||
import io.quarkus.test.QuarkusUnitTest; | ||
import io.quarkus.test.common.http.TestHTTPResource; | ||
|
||
public class ParamConverterReadAnnotationFromBeanTest { | ||
@RegisterExtension | ||
static final QuarkusUnitTest TEST = new QuarkusUnitTest(); | ||
|
||
@TestHTTPResource | ||
URI baseUri; | ||
|
||
@Test | ||
void shouldConvertPathParam() { | ||
Client client = RestClientBuilder.newBuilder().baseUri(baseUri).build(Client.class); | ||
Bean bean = new Bean(); | ||
bean.param = Param.FIRST; | ||
assertThat(client.get(bean)).isEqualTo("1"); | ||
|
||
bean = new Bean(); | ||
bean.param = Param.SECOND; | ||
assertThat(client.sub().get(bean)).isEqualTo("2"); | ||
} | ||
|
||
@Path("/echo") | ||
@RegisterProvider(ParamConverter.class) | ||
interface Client { | ||
@Path("/sub") | ||
SubClient sub(); | ||
|
||
@GET | ||
@Path("/param/{param}") | ||
String get(@BeanParam Bean beanParam); | ||
} | ||
|
||
interface SubClient { | ||
@GET | ||
@Path("/param/{param}") | ||
String get(@BeanParam Bean beanParam); | ||
} | ||
|
||
public static class Bean { | ||
@MyAnnotation("myValue") | ||
@PathParam("param") | ||
public Param param; | ||
} | ||
|
||
enum Param { | ||
FIRST, | ||
SECOND | ||
} | ||
|
||
@Target(ElementType.FIELD) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
public @interface MyAnnotation { | ||
String value() default ""; | ||
} | ||
|
||
public static class ParamConverter implements ParamConverterProvider { | ||
@SuppressWarnings("unchecked") | ||
@Override | ||
public <T> jakarta.ws.rs.ext.ParamConverter<T> getConverter(Class<T> rawType, Type genericType, | ||
Annotation[] annotations) { | ||
if (annotations == null) { | ||
fail("Annotations cannot be null!"); | ||
} | ||
|
||
assertTrue(Arrays.stream(annotations) | ||
.anyMatch(a -> a instanceof MyAnnotation && ((MyAnnotation) a).value().equals("myValue"))); | ||
|
||
if (rawType == Param.class) { | ||
return (jakarta.ws.rs.ext.ParamConverter<T>) new jakarta.ws.rs.ext.ParamConverter<Param>() { | ||
@Override | ||
public Param fromString(String value) { | ||
return null; | ||
} | ||
|
||
@Override | ||
public String toString(Param value) { | ||
if (value == null) { | ||
return null; | ||
} | ||
switch (value) { | ||
case FIRST: | ||
return "1"; | ||
case SECOND: | ||
return "2"; | ||
default: | ||
return "unexpected"; | ||
} | ||
} | ||
}; | ||
} | ||
return null; | ||
} | ||
} | ||
|
||
@Path("/echo") | ||
public static class EchoEndpoint { | ||
@Path("/param/{param}") | ||
@GET | ||
public String echoPath(@PathParam("param") String param) { | ||
return param; | ||
} | ||
|
||
@Path("/sub/param/{param}") | ||
@GET | ||
public String echoSubPath(@PathParam("param") String param) { | ||
return param; | ||
} | ||
|
||
@GET | ||
@Path("/query") | ||
public String get(@QueryParam("param") String param) { | ||
return param; | ||
} | ||
|
||
@Path("/sub/query") | ||
@GET | ||
public String getSub(@QueryParam("param") String param) { | ||
return param; | ||
} | ||
|
||
@GET | ||
@Path("/header") | ||
public String getHeader(@HeaderParam("param") String param) { | ||
return param; | ||
} | ||
|
||
@Path("/sub/header") | ||
@GET | ||
public String getSubHeader(@HeaderParam("param") String param) { | ||
return param; | ||
} | ||
|
||
@GET | ||
@Path("/cookie") | ||
public String getCookie(@CookieParam("cookie-param") String param) { | ||
return param; | ||
} | ||
|
||
@Path("/sub/cookie") | ||
@GET | ||
public String getSubCookie(@CookieParam("cookie-param") String param) { | ||
return param; | ||
} | ||
} | ||
} |
Oops, something went wrong.