From 3339af6f25a75ef1e06aa72c83cd3843fbd4e5db Mon Sep 17 00:00:00 2001 From: Jose Date: Thu, 13 Oct 2022 15:08:05 +0200 Subject: [PATCH] Resteasy Reactive JAXB using multipart only bound XML media types Fix https://github.com/quarkusio/quarkus/issues/28524 --- .../ResteasyReactiveJaxbProcessor.java | 52 +++++++++++++------ .../jaxb/deployment/test/MultipartTest.java | 36 ++++++++++++- .../deployment/src/test/resources/test.html | 9 ++++ 3 files changed, 78 insertions(+), 19 deletions(-) create mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive-jaxb/deployment/src/test/resources/test.html diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jaxb/deployment/src/main/java/io/quarkus/resteasy/reactive/jaxb/deployment/ResteasyReactiveJaxbProcessor.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jaxb/deployment/src/main/java/io/quarkus/resteasy/reactive/jaxb/deployment/ResteasyReactiveJaxbProcessor.java index 086822d660b7e..0e723c783f3b3 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jaxb/deployment/src/main/java/io/quarkus/resteasy/reactive/jaxb/deployment/ResteasyReactiveJaxbProcessor.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jaxb/deployment/src/main/java/io/quarkus/resteasy/reactive/jaxb/deployment/ResteasyReactiveJaxbProcessor.java @@ -39,6 +39,8 @@ public class ResteasyReactiveJaxbProcessor { + private static final List XML_TYPES = List.of(MediaType.APPLICATION_XML, MediaType.TEXT_XML); + @BuildStep void feature(BuildProducer feature) { feature.produce(new FeatureBuildItem(Feature.RESTEASY_REACTIVE_JAXB)); @@ -56,10 +58,10 @@ void additionalProviders(BuildProducer additionalBean, additionalReaders .produce(new MessageBodyReaderBuildItem(ServerJaxbMessageBodyReader.class.getName(), Object.class.getName(), - List.of(MediaType.APPLICATION_XML, MediaType.TEXT_XML), RuntimeType.SERVER, true, Priorities.USER)); + XML_TYPES, RuntimeType.SERVER, true, Priorities.USER)); additionalWriters .produce(new MessageBodyWriterBuildItem(ServerJaxbMessageBodyWriter.class.getName(), Object.class.getName(), - List.of(MediaType.APPLICATION_XML, MediaType.TEXT_XML), RuntimeType.SERVER, true, Priorities.USER)); + XML_TYPES, RuntimeType.SERVER, true, Priorities.USER)); } @BuildStep @@ -87,11 +89,17 @@ void registerClassesToBeBound(ResteasyReactiveResourceMethodEntriesBuildItem res } // If consumes "application/xml" or "multipart/form-data", we register all the classes of the parameters - if (consumesXml(resourceInfo) || consumesMultipart(resourceInfo)) { + boolean consumesXml = consumesXml(resourceInfo); + boolean consumesMultipart = consumesMultipart(resourceInfo); + if (consumesXml || consumesMultipart) { for (Type parameter : methodInfo.parameterTypes()) { ClassInfo effectiveParameter = getEffectiveClassInfo(parameter, indexView); if (effectiveParameter != null) { - classesInfo.add(effectiveParameter); + if (consumesXml) { + classesInfo.add(effectiveParameter); + } else if (consumesMultipart) { + classesInfo.addAll(getEffectivePartsUsingXml(effectiveParameter, indexView)); + } } } } @@ -112,18 +120,26 @@ void setupJaxbContextConfigForValidator(Capabilities capabilities, private List getEffectivePartsUsingXml(ClassInfo returnType, IndexView indexView) { List classInfos = new ArrayList<>(); for (FieldInfo field : returnType.fields()) { - AnnotationInstance partTypeInstance = field.annotation(ResteasyReactiveDotNames.PART_TYPE_NAME); - if (partTypeInstance != null) { - AnnotationValue partTypeValue = partTypeInstance.value(); - if (partTypeValue != null && MediaType.APPLICATION_XML.equals(partTypeValue.asString())) { - classInfos.add(getEffectiveClassInfo(field.type(), indexView)); - } + if (isPartTypeXml(field)) { + classInfos.add(getEffectiveClassInfo(field.type(), indexView)); } } return classInfos; } + private boolean isPartTypeXml(FieldInfo field) { + AnnotationInstance partType = field.annotation(ResteasyReactiveDotNames.PART_TYPE_NAME); + if (partType != null) { + AnnotationValue partTypeValue = partType.value(); + if (containsMediaType(new String[] { partTypeValue.asString() }, XML_TYPES)) { + return true; + } + } + + return false; + } + private ClassInfo getEffectiveClassInfo(Type type, IndexView indexView) { if (type.kind() == Type.Kind.VOID || type.kind() == Type.Kind.PRIMITIVE) { return null; @@ -158,26 +174,28 @@ private ClassInfo getEffectiveClassInfo(Type type, IndexView indexView) { } private boolean consumesXml(ResourceMethod resourceInfo) { - return containsMediaType(resourceInfo.getConsumes(), MediaType.APPLICATION_XML); + return containsMediaType(resourceInfo.getConsumes(), XML_TYPES); } private boolean consumesMultipart(ResourceMethod resourceInfo) { - return containsMediaType(resourceInfo.getConsumes(), MediaType.MULTIPART_FORM_DATA); + return containsMediaType(resourceInfo.getConsumes(), List.of(MediaType.MULTIPART_FORM_DATA)); } private boolean producesXml(ResourceMethod resourceInfo) { - return containsMediaType(resourceInfo.getProduces(), MediaType.APPLICATION_XML); + return containsMediaType(resourceInfo.getProduces(), XML_TYPES); } private boolean producesMultipart(ResourceMethod resourceInfo) { - return containsMediaType(resourceInfo.getProduces(), MediaType.MULTIPART_FORM_DATA); + return containsMediaType(resourceInfo.getProduces(), List.of(MediaType.MULTIPART_FORM_DATA)); } - private boolean containsMediaType(String[] types, String mediaType) { + private boolean containsMediaType(String[] types, List mediaTypes) { if (types != null) { for (String type : types) { - if (type.toLowerCase(Locale.ROOT).contains(mediaType)) { - return true; + for (String mediaType : mediaTypes) { + if (type.toLowerCase(Locale.ROOT).contains(mediaType)) { + return true; + } } } } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jaxb/deployment/src/test/java/io/quarkus/resteasy/reactive/jaxb/deployment/test/MultipartTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jaxb/deployment/src/test/java/io/quarkus/resteasy/reactive/jaxb/deployment/test/MultipartTest.java index 9fe9a051645de..0be6a017e31a1 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jaxb/deployment/src/test/java/io/quarkus/resteasy/reactive/jaxb/deployment/test/MultipartTest.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jaxb/deployment/src/test/java/io/quarkus/resteasy/reactive/jaxb/deployment/test/MultipartTest.java @@ -2,7 +2,12 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; + import javax.ws.rs.Consumes; +import javax.ws.rs.FormParam; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; @@ -12,6 +17,7 @@ import org.jboss.resteasy.reactive.MultipartForm; import org.jboss.resteasy.reactive.PartType; import org.jboss.resteasy.reactive.RestForm; +import org.jboss.resteasy.reactive.multipart.FileUpload; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.spec.JavaArchive; import org.junit.jupiter.api.Test; @@ -36,6 +42,7 @@ public class MultipartTest { + "" + "Divino Pastor" + ""; + private final File HTML_FILE = new File("./src/test/resources/test.html"); @RegisterExtension static QuarkusUnitTest test = new QuarkusUnitTest() @@ -51,7 +58,7 @@ public void testOutput() { .extract().asString(); assertContains(response, "name", MediaType.TEXT_PLAIN, EXPECTED_RESPONSE_NAME); - assertContains(response, "person", MediaType.APPLICATION_XML, EXPECTED_RESPONSE_PERSON); + assertContains(response, "person", MediaType.TEXT_XML, EXPECTED_RESPONSE_PERSON); } @Test @@ -68,6 +75,19 @@ public void testInput() { assertThat(response).isEqualTo("John-Divino Pastor"); } + @Test + public void testInputFile() throws IOException { + String response = RestAssured + .given() + .multiPart("file", HTML_FILE, "text/html") + .post("/multipart/input/file") + .then() + .statusCode(200) + .extract().asString(); + + assertThat(response).isEqualTo(String.valueOf(Files.readAllBytes(HTML_FILE.toPath()).length)); + } + private void assertContains(String response, String name, String contentType, Object value) { String[] lines = response.split("--"); assertThat(lines).anyMatch(line -> line.contains(String.format(EXPECTED_CONTENT_DISPOSITION_PART, name)) @@ -97,6 +117,13 @@ public String input(@MultipartForm MultipartInput input) { return input.name + "-" + input.school.name; } + @POST + @Path("/input/file") + @Consumes(MediaType.MULTIPART_FORM_DATA) + public int inputFile(@MultipartForm FileUploadData data) throws IOException { + return Files.readAllBytes(data.fileUpload.filePath()).length; + } + } private static class MultipartOutputResponse { @@ -105,10 +132,15 @@ private static class MultipartOutputResponse { String name; @RestForm - @PartType(MediaType.APPLICATION_XML) + @PartType(MediaType.TEXT_XML) Person person; } + public static class FileUploadData { + @FormParam("file") + FileUpload fileUpload; + } + public static class MultipartInput { @RestForm diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jaxb/deployment/src/test/resources/test.html b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jaxb/deployment/src/test/resources/test.html new file mode 100644 index 0000000000000..0d2c081db5dc9 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jaxb/deployment/src/test/resources/test.html @@ -0,0 +1,9 @@ + + + + + + + + +