-
Notifications
You must be signed in to change notification settings - Fork 2.5k
RESTEasy Reactive (Server)
This page documents some of the internals of RESTEasy Reactive.
These are classes that contain other request parameters, such as @FormParam
, @HeaderParam
, @PathParam
,
@MatrixParam
, @CookieParam
, @QueryParam
or other parameter containers.
Typically, these parameters are defined as fields on the endpoint, or as method parameters, but for convenience or reusability
we can group them into parameter containers. The endpoint parameter which is a parameter container may be annotated with @BeanParam
but it is not required because we can autodetect them based on the presence of its annotated fields.
NOTE: we treat endpoint classes as parameter containers if they have parameter fields, so it's exactly the same code.
These are documented for the users at https://quarkus.io/guides/resteasy-reactive#grouping-parameters-in-a-custom-class but this documents how this is implemented.
Generally speaking, we have a transformer (in ClassInjectorTransformer
) that transforms these parameter container classes by making them implement the ResteasyReactiveInjectionTarget
interface, and implementing an __quarkus_rest_inject
method which takes care of populating
the fields by using the request context.
Here is an example of the transformation we apply:
import java.io.File;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;
import org.jboss.resteasy.reactive.PartType;
import org.jboss.resteasy.reactive.RestForm;
import org.jboss.resteasy.reactive.common.util.DeploymentUtils;
import org.jboss.resteasy.reactive.multipart.FileUpload;
import org.jboss.resteasy.reactive.server.core.Deployment;
import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext;
import org.jboss.resteasy.reactive.server.core.multipart.MultipartSupport;
import org.jboss.resteasy.reactive.server.core.parameters.converters.CharParamConverter;
import org.jboss.resteasy.reactive.server.core.parameters.converters.ListConverter;
import org.jboss.resteasy.reactive.server.core.parameters.converters.ParameterConverter;
import org.jboss.resteasy.reactive.server.injection.ResteasyReactiveInjectionContext;
import org.jboss.resteasy.reactive.server.injection.ResteasyReactiveInjectionTarget;
import jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.DefaultValue;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.MediaType;
class X {}
class OtherBeanParamClass /* generated if our supertype is not already injectable: */ implements ResteasyReactiveInjectionTarget {
// ...
@Override
public void __quarkus_rest_inject(ResteasyReactiveInjectionContext ctx) {
// ...
}
}
class BeanParamClass /* generated if our supertype is not already injectable: */ implements ResteasyReactiveInjectionTarget {
@DefaultValue("default")
@RestForm
String regular;
@RestForm
char converted;
@RestForm
List<String> list;
@RestForm
List<Character> convertedList;
@RestForm
File multipartSpecial;
@RestForm
@PartType(MediaType.APPLICATION_JSON)
X multipartViaMessageBodyReader;
OtherBeanParamClass otherBeanParamClass;
// the rest of this class is generated
private static ParameterConverter __quarkus_converter__converted;
// this will be called at startup
public static void __quarkus_init_converter__converted(Deployment deployment) {
ParameterConverter converter = deployment.getRuntimeParamConverter(BeanParamClass.class, "converted", true);
// we have predefined ones in some cases
if(converter == null) {
converter = new CharParamConverter();
}
__quarkus_converter__converted = converter;
}
private static ParameterConverter __quarkus_converter__convertedList;
// this will be called at startup
public static void __quarkus_init_converter__convertedList(Deployment deployment) {
ParameterConverter converter = deployment.getRuntimeParamConverter(BeanParamClass.class, "convertedList", true);
// we have predefined ones in some cases
if(converter == null) {
converter = new CharParamConverter();
}
// this is a collection
converter = new ListConverter(converter);
__quarkus_converter__convertedList = converter;
}
private static Class multipartViaMessageBodyReader_type;
private static Type multipartViaMessageBodyReader_genericType;
private static MediaType multipartViaMessageBodyReader_mediaType;
static {
Class var0 = DeploymentUtils.loadClass("com.example.X");
// Note that in the case of collections or arrays, type/genericType represents the element type
multipartViaMessageBodyReader_type = var0;
// or TypeSignatureParser.parse("Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;"); for generic types
multipartViaMessageBodyReader_genericType = var0;
multipartViaMessageBodyReader_mediaType = MediaType.valueOf("application/json");
}
@Override
public void __quarkus_rest_inject(ResteasyReactiveInjectionContext ctx) {
// if our supertype is injectable
// super.__quarkus_rest_inject(ctx);
// a regular field with no converter
try {
Object val = ctx.getFormParameter("regular", true, true);
// if we have a default value
if(val == null) {
val = "default";
}
if(val != null) {
regular = (String)val;
}
} catch (WebApplicationException x) {
throw x;
} catch (Throwable x) {
throw new BadRequestException();
}
// a converted field
try {
Object val = ctx.getFormParameter("converted", true, true);
if(val != null) {
converted = (char) __quarkus_converter__converted.convert(val);
}
} catch (Throwable x){ /* omitted */}
// a collection field
try {
Object val = ctx.getFormParameter("list", true, true);
if(val != null && !((Collection)val).isEmpty()) {
list = (List) val;
}
} catch (Throwable x){ /* omitted */}
// a converted collection field
try {
Object val = ctx.getFormParameter("convertedList", true, true);
if(val != null && !((Collection)val).isEmpty()) {
convertedList = (List) __quarkus_converter__convertedList.convert(val);
}
} catch (Throwable x){ /* omitted */}
// a special multipart type
try {
// there are variants for List<X> or X[] or even for name.equals(FileUpload.ALL)
// where X is FileUpload, String, byte[], File, Path, InputStream
FileUpload val = MultipartSupport.getFileUpload("multipartSpecial", (ResteasyReactiveRequestContext) ctx);
if(val != null) {
multipartSpecial = val.uploadedFile().toFile();
}
} catch (Throwable x){ /* omitted */}
// a multipart via MessageBodyReader
try {
// there are variants for List<X> or X[]
Object val = MultipartSupport.getConvertedFormAttribute("multipartViaMessageBodyReader", multipartViaMessageBodyReader_type, multipartViaMessageBodyReader_genericType,
multipartViaMessageBodyReader_mediaType,
(ResteasyReactiveRequestContext) ctx);
if(val != null) {
multipartViaMessageBodyReader = (X) val;
}
} catch (Throwable x){ /* omitted */}
// another bean param class
otherBeanParamClass = new OtherBeanParamClass();
otherBeanParamClass.__quarkus_rest_inject(ctx);
}
}