diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/QuarkusServerEndpointIndexer.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/QuarkusServerEndpointIndexer.java index b7cfc7052729f..fb78eb42bf204 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/QuarkusServerEndpointIndexer.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/QuarkusServerEndpointIndexer.java @@ -1,6 +1,7 @@ package io.quarkus.resteasy.reactive.server.deployment; import static org.jboss.resteasy.reactive.server.processor.util.ResteasyReactiveServerDotNames.SERVER_MESSAGE_BODY_READER; +import static org.jboss.resteasy.reactive.server.processor.util.ResteasyReactiveServerDotNames.SERVER_MESSAGE_BODY_WRITER; import java.lang.annotation.Annotation; import java.util.List; @@ -156,7 +157,7 @@ protected void handleAdditionalMethodProcessing(ServerResourceMethod method, Cla @Override public boolean additionalRegisterClassForReflectionCheck(ResourceMethodCallbackEntry entry) { - return checkBodyParameterMessageBodyReader(entry); + return checkBodyParameterMessageBodyReader(entry) || checkReturnTypeMessageBodyWriter(entry); } /** @@ -194,12 +195,48 @@ private boolean checkBodyParameterMessageBodyReader(ResourceMethodCallbackEntry return false; } + /** + * Check whether the Resource Method has a return type for which there exists a matching + * {@link jakarta.ws.rs.ext.MessageBodyWriter} + * that is not a {@link org.jboss.resteasy.reactive.server.spi.ServerMessageBodyWriter}. + * In this case the Resource Class needs to be registered for reflection because the + * {@link jakarta.ws.rs.ext.MessageBodyWriter#isWriteable(Class, java.lang.reflect.Type, Annotation[], MediaType)} + * method expects to be passed the method annotations. + */ + private boolean checkReturnTypeMessageBodyWriter(ResourceMethodCallbackEntry entry) { + Type returnType = entry.getMethodInfo().returnType(); + String returnTypeName; + switch (returnType.kind()) { + case CLASS: + returnTypeName = returnType.asClassType().name().toString(); + break; + case PARAMETERIZED_TYPE: + returnTypeName = returnType.asParameterizedType().name().toString(); + break; + default: + returnTypeName = null; + } + if (returnTypeName == null) { + return false; + } + + List writers = getSerializerScanningResult().getWriters(); + + for (ScannedSerializer writer : writers) { + if (isSubclassOf(returnTypeName, writer.getHandledClassName()) + && !isServerMessageBodyWriter(writer.getClassInfo())) { + return true; + } + } + return false; + } + private boolean isSubclassOf(String className, String parentName) { if (className.equals(parentName)) { return true; } ClassInfo classByName = index.getClassByName(className); - if (classByName == null) { + if ((classByName == null) || (classByName.superName() == null)) { return false; } try { @@ -210,8 +247,12 @@ private boolean isSubclassOf(String className, String parentName) { } } - private boolean isServerMessageBodyReader(ClassInfo readerClassInfo) { - return index.getAllKnownImplementors(SERVER_MESSAGE_BODY_READER).contains(readerClassInfo); + private boolean isServerMessageBodyReader(ClassInfo classInfo) { + return index.getAllKnownImplementors(SERVER_MESSAGE_BODY_READER).contains(classInfo); + } + + private boolean isServerMessageBodyWriter(ClassInfo classInfo) { + return index.getAllKnownImplementors(SERVER_MESSAGE_BODY_WRITER).contains(classInfo); } private void warnAboutMissingJsonProviderIfNeeded(ServerResourceMethod method, MethodInfo info) { diff --git a/integration-tests/hibernate-orm-envers/src/main/java/io/quarkus/it/envers/Message2.java b/integration-tests/hibernate-orm-envers/src/main/java/io/quarkus/it/envers/Message2.java new file mode 100644 index 0000000000000..d169529071ac1 --- /dev/null +++ b/integration-tests/hibernate-orm-envers/src/main/java/io/quarkus/it/envers/Message2.java @@ -0,0 +1,21 @@ +package io.quarkus.it.envers; + +public class Message2 { + + private String data; + + public Message2() { + } + + public Message2(String data) { + this.data = data; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } +} diff --git a/integration-tests/hibernate-orm-envers/src/main/java/io/quarkus/it/envers/Message2Provider.java b/integration-tests/hibernate-orm-envers/src/main/java/io/quarkus/it/envers/Message2Provider.java new file mode 100644 index 0000000000000..8a83299533e1d --- /dev/null +++ b/integration-tests/hibernate-orm-envers/src/main/java/io/quarkus/it/envers/Message2Provider.java @@ -0,0 +1,54 @@ +package io.quarkus.it.envers; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.MultivaluedMap; +import jakarta.ws.rs.ext.MessageBodyReader; +import jakarta.ws.rs.ext.MessageBodyWriter; +import jakarta.ws.rs.ext.Provider; + +@Provider +@Consumes(MediaType.WILDCARD) +@Produces(MediaType.WILDCARD) +public class Message2Provider implements MessageBodyReader, MessageBodyWriter { + + @Override + public boolean isReadable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { + return Message2.class.isAssignableFrom(type); + } + + @Override + public Message2 readFrom(Class type, Type genericType, Annotation[] annotations, MediaType mediaType, + MultivaluedMap httpHeaders, InputStream entityStream) throws IOException, WebApplicationException { + return new Message2("in"); + } + + @Override + public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { + return Message2.class.isAssignableFrom(type); + } + + @Override + public void writeTo(Message2 event, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, + MultivaluedMap httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException { + String data = "out"; + if (annotations != null) { + for (Annotation annotation : annotations) { + if (annotation.annotationType().equals(CustomOutput.class)) { + data = ((CustomOutput) annotation).value(); + break; + } + } + } + entityStream.write(String.format("{\"data\": \"%s\"}", data).getBytes(StandardCharsets.UTF_8)); + } +} diff --git a/integration-tests/hibernate-orm-envers/src/main/java/io/quarkus/it/envers/Output2Resource.java b/integration-tests/hibernate-orm-envers/src/main/java/io/quarkus/it/envers/Output2Resource.java new file mode 100644 index 0000000000000..fd397cc5d2e50 --- /dev/null +++ b/integration-tests/hibernate-orm-envers/src/main/java/io/quarkus/it/envers/Output2Resource.java @@ -0,0 +1,16 @@ +package io.quarkus.it.envers; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +@Path("output2") +public class Output2Resource { + + @GET + @Produces(MediaType.APPLICATION_JSON) + public Message2 out() { + return new Message2("test"); + } +} diff --git a/integration-tests/hibernate-orm-envers/src/main/java/io/quarkus/it/envers/OutputResource.java b/integration-tests/hibernate-orm-envers/src/main/java/io/quarkus/it/envers/OutputResource.java index 4376c1378611f..874f0f0bd2691 100644 --- a/integration-tests/hibernate-orm-envers/src/main/java/io/quarkus/it/envers/OutputResource.java +++ b/integration-tests/hibernate-orm-envers/src/main/java/io/quarkus/it/envers/OutputResource.java @@ -4,7 +4,6 @@ import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; -import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; import org.jboss.resteasy.reactive.RestStreamElementType; @@ -14,12 +13,6 @@ @Path("output") public class OutputResource { - @GET - @Produces(MediaType.APPLICATION_JSON) - public Message out() { - return new Message("test"); - } - @GET @RestStreamElementType(MediaType.APPLICATION_JSON) @CustomOutput("dummy") diff --git a/integration-tests/hibernate-orm-envers/src/test/java/io/quarkus/it/envers/OutputResourceTest.java b/integration-tests/hibernate-orm-envers/src/test/java/io/quarkus/it/envers/OutputResourceTest.java index 74b6aa4ffa9bb..af038a6fecab4 100644 --- a/integration-tests/hibernate-orm-envers/src/test/java/io/quarkus/it/envers/OutputResourceTest.java +++ b/integration-tests/hibernate-orm-envers/src/test/java/io/quarkus/it/envers/OutputResourceTest.java @@ -38,7 +38,7 @@ class OutputResourceTest { void test() { given().accept(ContentType.JSON) .when() - .get(RESOURCE_PATH) + .get(RESOURCE_PATH + "2") .then() .statusCode(200) .body("data", equalTo("out"));