diff --git a/azure-pipelines.yml b/azure-pipelines.yml index a3e2dc642744c..eddbee1126e88 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -210,6 +210,7 @@ stages: - template: ci-templates/native-build-steps.yaml parameters: modules: + - jackson - jgit - kogito - kubernetes-client diff --git a/extensions/jackson/deployment/src/main/java/io/quarkus/jackson/deployment/JacksonProcessor.java b/extensions/jackson/deployment/src/main/java/io/quarkus/jackson/deployment/JacksonProcessor.java index 0561a21234df8..6301dc0b72ff8 100755 --- a/extensions/jackson/deployment/src/main/java/io/quarkus/jackson/deployment/JacksonProcessor.java +++ b/extensions/jackson/deployment/src/main/java/io/quarkus/jackson/deployment/JacksonProcessor.java @@ -1,16 +1,69 @@ package io.quarkus.jackson.deployment; +import java.util.Collection; + +import javax.inject.Inject; + +import org.jboss.jandex.*; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; + import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.CombinedIndexBuildItem; import io.quarkus.deployment.builditem.substrate.ReflectiveClassBuildItem; +import io.quarkus.deployment.builditem.substrate.ReflectiveHierarchyBuildItem; public class JacksonProcessor { + private static final DotName JSON_DESERIALIZE = DotName.createSimple(JsonDeserialize.class.getName()); + private static final DotName BUILDER_VOID = DotName.createSimple(Void.class.getName()); + + @Inject + BuildProducer reflectiveClass; + + @Inject + BuildProducer reflectiveHierarchyClass; + + @Inject + CombinedIndexBuildItem combinedIndexBuildItem; + @BuildStep - void register(BuildProducer reflectiveClass) { - reflectiveClass.produce(new ReflectiveClassBuildItem(true, false, + void register() { + addReflectiveClass(true, false, "com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector", - "com.fasterxml.jackson.databind.ser.std.SqlDateSerializer")); + "com.fasterxml.jackson.databind.ser.std.SqlDateSerializer"); + + IndexView index = combinedIndexBuildItem.getIndex(); + + // TODO: Here we only check for @JsonDeserialize to detect both Model and Builder + // classes to support @JsonPojoBuilder. The @JsonDeserialize annotiona can + // also be used for other scenarios than builder beside adding just the class + // no other scenarios are supported (like when the annotation is place on + // methods). + + Collection pojoBuilderInstances = index.getAnnotations(JSON_DESERIALIZE); + for (AnnotationInstance pojoBuilderInstance : pojoBuilderInstances) { + if (AnnotationTarget.Kind.CLASS.equals(pojoBuilderInstance.target().kind())) { + addReflectiveHierarchyClass(pojoBuilderInstance.target().asClass().name()); + + AnnotationValue annotationValue = pojoBuilderInstance.value("builder"); + if (null != annotationValue && AnnotationValue.Kind.CLASS.equals(annotationValue.kind())) { + DotName builderClassName = annotationValue.asClass().name(); + if (!BUILDER_VOID.equals(builderClassName)) { + addReflectiveHierarchyClass(builderClassName); + } + } + } + } } + private void addReflectiveHierarchyClass(DotName className) { + Type jandexType = Type.create(className, Type.Kind.CLASS); + reflectiveHierarchyClass.produce(new ReflectiveHierarchyBuildItem(jandexType)); + } + + private void addReflectiveClass(boolean methods, boolean fields, String... className) { + reflectiveClass.produce(new ReflectiveClassBuildItem(methods, fields, className)); + } } diff --git a/integration-tests/jackson/Readme.md b/integration-tests/jackson/Readme.md new file mode 100644 index 0000000000000..316091e562645 --- /dev/null +++ b/integration-tests/jackson/Readme.md @@ -0,0 +1,124 @@ +# Quarkus Jackson extension tester + +This project verifies Jackson (de-)serialization support in native mode. + +This project consists of the following modules: + +- model + + This module contains the library with a simple models. This model makes use + of Jackson to support (de-)serialization to JSON. Various forms of models + exists. At the time of writing the following models are present/tested: + - Immutable models using a Builder to construct new instances. + - Simple POJO model being registered for reflection. + + Unit tests are available to prove JVM based JSON (de-)serialization works + properly. + +- service + + This module contains a very simple RESTful resources with only a POST method + to POST new models to this service. A simple unit test is included to verify + correct behaviour for both unit and integration test. + + The following curl command can be used to send POST request to this service: + + ``` + curl -X POST -H "Content-Type: application/json" \ + -d '{"version": 2, "id": "123", "value": "val"}' \ + -v localhost:8080/ + ``` + + +## Build + +To build the project, run the following command from the project root directory: + +``` +mvn clean package +``` + +This build should run correctly showing no errors and no test failures. + +For the remainder make the service module your current working directory: + +``` +cd service +``` + +## Package JVM + +Running a JVM based version of the service can either be done with `quarkus:dev` +or by using the JVM based runner. + +- **Using `quarkus:dev`** + ``` + mvn quarkus:dev + ``` + +- **Using JVM runner** + ``` + java -jar target/service-999-SNAPSHOT-runner.jar + ``` + +In either case posting new model data like described earlier should result in +a successful `201` response code with the posted message in the body. For example: + +``` +~$ curl -X POST -H "Content-Type: application/json" -d '{"version": 2, "id": "123", "value": "val"}' -v localhost:8080/model +Note: Unnecessary use of -X or --request, POST is already inferred. +* Trying 127.0.0.1... +* TCP_NODELAY set +* Connected to localhost (127.0.0.1) port 8080 (#0) +> POST /model HTTP/1.1 +> Host: localhost:8080 +> User-Agent: curl/7.58.0 +> Accept: */* +> Content-Type: application/json +> Content-Length: 43 +> +* upload completely sent off: 43 out of 43 bytes +< HTTP/1.1 201 Created +< Connection: keep-alive +< Content-Type: application/json +< Content-Length: 38 +< Date: Thu, 22 Aug 2019 13:31:48 GMT +< +* Connection #0 to host localhost left intact +{"version":2,"id":"123","value":"val"} +``` + +## Package Native + +Checking proper behaviour can be achieved in the following 2 ways: + +- **Integration test** + + This scenario requires no additional manual steps besides + executing the following command: + + ``` + mvn integration-test verify -Pnative + ``` + + The application will be started automatically and test + scenario's will run. The output will indicate whether the + test ran successfully or not. + + In this scenario it is not possible to post new model data + manually. This can be achieved by using the next scenario. + +- **Native runner** + + Running the native version of the service manually like: + + ``` + mvn package -Pnative + ... + ./target/service-999-SNAPSHOT-runner + ``` + + Now the application is running new model data can be posted + like described earlier. This should result in a successful + `201` response code with the posted message in the response + body. Just like the JVM example given previously. diff --git a/integration-tests/jackson/model/pom.xml b/integration-tests/jackson/model/pom.xml new file mode 100644 index 0000000000000..8555dc6bfb9c2 --- /dev/null +++ b/integration-tests/jackson/model/pom.xml @@ -0,0 +1,108 @@ + + + + 4.0.0 + + + quarkus-integration-test-jackson-parent + io.quarkus + 999-SNAPSHOT + + + quarkus-integration-test-jackson-model + + Quarkus - Integration Tests - Jackson - model + + + 2.9.9.20190807 + + + + + + com.fasterxml.jackson + jackson-bom + ${jackson.version} + import + pom + + + + + + + io.quarkus + quarkus-core + + + + + org.slf4j + slf4j-api + + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.module + jackson-module-parameter-names + + + com.fasterxml.jackson.datatype + jackson-datatype-jdk8 + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-params + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.assertj + assertj-core + 3.12.2 + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + false + + + + + + diff --git a/integration-tests/jackson/model/src/main/java/io/quarkus/reproducer/jacksonbuilder/model/InheritedModelWithBuilder.java b/integration-tests/jackson/model/src/main/java/io/quarkus/reproducer/jacksonbuilder/model/InheritedModelWithBuilder.java new file mode 100644 index 0000000000000..a6b290536fc3b --- /dev/null +++ b/integration-tests/jackson/model/src/main/java/io/quarkus/reproducer/jacksonbuilder/model/InheritedModelWithBuilder.java @@ -0,0 +1,99 @@ +package io.quarkus.reproducer.jacksonbuilder.model; + +import java.io.IOException; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; + +/** + * Simple model class. + */ +@JsonPropertyOrder({ "version", "id", "value" }) +@JsonDeserialize(builder = InheritedModelWithBuilder.Builder.class) +public class InheritedModelWithBuilder extends InheritedModelWithBuilderBase { + + // ------------------------------------------------------------------------- + // Object attributes + // ------------------------------------------------------------------------- + + private final String value; + + // ------------------------------------------------------------------------- + // Constructors + // ------------------------------------------------------------------------- + + private InheritedModelWithBuilder(final Builder builder) { + super(builder); + this.value = builder.value; + } + + // ------------------------------------------------------------------------- + // Interface + // ------------------------------------------------------------------------- + + public String toJson() throws IOException { + String json = getObjectMapper().writeValueAsString(this); + return json; + } + + public static String toJson(final InheritedModelWithBuilder model) throws IOException { + return model.toJson(); + } + + public static InheritedModelWithBuilder fromJson(final String json) throws IOException { + return getObjectMapper().readerFor(InheritedModelWithBuilder.class).readValue(json); + } + + // ------------------------------------------------------------------------- + // Getters + // ------------------------------------------------------------------------- + + @JsonProperty(value = "value") + public String getValue() { + return value; + } + + // ------------------------------------------------------------------------- + // Inner classes + // ------------------------------------------------------------------------- + + @JsonPOJOBuilder(buildMethodName = "build", withPrefix = "with") + public static final class Builder + extends InheritedModelWithBuilderBase.Builder { + + // ------------------------------------------------------------------------- + // Object attributes + // ------------------------------------------------------------------------- + + protected String value = ""; + + // ------------------------------------------------------------------------- + // Constructor + // ------------------------------------------------------------------------- + + public Builder(@JsonProperty(value = "id", required = true) final String id) { + super(id); + } + + public Builder(final InheritedModelWithBuilder object) { + super(object); + this.value = object.value; + } + + // ------------------------------------------------------------------------- + // Builder methods + // ------------------------------------------------------------------------- + + @JsonProperty(value = "value", required = true) + public Builder withValue(final String value) { + this.value = value; + return this; + } + + public InheritedModelWithBuilder build() { + return new InheritedModelWithBuilder(this); + } + } +} diff --git a/integration-tests/jackson/model/src/main/java/io/quarkus/reproducer/jacksonbuilder/model/InheritedModelWithBuilderBase.java b/integration-tests/jackson/model/src/main/java/io/quarkus/reproducer/jacksonbuilder/model/InheritedModelWithBuilderBase.java new file mode 100644 index 0000000000000..a3eec8478ba5b --- /dev/null +++ b/integration-tests/jackson/model/src/main/java/io/quarkus/reproducer/jacksonbuilder/model/InheritedModelWithBuilderBase.java @@ -0,0 +1,115 @@ +package io.quarkus.reproducer.jacksonbuilder.model; + +import java.util.Optional; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.fasterxml.jackson.module.paramnames.ParameterNamesModule; + +/** + * Model class with inheritance and builder. + */ +abstract class InheritedModelWithBuilderBase { + + // ------------------------------------------------------------------------- + // Class attributes + // ------------------------------------------------------------------------- + + private static ObjectMapper objectMapper; + + // ------------------------------------------------------------------------- + // Object attributes + // ------------------------------------------------------------------------- + + private final int version; + private final String id; + + // ------------------------------------------------------------------------- + // Constructors + // ------------------------------------------------------------------------- + + @SuppressWarnings("unchecked") + protected InheritedModelWithBuilderBase(final Builder builder) { + this.version = (Integer) builder.version.orElse(1); + this.id = builder.id; + } + + // ------------------------------------------------------------------------- + // Getters + // ------------------------------------------------------------------------- + + @JsonProperty(value = "version") + public Integer getVersion() { + return version; + } + + @JsonProperty(value = "id") + public String getId() { + return id; + } + + // ------------------------------------------------------------------------- + // Inner classes + // ------------------------------------------------------------------------- + + abstract static class Builder { + + // ------------------------------------------------------------------------- + // Object attributes + // ------------------------------------------------------------------------- + + protected Optional version = Optional.empty(); + protected String id; + + // ------------------------------------------------------------------------- + // Constructor + // ------------------------------------------------------------------------- + + protected Builder(@JsonProperty(value = "id", required = true) final String id) { + this.id = id; + } + + protected Builder(final T object) { + this.version = Optional.of(object.getVersion()); + this.id = object.getId(); + } + + // ------------------------------------------------------------------------- + // Builder methods + // ------------------------------------------------------------------------- + + @JsonProperty(value = "version") + @SuppressWarnings("unchecked") + public B withVersion(int version) { + this.version = Optional.of(version); + return (B) this; + } + + abstract public T build(); + } + + // ------------------------------------------------------------------------- + // Private methods + // ------------------------------------------------------------------------- + + protected static ObjectMapper getObjectMapper() { + if (null == objectMapper) { + objectMapper = new ObjectMapper() + .configure(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES, false) + .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) + .configure(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS, false) + .setSerializationInclusion(JsonInclude.Include.NON_NULL) + .setSerializationInclusion(JsonInclude.Include.NON_ABSENT) + .registerModule(new ParameterNamesModule()) + .registerModule(new Jdk8Module()) + .registerModule(new JavaTimeModule()); + } + return objectMapper; + } + +} diff --git a/integration-tests/jackson/model/src/main/java/io/quarkus/reproducer/jacksonbuilder/model/ModelWithBuilder.java b/integration-tests/jackson/model/src/main/java/io/quarkus/reproducer/jacksonbuilder/model/ModelWithBuilder.java new file mode 100644 index 0000000000000..2e5fddb3bed04 --- /dev/null +++ b/integration-tests/jackson/model/src/main/java/io/quarkus/reproducer/jacksonbuilder/model/ModelWithBuilder.java @@ -0,0 +1,153 @@ +package io.quarkus.reproducer.jacksonbuilder.model; + +import java.io.IOException; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.fasterxml.jackson.module.paramnames.ParameterNamesModule; + +/** + * Simple model class. + */ +@JsonPropertyOrder({ "version", "id", "value" }) +@JsonDeserialize(builder = ModelWithBuilder.Builder.class) +public class ModelWithBuilder { + + // ------------------------------------------------------------------------- + // Class attributes + // ------------------------------------------------------------------------- + + private static ObjectMapper objectMapper; + + // ------------------------------------------------------------------------- + // Object attributes + // ------------------------------------------------------------------------- + + private final int version; + private final String id; + private final String value; + + // ------------------------------------------------------------------------- + // Constructors + // ------------------------------------------------------------------------- + + private ModelWithBuilder(final Builder builder) { + this.version = builder.version; + this.id = builder.id; + this.value = builder.value; + } + + // ------------------------------------------------------------------------- + // Interface + // ------------------------------------------------------------------------- + + public String toJson() throws IOException { + String json = getObjectMapper().writeValueAsString(this); + return json; + } + + public static String toJson(final ModelWithBuilder model) throws IOException { + return model.toJson(); + } + + public static ModelWithBuilder fromJson(final String json) throws IOException { + return getObjectMapper().readerFor(ModelWithBuilder.class).readValue(json); + } + + // ------------------------------------------------------------------------- + // Getters + // ------------------------------------------------------------------------- + + @JsonProperty(value = "version") + public Integer getVersion() { + return version; + } + + @JsonProperty(value = "id") + public String getId() { + return id; + } + + @JsonProperty(value = "value") + public String getValue() { + return value; + } + + // ------------------------------------------------------------------------- + // Inner classes + // ------------------------------------------------------------------------- + + @JsonPOJOBuilder(buildMethodName = "build", withPrefix = "with") + public static final class Builder { + + // ------------------------------------------------------------------------- + // Object attributes + // ------------------------------------------------------------------------- + + private int version = 1; + private String id; + private String value = ""; + + // ------------------------------------------------------------------------- + // Constructor + // ------------------------------------------------------------------------- + + public Builder(@JsonProperty(value = "id", required = true) final String id) { + this.id = id; + } + + public Builder(final ModelWithBuilder object) { + this.version = object.version; + this.id = object.id; + this.value = object.value; + } + + // ------------------------------------------------------------------------- + // Builder methods + // ------------------------------------------------------------------------- + + @JsonProperty(value = "version") + public Builder withVersion(final int version) { + this.version = version; + return this; + } + + @JsonProperty(value = "value", required = true) + public Builder withValue(final String value) { + this.value = value; + return this; + } + + public ModelWithBuilder build() { + return new ModelWithBuilder(this); + } + } + + // ------------------------------------------------------------------------- + // Private methods + // ------------------------------------------------------------------------- + + private static ObjectMapper getObjectMapper() { + if (null == objectMapper) { + objectMapper = new ObjectMapper() + .configure(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES, false) + .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) + .configure(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS, false) + .setSerializationInclusion(JsonInclude.Include.NON_NULL) + .setSerializationInclusion(JsonInclude.Include.NON_ABSENT) + .registerModule(new ParameterNamesModule()) + .registerModule(new Jdk8Module()) + .registerModule(new JavaTimeModule()); + } + return objectMapper; + } + +} diff --git a/integration-tests/jackson/model/src/main/java/io/quarkus/reproducer/jacksonbuilder/model/RegisteredPojoModel.java b/integration-tests/jackson/model/src/main/java/io/quarkus/reproducer/jacksonbuilder/model/RegisteredPojoModel.java new file mode 100644 index 0000000000000..1997b30a8905d --- /dev/null +++ b/integration-tests/jackson/model/src/main/java/io/quarkus/reproducer/jacksonbuilder/model/RegisteredPojoModel.java @@ -0,0 +1,106 @@ +package io.quarkus.reproducer.jacksonbuilder.model; + +import java.io.IOException; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.fasterxml.jackson.module.paramnames.ParameterNamesModule; + +import io.quarkus.runtime.annotations.RegisterForReflection; + +/** + * Simple POJO model class. + */ +@RegisterForReflection +public class RegisteredPojoModel { + + // ------------------------------------------------------------------------- + // Class attributes + // ------------------------------------------------------------------------- + + private static ObjectMapper objectMapper; + + // ------------------------------------------------------------------------- + // Object attributes + // ------------------------------------------------------------------------- + + private int version = 1; + private String id = null; + private String value = null; + + // ------------------------------------------------------------------------- + // Constructors + // ------------------------------------------------------------------------- + + public RegisteredPojoModel() { + } + + // ------------------------------------------------------------------------- + // Interface + // ------------------------------------------------------------------------- + + public String toJson() throws IOException { + String json = getObjectMapper().writeValueAsString(this); + return json; + } + + public static String toJson(final RegisteredPojoModel model) throws IOException { + return model.toJson(); + } + + public static RegisteredPojoModel fromJson(final String json) throws IOException { + return getObjectMapper().readerFor(RegisteredPojoModel.class).readValue(json); + } + + // ------------------------------------------------------------------------- + // Getters / Setters + // ------------------------------------------------------------------------- + + public int getVersion() { + return version; + } + + public void setVersion(int version) { + this.version = version; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + // ------------------------------------------------------------------------- + // Private methods + // ------------------------------------------------------------------------- + + private static ObjectMapper getObjectMapper() { + if (null == objectMapper) { + objectMapper = new ObjectMapper() + .configure(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES, false) + .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) + .configure(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS, false) + .setSerializationInclusion(JsonInclude.Include.NON_NULL) + .setSerializationInclusion(JsonInclude.Include.NON_ABSENT) + .registerModule(new ParameterNamesModule()) + .registerModule(new Jdk8Module()) + .registerModule(new JavaTimeModule()); + } + return objectMapper; + } + +} diff --git a/integration-tests/jackson/model/src/main/resources/META-INF/beans.xml b/integration-tests/jackson/model/src/main/resources/META-INF/beans.xml new file mode 100644 index 0000000000000..a89ed29a0fed7 --- /dev/null +++ b/integration-tests/jackson/model/src/main/resources/META-INF/beans.xml @@ -0,0 +1,8 @@ + + + diff --git a/integration-tests/jackson/model/src/test/java/io/quarkus/reproducer/jacksonbuilder/model/InheritedModelWithBuilderTest.java b/integration-tests/jackson/model/src/test/java/io/quarkus/reproducer/jacksonbuilder/model/InheritedModelWithBuilderTest.java new file mode 100644 index 0000000000000..53c6cc47132a1 --- /dev/null +++ b/integration-tests/jackson/model/src/test/java/io/quarkus/reproducer/jacksonbuilder/model/InheritedModelWithBuilderTest.java @@ -0,0 +1,61 @@ +package io.quarkus.reproducer.jacksonbuilder.model; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +/** + * Test cases for InheritedModelWithBuilder.Builder + */ +public class InheritedModelWithBuilderTest { + + // ------------------------------------------------------------------------- + // Test cases + // ------------------------------------------------------------------------- + + @Test + public void testBuilderMinimal() { + // prepare + InheritedModelWithBuilder.Builder builder = new InheritedModelWithBuilder.Builder("device1"); + + // execute + InheritedModelWithBuilder data = builder.build(); + + // verify + assertThat(data.getVersion()).isEqualTo(1); + assertThat(data.getId()).isEqualTo("device1"); + assertThat(data.getValue()).isEqualTo(""); + } + + @Test + public void testBuilderUsingOptionals() { + // prepare + InheritedModelWithBuilder.Builder builder = new InheritedModelWithBuilder.Builder("device1") + .withVersion(2) + .withValue("value"); + + // execute + InheritedModelWithBuilder data = builder.build(); + + // verify + assertThat(data.getVersion()).isEqualTo(2); + assertThat(data.getId()).isEqualTo("device1"); + assertThat(data.getValue()).isEqualTo("value"); + } + + @Test + public void testBuilderCloneConstructor() { + // prepare + InheritedModelWithBuilder original = new InheritedModelWithBuilder.Builder("device1") + .withValue("value") + .build(); + + // execute + InheritedModelWithBuilder clone = new InheritedModelWithBuilder.Builder(original).build(); + + // verify + assertThat(clone.getVersion()).isEqualTo(1); + assertThat(clone.getId()).isEqualTo("device1"); + assertThat(clone.getValue()).isEqualTo("value"); + } +} diff --git a/integration-tests/jackson/model/src/test/java/io/quarkus/reproducer/jacksonbuilder/model/ModelWithBuilderTest.java b/integration-tests/jackson/model/src/test/java/io/quarkus/reproducer/jacksonbuilder/model/ModelWithBuilderTest.java new file mode 100644 index 0000000000000..aae001da3a01e --- /dev/null +++ b/integration-tests/jackson/model/src/test/java/io/quarkus/reproducer/jacksonbuilder/model/ModelWithBuilderTest.java @@ -0,0 +1,62 @@ +package io.quarkus.reproducer.jacksonbuilder.model; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +/** + * Test cases for SensorData.Builder + */ +public class ModelWithBuilderTest { + + // ------------------------------------------------------------------------- + // Test cases + // ------------------------------------------------------------------------- + + @Test + public void testBuilderMinimal() { + // prepare + ModelWithBuilder.Builder builder = new ModelWithBuilder.Builder("id1"); + + // execute + ModelWithBuilder data = builder.build(); + + // verify + assertThat(data.getVersion()).isEqualTo(1); + assertThat(data.getId()).isEqualTo("id1"); + assertThat(data.getValue()).isEqualTo(""); + } + + @Test + public void testBuilder() { + // prepare + ModelWithBuilder.Builder builder = new ModelWithBuilder.Builder("id2") + .withVersion(2) + .withValue("value"); + + // execute + ModelWithBuilder data = builder.build(); + + // verify + assertThat(data.getVersion()).isEqualTo(2); + assertThat(data.getId()).isEqualTo("id2"); + assertThat(data.getValue()).isEqualTo("value"); + } + + @Test + public void testBuilderCloneConstructor() { + // prepare + ModelWithBuilder original = new ModelWithBuilder.Builder("id1") + .withVersion(3) + .withValue("val") + .build(); + + // execute + ModelWithBuilder clone = new ModelWithBuilder.Builder(original).build(); + + // verify + assertThat(clone.getVersion()).isEqualTo(3); + assertThat(clone.getId()).isEqualTo("id1"); + assertThat(clone.getValue()).isEqualTo("val"); + } +} diff --git a/integration-tests/jackson/model/src/test/java/io/quarkus/reproducer/jacksonbuilder/model/RegisteredPojoModelTest.java b/integration-tests/jackson/model/src/test/java/io/quarkus/reproducer/jacksonbuilder/model/RegisteredPojoModelTest.java new file mode 100644 index 0000000000000..b0aeceb31a8cf --- /dev/null +++ b/integration-tests/jackson/model/src/test/java/io/quarkus/reproducer/jacksonbuilder/model/RegisteredPojoModelTest.java @@ -0,0 +1,44 @@ +package io.quarkus.reproducer.jacksonbuilder.model; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +/** + * Test cases for RegisteredPojoModel + */ +public class RegisteredPojoModelTest { + + // ------------------------------------------------------------------------- + // Test cases + // ------------------------------------------------------------------------- + + @Test + public void testBuilderMinimal() { + // prepare + + // execute + RegisteredPojoModel data = new RegisteredPojoModel(); + + // verify + assertThat(data.getVersion()).isEqualTo(1); + assertThat(data.getId()).isNull(); + assertThat(data.getValue()).isNull(); + } + + @Test + public void testBuilder() { + // prepare + + // execute + RegisteredPojoModel data = new RegisteredPojoModel(); + data.setVersion(2); + data.setId("id1"); + data.setValue("value"); + + // verify + assertThat(data.getVersion()).isEqualTo(2); + assertThat(data.getId()).isEqualTo("id1"); + assertThat(data.getValue()).isEqualTo("value"); + } +} diff --git a/integration-tests/jackson/pom.xml b/integration-tests/jackson/pom.xml new file mode 100644 index 0000000000000..9ef944831065e --- /dev/null +++ b/integration-tests/jackson/pom.xml @@ -0,0 +1,33 @@ + + + 4.0.0 + + + quarkus-integration-tests-parent + io.quarkus + 999-SNAPSHOT + ../ + + + quarkus-integration-test-jackson-parent + pom + + Quarkus - Integration Tests - Jackson + + + + + io.quarkus + quarkus-integration-test-jackson-model + ${project.version} + + + + + + model + service + + diff --git a/integration-tests/jackson/service/pom.xml b/integration-tests/jackson/service/pom.xml new file mode 100644 index 0000000000000..6e59255a893bd --- /dev/null +++ b/integration-tests/jackson/service/pom.xml @@ -0,0 +1,121 @@ + + + + 4.0.0 + + + quarkus-integration-test-jackson-parent + io.quarkus + 999-SNAPSHOT + + + quarkus-integration-test-jackson-service + + Quarkus - Integration Tests - Jackson - service + + + + io.quarkus + quarkus-integration-test-jackson-model + + + io.quarkus + quarkus-jackson + + + + io.quarkus + quarkus-resteasy + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + + + + + ${project.groupId} + quarkus-maven-plugin + ${project.version} + + + + build + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + org.jboss.logmanager.LogManager + + + + + + + + + native + + + native + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + + integration-test + verify + + + + + ${project.build.directory}/${project.build.finalName}-runner + + + + + + + + ${project.groupId} + quarkus-maven-plugin + ${project.version} + + + native-image + + native-image + + + true + true + ${graalvmHome} + + + + + + + + + diff --git a/integration-tests/jackson/service/src/main/java/io/quarkus/reproducer/InheritedModelWithBuilderResource.java b/integration-tests/jackson/service/src/main/java/io/quarkus/reproducer/InheritedModelWithBuilderResource.java new file mode 100644 index 0000000000000..e36b0590762e2 --- /dev/null +++ b/integration-tests/jackson/service/src/main/java/io/quarkus/reproducer/InheritedModelWithBuilderResource.java @@ -0,0 +1,24 @@ +package io.quarkus.reproducer; + +import java.io.IOException; + +import javax.ws.rs.Consumes; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import io.quarkus.reproducer.jacksonbuilder.model.InheritedModelWithBuilder; + +@Path("/inheritedmodelwithbuilder") +public class InheritedModelWithBuilderResource { + + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response newModel(String body) throws IOException { + InheritedModelWithBuilder model = InheritedModelWithBuilder.fromJson(body); + return Response.status(201).entity(model.toJson()).build(); + } +} diff --git a/integration-tests/jackson/service/src/main/java/io/quarkus/reproducer/ModelWithBuilderResource.java b/integration-tests/jackson/service/src/main/java/io/quarkus/reproducer/ModelWithBuilderResource.java new file mode 100644 index 0000000000000..f0cc6791011ac --- /dev/null +++ b/integration-tests/jackson/service/src/main/java/io/quarkus/reproducer/ModelWithBuilderResource.java @@ -0,0 +1,24 @@ +package io.quarkus.reproducer; + +import java.io.IOException; + +import javax.ws.rs.Consumes; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import io.quarkus.reproducer.jacksonbuilder.model.ModelWithBuilder; + +@Path("/modelwithbuilder") +public class ModelWithBuilderResource { + + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response newModel(String body) throws IOException { + ModelWithBuilder model = ModelWithBuilder.fromJson(body); + return Response.status(201).entity(model.toJson()).build(); + } +} diff --git a/integration-tests/jackson/service/src/main/java/io/quarkus/reproducer/RegisteredPojoModelResource.java b/integration-tests/jackson/service/src/main/java/io/quarkus/reproducer/RegisteredPojoModelResource.java new file mode 100644 index 0000000000000..ec4922605463b --- /dev/null +++ b/integration-tests/jackson/service/src/main/java/io/quarkus/reproducer/RegisteredPojoModelResource.java @@ -0,0 +1,24 @@ +package io.quarkus.reproducer; + +import java.io.IOException; + +import javax.ws.rs.Consumes; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import io.quarkus.reproducer.jacksonbuilder.model.RegisteredPojoModel; + +@Path("/registeredpojomodel") +public class RegisteredPojoModelResource { + + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response newModel(String body) throws IOException { + RegisteredPojoModel model = RegisteredPojoModel.fromJson(body); + return Response.status(201).entity(model.toJson()).build(); + } +} diff --git a/integration-tests/jackson/service/src/main/resources/application.properties b/integration-tests/jackson/service/src/main/resources/application.properties new file mode 100644 index 0000000000000..3c1ac56a1ad0a --- /dev/null +++ b/integration-tests/jackson/service/src/main/resources/application.properties @@ -0,0 +1,2 @@ +# Configuration file +# key = value \ No newline at end of file diff --git a/integration-tests/jackson/service/src/test/java/io/quarkus/reproducer/InheritedModelWithBuilderResourceIT.java b/integration-tests/jackson/service/src/test/java/io/quarkus/reproducer/InheritedModelWithBuilderResourceIT.java new file mode 100644 index 0000000000000..d851b198d7be1 --- /dev/null +++ b/integration-tests/jackson/service/src/test/java/io/quarkus/reproducer/InheritedModelWithBuilderResourceIT.java @@ -0,0 +1,9 @@ +package io.quarkus.reproducer; + +import io.quarkus.test.junit.SubstrateTest; + +@SubstrateTest +public class InheritedModelWithBuilderResourceIT extends InheritedModelWithBuilderResourceTest { + + // Execute the same tests but in native mode. +} \ No newline at end of file diff --git a/integration-tests/jackson/service/src/test/java/io/quarkus/reproducer/InheritedModelWithBuilderResourceTest.java b/integration-tests/jackson/service/src/test/java/io/quarkus/reproducer/InheritedModelWithBuilderResourceTest.java new file mode 100644 index 0000000000000..626fb2c94c597 --- /dev/null +++ b/integration-tests/jackson/service/src/test/java/io/quarkus/reproducer/InheritedModelWithBuilderResourceTest.java @@ -0,0 +1,33 @@ +package io.quarkus.reproducer; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.equalTo; + +import java.io.IOException; + +import org.junit.jupiter.api.Test; + +import io.quarkus.reproducer.jacksonbuilder.model.InheritedModelWithBuilder; +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +public class InheritedModelWithBuilderResourceTest { + + @Test + public void testModelWithBuilder() throws IOException { + InheritedModelWithBuilder model = new InheritedModelWithBuilder.Builder("123") + .withVersion(3) + .withValue("some") + .build(); + + given() + .contentType("application/json") + .body(model.toJson()) + .when().post("/inheritedmodelwithbuilder") + .then() + .statusCode(201) + .body("id", equalTo("123")) + .body("version", equalTo(3)) + .body("value", equalTo("some")); + } +} diff --git a/integration-tests/jackson/service/src/test/java/io/quarkus/reproducer/ModelWithBuilderResourceIT.java b/integration-tests/jackson/service/src/test/java/io/quarkus/reproducer/ModelWithBuilderResourceIT.java new file mode 100644 index 0000000000000..421b26568bfa5 --- /dev/null +++ b/integration-tests/jackson/service/src/test/java/io/quarkus/reproducer/ModelWithBuilderResourceIT.java @@ -0,0 +1,9 @@ +package io.quarkus.reproducer; + +import io.quarkus.test.junit.SubstrateTest; + +@SubstrateTest +public class ModelWithBuilderResourceIT extends ModelWithBuilderResourceTest { + + // Execute the same tests but in native mode. +} \ No newline at end of file diff --git a/integration-tests/jackson/service/src/test/java/io/quarkus/reproducer/ModelWithBuilderResourceTest.java b/integration-tests/jackson/service/src/test/java/io/quarkus/reproducer/ModelWithBuilderResourceTest.java new file mode 100644 index 0000000000000..6aa87b373a518 --- /dev/null +++ b/integration-tests/jackson/service/src/test/java/io/quarkus/reproducer/ModelWithBuilderResourceTest.java @@ -0,0 +1,33 @@ +package io.quarkus.reproducer; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.equalTo; + +import java.io.IOException; + +import org.junit.jupiter.api.Test; + +import io.quarkus.reproducer.jacksonbuilder.model.ModelWithBuilder; +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +public class ModelWithBuilderResourceTest { + + @Test + public void testModelWithBuilder() throws IOException { + ModelWithBuilder model = new ModelWithBuilder.Builder("123") + .withVersion(3) + .withValue("some") + .build(); + + given() + .contentType("application/json") + .body(model.toJson()) + .when().post("/modelwithbuilder") + .then() + .statusCode(201) + .body("id", equalTo("123")) + .body("version", equalTo(3)) + .body("value", equalTo("some")); + } +} diff --git a/integration-tests/jackson/service/src/test/java/io/quarkus/reproducer/RegisteredPojoModelResourceIT.java b/integration-tests/jackson/service/src/test/java/io/quarkus/reproducer/RegisteredPojoModelResourceIT.java new file mode 100644 index 0000000000000..adeab4f4304bb --- /dev/null +++ b/integration-tests/jackson/service/src/test/java/io/quarkus/reproducer/RegisteredPojoModelResourceIT.java @@ -0,0 +1,9 @@ +package io.quarkus.reproducer; + +import io.quarkus.test.junit.SubstrateTest; + +@SubstrateTest +public class RegisteredPojoModelResourceIT extends RegisteredPojoModelResourceTest { + + // Execute the same tests but in native mode. +} \ No newline at end of file diff --git a/integration-tests/jackson/service/src/test/java/io/quarkus/reproducer/RegisteredPojoModelResourceTest.java b/integration-tests/jackson/service/src/test/java/io/quarkus/reproducer/RegisteredPojoModelResourceTest.java new file mode 100644 index 0000000000000..63f8d65bcabe3 --- /dev/null +++ b/integration-tests/jackson/service/src/test/java/io/quarkus/reproducer/RegisteredPojoModelResourceTest.java @@ -0,0 +1,33 @@ +package io.quarkus.reproducer; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.equalTo; + +import java.io.IOException; + +import org.junit.jupiter.api.Test; + +import io.quarkus.reproducer.jacksonbuilder.model.RegisteredPojoModel; +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +public class RegisteredPojoModelResourceTest { + + @Test + public void testSimplePojoModel() throws IOException { + RegisteredPojoModel model = new RegisteredPojoModel(); + model.setId("123"); + model.setVersion(3); + model.setValue("some"); + + given() + .contentType("application/json") + .body(model.toJson()) + .when().post("/registeredpojomodel") + .then() + .statusCode(201) + .body("id", equalTo("123")) + .body("version", equalTo(3)) + .body("value", equalTo("some")); + } +} diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 33f0586dad48f..cbff0d1ea8dba 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -53,6 +53,7 @@ tika neo4j mongodb-client + jackson resteasy-jackson jgit virtual-http