From 3236502ca80588c9bb5ac847d4cb867d55c37fec Mon Sep 17 00:00:00 2001 From: Johannes Date: Thu, 13 Feb 2020 18:48:47 +0100 Subject: [PATCH] #7152 Classes with @ConstructorProperties for Reflection --- .../ConstructorPropertiesProcessor.java | 42 +++++++++ .../TestResourceForConstructorProperties.java | 86 +++++++++++++++++++ .../JaxRsConstructorPropertiesITCase.java | 8 ++ .../JaxRsConstructorPropertiesTestCase.java | 44 ++++++++++ 4 files changed, 180 insertions(+) create mode 100644 core/deployment/src/main/java/io/quarkus/deployment/ConstructorPropertiesProcessor.java create mode 100644 integration-tests/main/src/main/java/io/quarkus/it/rest/TestResourceForConstructorProperties.java create mode 100644 integration-tests/main/src/test/java/io/quarkus/it/main/JaxRsConstructorPropertiesITCase.java create mode 100644 integration-tests/main/src/test/java/io/quarkus/it/main/JaxRsConstructorPropertiesTestCase.java diff --git a/core/deployment/src/main/java/io/quarkus/deployment/ConstructorPropertiesProcessor.java b/core/deployment/src/main/java/io/quarkus/deployment/ConstructorPropertiesProcessor.java new file mode 100644 index 000000000..28bb0d5da --- /dev/null +++ b/core/deployment/src/main/java/io/quarkus/deployment/ConstructorPropertiesProcessor.java @@ -0,0 +1,42 @@ +package io.quarkus.deployment; + +import org.jboss.jandex.AnnotationInstance; +import org.jboss.jandex.AnnotationTarget; +import org.jboss.jandex.DotName; +import org.jboss.jandex.IndexView; +import org.jboss.jandex.MethodInfo; + +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.CombinedIndexBuildItem; +import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; + +/** + * Registers all classes for reflection, + * that contain a constructor annotated with @java.beans.ConstructorProperties. + */ +public class ConstructorPropertiesProcessor { + + private static final DotName CONSTRUCTOR_PROPERTIES = DotName.createSimple("java.beans.ConstructorProperties"); + + @BuildStep + void build(BuildProducer reflectiveClass, CombinedIndexBuildItem indexBuildItem) { + IndexView index = indexBuildItem.getIndex(); + for (AnnotationInstance annotationInstance : index.getAnnotations(CONSTRUCTOR_PROPERTIES)) { + registerInstance(reflectiveClass, annotationInstance); + } + } + + private void registerInstance(BuildProducer reflectiveClass, AnnotationInstance instance) { + AnnotationTarget annotationTarget = instance.target(); + if (annotationTarget instanceof MethodInfo) { + MethodInfo methodInfo = (MethodInfo) annotationTarget; + String classname = methodInfo.declaringClass().toString(); + reflectiveClass.produce(asReflectiveClassBuildItem(classname)); + } + } + + private ReflectiveClassBuildItem asReflectiveClassBuildItem(String annotatedClass) { + return new ReflectiveClassBuildItem(true, false, annotatedClass); + } +} diff --git a/integration-tests/main/src/main/java/io/quarkus/it/rest/TestResourceForConstructorProperties.java b/integration-tests/main/src/main/java/io/quarkus/it/rest/TestResourceForConstructorProperties.java new file mode 100644 index 000000000..6550a7886 --- /dev/null +++ b/integration-tests/main/src/main/java/io/quarkus/it/rest/TestResourceForConstructorProperties.java @@ -0,0 +1,86 @@ +package io.quarkus.it.rest; + +import java.beans.ConstructorProperties; + +import javax.json.bind.JsonbBuilder; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.sse.OutboundSseEvent; +import javax.ws.rs.sse.Sse; +import javax.ws.rs.sse.SseEventSink; + +@Path("/constructorproperties") +public class TestResourceForConstructorProperties { + + @Context + Sse sse; + + @GET + @Path("/direct") + @Produces(MediaType.APPLICATION_JSON) + public VanillaJavaImmutableData direct() { + return new VanillaJavaImmutableData("direct", "directvalue"); + } + + @GET + @Path("/jsonb") + @Produces(MediaType.APPLICATION_JSON) + public String jsonb() { + VanillaJavaImmutableData entity = new VanillaJavaImmutableData("jsonb", "jsonbvalue"); + return JsonbBuilder.create().toJson(entity); + } + + @GET + @Path("/response") + @Produces(MediaType.APPLICATION_JSON) + public Response response() { + VanillaJavaImmutableData entity = new VanillaJavaImmutableData("response", "responsevalue"); + return Response.ok(entity).build(); + } + + @GET + @Path("/sse") + @Produces(MediaType.SERVER_SENT_EVENTS + ";element-type=" + MediaType.APPLICATION_JSON) + public void serverSentEvents(@Context SseEventSink sink) { + VanillaJavaImmutableData data = new VanillaJavaImmutableData("sse", "ssevalue"); + try { + OutboundSseEvent.Builder builder = sse.newEventBuilder(); + builder.id(String.valueOf(1)) + .mediaType(MediaType.APPLICATION_JSON_TYPE) + .data(data) + .name("stream of json data"); + + sink.send(builder.build()); + } finally { + sink.close(); + } + } + + public static class VanillaJavaImmutableData { + private final String name; + private final String value; + + @ConstructorProperties({ "name", "value" }) + public VanillaJavaImmutableData(String name, String value) { + this.name = name; + this.value = value; + } + + public String getName() { + return name; + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return "ConstructorPropertiesAnnotatedImmutableData [name=" + name + ", value=" + value + "]"; + } + } +} diff --git a/integration-tests/main/src/test/java/io/quarkus/it/main/JaxRsConstructorPropertiesITCase.java b/integration-tests/main/src/test/java/io/quarkus/it/main/JaxRsConstructorPropertiesITCase.java new file mode 100644 index 000000000..99ef598a1 --- /dev/null +++ b/integration-tests/main/src/test/java/io/quarkus/it/main/JaxRsConstructorPropertiesITCase.java @@ -0,0 +1,8 @@ +package io.quarkus.it.main; + +import io.quarkus.test.junit.NativeImageTest; + +@NativeImageTest +public class JaxRsConstructorPropertiesITCase extends JaxRsConstructorPropertiesTestCase { + +} diff --git a/integration-tests/main/src/test/java/io/quarkus/it/main/JaxRsConstructorPropertiesTestCase.java b/integration-tests/main/src/test/java/io/quarkus/it/main/JaxRsConstructorPropertiesTestCase.java new file mode 100644 index 000000000..a00a64f7b --- /dev/null +++ b/integration-tests/main/src/test/java/io/quarkus/it/main/JaxRsConstructorPropertiesTestCase.java @@ -0,0 +1,44 @@ +package io.quarkus.it.main; + +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.RestAssured; +import io.restassured.specification.RequestSender; + +@QuarkusTest +public class JaxRsConstructorPropertiesTestCase { + + @Test + public void testReturnedDirectly() { + when().get("/constructorproperties/direct").then() + .body("name", is("direct"), + "value", is("directvalue")); + } + + @Test + public void testConvertedWithJsonbAndReturnedAsString() { + when().get("/constructorproperties/jsonb").then() + .body("name", is("jsonb"), + "value", is("jsonbvalue")); + } + + @Test + public void testWrappedInResponse() { + when().get("/constructorproperties/response").then() + .body("name", is("response"), + "value", is("responsevalue")); + } + + @Test + public void testWrappedInServerSentEventMessage() { + when().get("/constructorproperties/sse").then().body(containsString("ssevalue")); + } + + private static RequestSender when() { + return RestAssured.when(); + } +}